diff options
| author | Pavel Pisa <pisa@cmp.felk.cvut.cz> | 2019-02-17 21:15:53 +0100 | 
|---|---|---|
| committer | Pavel Pisa <pisa@cmp.felk.cvut.cz> | 2019-02-17 21:15:53 +0100 | 
| commit | 9d82517dea100d94fd8d0d5326ca5db7b5a1e595 (patch) | |
| tree | 3cde4e7a04d40c2134509e9b675700dc5a1a2b0c /qtmips_machine | |
| parent | c58935b7587980cdf16015da5e21b5896c96678e (diff) | |
| download | qtmips-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>
Diffstat (limited to 'qtmips_machine')
| -rw-r--r-- | qtmips_machine/alu.cpp | 46 | ||||
| -rw-r--r-- | qtmips_machine/alu.h | 7 | ||||
| -rw-r--r-- | qtmips_machine/core.cpp | 57 | ||||
| -rw-r--r-- | qtmips_machine/instruction.cpp | 49 | ||||
| -rw-r--r-- | qtmips_machine/machinedefs.h | 8 | ||||
| -rw-r--r-- | qtmips_machine/memory.h | 1 | ||||
| -rw-r--r-- | qtmips_machine/tests/testalu.cpp | 71 | ||||
| -rw-r--r-- | 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<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, ®s, 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, ®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 | 
