#include "core.h" #include "programloader.h" #define DM_SUPPORTED (1L<<0) #define DM_MEM2REG (1L<<1) #define DM_MEMWRITE (1L<<2) #define DM_ALUSRC (1L<<3) #define DM_REGD (1L<<4) #define DM_REGWRITE (1L<<5) #define DM_BRANCH (1L<<6) struct DecodeMap { long flags; enum AluOp alu; }; // This is temporally operation place holder #define NOPE { .flags = 0, .alu = ALU_OP_SLL } // This is map from opcode to signals. static const struct DecodeMap dmap[] = { { .flags = DM_SUPPORTED | DM_REGD | DM_REGWRITE, .alu = ALU_OP_SLL }, // Alu operations NOPE, // Branch on alu operations NOPE, // J NOPE, // JAL NOPE, // BEQ NOPE, // BNE NOPE, // BLEZ NOPE, // BGTZ { .flags = DM_SUPPORTED | DM_ALUSRC | DM_REGWRITE, .alu = ALU_OP_ADD }, // ADDI { .flags = DM_SUPPORTED | DM_ALUSRC | DM_REGWRITE, .alu = ALU_OP_ADDU }, // ADDIU { .flags = DM_SUPPORTED | DM_ALUSRC | DM_REGWRITE, .alu = ALU_OP_SLT }, // SLTI { .flags = DM_SUPPORTED | DM_ALUSRC | DM_REGWRITE, .alu = ALU_OP_SLTU }, // SLTIU NOPE, // ANDI NOPE, // ORI NOPE, // XORI NOPE, // LUI NOPE, // 16 NOPE, // 17 NOPE, // 18 NOPE, // 19 NOPE, // 20 NOPE, // 21 NOPE, // 22 NOPE, // 23 NOPE, // 24 NOPE, // 25 NOPE, // 26 NOPE, // 27 NOPE, // 28 NOPE, // 29 NOPE, // 30 NOPE, // 31 NOPE, // LB NOPE, // LH NOPE, // LWL NOPE, // LW NOPE, // LBU NOPE, // LHU NOPE, // LWR NOPE, // 39 NOPE, // SB NOPE, // SH NOPE, // SWL NOPE, // SW NOPE, // 44 NOPE, // 45 NOPE, // SWR NOPE, // 47 NOPE, // 48 NOPE, // 49 NOPE, // 50 NOPE, // 51 NOPE, // 52 NOPE, // 53 NOPE, // 54 NOPE, // 55 NOPE, // 56 NOPE, // 57 NOPE, // 58 NOPE, // 59 NOPE, // 60 NOPE, // 61 NOPE, // 62 NOPE // 63 }; Core::Core(Registers *regs, MemoryAccess *mem) { this->regs = regs; this->mem = mem; } struct Core::dtFetch Core::fetch() { // TODO signals Instruction inst(mem->read_word(regs->read_pc())); regs->pc_inc(); return { .inst = inst }; } struct Core::dtDecode Core::decode(struct dtFetch dt) { struct DecodeMap dec = dmap[dt.inst.opcode()]; if (!dec.flags & DM_SUPPORTED) // TODO message throw QTMIPS_EXCEPTION(UnsupportedInstruction, "", ""); return { .inst = dt.inst, .mem2reg = dec.flags & DM_MEM2REG, .memwrite = dec.flags & DM_MEMWRITE, .alusrc = dec.flags & DM_ALUSRC, .regd = dec.flags & DM_REGD, .regwrite = dec.flags & DM_REGWRITE, .branch = dec.flags & DM_BRANCH, .aluop = dt.inst.opcode() == 0 ? (enum AluOp)dt.inst.funct() : dec.alu, .val_rs = regs->read_gp(dt.inst.rs()), .val_rt = regs->read_gp(dt.inst.rt()), }; // TODO on jump there should be delay slot. Does processor addes it or compiler. And do we care? } struct Core::dtExecute Core::execute(struct dtDecode dt) { // TODO signals return { .regwrite = dt.regwrite, .rwrite = dt.regd ? dt.inst.rd() : dt.inst.rt(), .alu_val = alu_operate(dt.aluop, dt.val_rs, dt.alusrc ? dt.inst.immediate() : dt.val_rt, dt.inst.shamt()), }; } struct Core::dtMemory Core::memory(struct dtExecute dt) { // TODO signals return { .regwrite = dt.regwrite, .rwrite = dt.rwrite, .alu_val = dt.alu_val, }; } void Core::writeback(struct dtMemory dt) { if (dt.regwrite) { regs->write_gp(dt.rwrite, dt.alu_val); } } CoreSingle::CoreSingle(Registers *regs, MemoryAccess *mem) : \ Core(regs, mem) { // Nothing to do } void CoreSingle::step() { struct dtFetch f = fetch(); struct dtDecode d = decode(f); struct dtExecute e = execute(d); struct dtMemory m = memory(e); writeback(m); } CorePipelined::CorePipelined(Registers *regs, MemoryAccess *mem) : \ Core(regs, mem) { // Nothing to do } void CorePipelined::step() { // TODO implement pipelined struct dtFetch f = fetch(); struct dtDecode d = decode(f); struct dtExecute e = execute(d); struct dtMemory m =memory(e); writeback(m); }