diff options
Diffstat (limited to 'qtmips_machine')
35 files changed, 872 insertions, 740 deletions
diff --git a/qtmips_machine/alu.cpp b/qtmips_machine/alu.cpp index 5c74a5d..85bc804 100644 --- a/qtmips_machine/alu.cpp +++ b/qtmips_machine/alu.cpp @@ -1,6 +1,53 @@ #include "alu.h" +#include "qtmipsexception.h" -Alu::Alu() -{ +std::uint32_t alu_operate(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa) { + switch(operation) { + case ALU_OP_SLL: + return t << sa; + case ALU_OP_SRL: + return t >> sa; + case ALU_OP_SRA: + // TODO is this correct implementation? (Shouldn't we be masking top most bit?) + return (t >> sa) | (t & 0x80000000); + case ALU_OP_SLLV: + return t << s; + case ALU_OP_SRLV: + return t >> s; + case ALU_OP_SRAV: + // TODO is this correct implementation? (Shouldn't we be masking top most bit?) + return (t >> s) | (t & 0x80000000); + case ALU_OP_ADD: + if (s > (0xFFFFFFFF - t)) + throw QTMIPS_EXCEPTION(Overflow, "ADD operation overflow/underflow", QString::number(s) + QString(" + ") + QString::number(t)); + // Intentional falltrough + case ALU_OP_ADDU: + return s + t; + case ALU_OP_SUB: + if (s < t) + throw QTMIPS_EXCEPTION(Overflow, "SUB operation overflow/underflow", QString::number(s) + QString(" - ") + QString::number(t)); + // Intentional falltrough + case ALU_OP_SUBU: + return s - t; + case ALU_OP_AND: + return s & t; + case ALU_OP_OR: + return s | t; + case ALU_OP_XOR: + return s ^ t; + case ALU_OP_NOR: + return ~(s | t); + case ALU_OP_SLTU: + // TODO is this correct implementation? (this is two's complement signed representation so do we care?) + // Intentional falltrough + case ALU_OP_SLT: + return (s < t) ? 1 : 0; + default: + throw QTMIPS_EXCEPTION(UnsupportedAluOperation, "Unknown ALU operation", QString::number(operation, 16)); + } +} +QString alu_str(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa) { + // TODO + return QString(""); } diff --git a/qtmips_machine/alu.h b/qtmips_machine/alu.h index e913b0c..974b462 100644 --- a/qtmips_machine/alu.h +++ b/qtmips_machine/alu.h @@ -1,11 +1,39 @@ #ifndef ALU_H #define ALU_H +#include <cstdint> +#include <QString> -class Alu -{ -public: - Alu(); +// TODO Any other operations? We seems to be missing a lot of them. +enum AluOp : std::uint8_t { + ALU_OP_SLL = 0, + ALU_OP_SRL = 2, + ALU_OP_SRA, + ALU_OP_SLLV, + ALU_OP_SRLV = 6, + ALU_OP_SRAV, + ALU_OP_ADD = 32, + ALU_OP_ADDU, + ALU_OP_SUB, + ALU_OP_SUBU, + ALU_OP_AND, + ALU_OP_OR, + ALU_OP_XOR, + ALU_OP_NOR, + ALU_OP_SLT = 42, + ALU_OP_SLTU, + ALU_OP_LAST = 64 // First impossible operation (just to be sure that we don't overflow) }; -#endif // ALU_H
\ No newline at end of file +// Do ALU operation. +// operation: This is function field from instruction or shifted opcode for immediate instructions +// s: Loaded from rs. Also calles as source. +// t: Loaded from rt or immediate field from instruction it self. Also called as target. +// sa: This is value directly from instruction it self (sa section) used for shift operations +// Returned value is commonly saved to rt/rd or any other way passed trough core +std::uint32_t alu_operate(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa); + +// Returns string representation of ALU instruction (internally used by Instruction::to_str) +QString alu_str(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa); + +#endif // ALU_H diff --git a/qtmips_machine/core.cpp b/qtmips_machine/core.cpp index a6c92b5..1edcc97 100644 --- a/qtmips_machine/core.cpp +++ b/qtmips_machine/core.cpp @@ -1,5 +1,164 @@ #include "core.h" +#include "programloader.h" -Core::Core() { + struct DecodeMap { + bool supported, mem2reg, memwrite, alubimm, regd, regwrite, branch; +}; + +// This is temporally operation place holder +#define NOPE { .supported = false } + +// This is map from opcode to signals. +static const struct DecodeMap dmap[] = { + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = false, .regwrite = true, .branch = false }, // Alu operations and more + // TODO These are just copies of first one + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = false, .regwrite = false, .branch = true }, // Branch on alu operations + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // J + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // JAL + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BEQ + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BNE + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BLEZ + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BGTZ + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ADDI + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ADDIU + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SLTI + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SLTIU + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ANDI + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ORI + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // XORI + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // 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 + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LB + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LH + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LWL + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LW + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LBU + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LHU + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LWR + NOPE, // 39 + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SB + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SH + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SWL + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SW + NOPE, // 44 + NOPE, // 45 + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // 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.supported) + // TODO message + throw QTMIPS_EXCEPTION(UnsupportedInstruction, "", ""); + enum AluOp d_alu = ALU_OP_SLL; // TODO decode for real + return { + .mem2reg = dec.mem2reg, + .memwrite = dec.memwrite, + .alubimm = dec.alubimm, + .regd = dec.regd, + .regwrite = dec.regwrite, + .branch = dec.branch, + .aluop = d_alu, + .val_rs = regs->read_gp(dt.inst.rs()), + .val_rt = regs->read_gp(dt.inst.rt()), + .val_sa = dt.inst.shamt(), + .val_immediate = dt.inst.immediate(), + }; + // 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 { + .mem2reg = dt.mem2reg, + .val = alu_operate(dt.aluop, dt.val_rs, dt.val_rt, dt.val_sa) + }; +} + +struct Core::dtMemory Core::memory(struct dtExecute dt) { + // TODO signals + return { + .mem2reg = dt.mem2reg, + .val = dt.val, + }; +} + +void Core::writeback(struct dtMemory dt) { + if (dt.mem2reg) { + + } +} + +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); } diff --git a/qtmips_machine/core.h b/qtmips_machine/core.h index 2fd0a40..d4523c3 100644 --- a/qtmips_machine/core.h +++ b/qtmips_machine/core.h @@ -2,20 +2,72 @@ #define CORE_H #include <QObject> -#include "instruction.h" +#include "qtmipsexception.h" #include "registers.h" #include "memory.h" -#include "programloader.h" -#include "programmemory.h" +#include "instruction.h" +#include "alu.h" class Core : public QObject { Q_OBJECT public: - Core(); + Core(Registers *regs, MemoryAccess *mem); + + virtual void step() = 0; // Do single step signals: -public slots: +protected: + Registers *regs; + MemoryAccess *mem; + + struct dtFetch { + Instruction inst; // Loaded instruction + }; + struct dtDecode { + bool mem2reg; // Write memory output to register (instead alu output) + bool memwrite; // If memory should write input + bool alubimm; // If b value to alu is immediate value (rt used otherwise) + bool regd; // If rd is used (otherwise rt is used for write target) + bool regwrite; // If output should be written back to register (which one depends on regd) + bool branch; // If this is branch instruction + enum AluOp aluop; // Decoded ALU operation + std::uint32_t val_rs; // Value from register rs + std::uint32_t val_rt; // Value from register rt + std::uint8_t val_sa; // Value of sa in instruction it self + std::uint16_t val_immediate; // Value of immediate in instruction it self + }; + struct dtExecute { + bool mem2reg; + std::uint32_t val; + // TODO + }; + struct dtMemory { + bool mem2reg; + // TODO + std::uint32_t val; + }; + + struct dtFetch fetch(); + struct dtDecode decode(struct dtFetch); + struct dtExecute execute(struct dtDecode); + struct dtMemory memory(struct dtExecute); + void writeback(struct dtMemory); + +}; + +class CoreSingle : public Core { +public: + CoreSingle(Registers *regs, MemoryAccess *mem); + + void step(); +}; + +class CorePipelined : public Core { +public: + CorePipelined(Registers *regs, MemoryAccess *mem); + + void step(); }; #endif // CORE_H diff --git a/qtmips_machine/instruction.cpp b/qtmips_machine/instruction.cpp index 1771afb..8591d93 100644 --- a/qtmips_machine/instruction.cpp +++ b/qtmips_machine/instruction.cpp @@ -1,102 +1,106 @@ #include "instruction.h" #include "qtmipsexception.h" -Instruction::Instruction() { - this->st = IS_FETCH; +struct InstructionMap { + const char *name; +}; + +#define IM_UNKNOWN {"UNKNOWN"} +const struct InstructionMap instruction_map[] = { + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + {"ADDI"}, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN +}; + +Instruction::Instruction(std::uint32_t inst) { + this->dt = inst; +} + +Instruction::Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd, std::uint8_t shamt, std::uint8_t funct) { + this->dt = 0; + this->dt |= opcode << 26; + this->dt |= rs << 21; + this->dt |= rt << 16; + this->dt |= rd << 11; + this->dt |= shamt << 6; + this->dt |= funct; } -void Instruction::decode(Registers *regs) { - if (this->st != IS_FETCH) - // TODO other exception - throw std::exception(); - this->st = IS_DECODE; +Instruction::Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate) { + this->dt = 0; + this->dt |= opcode << 26; + this->dt |= rs << 21; + this->dt |= rt << 16; + this->dt |= immediate; } -void Instruction::execute() { - if (this->st != IS_DECODE) - // TODO other exception - throw std::exception(); - this->st = IS_EXECUTE; +Instruction::Instruction(std::uint8_t opcode, std::uint32_t address) { + this->dt = 0; + this->dt |= opcode << 26; + this->dt |= address; } -void Instruction::memory(Memory *mem) { - if (this->st != IS_EXECUTE) - // TODO other exception - throw std::exception(); - this->st = IS_MEMORY; +QString Instruction::to_str() { + if (this->opcode() >= sizeof(instruction_map)) + return QString("UNKNOWN"); + return QString(instruction_map[this->opcode()].name); } -void Instruction::write_back(Registers *regs) { - if (this->st != IS_MEMORY) - // TODO other exception - throw std::exception(); - this->st = IS_WRITE_BACK; +#define MASK(LEN,OFF) ((this->dt >> (OFF)) & ((1 << (LEN)) - 1)) + +std::uint8_t Instruction::opcode() const { + return (std::uint8_t) MASK(6, 26); } -enum InstructionState Instruction::state() { - return this->st; +std::uint8_t Instruction::rs() const { + return (std::uint8_t) MASK(5, 21); } -bool Instruction::running() { - return this->st > IS_FETCH && this->st < IS_WRITE_BACK; +std::uint8_t Instruction::rt() const { + return (std::uint8_t) MASK(5, 16); } -bool Instruction::done() { - return this->st >= IS_WRITE_BACK; +std::uint8_t Instruction::rd() const { + return (std::uint8_t) MASK(5, 11); } -InstructionR::InstructionR(std::uint8_t rs, std::uint8_t rd, std::uint8_t rt, std::uint8_t sa) : Instruction() { - this->rs = rs; - this->rd = rd; - this->rt = rt; - this->sa = sa; +std::uint8_t Instruction::shamt() const { + return (std::uint8_t) MASK(5, 6); + } -// TODO for registers output as register ($0)! - -QVector<QString> InstructionR::to_strs() { - QVector<QString> str; - // Instruction name - str << "unknown"; // unknown instruction, should be replaced by child - // Source register - str << QString::number((unsigned)this->rs, 10); - // Target register - str << QString::number((unsigned)this->rt, 10); - // Destination register - str << QString::number((unsigned)this->rd, 10); - // Shift amount - str << QString::number((unsigned)this->sa, 10); - return str; +std::uint8_t Instruction::funct() const { + return (std::uint8_t) MASK(6, 0); } -InstructionI::InstructionI(std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate) : Instruction() { - this->rs = rs; - this->rt = rt; - this->immediate = immediate; +std::uint16_t Instruction::immediate() const { + return (std::uint16_t) MASK(16, 0); } -QVector<QString> InstructionI::to_strs() { - QVector<QString> str; - // Instruction name - str << "unknown"; // unknown instruction, should be replaced by child - // Source register - str << QString::number((unsigned)this->rs, 10); - // Target register - str << QString::number((unsigned)this->rt, 10); - // Immediate value - str << QString::number((unsigned)this->immediate, 16); - return str; +std::uint32_t Instruction::address() const { + return (std::uint32_t) MASK(26, 0); } -InstructionJ::InstructionJ(std::uint32_t address) : Instruction() { - this->address = address; +std::uint32_t Instruction::data() const { + return this->dt; } -QVector<QString> InstructionJ::to_strs() { - QVector<QString> str; - // Instruction name - str << "unknown"; // unknown instruction, should be replaced by child - // Source register - str << QString::number((unsigned)this->address, 16); - return str; +bool Instruction::operator ==(const Instruction &c) const { + return (this->data() == c.data()); } diff --git a/qtmips_machine/instruction.h b/qtmips_machine/instruction.h index 8c5ede9..3b76fba 100644 --- a/qtmips_machine/instruction.h +++ b/qtmips_machine/instruction.h @@ -2,64 +2,30 @@ #define INSTRUCTION_H #include <qstring.h> -#include <qvector.h> -#include "registers.h" -#include "memory.h" - -enum InstructionState { - IS_FETCH, - IS_DECODE, - IS_EXECUTE, - IS_MEMORY, - IS_WRITE_BACK, -}; class Instruction { public: - Instruction(); + Instruction(std::uint32_t inst); + Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd, std::uint8_t shamt, std::uint8_t funct); // Type R + Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate); // Type I + Instruction(std::uint8_t opcode, std::uint32_t address); // Type J - // TODO return some info for forwarding, stall, flush - virtual void decode(Registers *regs); // Read and prepare instructions - virtual void execute(); // ALU operations - virtual void memory(Memory *mem); // Read or write to memory - virtual void write_back(Registers *regs); // Write results to registers + QString to_str(); - enum InstructionState state(); - bool running(); - bool done(); + std::uint8_t opcode() const; + std::uint8_t rs() const; + std::uint8_t rt() const; + std::uint8_t rd() const; + std::uint8_t shamt() const; + std::uint8_t funct() const; + std::uint16_t immediate() const; + std::uint32_t address() const; + std::uint32_t data() const; - virtual QVector<QString> to_strs() = 0; // Returns all fields of instructions in string + bool operator ==(const Instruction &c) const; private: - enum InstructionState st; -}; - -class InstructionR : public Instruction { -public: - InstructionR(std::uint8_t rs, std::uint8_t rd, std::uint8_t rt, std::uint8_t sa); - - QVector<QString> to_strs(); -protected: - std::uint8_t rs, rd, rt, sa; -}; - -class InstructionI : public Instruction { -public: - InstructionI(std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate); - - QVector<QString> to_strs(); -protected: - std::uint8_t rs, rt; - std::uint16_t immediate; -}; - -class InstructionJ : public Instruction { -public: - InstructionJ(std::uint32_t address); - - QVector<QString> to_strs(); -protected: - std::uint32_t address; + std::uint32_t dt; }; #endif // INSTRUCTION_H diff --git a/qtmips_machine/instructions/arithmetic.cpp b/qtmips_machine/instructions/arithmetic.cpp deleted file mode 100644 index f3cad86..0000000 --- a/qtmips_machine/instructions/arithmetic.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "instructions/arithmetic.h" - -InstructionArithmetic::InstructionArithmetic(enum InstructionArithmeticT type, std::uint8_t rs, std::uint8_t rd, std::uint8_t rt) - : InstructionR(rs, rd, rt, 0) { - this->type = type; -} - -void InstructionArithmetic::decode(Registers *regs) { - Instruction::decode(regs); - this->rs_d = regs->read_gp(this->rs); - this->rd_d = regs->read_gp(this->rd); -} - -void InstructionArithmetic::execute() { - Instruction::execute(); - switch (this->type) { - case IAT_ADD: - this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d + (std::int32_t)this->rd_d); - break; - case IAT_ADDU: - this->rt_d = this->rs_d + this->rd_d; - break; - case IAT_SUB: - this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d - (std::int32_t)this->rd_d); - break; - case IAT_SUBU: - this->rt_d = this->rs_d - this->rd_d; - break; - case IAT_AND: - this->rt_d = this->rs_d & this->rd_d; - break; - case IAT_OR: - this->rt_d = this->rs_d | this->rd_d; - break; - case IAT_XOR: - this->rt_d = this->rs_d ^ this->rd_d; - break; - case IAT_NOR: - // TODO - break; - case IAT_SLT: - // TODO - break; - case IAT_SLTU: - // TODO - break; - } -} - -void InstructionArithmetic::memory(Memory *mem) { - Instruction::memory(mem); - // pass -} - -void InstructionArithmetic::write_back(Registers *regs) { - Instruction::write_back(regs); - regs->write_gp(this->rt, this->rt_d); -} - -QVector<QString> InstructionArithmetic::to_strs() { - QVector<QString> str = this->InstructionR::to_strs(); - str.erase(str.begin() + 4); // Drop sa field - switch (this->type) { - case IAT_ADD: - str[0] = "add"; - break; - case IAT_ADDU: - str[0] = "addu"; - break; - case IAT_SUB: - str[0] = "sub"; - break; - case IAT_SUBU: - str[0] = "subu"; - break; - case IAT_AND: - str[0] = "and"; - break; - case IAT_OR: - str[0] = "or"; - break; - case IAT_XOR: - str[0] = "xor"; - break; - case IAT_NOR: - str[0] = "nor"; - break; - case IAT_SLT: - str[0] = "slt"; - break; - case IAT_SLTU: - str[0] = "sltu"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} - -InstructionArithmeticImmediate::InstructionArithmeticImmediate(enum InstructionArithmeticImmediateT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t value) - : InstructionI(rs, rt, value) { - this->type = type; -} - -void InstructionArithmeticImmediate::decode(Registers *regs) { - Instruction::decode(regs); - this->rs_d = regs->read_gp(this->rs); -} - -void InstructionArithmeticImmediate::execute() { - Instruction::execute(); - switch (this->type) { - case IAT_ADDI: - this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d + (std::int32_t)this->immediate); - break; - case IAT_ADDIU: - this->rt_d = this->rs_d + this->immediate; - break; - case IAT_ANDI: - this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d - (std::int32_t)this->immediate); - break; - case IAT_ORI: - this->rt_d = this->rs_d - this->immediate; - break; - case IAT_XORI: - this->rt_d = this->rs_d & this->immediate; - break; - } -} - -void InstructionArithmeticImmediate::memory(Memory *mem) { - Instruction::memory(mem); - // pass -} - -void InstructionArithmeticImmediate::write_back(Registers *regs) { - Instruction::write_back(regs); - regs->write_gp(this->rt, this->rt_d); -} - -QVector<QString> InstructionArithmeticImmediate::to_strs() { - QVector<QString> str = this->InstructionI::to_strs(); - switch (this->type) { - case IAT_ADDI: - str[0] = "addi"; - break; - case IAT_ADDIU: - str[0] = "addiu"; - break; - case IAT_ANDI: - str[0] = "andi"; - break; - case IAT_ORI: - str[0] = "ori"; - break; - case IAT_XORI: - str[0] = "xori"; - break; - case IAT_SLTI: - str[0] = "slti"; - break; - case IAT_SLTIU: - str[0] = "sltiu"; - break; - case IAT_LUI: - str[0] = "lui"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} diff --git a/qtmips_machine/instructions/arithmetic.h b/qtmips_machine/instructions/arithmetic.h deleted file mode 100644 index 185ed95..0000000 --- a/qtmips_machine/instructions/arithmetic.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef ARITHMETIC_H -#define ARITHMETIC_H - -#include "instruction.h" - -enum InstructionArithmeticT { - IAT_ADD, // Add - IAT_ADDU, // Add unsigned - IAT_SUB, // Subtract - IAT_SUBU, // Subtract unsigned - IAT_AND, - IAT_OR, - IAT_XOR, - IAT_NOR, - IAT_SLT, // set on less than - IAT_SLTU, // set on less than unsigned -}; - -class InstructionArithmetic : public InstructionR { -public: - InstructionArithmetic(enum InstructionArithmeticT type, std::uint8_t rs, std::uint8_t rd, std::uint8_t rt); - - void decode(Registers *regs); - void execute(); - void memory(Memory *mem); - void write_back(Registers *regs); - - QVector<QString> to_strs(); -private: - enum InstructionArithmeticT type; - std::uint32_t rs_d, rd_d, rt_d; -}; - -enum InstructionArithmeticImmediateT { - IAT_ADDI, - IAT_ADDIU, - IAT_ANDI, - IAT_ORI, - IAT_XORI, - IAT_SLTI, - IAT_SLTIU, - IAT_LUI -}; - -class InstructionArithmeticImmediate : public InstructionI { -public: - InstructionArithmeticImmediate(enum InstructionArithmeticImmediateT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t value); - - void decode(Registers *regs); - void execute(); - void memory(Memory *mem); - void write_back(Registers *regs); - - QVector<QString> to_strs(); -private: - enum InstructionArithmeticImmediateT type; - std::uint32_t rs_d, rt_d; -}; - -#endif // ARITHMETIC_H diff --git a/qtmips_machine/instructions/jumpbranch.cpp b/qtmips_machine/instructions/jumpbranch.cpp deleted file mode 100644 index 6579c2b..0000000 --- a/qtmips_machine/instructions/jumpbranch.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "jumpbranch.h" - -InstructionJump::InstructionJump(bool link, std::uint32_t address) - : InstructionJ(address) { - this->link = link; -} - -QVector<QString> InstructionJump::to_strs() { - QVector<QString> str = this->InstructionJ::to_strs(); - if (link) - str[0] = "j"; - else - str[0] = "jal"; - return str; -} - -InstructionJumpRegister::InstructionJumpRegister(bool link, std::uint8_t rs) - : InstructionR(rs, 0, 0, 0) { - this->link = link; -} - -QVector<QString> InstructionJumpRegister::to_strs() { - QVector<QString> str = this->InstructionR::to_strs(); - str.erase(str.begin() + 2, str.end()); // Drop every field after rs - if (link) - str[0] = "j"; - else - str[0] = "jal"; - return str; -} diff --git a/qtmips_machine/instructions/jumpbranch.h b/qtmips_machine/instructions/jumpbranch.h deleted file mode 100644 index 762ad95..0000000 --- a/qtmips_machine/instructions/jumpbranch.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef JUMPBRANCH_H -#define JUMPBRANCH_H - -#include "instruction.h" - -class InstructionJump : InstructionJ { -public: - InstructionJump(bool link, std::uint32_t address); - QVector<QString> to_strs(); -private: - bool link; -}; - -class InstructionJumpRegister : InstructionR { -public: - InstructionJumpRegister(bool link, std::uint8_t rs); - QVector<QString> to_strs(); -private: - bool link; -}; - -enum InstructionBranchT { - -}; - -class InstructionBranch : InstructionI { -public: - InstructionBranch(); - QVector<QString> to_strs(); -private: - // TODO -}; - -#endif // JUMPBRANCH_H diff --git a/qtmips_machine/instructions/loadstore.cpp b/qtmips_machine/instructions/loadstore.cpp deleted file mode 100644 index 27c6402..0000000 --- a/qtmips_machine/instructions/loadstore.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "loadstore.h" - -InstructionLoad::InstructionLoad(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset) - : InstructionI(rs, rt, offset) { - this->type = type; -} - -QVector<QString> InstructionLoad::to_strs() { - QVector<QString> str = this->InstructionI::to_strs(); - switch (this->type) { - case ILST_B: - str[0] = "lb"; - break; - case ILST_HW: - str[0] = "lh"; - break; - case ILST_WL: - str[0] = "lwl"; - break; - case ILST_W: - str[0] = "lw"; - break; - case ILST_BU: - str[0] = "lbu"; - break; - case ILST_HU: - str[0] = "lhu"; - break; - case ILST_WR: - str[0] = "lwr"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} - -InstructionStore::InstructionStore(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset) - : InstructionI(rs, rt, offset) { - this->type = type; -} - -QVector<QString> InstructionStore::to_strs() { - QVector<QString> str = this->InstructionI::to_strs(); - switch (this->type) { - case ILST_B: - str[0] = "sb"; - break; - case ILST_HW: - str[0] = "sh"; - break; - case ILST_WL: - str[0] = "swl"; - break; - case ILST_W: - str[0] = "sw"; - break; - case ILST_WR: - str[0] = "swr"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} diff --git a/qtmips_machine/instructions/loadstore.h b/qtmips_machine/instructions/loadstore.h deleted file mode 100644 index 6f028fd..0000000 --- a/qtmips_machine/instructions/loadstore.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef LOADSTORE_H -#define LOADSTORE_H - -#include "instruction.h" - -enum InstructionLoadStoreT { - ILST_B, // Byte - ILST_HW, // Half word - ILST_WL, // Word left - ILST_W, // Word - ILST_BU, // Byte unsigned - ILST_HU, // Half word unsigned - ILST_WR // Word right -}; - -class InstructionLoad : public InstructionI { -public: - InstructionLoad(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset); - QVector<QString> to_strs(); -private: - enum InstructionLoadStoreT type; -}; - -class InstructionStore : public InstructionI { -public: - InstructionStore(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset); - QVector<QString> to_strs(); -private: - enum InstructionLoadStoreT type; -}; - -#endif // LOADSTORE_H diff --git a/qtmips_machine/instructions/nop.cpp b/qtmips_machine/instructions/nop.cpp deleted file mode 100644 index 7623dff..0000000 --- a/qtmips_machine/instructions/nop.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "nop.h" - -QVector<QString> InstructionNop::to_strs() { - QVector<QString> str; - str << QString("nop"); - return str; -} diff --git a/qtmips_machine/instructions/nop.h b/qtmips_machine/instructions/nop.h deleted file mode 100644 index 5c019fd..0000000 --- a/qtmips_machine/instructions/nop.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef NOP_H -#define NOP_H - -#include "instruction.h" - -class InstructionNop : public Instruction { -public: - QVector<QString> to_strs(); -}; - -#endif // NOP_H diff --git a/qtmips_machine/instructions/shift.cpp b/qtmips_machine/instructions/shift.cpp deleted file mode 100644 index 34bc1c9..0000000 --- a/qtmips_machine/instructions/shift.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "shift.h" - -InstructionShift::InstructionShift(enum InstructionShiftT type, std::uint8_t rt, std::uint8_t rd, std::uint8_t sa) - : InstructionR(0, rt, rd, sa) { - this->type = type; -} - -QVector<QString> InstructionShift::to_strs() { - QVector<QString> str = this->InstructionR::to_strs(); - str.erase(str.begin() + 1); // Drop rs field - switch (this->type) { - case IST_LL: - str[0] = "sll"; - break; - case IST_RL: - str[0] = "srl"; - break; - case IST_RA: - str[0] = "sra"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} - -InstructionShiftVariable::InstructionShiftVariable(enum InstructionShiftT type, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd) - : InstructionR(rs, rt, rd, 0) { - this->type = type; -} - -QVector<QString> InstructionShiftVariable::to_strs() { - QVector<QString> str = this->InstructionR::to_strs(); - str.erase(str.begin() + 4); // Drop sa field - switch (this->type) { - case IST_LL: - str[0] = "sllv"; - break; - case IST_RL: - str[0] = "srlv"; - break; - case IST_RA: - str[0] = "srav"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} diff --git a/qtmips_machine/instructions/shift.h b/qtmips_machine/instructions/shift.h deleted file mode 100644 index 69e2e1e..0000000 --- a/qtmips_machine/instructions/shift.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SHIFT_H -#define SHIFT_H - -#include "instruction.h" - -enum InstructionShiftT { - IST_LL, // Left logical - IST_RL, // Right logical - IST_RA // Right arithmetic -}; - -class InstructionShift : public InstructionR { -public: - InstructionShift(enum InstructionShiftT type, std::uint8_t rt, std::uint8_t rd, std::uint8_t sa); - QVector<QString> to_strs(); -private: - enum InstructionShiftT type; -}; - -class InstructionShiftVariable : public InstructionR { -public: - InstructionShiftVariable(enum InstructionShiftT type, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd); - QVector<QString> to_strs(); -private: - enum InstructionShiftT type; -}; - -#endif // SHIFT_H diff --git a/qtmips_machine/machineconfig.cpp b/qtmips_machine/machineconfig.cpp index 8562ab9..f23140f 100644 --- a/qtmips_machine/machineconfig.cpp +++ b/qtmips_machine/machineconfig.cpp @@ -1,6 +1,45 @@ #include "machineconfig.h" -MachineConfig::MachineConfig() -{ +MachineConfig::MachineConfig() { + pipeline = false; + jumppred = false; +} + +MachineConfig::MachineConfig(MachineConfig *cc) { + pipeline = cc->pipelined(); + jumppred = cc->jump_prediction(); +} + +void MachineConfig::set_pipelined(bool v) { + pipeline = v; +} + +void MachineConfig::set_jump_prediction(bool v) { + jumppred = v; + if (jumppred) + pipeline = true; +} + +void MachineConfig::set_cache(enum CacheType cc) { + cache_type = cc; +} + +void MachineConfig::set_elf(QString path) { + elf_path = path; +} + +bool MachineConfig::pipelined() const { + return pipeline; +} + +bool MachineConfig::jump_prediction() const { + return jumppred; +} + +enum MachineConfig::CacheType MachineConfig::cache() const { + return cache_type; +} +QString MachineConfig::elf() const { + return elf_path; } diff --git a/qtmips_machine/machineconfig.h b/qtmips_machine/machineconfig.h index 58c2fc2..352e62b 100644 --- a/qtmips_machine/machineconfig.h +++ b/qtmips_machine/machineconfig.h @@ -1,11 +1,40 @@ #ifndef MACHINECONFIG_H #define MACHINECONFIG_H +#include <QString> -class MachineConfig -{ +class MachineConfig { public: MachineConfig(); + MachineConfig(MachineConfig *cc); + + enum CacheType { + CCT_NONE, + CCT_ASSOCIATIVE, + // TODO + }; + + // Configure if CPU is pipelined + // In default disabled. + void set_pipelined(bool); + // Configure if we want to do jump prediction + // In default disabled. When enabled it also automatically enables pipelining + void set_jump_prediction(bool); + // Configure cache type + // In default CCT_NONE is used. + void set_cache(enum CacheType); + // Set path to source elf file. This has to be set before core is initialized. + void set_elf(QString path); + + bool pipelined() const; + bool jump_prediction() const; + enum CacheType cache() const; + QString elf() const; + +private: + bool pipeline, jumppred; + enum CacheType cache_type; + QString elf_path; }; -#endif // MACHINECONFIG_H
\ No newline at end of file +#endif // MACHINECONFIG_H diff --git a/qtmips_machine/programloader.cpp b/qtmips_machine/programloader.cpp index 6eefa6c..f49fa3c 100644 --- a/qtmips_machine/programloader.cpp +++ b/qtmips_machine/programloader.cpp @@ -2,12 +2,11 @@ #include <exception> #include <unistd.h> #include <fcntl.h> -#include <iostream> #include <errno.h> #include <cstring> #include "qtmipsexception.h" -ProgramLoader::ProgramLoader(char *file) { +ProgramLoader::ProgramLoader(const char *file) { // Initialize elf library if (elf_version(EV_CURRENT) == EV_NONE) throw QTMIPS_EXCEPTION(Input, "Elf library initialization failed", elf_errmsg(-1)); @@ -53,29 +52,35 @@ ProgramLoader::ProgramLoader(char *file) { // TODO instead of direct access should we be using sections and elf_data? And if so how to link program header and section? } +ProgramLoader::ProgramLoader(QString file) : ProgramLoader(file.toStdString().c_str()) { } + ProgramLoader::~ProgramLoader() { // Close elf - elf_end(this->elf); + // TODO fix (this results to segfault, there is probably somethig passed to it on stack or something) + //elf_end(this->elf); // Close file close(this->fd); } -size_t ProgramLoader::get_nsec() { - return this->map.size(); -} - -std::uint32_t ProgramLoader::get_address(size_t sec) { - SANITY_ASSERT(sec > this->get_nsec(), "Requesting too big section"); - return this->phdrs[this->map[sec]].p_vaddr; +void ProgramLoader::to_memory(Memory *mem) { + // Load program to memory (just dump it byte by byte) + for (int i = 0; i < this->map.size(); i++) { + std::uint32_t base_address = this->phdrs[this->map[i]].p_vaddr; + char *f = elf_rawfile(this->elf, NULL); + size_t phdrs_i = this->map[i]; + for (unsigned y = 0; y < this->phdrs[phdrs_i].p_filesz; y++) { + mem->write_byte(base_address + y, (std::uint8_t) f[this->phdrs[phdrs_i].p_offset + y]); + } + } } -QVector<std::uint8_t> ProgramLoader::get_data(size_t sec) { - SANITY_ASSERT(sec > this->get_nsec(), "Requesting too big section"); - QVector<std::uint8_t> d; - char *f = elf_rawfile(this->elf, NULL); - size_t phdrs_i = this->map[sec]; - for (unsigned i = 0; i < this->phdrs[phdrs_i].p_filesz; i++) { - d << (std::uint8_t) f[this->phdrs[phdrs_i].p_offset + i]; +std::uint32_t ProgramLoader::end() { + std::uint32_t last = 0; + // Go trough all sections and found out last one + for (int i = 0; i < this->map.size(); i++) { + Elf32_Phdr *phdr = &(this->phdrs[this->map[i]]); + if ((phdr->p_vaddr + phdr->p_filesz) > last) + last = phdr->p_vaddr + phdr->p_filesz; } - return d; + return last + 0x10; // We add offset so we are sure that also pipeline is empty } diff --git a/qtmips_machine/programloader.h b/qtmips_machine/programloader.h index 4d722d2..7da241c 100644 --- a/qtmips_machine/programloader.h +++ b/qtmips_machine/programloader.h @@ -6,16 +6,18 @@ #include <gelf.h> #include <cstdint> #include <qvector.h> +#include <qstring.h> +#include "memory.h" class ProgramLoader { public: - ProgramLoader(char *file); + ProgramLoader(const char *file); + ProgramLoader(QString file); ~ProgramLoader(); - size_t get_nsec(); // Returns number of loadable sections - std::uint32_t get_address(size_t sec); // Get target address for given section - QVector<std::uint8_t> get_data(size_t sec); // Returns bytes of given section + void to_memory(Memory *mem); // Writes all loaded sections to memory + std::uint32_t end(); // Return address after which there is no more code for sure private: int fd; Elf *elf; diff --git a/qtmips_machine/programmemory.h b/qtmips_machine/programmemory.h deleted file mode 100644 index 14187c5..0000000 --- a/qtmips_machine/programmemory.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef PROGRAMMEMORY_H -#define PROGRAMMEMORY_H - -#include <vector> -#include "programloader.h" -#include "memory.h" -#include "instruction.h" - -class ProgramMemory { -public: - ProgramMemory(MemoryAccess *memory); - - void load(ProgramLoader *l); - Instruction *at(std::uint32_t address); // return instruction isntance for given address - -private: - MemoryAccess *memory; - Instruction *decode_r(std::uint32_t dt); - Instruction *decode_j(std::uint32_t dt, std::uint8_t opcode); - Instruction *decode_i(std::uint32_t dt, std::uint8_t opcode); -}; - -#endif // PROGRAMMEMORY_H diff --git a/qtmips_machine/qtmips_machine.pro b/qtmips_machine/qtmips_machine.pro index 213727d..a1edcaa 100644 --- a/qtmips_machine/qtmips_machine.pro +++ b/qtmips_machine/qtmips_machine.pro @@ -7,6 +7,7 @@ TEMPLATE = lib LIBS += -lelf QMAKE_CXXFLAGS += -std=c++0x +QMAKE_CXXFLAGS += -ggdb DEFINES += QTMIPS_MACHINE_LIBRARY DEFINES += QT_DEPRECATED_WARNINGS @@ -19,13 +20,9 @@ SOURCES += \ instruction.cpp \ registers.cpp \ programloader.cpp \ - programmemory.cpp \ - instructions/arithmetic.cpp \ - instructions/loadstore.cpp \ - instructions/shift.cpp \ - instructions/nop.cpp \ - instructions/jumpbranch.cpp \ - cache.cpp + cache.cpp \ + alu.cpp \ + machineconfig.cpp HEADERS += \ qtmipsmachine.h \ @@ -41,4 +38,6 @@ HEADERS += \ instructions/shift.h \ instructions/nop.h \ instructions/jumpbranch.h \ - cache.h + cache.h \ + alu.h \ + machineconfig.h diff --git a/qtmips_machine/qtmipsexception.cpp b/qtmips_machine/qtmipsexception.cpp index 4cec6b0..02193cb 100644 --- a/qtmips_machine/qtmipsexception.cpp +++ b/qtmips_machine/qtmipsexception.cpp @@ -19,7 +19,7 @@ const char *QtMipsException::what() const throw() { QString QtMipsException::msg(bool pos) const { QString message; if (pos) - message += QString("(") + QString(this->file) + QString(":") + QString(this->line) + QString(") "); + message += QString("(") + QString(this->file) + QString(":") + QString::number(this->line) + QString(") "); message += this->reason; if (!this->ext.isEmpty()) { message += QString(": "); @@ -44,6 +44,16 @@ QtMipsExceptionUnsupportedInstruction::QtMipsExceptionUnsupportedInstruction(QTM return; } +QtMipsExceptionUnsupportedAluOperation::QtMipsExceptionUnsupportedAluOperation(QTMIPS_ARGS_COMMON) + : QtMipsExceptionRuntime(reason, ext, file, line) { + return; +} + +QtMipsExceptionOverflow::QtMipsExceptionOverflow(QTMIPS_ARGS_COMMON) + : QtMipsExceptionRuntime(reason, ext, file, line) { + return; +} + QtMipsExceptionUnalignedJump::QtMipsExceptionUnalignedJump(QTMIPS_ARGS_COMMON) : QtMipsExceptionRuntime(reason, ext, file, line) { return; diff --git a/qtmips_machine/qtmipsexception.h b/qtmips_machine/qtmipsexception.h index 81895d6..b81f748 100644 --- a/qtmips_machine/qtmipsexception.h +++ b/qtmips_machine/qtmipsexception.h @@ -37,6 +37,20 @@ public: QtMipsExceptionUnsupportedInstruction(QTMIPS_ARGS_COMMON); }; +// Decoded ALU operation is not supported +// This is basically same exception as QtMipsExceptionUnsupportedInstruction but it is emmited from ALU when executed and not before that. +class QtMipsExceptionUnsupportedAluOperation : public QtMipsExceptionRuntime { +public: + QtMipsExceptionUnsupportedAluOperation(QTMIPS_ARGS_COMMON); +}; + +// Integer operation resulted to overflow (or underflow as we are working with unsigned values) +// This is for sure caused by program it self. +class QtMipsExceptionOverflow : public QtMipsExceptionRuntime { +public: + QtMipsExceptionOverflow(QTMIPS_ARGS_COMMON); +}; + // Instruction is jumping to unaligned address (ADDR%4!=0) // This can be caused by bug or by user program as it can be jumping relative to register // This shouldn't be happening with non-register jumps as those should be verified by compiler diff --git a/qtmips_machine/qtmipsmachine.cpp b/qtmips_machine/qtmipsmachine.cpp index 0fc207c..3d5ce98 100644 --- a/qtmips_machine/qtmipsmachine.cpp +++ b/qtmips_machine/qtmipsmachine.cpp @@ -1,5 +1,70 @@ #include "qtmipsmachine.h" +#include "programloader.h" -QtMipsMachine::QtMipsMachine(char *file) { - this->loader = new ProgramLoader(file); +QtMipsMachine::QtMipsMachine(const MachineConfig &cc) { + ProgramLoader program(cc.elf()); + + regs = new Registers(); + mem = new Memory(); + + program.to_memory(mem); + program_end = program.end(); + + MemoryAccess *coremem; + switch (cc.cache()) { + case MachineConfig::CCT_NONE: + cch = nullptr; + coremem = mem; + break; + case MachineConfig::CCT_ASSOCIATIVE: + // TODO + coremem = mem; + //coremem = cch = new CacheAssociative(); + break; + } + + // TODO pipelined + cr = new CoreSingle(regs, coremem); + + run_speed = 1; + run_t = new QTimer(this); + connect(run_t, SIGNAL(timeout()), this, SLOT(step())); +} + +void QtMipsMachine::set_speed(unsigned val) { + run_speed = val; +} + +const Registers *QtMipsMachine::registers() { + return regs; +} + +const Memory *QtMipsMachine::memory() { + return mem; +} + +const Cache *QtMipsMachine::cache() { + return cch; +} + +const Core *QtMipsMachine::core() { + return cr; +} + +void QtMipsMachine::play() { + run_t->start(run_speed); +} + +void QtMipsMachine::pause() { + run_t->stop(); +} + +void QtMipsMachine::step() { + cr->step(); + if (regs->read_pc() >= program_end) + emit program_exit(); +} + +void QtMipsMachine::restart() { + // TODO } diff --git a/qtmips_machine/qtmipsmachine.h b/qtmips_machine/qtmipsmachine.h index af981b3..829e571 100644 --- a/qtmips_machine/qtmipsmachine.h +++ b/qtmips_machine/qtmipsmachine.h @@ -2,24 +2,47 @@ #define QTMIPSMACHINE_H #include <QObject> - +#include <QTimer> +#include <cstdint> #include "qtmipsexception.h" -#include "programloader.h" +#include "machineconfig.h" +#include "registers.h" +#include "memory.h" #include "core.h" -// TODO piplined core +#include "cache.h" -class QtMipsMachine : QObject { +class QtMipsMachine : public QObject { Q_OBJECT public: - QtMipsMachine(char *file); + QtMipsMachine(const MachineConfig &cc); + + void set_speed(unsigned); + const Registers *registers(); + const Memory *memory(); + const Cache *cache(); + const Core *core(); + +public slots: // TODO handle speed void play(); void pause(); void step(); void restart(); + +signals: + void program_exit(); + private: - ProgramLoader *loader; + Registers *regs; + Memory *mem; + Cache *cch; + Core *cr; + + unsigned run_speed; + QTimer *run_t; + + std::uint32_t program_end; }; #endif // QTMIPSMACHINE_H diff --git a/qtmips_machine/registers.cpp b/qtmips_machine/registers.cpp index 837f403..fa984fb 100644 --- a/qtmips_machine/registers.cpp +++ b/qtmips_machine/registers.cpp @@ -14,12 +14,21 @@ Registers::Registers() { this->hi = this->lo = 0; } -std::uint32_t Registers::read_pc() { +Registers::Registers(const Registers *orig) : Registers() { + this->pc = orig->read_pc(); + for (int i = 0; i < 31; i++) + this->gp[i] = orig->read_gp(i); + this->lo = orig->read_hi_lo(false); + this->hi = orig->read_hi_lo(true); +} + +std::uint32_t Registers::read_pc() const { return this->pc; } std::uint32_t Registers::pc_inc() { this->pc += 4; + emit pc_update(this->pc); return this->pc; } @@ -27,6 +36,7 @@ std::uint32_t Registers::pc_jmp(std::int32_t offset) { if (offset % 4) throw QTMIPS_EXCEPTION(UnalignedJump, "Trying to jump by unaligned offset", QString::number(offset, 16)); this->pc += offset; + emit pc_update(this->pc); return this->pc; } @@ -36,7 +46,7 @@ void Registers::pc_abs_jmp(std::uint32_t address) { this->pc = address; } -std::uint32_t Registers::read_gp(std::uint8_t i) { +std::uint32_t Registers::read_gp(std::uint8_t i) const { SANITY_ASSERT(i < 32, QString("Trying to read from register ") + QString(i)); if (!i) // $0 always reads as 0 return 0; @@ -50,7 +60,7 @@ void Registers::write_gp(std::uint8_t i, std::uint32_t value) { this->gp[i - 1] = value; } -std::uint32_t Registers::read_hi_lo(bool hi) { +std::uint32_t Registers::read_hi_lo(bool hi) const { if (hi) return this->hi; else @@ -63,3 +73,16 @@ void Registers::write_hi_lo(bool hi, std::uint32_t value) { else this->lo = value; } + +bool Registers::operator ==(const Registers &c) const { + if (read_pc() != c.read_pc()) + return false; + for (int i = 0; i < 31; i++) + if (read_gp(i) != c.read_gp(i)) + return false; + if (read_hi_lo(false) != c.read_hi_lo(false)) + return false; + if (read_hi_lo(true) != c.read_hi_lo(true)) + return false; + return true; +} diff --git a/qtmips_machine/registers.h b/qtmips_machine/registers.h index a550f4a..905a212 100644 --- a/qtmips_machine/registers.h +++ b/qtmips_machine/registers.h @@ -8,18 +8,22 @@ class Registers : public QObject { Q_OBJECT public: Registers(); + Registers(const Registers*); - std::uint32_t read_pc(); // Return current value of program counter + std::uint32_t read_pc() const; // Return current value of program counter std::uint32_t pc_inc(); // Increment program counter by four bytes std::uint32_t pc_jmp(std::int32_t offset); // Relative jump from current location in program counter void pc_abs_jmp(std::uint32_t address); // Absolute jump in program counter (write to pc) - std::uint32_t read_gp(std::uint8_t i); // Read general-purpose register + std::uint32_t read_gp(std::uint8_t i) const; // Read general-purpose register void write_gp(std::uint8_t i, std::uint32_t value); // Write general-purpose register - std::uint32_t read_hi_lo(bool hi); // true - read HI / false - read LO + std::uint32_t read_hi_lo(bool hi) const; // true - read HI / false - read LO void write_hi_lo(bool hi, std::uint32_t value); + bool operator ==(const Registers &c) const; + signals: + void pc_update(std::uint32_t val); // TODO signals private: diff --git a/qtmips_machine/tests/testalu.cpp b/qtmips_machine/tests/testalu.cpp index e69de29..37accdf 100644 --- a/qtmips_machine/tests/testalu.cpp +++ b/qtmips_machine/tests/testalu.cpp @@ -0,0 +1,91 @@ +#include "tst_machine.h" +#include "alu.h" +#include "qtmipsexception.h" + +void MachineTests::alu_data() { + QTest::addColumn<std::uint8_t>("op"); + QTest::addColumn<std::uint32_t>("s"); + QTest::addColumn<std::uint32_t>("t"); + QTest::addColumn<std::uint8_t>("sa"); + QTest::addColumn<std::uint32_t>("res"); + + // TODO SLL-SRAV + QTest::newRow("ADD") << (std::uint8_t)ALU_OP_ADD \ + << (std::uint32_t)24 \ + << (std::uint32_t)66 \ + << (std::uint8_t)0 \ + << (std::uint32_t)90; + QTest::newRow("ADDU") << (std::uint8_t)ALU_OP_ADDU \ + << (std::uint32_t)24 \ + << (std::uint32_t)66 \ + << (std::uint8_t)0 \ + << (std::uint32_t)90; + QTest::newRow("SUB") << (std::uint8_t)ALU_OP_SUB \ + << (std::uint32_t)66 \ + << (std::uint32_t)24 \ + << (std::uint8_t)0 \ + << (std::uint32_t)42; + QTest::newRow("SUBU") << (std::uint8_t)ALU_OP_SUBU \ + << (std::uint32_t)24 \ + << (std::uint32_t)66 \ + << (std::uint8_t)0 \ + << (std::uint32_t)-42; + QTest::newRow("AND") << (std::uint8_t)ALU_OP_AND \ + << (std::uint32_t)0xA81 \ + << (std::uint32_t)0x603 \ + << (std::uint8_t)0 \ + << (std::uint32_t)0x201; + QTest::newRow("OR") << (std::uint8_t)ALU_OP_OR \ + << (std::uint32_t)0xA81 \ + << (std::uint32_t)0x603 \ + << (std::uint8_t)0 \ + << (std::uint32_t)0xE83; + QTest::newRow("XOR") << (std::uint8_t)ALU_OP_XOR \ + << (std::uint32_t)0xA81 \ + << (std::uint32_t)0x603 \ + << (std::uint8_t)0 \ + << (std::uint32_t)0xC82; + QTest::newRow("NOR") << (std::uint8_t)ALU_OP_NOR \ + << (std::uint32_t)0xA81 \ + << (std::uint32_t)0x603 \ + << (std::uint8_t)0 \ + << (std::uint32_t)0xFFFFF17C; + // TODO SLT-SLTU +} + +void MachineTests::alu() { + QFETCH(std::uint8_t, op); + QFETCH(std::uint32_t, s); + QFETCH(std::uint32_t, t); + QFETCH(std::uint8_t, sa); + QFETCH(std::uint32_t, res); + + QCOMPARE(alu_operate((enum AluOp)op, s , t, sa), res); +} + +void MachineTests::alu_except_data() { + QTest::addColumn<std::uint8_t>("op"); + QTest::addColumn<std::uint32_t>("s"); + QTest::addColumn<std::uint32_t>("t"); + // Note no sa as shift unstruction has no exceptions + + QTest::newRow("ADD") << (std::uint8_t)ALU_OP_ADD \ + << (std::uint32_t)0x8fffffff \ + << (std::uint32_t)0x90000000; + QTest::newRow("SUB") << (std::uint8_t)ALU_OP_SUB \ + << (std::uint32_t)3 \ + << (std::uint32_t)4; + // Just test that we can throw unsupported ALU operation + QTest::newRow("?") << (std::uint8_t)ALU_OP_LAST \ + << (std::uint32_t)0 \ + << (std::uint32_t)0; +} + +void MachineTests::alu_except() { + QFETCH(std::uint8_t, op); + QFETCH(std::uint32_t, s); + QFETCH(std::uint32_t, t); + + // Only runtime exception is expected as any other exception is a bug + QVERIFY_EXCEPTION_THROWN(alu_operate((enum AluOp)op, s , t, 0), QtMipsExceptionRuntime); +} diff --git a/qtmips_machine/tests/testcore.cpp b/qtmips_machine/tests/testcore.cpp index e69de29..bbf8086 100644 --- a/qtmips_machine/tests/testcore.cpp +++ b/qtmips_machine/tests/testcore.cpp @@ -0,0 +1,35 @@ +#include "tst_machine.h" +#include "core.h" + +void MachineTests::core_regs_data() { + /* + QTest::addColumn<Instruction>("i"); + QTest::addColumn<Registers>("init"); + QTest::addColumn<Registers>("res"); + + // Test arithmetic instructions + { + Registers regs_init(); + regs_init.write_gp(24, 12); + regs_init.write_gp(25, 24); + Registers regs_res(®s_init); + regs_res.write_gp(26, 36); + QTest::newRow("ADD") << Instruction(0, 24, 25, 26, 0, 32) \ + << regs_init \ + << regs_res; + } + */ + // TODO test other operations +} + +void MachineTests::core_regs() { + +} + +void MachineTests::core_mem_data() { + +} + +void MachineTests::core_mem() { + +} diff --git a/qtmips_machine/tests/testinstruction.cpp b/qtmips_machine/tests/testinstruction.cpp index e69de29..4efedac 100644 --- a/qtmips_machine/tests/testinstruction.cpp +++ b/qtmips_machine/tests/testinstruction.cpp @@ -0,0 +1,25 @@ +#include "tst_machine.h" +#include "instruction.h" + +// Test that we are correctly encoding instructions in constructor +void MachineTests::instruction() { + QCOMPARE(Instruction(0x00), Instruction(0,0)); + QCOMPARE(Instruction(0x4000002), Instruction(1, 2)); + // QCOMPARE(Instruction(0x4000002), Instruction(1, 2, 3, 4)); + // TODO other combinations +} + +// Test that we are correctly decoding instruction fields +void MachineTests::instruction_access() { + Instruction i(0xffffffff); + + QCOMPARE(i.data(), (std::uint32_t) 0xffffffff); + QCOMPARE(i.opcode(), (std::uint8_t) 0x3f); + QCOMPARE(i.rs(), (std::uint8_t) 0x1f); + QCOMPARE(i.rt(), (std::uint8_t) 0x1f); + QCOMPARE(i.rd(), (std::uint8_t) 0x1f); + QCOMPARE(i.shamt(), (std::uint8_t) 0x1f); + QCOMPARE(i.funct(), (std::uint8_t) 0x3f); + QCOMPARE(i.immediate(), (std::uint16_t) 0xffff); + QCOMPARE(i.address(), (std::uint32_t) 0x3ffffff); +} diff --git a/qtmips_machine/tests/testmemory.cpp b/qtmips_machine/tests/testmemory.cpp index eac7dd6..e450231 100644 --- a/qtmips_machine/tests/testmemory.cpp +++ b/qtmips_machine/tests/testmemory.cpp @@ -13,21 +13,21 @@ void MachineTests::memory_data() { void MachineTests::memory() { Memory m; - QFETCH(std::uint32_t, address); + QFETCH(std::uint32_t, address); - // Uninitialize memory should read as zero - QCOMPARE(m.read_byte(address), (std::uint8_t)0); - QCOMPARE(m.read_hword(address), (std::uint16_t)0); - QCOMPARE(m.read_word(address), (std::uint32_t)0); - // Just a byte - m.write_byte(address, 0x42); - QCOMPARE(m.read_byte(address), (std::uint8_t)0x42); - // Half word - m.write_hword(address, 0x4243); - QCOMPARE(m.read_hword(address), (std::uint16_t)0x4243); - // Word - m.write_word(address, 0x42434445); - QCOMPARE(m.read_word(address), (std::uint32_t)0x42434445); + // Uninitialize memory should read as zero + QCOMPARE(m.read_byte(address), (std::uint8_t)0); + QCOMPARE(m.read_hword(address), (std::uint16_t)0); + QCOMPARE(m.read_word(address), (std::uint32_t)0); + // Just a byte + m.write_byte(address, 0x42); + QCOMPARE(m.read_byte(address), (std::uint8_t)0x42); + // Half word + m.write_hword(address, 0x4243); + QCOMPARE(m.read_hword(address), (std::uint16_t)0x4243); + // Word + m.write_word(address, 0x42434445); + QCOMPARE(m.read_word(address), (std::uint32_t)0x42434445); } void MachineTests::memory_section_data() { @@ -40,24 +40,24 @@ void MachineTests::memory_section_data() { } void MachineTests::memory_section() { - Memory m; + Memory m; - QFETCH(std::uint32_t, address); + QFETCH(std::uint32_t, address); - // First section shouldn't exists - QCOMPARE(m.get_section(address, false), (MemorySection*)nullptr); - // Create section - MemorySection *s = m.get_section(address, true); - QVERIFY(s != nullptr); + // First section shouldn't exists + QCOMPARE(m.get_section(address, false), (MemorySection*)nullptr); + // Create section + MemorySection *s = m.get_section(address, true); + QVERIFY(s != nullptr); - // Write some data to memory - m.write_byte(address, 0x42); - // Read it trough section (mask bits outside of the memory section) - QCOMPARE(s->read_byte(address & ((1 << MEMORY_SECTION_BITS) - 1)), (std::uint8_t)0x42); - // Write some other data trough section - s->write_byte(address & ((1 << MEMORY_SECTION_BITS) - 1), 0x66); - // Read trough memory - QCOMPARE(m.read_byte(address), (std::uint8_t)0x66); + // Write some data to memory + m.write_byte(address, 0x42); + // Read it trough section (mask bits outside of the memory section) + QCOMPARE(s->read_byte(address & ((1 << MEMORY_SECTION_BITS) - 1)), (std::uint8_t)0x42); + // Write some other data trough section + s->write_byte(address & ((1 << MEMORY_SECTION_BITS) - 1), 0x66); + // Read trough memory + QCOMPARE(m.read_byte(address), (std::uint8_t)0x66); } void MachineTests::memory_endian() { diff --git a/qtmips_machine/tests/testprogramloader.cpp b/qtmips_machine/tests/testprogramloader.cpp index e69de29..7ff1c54 100644 --- a/qtmips_machine/tests/testprogramloader.cpp +++ b/qtmips_machine/tests/testprogramloader.cpp @@ -0,0 +1,20 @@ +#include <iostream> +#include "tst_machine.h" +#include "programloader.h" +#include "instruction.h" + +// This is common program start (initial value of program counter) +#define PC_INIT 0x80020000 + +void MachineTests::program_loader() { + ProgramLoader pl("data"); + Memory m; + pl.to_memory(&m); + + // addi $1, $0, 6 + QCOMPARE(Instruction(m.read_word(PC_INIT)), Instruction(8, 0, 1, 6)); + // j 80020000 + // TODO wtf to je relativni skok asi tady + //QCOMPARE(Instruction(m.read_word(PC_INIT + 4)), Instruction(2, PC_INIT)); + // TODO add some more code to data and do more compares (for example more sections) +} diff --git a/qtmips_machine/tests/tests.pro b/qtmips_machine/tests/tests.pro index 287d1e8..ffe75b7 100644 --- a/qtmips_machine/tests/tests.pro +++ b/qtmips_machine/tests/tests.pro @@ -18,8 +18,10 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += tst_machine.cpp \ testmemory.cpp \ testregisters.cpp \ - testprogrammemory.cpp \ - testinstruction.cpp + testprogramloader.cpp \ + testinstruction.cpp \ + testalu.cpp \ + testcore.cpp HEADERS += tst_machine.h diff --git a/qtmips_machine/tests/tst_machine.h b/qtmips_machine/tests/tst_machine.h index c05e8ad..da8082a 100644 --- a/qtmips_machine/tests/tst_machine.h +++ b/qtmips_machine/tests/tst_machine.h @@ -17,14 +17,21 @@ private Q_SLOTS: void memory_section(); void memory_section_data(); void memory_endian(); - // ProgramMemory - void program_memory(); - void program_memory_data(); + // Program loader + void program_loader(); // Instruction - void instruction_arithmetic(); - void instruction_arithmetic_data(); - void instruction_arithmetic_immediate(); - void instruction_arithmetic_immediate_data(); + void instruction(); + void instruction_access(); + // Alu + void alu(); + void alu_data(); + void alu_except(); + void alu_except_data(); + // Core + void core_regs(); + void core_regs_data(); + void core_mem(); + void core_mem_data(); }; #endif // TST_MACHINE_H |