aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Pisa <pisa@cmp.felk.cvut.cz>2019-02-17 21:15:53 +0100
committerPavel Pisa <pisa@cmp.felk.cvut.cz>2019-02-17 21:15:53 +0100
commit9d82517dea100d94fd8d0d5326ca5db7b5a1e595 (patch)
tree3cde4e7a04d40c2134509e9b675700dc5a1a2b0c
parentc58935b7587980cdf16015da5e21b5896c96678e (diff)
downloadqtmips-9d82517dea100d94fd8d0d5326ca5db7b5a1e595.tar.gz
qtmips-9d82517dea100d94fd8d0d5326ca5db7b5a1e595.tar.bz2
qtmips-9d82517dea100d94fd8d0d5326ca5db7b5a1e595.zip
Pass arithmetic exception trough pipeline and implement trap support and instructions.
Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
-rw-r--r--qtmips_machine/alu.cpp46
-rw-r--r--qtmips_machine/alu.h7
-rw-r--r--qtmips_machine/core.cpp57
-rw-r--r--qtmips_machine/instruction.cpp49
-rw-r--r--qtmips_machine/machinedefs.h8
-rw-r--r--qtmips_machine/memory.h1
-rw-r--r--qtmips_machine/tests/testalu.cpp71
-rw-r--r--qtmips_machine/tests/tst_machine.h2
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, &regs_init, discard), res);
+ QCOMPARE(alu_operate(op, s , t, sa, 0, &regs_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<std::uint8_t>("op");
QTest::addColumn<std::uint32_t>("s");
QTest::addColumn<std::uint32_t>("t");
+ QTest::addColumn<enum ExceptionCause>("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, &regs, discard, exc);
+ QCOMPARE((uint)exc, (uint)excause);
+}
+
+
+void MachineTests::alu_except_data() {
+ QTest::addColumn<std::uint8_t>("op");
+ QTest::addColumn<std::uint32_t>("s");
+ QTest::addColumn<std::uint32_t>("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, &regs, discard), QtMipsExceptionRuntime);
+ QVERIFY_EXCEPTION_THROWN(alu_operate((enum AluOp)op, s , t, 0, 0, &regs, 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