aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Pisa <pisa@cmp.felk.cvut.cz>2019-02-06 23:17:47 +0100
committerPavel Pisa <pisa@cmp.felk.cvut.cz>2019-02-06 23:17:47 +0100
commitb232bb867c1ac8cb7369190e8cd4f9f7af425cd1 (patch)
treeb02e07497de7ddf4c70fa335a06c23040559e5d7
parent9634200c1041eca1c7ac0ce25d79bb8d961530f6 (diff)
downloadqtmips-b232bb867c1ac8cb7369190e8cd4f9f7af425cd1.tar.gz
qtmips-b232bb867c1ac8cb7369190e8cd4f9f7af425cd1.tar.bz2
qtmips-b232bb867c1ac8cb7369190e8cd4f9f7af425cd1.zip
Implemented base for exception handling.
Memory stage is chosen to be exception commit stage. Instructions flow postponed and stages holding following instructions are cleaned. Processing of syscall at decode stage as jump to the handler would be better solution in real hardware but for future emulated syscalls it is better to reach consistent state of registers. Memory access caused exceptions would require cleanup even in real hardware. Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
-rw-r--r--qtmips_machine/core.cpp79
-rw-r--r--qtmips_machine/core.h7
-rw-r--r--qtmips_machine/instruction.cpp22
-rw-r--r--qtmips_machine/instruction.h2
-rw-r--r--qtmips_machine/machinedefs.h7
5 files changed, 101 insertions, 16 deletions
diff --git a/qtmips_machine/core.cpp b/qtmips_machine/core.cpp
index fb3db5c..8bf3977 100644
--- a/qtmips_machine/core.cpp
+++ b/qtmips_machine/core.cpp
@@ -67,6 +67,7 @@ struct Core::dtFetch Core::fetch() {
return {
.inst = inst,
.inst_addr = inst_addr,
+ .excause = EXCAUSE_NONE,
};
}
@@ -76,6 +77,7 @@ struct Core::dtDecode Core::decode(const struct dtFetch &dt) {
enum InstructionFlags flags;
enum AluOp alu_op;
enum AccessControl mem_ctl;
+ enum ExceptionCause excause = dt.excause;
dt.inst.flags_alu_op_mem_ctl(flags, alu_op, mem_ctl);
@@ -101,6 +103,10 @@ struct Core::dtDecode Core::decode(const struct dtFetch &dt) {
else
immediate_val = sign_extend(dt.inst.immediate());
+ if ((flags & IMF_EXCEPTION) && (excause == EXCAUSE_NONE)) {
+ excause = dt.inst.encoded_exception();
+ }
+
emit decode_instruction_value(dt.inst.data());
emit decode_reg1_value(val_rs);
emit decode_reg2_value(val_rt);
@@ -148,6 +154,8 @@ struct Core::dtDecode Core::decode(const struct dtFetch &dt) {
.rwrite = rwrite,
.ff_rs = FORWARD_NONE,
.ff_rt = FORWARD_NONE,
+ .inst_addr = dt.inst_addr,
+ .excause = excause,
};
}
@@ -189,37 +197,50 @@ struct Core::dtExecute Core::execute(const struct dtDecode &dt) {
.val_rt = dt.val_rt,
.rwrite = dt.rwrite,
.alu_val = alu_val,
+ .inst_addr = dt.inst_addr,
+ .excause = dt.excause,
};
}
struct Core::dtMemory Core::memory(const struct dtExecute &dt) {
emit instruction_memory(dt.inst);
std::uint32_t towrite_val = dt.alu_val;
+ bool memread = dt.memread;
+ bool memwrite = dt.memwrite;
+ bool regwrite = dt.regwrite;
- if (dt.memctl == AC_CACHE_OP)
- mem_data->sync();
- else if (dt.memwrite)
- mem_data->write_ctl(dt.memctl, dt.alu_val, dt.val_rt);
- else if (dt.memread)
- towrite_val = mem_data->read_ctl(dt.memctl, dt.alu_val);
+ if (dt.excause != EXCAUSE_NONE) {
+ memread = false;
+ memwrite = false;
+ regwrite = false;
+ } else {
+ if (dt.memctl == AC_CACHE_OP)
+ mem_data->sync();
+ else if (memwrite)
+ mem_data->write_ctl(dt.memctl, dt.alu_val, dt.val_rt);
+ else if (memread)
+ towrite_val = mem_data->read_ctl(dt.memctl, dt.alu_val);
+ }
emit memory_alu_value(dt.alu_val);
emit memory_rt_value(dt.val_rt);
- emit memory_mem_value(dt.memread ? towrite_val : 0);
- emit memory_regw_value(dt.regwrite);
+ emit memory_mem_value(memread ? towrite_val : 0);
+ emit memory_regw_value(regwrite);
emit memory_memtoreg_value(dt.memread);
emit memory_memread_value(dt.memread);
- emit memory_memwrite_value(dt.memwrite);
+ emit memory_memwrite_value(memwrite);
emit memory_regw_num_value(dt.rwrite);
- if (dt.inst.is_break())
+ if (dt.excause == EXCAUSE_BREAK)
emit memory_break_reached();
return {
.inst = dt.inst,
- .regwrite = dt.regwrite,
+ .regwrite = regwrite,
.rwrite = dt.rwrite,
.towrite_val = towrite_val,
+ .inst_addr = dt.inst_addr,
+ .excause = dt.excause,
};
}
@@ -275,6 +296,7 @@ void Core::handle_pc(const struct dtDecode &dt) {
void Core::dtFetchInit(struct dtFetch &dt) {
dt.inst = Instruction(0x00);
+ dt.excause = EXCAUSE_NONE;
}
void Core::dtDecodeInit(struct dtDecode &dt) {
@@ -296,6 +318,8 @@ void Core::dtDecodeInit(struct dtDecode &dt) {
dt.immediate_val = 0;
dt.ff_rs = FORWARD_NONE;
dt.ff_rt = FORWARD_NONE;
+ dt.inst_addr = 0;
+ dt.excause = EXCAUSE_NONE;
}
void Core::dtExecuteInit(struct dtExecute &dt) {
@@ -307,6 +331,8 @@ void Core::dtExecuteInit(struct dtExecute &dt) {
dt.val_rt = 0;
dt.rwrite = 0;
dt.alu_val = 0;
+ dt.inst_addr = 0;
+ dt.excause = EXCAUSE_NONE;
}
void Core::dtMemoryInit(struct dtMemory &dt) {
@@ -314,6 +340,8 @@ void Core::dtMemoryInit(struct dtMemory &dt) {
dt.regwrite = false;
dt.rwrite = false;
dt.towrite_val = 0;
+ dt.inst_addr = 0;
+ dt.excause = EXCAUSE_NONE;
}
CoreSingle::CoreSingle(Registers *regs, MemoryAccess *mem_program, MemoryAccess *mem_data, bool jmp_delay_slot) : \
@@ -335,7 +363,16 @@ void CoreSingle::do_step() {
struct dtDecode d = decode(f);
struct dtExecute e = execute(d);
struct dtMemory m = memory(e);
+
+ if (m.excause != EXCAUSE_NONE) {
+ regs->pc_abs_jmp(m.inst_addr + 4);
+ if (jmp_delay_decode != nullptr)
+ dtDecodeInit(*jmp_delay_decode);
+ return;
+ }
+
writeback(m);
+
if (jmp_delay_decode != nullptr) {
handle_pc(*jmp_delay_decode);
*jmp_delay_decode = d; // Copy current decode
@@ -355,14 +392,30 @@ CorePipelined::CorePipelined(Registers *regs, MemoryAccess *mem_program, MemoryA
}
void CorePipelined::do_step() {
+ bool stall = false;
+ bool excpt_in_progress = false;
+
// Process stages
writeback(dt_m);
dt_m = memory(dt_e);
dt_e = execute(dt_d);
dt_d = decode(dt_f);
- // TODO signals
- bool stall = false;
+ // Resolve exceptions
+ excpt_in_progress = dt_m.excause != EXCAUSE_NONE;
+ if (excpt_in_progress)
+ dtExecuteInit(dt_e);
+ excpt_in_progress = excpt_in_progress || dt_e.excause != EXCAUSE_NONE;
+ if (excpt_in_progress)
+ dtDecodeInit(dt_d);
+ excpt_in_progress = excpt_in_progress || dt_e.excause != EXCAUSE_NONE;
+ if (excpt_in_progress) {
+ dtFetchInit(dt_f);
+ if (dt_m.excause != EXCAUSE_NONE) {
+ regs->pc_abs_jmp(dt_m.inst_addr + 4);
+ }
+ return;
+ }
dt_d.ff_rs = FORWARD_NONE;
dt_d.ff_rt = FORWARD_NONE;
diff --git a/qtmips_machine/core.h b/qtmips_machine/core.h
index ac0ee81..d5ce4fb 100644
--- a/qtmips_machine/core.h
+++ b/qtmips_machine/core.h
@@ -125,6 +125,7 @@ protected:
struct dtFetch {
Instruction inst; // Loaded instruction
uint32_t inst_addr; // Address of instruction
+ enum ExceptionCause excause;
};
struct dtDecode {
Instruction inst;
@@ -152,6 +153,8 @@ protected:
std::uint8_t rwrite; // Writeback register (multiplexed between rt and rd according to regd)
ForwardFrom ff_rs;
ForwardFrom ff_rt;
+ uint32_t inst_addr; // Address of instruction
+ enum ExceptionCause excause;
};
struct dtExecute {
Instruction inst;
@@ -162,12 +165,16 @@ protected:
std::uint32_t val_rt;
std::uint8_t rwrite; // Writeback register (multiplexed between rt and rd according to regd)
std::uint32_t alu_val; // Result of ALU execution
+ uint32_t inst_addr; // Address of instruction
+ enum ExceptionCause excause;
};
struct dtMemory {
Instruction inst;
bool regwrite;
std::uint8_t rwrite;
std::uint32_t towrite_val;
+ uint32_t inst_addr; // Address of instruction
+ enum ExceptionCause excause;
};
struct dtFetch fetch();
diff --git a/qtmips_machine/instruction.cpp b/qtmips_machine/instruction.cpp
index ed9064b..9eae5f3 100644
--- a/qtmips_machine/instruction.cpp
+++ b/qtmips_machine/instruction.cpp
@@ -106,9 +106,10 @@ static const struct InstructionMap alu_instruction_map[] = {
.flags = FLAGS_ALU_T_R_STD},
{"MOVN", IT_R, ALU_OP_MOVN, NOMEM, nullptr,
.flags = FLAGS_ALU_T_R_STD},
- IM_UNKNOWN,
+ {"SYSCALL",IT_R, ALU_OP_SYSCALL, NOMEM, nullptr,
+ .flags = IMF_SUPPORTED | IMF_EXCEPTION},
{"BREAK", IT_R, ALU_OP_BREAK, NOMEM, nullptr,
- .flags = IMF_SUPPORTED},
+ .flags = IMF_SUPPORTED | IMF_EXCEPTION},
IM_UNKNOWN,
IM_UNKNOWN,
{"MFHI", IT_R, ALU_OP_MFHI, NOMEM, nullptr,
@@ -426,8 +427,23 @@ void Instruction::flags_alu_op_mem_ctl(enum InstructionFlags &flags,
mem_ctl = im.mem_ctl;
}
+enum ExceptionCause Instruction::encoded_exception() const {
+ const struct InstructionMap &im = InstructionMapFind(dt);
+ if (!(im.flags & IMF_EXCEPTION))
+ return EXCAUSE_NONE;
+ switch (im.alu) {
+ case ALU_OP_BREAK:
+ return EXCAUSE_BREAK;
+ case ALU_OP_SYSCALL:
+ return EXCAUSE_SYSCALL;
+ default:
+ return EXCAUSE_NONE;
+ }
+}
+
bool Instruction::is_break() const {
- return opcode() == 0 && funct() == ALU_OP_BREAK;
+ const struct InstructionMap &im = InstructionMapFind(dt);
+ return im.alu == ALU_OP_BREAK;
}
bool Instruction::operator==(const Instruction &c) const {
diff --git a/qtmips_machine/instruction.h b/qtmips_machine/instruction.h
index a8779f5..b26d9ac 100644
--- a/qtmips_machine/instruction.h
+++ b/qtmips_machine/instruction.h
@@ -67,6 +67,7 @@ enum InstructionFlags {
IMF_JUMP = 1L<<19,
IMF_BJ_NOT = 1L<<20,
IMF_BGTZ_BLEZ = 1L<<21,
+ IMF_EXCEPTION = 1L<<22,
};
class Instruction {
@@ -98,6 +99,7 @@ public:
enum InstructionFlags flags() const;
enum AluOp alu_op() const;
enum AccessControl mem_ctl() const;
+ enum ExceptionCause encoded_exception() const;
void flags_alu_op_mem_ctl(enum InstructionFlags &flags,
enum AluOp &alu_op, enum AccessControl &mem_ctl) const;
diff --git a/qtmips_machine/machinedefs.h b/qtmips_machine/machinedefs.h
index 2e419a8..f4a4a81 100644
--- a/qtmips_machine/machinedefs.h
+++ b/qtmips_machine/machinedefs.h
@@ -50,6 +50,12 @@ enum AccessControl {
AC_CACHE_OP,
};
+enum ExceptionCause {
+ EXCAUSE_NONE,
+ EXCAUSE_BREAK,
+ EXCAUSE_SYSCALL,
+};
+
enum AluOp : std::uint8_t {
ALU_OP_SLL = 0,
ALU_OP_SRL = 2,
@@ -82,6 +88,7 @@ enum AluOp : std::uint8_t {
ALU_OP_SLTU,
ALU_OP_LUI = 64, // We don't care about exact index for this one
ALU_OP_PASS_T, // Pass t argument without change for JAL
+ ALU_OP_SYSCALL,
ALU_OP_UNKNOWN,
ALU_OP_LAST // First impossible operation (just to be sure that we don't overflow)
};