From 9d82517dea100d94fd8d0d5326ca5db7b5a1e595 Mon Sep 17 00:00:00 2001 From: Pavel Pisa Date: Sun, 17 Feb 2019 21:15:53 +0100 Subject: Pass arithmetic exception trough pipeline and implement trap support and instructions. Signed-off-by: Pavel Pisa --- qtmips_machine/alu.cpp | 46 ++++++++++++++++++++---- qtmips_machine/alu.h | 7 ++-- qtmips_machine/core.cpp | 57 ++++++++++++++++-------------- qtmips_machine/instruction.cpp | 49 +++++++++++++++----------- qtmips_machine/machinedefs.h | 8 +++++ qtmips_machine/memory.h | 1 + qtmips_machine/tests/testalu.cpp | 71 +++++++++++++++++++++++++++++++++++--- qtmips_machine/tests/tst_machine.h | 2 ++ 8 files changed, 182 insertions(+), 59 deletions(-) diff --git a/qtmips_machine/alu.cpp b/qtmips_machine/alu.cpp index c86331b..de38c96 100644 --- a/qtmips_machine/alu.cpp +++ b/qtmips_machine/alu.cpp @@ -92,9 +92,10 @@ static inline void alu_write_hi_lo_64bit(Registers *regs, std::uint64_t val) } -std::uint32_t machine::alu_operate(enum AluOp operation, std::uint32_t s, std::uint32_t t, - std::uint8_t sa, std::uint8_t sz, Registers *regs, - bool &discard) { +std::uint32_t machine::alu_operate(enum AluOp operation, std::uint32_t s, + std::uint32_t t, std::uint8_t sa, std::uint8_t sz, + Registers *regs, bool &discard, + ExceptionCause &excause) { std::int64_t s64_val; std::uint64_t u64_val; discard = false; @@ -154,7 +155,7 @@ std::uint32_t machine::alu_operate(enum AluOp operation, std::uint32_t s, std::u /* s(31) ^ ~t(31) ... same signs on input */ /* (s + t)(31) ^ s(31) ... different sign on output */ if (((s ^ ~t) & ((s + t) ^ s)) & 0x80000000) - throw QTMIPS_EXCEPTION(Overflow, "ADD operation overflow/underflow", QString::number(s) + QString(" + ") + QString::number(t)); + excause = EXCAUSE_OVERFLOW; FALLTROUGH case ALU_OP_ADDU: return s + t; @@ -162,7 +163,7 @@ std::uint32_t machine::alu_operate(enum AluOp operation, std::uint32_t s, std::u /* s(31) ^ t(31) ... differnt signd on input */ /* (s - t)(31) ^ ~s(31) <> 0 ... otput sign differs from s */ if (((s ^ t) & ((s - t) ^ s)) & 0x80000000) - throw QTMIPS_EXCEPTION(Overflow, "SUB operation overflow/underflow", QString::number(s) + QString(" - ") + QString::number(t)); + excause = EXCAUSE_OVERFLOW; FALLTROUGH case ALU_OP_SUBU: return s - t; @@ -201,10 +202,43 @@ std::uint32_t machine::alu_operate(enum AluOp operation, std::uint32_t s, std::u u64_val -= (std::uint64_t)s * t; alu_write_hi_lo_64bit(regs, u64_val); return 0x0; + case ALU_OP_TGE: + if ((std::int32_t)s >= (std::int32_t)t) + excause = EXCAUSE_TRAP; + return 0; + case ALU_OP_TGEU: + if (s >= t) + excause = EXCAUSE_TRAP; + return 0; + case ALU_OP_TLT: + if ((std::int32_t)s < (std::int32_t)t) + excause = EXCAUSE_TRAP; + return 0; + case ALU_OP_TLTU: + if (s < t) + excause = EXCAUSE_TRAP; + return 0; + case ALU_OP_TEQ: + if (s == t) + excause = EXCAUSE_TRAP; + return 0; + case ALU_OP_TNE: + if (s != t) + excause = EXCAUSE_TRAP; + return 0; case ALU_OP_LUI: return t << 16; case ALU_OP_BSHFL: - return (uint32_t)(int32_t)(int8_t)t; + switch (sa) { + case 0x02: + return ((t << 8) & 0xff00ff00) | ((t >> 8) & 0x00ff00ff); + case 0x10: + return (uint32_t)(int32_t)(int8_t)t; + case 0x18: + return (uint32_t)(int32_t)(int16_t)t; + default: + throw QTMIPS_EXCEPTION(UnsupportedAluOperation, "Unknown BSHFL variant", QString::number(sa, 16)); + } case ALU_OP_EXT: return (s >> sa) & ((1 << sz) - 1); case ALU_OP_CLZ: diff --git a/qtmips_machine/alu.h b/qtmips_machine/alu.h index e14a903..97cb33e 100644 --- a/qtmips_machine/alu.h +++ b/qtmips_machine/alu.h @@ -46,6 +46,7 @@ namespace machine { // Do ALU operation. +// excause: Reported exception by given 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. @@ -53,11 +54,13 @@ namespace machine { // sz: This is value directly from instruction it self used in filed extract instructions // regs: Registers used. We need direct access to lo and hi registers (those are not accessed from core it self but from alu directly // 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, std::uint8_t sz, Registers *regs, bool &discard); +std::uint32_t alu_operate(enum AluOp operation, std::uint32_t s, + std::uint32_t t, std::uint8_t sa, std::uint8_t sz, + Registers *regs, bool &discard, ExceptionCause &excause); } Q_DECLARE_METATYPE(machine::AluOp) +Q_DECLARE_METATYPE(machine::ExceptionCause) #endif // ALU_H diff --git a/qtmips_machine/core.cpp b/qtmips_machine/core.cpp index dccc20b..6ef9103 100644 --- a/qtmips_machine/core.cpp +++ b/qtmips_machine/core.cpp @@ -314,6 +314,8 @@ struct Core::dtDecode Core::decode(const struct dtFetch &dt) { struct Core::dtExecute Core::execute(const struct dtDecode &dt) { bool discard; + enum ExceptionCause excause = dt.excause; + std::uint32_t alu_val = 0; // Handle conditional move (we have to change regwrite signal if conditional is not met) bool regwrite = dt.regwrite; @@ -322,35 +324,38 @@ struct Core::dtExecute Core::execute(const struct dtDecode &dt) { if (dt.alusrc) alu_sec = dt.immediate_val; // Sign or zero extend immediate value - std::uint32_t alu_val = alu_operate(dt.aluop, dt.val_rs, alu_sec, - dt.inst.shamt(), dt.inst.rd(), regs, discard); - if (discard) - regwrite = false; - - if (dt.aluop == ALU_OP_RDHWR) { - switch (dt.inst.rd()) { - case 0: // CPUNum - alu_val = 0; - break; - case 1: // SYNCI_Step - alu_val = min_cache_row_size; - break; - case 2: // CC - alu_val = cycle_c; - break; - case 3: // CCRes - alu_val = 1; - break; - case 29: // UserLocal - alu_val = hwr_userlocal; - break; - default: - alu_val = 0; + if (excause == EXCAUSE_NONE) { + alu_val = alu_operate(dt.aluop, dt.val_rs, + alu_sec, dt.inst.shamt(), dt.inst.rd(), regs, + discard, excause); + if (discard) + regwrite = false; + + if (dt.aluop == ALU_OP_RDHWR) { + switch (dt.inst.rd()) { + case 0: // CPUNum + alu_val = 0; + break; + case 1: // SYNCI_Step + alu_val = min_cache_row_size; + break; + case 2: // CC + alu_val = cycle_c; + break; + case 3: // CCRes + alu_val = 1; + break; + case 29: // UserLocal + alu_val = hwr_userlocal; + break; + default: + alu_val = 0; + } } } emit execute_inst_addr_value(dt.inst_addr); - emit instruction_executed(dt.inst, dt.inst_addr, dt.excause); + emit instruction_executed(dt.inst, dt.inst_addr, excause); emit execute_alu_value(alu_val); emit execute_reg1_value(dt.val_rs); emit execute_reg2_value(dt.val_rt); @@ -375,7 +380,7 @@ struct Core::dtExecute Core::execute(const struct dtDecode &dt) { .rwrite = dt.rwrite, .alu_val = alu_val, .inst_addr = dt.inst_addr, - .excause = dt.excause, + .excause = excause, .in_delay_slot = dt.in_delay_slot, }; } diff --git a/qtmips_machine/instruction.cpp b/qtmips_machine/instruction.cpp index 68956b6..1d37309 100644 --- a/qtmips_machine/instruction.cpp +++ b/qtmips_machine/instruction.cpp @@ -60,6 +60,9 @@ using namespace machine; #define FLAGS_ALU_T_R_SD (FLAGS_ALU_T_R_D | IMF_ALU_REQ_RS) #define FLAGS_ALU_T_R_ST (IMF_SUPPORTED | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT) +#define FLAGS_ALU_TRAP_ST (IMF_SUPPORTED | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT) +#define FLAGS_ALU_TRAP_SI (IMF_SUPPORTED | IMF_ALU_REQ_RS | IMF_ALUSRC) + #define FLAGS_J_B_PC_TO_R31 (IMF_SUPPORTED | IMF_PC_TO_R31 | IMF_REGWRITE) #define NOALU .alu = ALU_OP_SLL @@ -165,13 +168,19 @@ static const struct InstructionMap alu_instruction_map[] = { IM_UNKNOWN, // 45 IM_UNKNOWN, // 46 IM_UNKNOWN, // 47 - IM_UNKNOWN, // 48 - IM_UNKNOWN, // 49 - IM_UNKNOWN, // 50 - IM_UNKNOWN, // 51 - IM_UNKNOWN, // 52 + {"TGE", IT_I, ALU_OP_TGE, NOMEM, nullptr, // TGE 48 + .flags = FLAGS_ALU_TRAP_ST}, + {"TGEU", IT_I, ALU_OP_TGEU, NOMEM, nullptr, // TGEU 49 + .flags = FLAGS_ALU_TRAP_ST}, + {"TLT", IT_I, ALU_OP_TLT, NOMEM, nullptr, // TLT 50 + .flags = FLAGS_ALU_TRAP_ST}, + {"TLTU", IT_I, ALU_OP_TGEU, NOMEM, nullptr, // TLTU 51 + .flags = FLAGS_ALU_TRAP_ST}, + {"TEQ", IT_I, ALU_OP_TEQ, NOMEM, nullptr, // TEQ 52 + .flags = FLAGS_ALU_TRAP_ST}, IM_UNKNOWN, // 53 - IM_UNKNOWN, // 54 + {"TNE", IT_I, ALU_OP_TNE, NOMEM, nullptr, // TNE 54 + .flags = FLAGS_ALU_TRAP_ST}, IM_UNKNOWN, // 55 IM_UNKNOWN, // 56 IM_UNKNOWN, // 57 @@ -340,20 +349,20 @@ static const struct InstructionMap regimm_instruction_map[] = { IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, - {"TGEI", IT_I, NOALU, NOMEM, nullptr, // TGEI - .flags = IMF_BJR_REQ_RS}, - {"TGEIU", IT_I, NOALU, NOMEM, nullptr, // TGEIU - .flags = IMF_BJR_REQ_RS}, - {"TLTI", IT_I, NOALU, NOMEM, nullptr, // TLTI - .flags = IMF_BJR_REQ_RS}, - {"TLTIU", IT_I, NOALU, NOMEM, nullptr, // TLTIU - .flags = IMF_BJR_REQ_RS}, - {"TEQI", IT_I, NOALU, NOMEM, nullptr, // TEQI - .flags = IMF_BJR_REQ_RS}, - IM_UNKNOWN, - {"TNEI", IT_I, NOALU, NOMEM, nullptr, // TNEI - .flags = IMF_BJR_REQ_RS}, - IM_UNKNOWN, + {"TGEI", IT_I, ALU_OP_TGE, NOMEM, nullptr, // TGEI 16 + .flags = FLAGS_ALU_TRAP_SI}, + {"TGEIU", IT_I, ALU_OP_TGEU, NOMEM, nullptr, // TGEIU 17 + .flags = FLAGS_ALU_TRAP_SI}, + {"TLTI", IT_I, ALU_OP_TLT, NOMEM, nullptr, // TLTI 18 + .flags = FLAGS_ALU_TRAP_SI}, + {"TLTIU", IT_I, ALU_OP_TGEU, NOMEM, nullptr, // TLTIU 19 + .flags = FLAGS_ALU_TRAP_SI}, + {"TEQI", IT_I, ALU_OP_TEQ, NOMEM, nullptr, // TEQI 20 + .flags = FLAGS_ALU_TRAP_SI}, + IM_UNKNOWN, // 21 + {"TNEI", IT_I, ALU_OP_TNE, NOMEM, nullptr, // TNEI 22 + .flags = FLAGS_ALU_TRAP_SI}, + IM_UNKNOWN, // 23 {"BLTZAL", IT_I, ALU_OP_PASS_T, NOMEM, nullptr, // BLTZAL .flags = FLAGS_J_B_PC_TO_R31 | IMF_BJR_REQ_RS | IMF_BRANCH}, {"BGEZAL", IT_I, ALU_OP_PASS_T, NOMEM, nullptr, // BGEZAL diff --git a/qtmips_machine/machinedefs.h b/qtmips_machine/machinedefs.h index 8481540..bad585f 100644 --- a/qtmips_machine/machinedefs.h +++ b/qtmips_machine/machinedefs.h @@ -62,6 +62,8 @@ enum ExceptionCause { EXCAUSE_BREAK, EXCAUSE_SYSCALL, EXCAUSE_HWBREAK, + EXCAUSE_TRAP, + EXCAUSE_OVERFLOW, }; enum AluOp : std::uint8_t { @@ -97,6 +99,12 @@ enum AluOp : std::uint8_t { ALU_OP_MADDU, ALU_OP_MSUB, ALU_OP_MSUBU, + ALU_OP_TGE, + ALU_OP_TGEU, + ALU_OP_TLT, + ALU_OP_TLTU, + ALU_OP_TEQ, + ALU_OP_TNE, ALU_OP_LUI, ALU_OP_BSHFL, ALU_OP_EXT, diff --git a/qtmips_machine/memory.h b/qtmips_machine/memory.h index b354c4c..49ae04e 100644 --- a/qtmips_machine/memory.h +++ b/qtmips_machine/memory.h @@ -133,5 +133,6 @@ private: Q_DECLARE_METATYPE(machine::AccessControl) Q_DECLARE_METATYPE(machine::Memory) +Q_DECLARE_METATYPE(machine::LocationStatus) #endif // MEMORY_H diff --git a/qtmips_machine/tests/testalu.cpp b/qtmips_machine/tests/testalu.cpp index b0e7073..4edd92a 100644 --- a/qtmips_machine/tests/testalu.cpp +++ b/qtmips_machine/tests/testalu.cpp @@ -216,6 +216,7 @@ void MachineTests::alu_data() { void MachineTests::alu() { bool discard; + enum ExceptionCause excause = EXCAUSE_NONE; QFETCH(AluOp, op); QFETCH(std::uint32_t, s); QFETCH(std::uint32_t, t); @@ -224,22 +225,80 @@ void MachineTests::alu() { QFETCH(Registers, regs_res); QFETCH(std::uint32_t, res); - QCOMPARE(alu_operate(op, s , t, sa, 0, ®s_init, discard), res); + QCOMPARE(alu_operate(op, s , t, sa, 0, ®s_init, discard, excause), res); QCOMPARE(regs_res, regs_init); + QCOMPARE(excause, EXCAUSE_NONE); } -void MachineTests::alu_except_data() { +void MachineTests::alu_trap_overflow_data() { QTest::addColumn("op"); QTest::addColumn("s"); QTest::addColumn("t"); + QTest::addColumn("excause"); // 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; + << (std::uint32_t)0x90000000 \ + << EXCAUSE_OVERFLOW; QTest::newRow("SUB") << (std::uint8_t)ALU_OP_SUB \ << (std::uint32_t)0x80000003 \ - << (std::uint32_t)4; + << (std::uint32_t)4 \ + << EXCAUSE_OVERFLOW; + QTest::newRow("TEQ") << (std::uint8_t)ALU_OP_TEQ \ + << (std::uint32_t)0x12345678 \ + << (std::uint32_t)0x12345678 \ + << EXCAUSE_TRAP; + QTest::newRow("TEQ") << (std::uint8_t)ALU_OP_TEQ \ + << (std::uint32_t)0x12345679 \ + << (std::uint32_t)0x12345678 \ + << EXCAUSE_NONE; + QTest::newRow("TNE") << (std::uint8_t)ALU_OP_TNE \ + << (std::uint32_t)0x12345678 \ + << (std::uint32_t)0x12345679 \ + << EXCAUSE_TRAP; + QTest::newRow("TNE") << (std::uint8_t)ALU_OP_TNE \ + << (std::uint32_t)0x12345678 \ + << (std::uint32_t)0x12345678 \ + << EXCAUSE_NONE; + QTest::newRow("TGE") << (std::uint8_t)ALU_OP_TGE \ + << (std::uint32_t)0x12345679 \ + << (std::uint32_t)0x12345678 \ + << EXCAUSE_TRAP; + QTest::newRow("TGEU") << (std::uint8_t)ALU_OP_TGEU \ + << (std::uint32_t)0x12345679 \ + << (std::uint32_t)0x12345678 \ + << EXCAUSE_TRAP; + QTest::newRow("TLT") << (std::uint8_t)ALU_OP_TLT \ + << (std::uint32_t)0x12345678 \ + << (std::uint32_t)0x12345679 \ + << EXCAUSE_TRAP; + QTest::newRow("TLTU") << (std::uint8_t)ALU_OP_TLTU \ + << (std::uint32_t)0x12345678 \ + << (std::uint32_t)0x12345679 \ + << EXCAUSE_TRAP; +} + +void MachineTests::alu_trap_overflow() { + bool discard; + enum ExceptionCause exc = EXCAUSE_NONE; + QFETCH(std::uint8_t, op); + QFETCH(std::uint32_t, s); + QFETCH(std::uint32_t, t); + QFETCH(enum ExceptionCause, excause); + Registers regs; + + // Only runtime exception is expected as any other exception is a bug + alu_operate((enum AluOp)op, s , t, 0, 0, ®s, discard, exc); + QCOMPARE((uint)exc, (uint)excause); +} + + +void MachineTests::alu_except_data() { + QTest::addColumn("op"); + QTest::addColumn("s"); + QTest::addColumn("t"); + // Just test that we can throw unsupported ALU operation QTest::newRow("?") << (std::uint8_t)ALU_OP_LAST \ << (std::uint32_t)0 \ @@ -248,11 +307,13 @@ void MachineTests::alu_except_data() { void MachineTests::alu_except() { bool discard; + enum ExceptionCause excause = EXCAUSE_NONE; QFETCH(std::uint8_t, op); QFETCH(std::uint32_t, s); QFETCH(std::uint32_t, t); Registers regs; // Only runtime exception is expected as any other exception is a bug - QVERIFY_EXCEPTION_THROWN(alu_operate((enum AluOp)op, s , t, 0, 0, ®s, discard), QtMipsExceptionRuntime); + QVERIFY_EXCEPTION_THROWN(alu_operate((enum AluOp)op, s , t, 0, 0, ®s, discard, excause), + QtMipsExceptionRuntime); } diff --git a/qtmips_machine/tests/tst_machine.h b/qtmips_machine/tests/tst_machine.h index 07b5e42..0f9d753 100644 --- a/qtmips_machine/tests/tst_machine.h +++ b/qtmips_machine/tests/tst_machine.h @@ -66,6 +66,8 @@ private Q_SLOTS: // Alu void alu(); void alu_data(); + void alu_trap_overflow(); + void alu_trap_overflow_data(); void alu_except(); void alu_except_data(); // Core -- cgit v1.2.3