[WIP] Add hazard unit to RV5S
MODIFY
src/processors/RISC-V/rv5s/rv5s.h
src/processors/RISC-V/rv5s/rv5s.h
@@ -42,6 +42,8 @@ public:
pc_reg->out >> pc_4->op1;
4 >> pc_4->op2;
pc_src->out >> pc_reg->in;
+ 0 >> pc_reg->clear;
+ hzunit->hazardEnable >> pc_reg->enable;
// Note: pc_src works uses the PcSrc enum, but is selected by the boolean signal
// from the controlflow OR gate. PcSrc enum values must adhere to the boolean
@@ -137,21 +139,23 @@ public:
// -----------------------------------------------------------------------
// Ecall checker
- decode->opcode >> ecallChecker->opcode;
+
+ idex_reg->opcode_out >> ecallChecker->opcode;
ecallChecker->setSysCallSignal(&handleSysCall);
+ hzunit->stallEcallHandling >> ecallChecker->stallEcallHandling;
// -----------------------------------------------------------------------
// IF/ID
pc_4->out >> ifid_reg->pc4_in;
pc_reg->out >> ifid_reg->pc_in;
instr_mem->data_out >> ifid_reg->instr_in;
- 1 >> ifid_reg->enable;
+ hzunit->hazardEnable >> ifid_reg->enable;
efsc_or->out >> ifid_reg->clear;
1 >> ifid_reg->valid_in; // Always valid unless register is cleared
// -----------------------------------------------------------------------
// ID/EX
- 1 >> idex_reg->enable;
+ hzunit->hazardEnable >> idex_reg->enable;
controlflow_or->out >> idex_reg->clear;
// Data
@@ -175,11 +179,15 @@ public:
control->do_jump >> idex_reg->do_jmp_in;
decode->r1_reg_idx >> idex_reg->rd_reg1_idx_in;
decode->r2_reg_idx >> idex_reg->rd_reg2_idx_in;
+ decode->opcode >> idex_reg->opcode_in;
+ control->mem_do_read_ctrl >> idex_reg->mem_do_read_in;
ifid_reg->valid_out >> idex_reg->valid_in;
// -----------------------------------------------------------------------
// EX/MEM
+ 1 >> exmem_reg->enable;
+ hzunit->hazardClear >> exmem_reg->clear;
// Data
idex_reg->pc_out >> exmem_reg->pc_in;
@@ -192,6 +200,7 @@ public:
idex_reg->wr_reg_idx_out >> exmem_reg->wr_reg_idx_in;
idex_reg->reg_do_write_out >> exmem_reg->reg_do_write_in;
idex_reg->mem_do_write_out >> exmem_reg->mem_do_write_in;
+ idex_reg->mem_do_read_out >> exmem_reg->mem_do_read_in;
idex_reg->mem_op_out >> exmem_reg->mem_op_in;
idex_reg->valid_out >> exmem_reg->valid_in;
@@ -225,6 +234,15 @@ public:
// -----------------------------------------------------------------------
// Hazard detection unit
+ idex_reg->rd_reg1_idx_out >> hzunit->id_reg1_idx;
+ idex_reg->rd_reg2_idx_out >> hzunit->id_reg2_idx;
+
+ exmem_reg->wr_reg_idx_out >> hzunit->mem_reg_wr_idx;
+ exmem_reg->mem_do_read_out >> hzunit->mem_do_read_en;
+ exmem_reg->reg_do_write_out >> hzunit->mem_reg_do_write;
+
+ memwb_reg->reg_do_write_out >> hzunit->wb_reg_do_write;
+ idex_reg->opcode_out >> hzunit->opcode;
}
// Design subcomponents
@@ -237,7 +255,7 @@ public:
SUBCOMPONENT(pc_4, Adder<RV_REG_WIDTH>);
// Registers
- SUBCOMPONENT(pc_reg, Register<RV_REG_WIDTH>);
+ SUBCOMPONENT(pc_reg, RegisterClEn<RV_REG_WIDTH>);
// Stage seperating registers
SUBCOMPONENT(ifid_reg, IFID);

MODIFY
src/processors/RISC-V/rv5s/rv5s_hazardunit.h
src/processors/RISC-V/rv5s/rv5s_hazardunit.h
@@ -10,7 +10,51 @@ using namespace Ripes;
class HazardUnit : public Component {
public:
- HazardUnit(std::string name, SimComponent* parent) : Component(name, parent) {}
+ HazardUnit(std::string name, SimComponent* parent) : Component(name, parent) {
+ hazardEnable << [=] { return !hasHazard(); };
+ hazardClear << [=] { return hasHazard(); };
+ stallEcallHandling << [=] { return hasEcallHazard(); };
+ }
+
+ INPUTPORT(id_reg1_idx, RV_REGS_BITS);
+ INPUTPORT(id_reg2_idx, RV_REGS_BITS);
+
+ INPUTPORT(mem_reg_wr_idx, RV_REGS_BITS);
+ INPUTPORT(mem_do_read_en, 1);
+ INPUTPORT(mem_reg_do_write, 1);
+
+ INPUTPORT(wb_reg_do_write, 1);
+
+ INPUTPORT_ENUM(opcode, RVInstr);
+
+ // Hazard Enable: Low when stalling (shall be connected to a register 'enable' input port
+ OUTPUTPORT(hazardEnable, 1);
+ // Hazard clear: High when stalling (shall be connected to a register 'clear' input port.
+ OUTPUTPORT(hazardClear, 1);
+ // Stall Ecall Handling: High whenever we are about to handle an ecall, but have outstanding writes in the pipeline
+ // which must be comitted to the register file before handling the ecall.
+ OUTPUTPORT(stallEcallHandling, 1);
+
+private:
+ bool hasHazard() { return hasLoadUseHazard() || hasEcallHazard(); }
+
+ bool hasLoadUseHazard() const {
+ const unsigned midx = mem_reg_wr_idx.uValue();
+ const unsigned idx1 = id_reg1_idx.uValue();
+ const unsigned idx2 = id_reg2_idx.uValue();
+ const bool mrd = mem_do_read_en.uValue();
+
+ return (midx == idx1 || midx == idx2) && mrd;
+ }
+
+ bool hasEcallHazard() const {
+ // Check for ECALL hazard. We are implictly dependent on all registers when performing an ECALL operation. As
+ // such, all outstanding writes to the register file must be performed before handling the ecall. Hence, the
+ // front-end of the pipeline shall be stalled until the remainder of the pipeline has been cleared and there are
+ // no more outstanding writes.
+ const bool isEcall = opcode.uValue() == RVInstr::ECALL;
+ return isEcall && (mem_reg_do_write.uValue() || wb_reg_do_write.uValue());
+ }
};
} // namespace core
} // namespace vsrtl

MODIFY
src/processors/RISC-V/rv5s/rv5s_idex.h
src/processors/RISC-V/rv5s/rv5s_idex.h
@@ -21,10 +21,12 @@ public:
RV5S_IDEX(std::string name, SimComponent* parent) : IDEX(name, parent) {
CONNECT_REGISTERED_CLEN_INPUT(rd_reg1_idx, clear, enable);
CONNECT_REGISTERED_CLEN_INPUT(rd_reg2_idx, clear, enable);
+ CONNECT_REGISTERED_CLEN_INPUT(opcode, clear, enable);
}
REGISTERED_CLEN_INPUT(rd_reg1_idx, RV_REGS_BITS);
REGISTERED_CLEN_INPUT(rd_reg2_idx, RV_REGS_BITS);
+ REGISTERED_CLEN_INPUT(opcode, RVInstr::width());
};
} // namespace core

MODIFY
src/processors/RISC-V/rv5swof/rv5swof.h
src/processors/RISC-V/rv5swof/rv5swof.h
@@ -121,6 +121,7 @@ public:
// Ecall checker
decode->opcode >> ecallChecker->opcode;
ecallChecker->setSysCallSignal(&handleSysCall);
+ 0 >> ecallChecker->stallEcallHandling;
// -----------------------------------------------------------------------
// IF/ID
@@ -150,6 +151,7 @@ public:
control->alu_op1_ctrl >> idex_reg->alu_op1_ctrl_in;
control->alu_op2_ctrl >> idex_reg->alu_op2_ctrl_in;
control->mem_do_write_ctrl >> idex_reg->mem_do_write_in;
+ control->mem_do_read_ctrl >> idex_reg->mem_do_read_in;
control->alu_ctrl >> idex_reg->alu_ctrl_in;
control->mem_ctrl >> idex_reg->mem_op_in;
control->comp_ctrl >> idex_reg->br_op_in;
@@ -160,6 +162,8 @@ public:
// -----------------------------------------------------------------------
// EX/MEM
+ 0 >> exmem_reg->clear;
+ 1 >> exmem_reg->enable;
// Data
idex_reg->pc_out >> exmem_reg->pc_in;
@@ -173,6 +177,7 @@ public:
idex_reg->reg_do_write_out >> exmem_reg->reg_do_write_in;
idex_reg->mem_do_write_out >> exmem_reg->mem_do_write_in;
idex_reg->mem_op_out >> exmem_reg->mem_op_in;
+ idex_reg->mem_do_read_out >> exmem_reg->mem_do_read_in;
idex_reg->valid_out >> exmem_reg->valid_in;

MODIFY
src/processors/RISC-V/rv5swof/rv5swof_exmem.h
src/processors/RISC-V/rv5swof/rv5swof_exmem.h
@@ -12,36 +12,42 @@ using namespace Ripes;
class EXMEM : public Component {
public:
EXMEM(std::string name, SimComponent* parent) : Component(name, parent) {
- CONNECT_REGISTERED_INPUT(pc);
- CONNECT_REGISTERED_INPUT(pc4);
- CONNECT_REGISTERED_INPUT(alures);
- CONNECT_REGISTERED_INPUT(r2);
-
- CONNECT_REGISTERED_INPUT(reg_wr_src_ctrl);
- CONNECT_REGISTERED_INPUT(wr_reg_idx);
- CONNECT_REGISTERED_INPUT(reg_do_write);
- CONNECT_REGISTERED_INPUT(mem_do_write);
- CONNECT_REGISTERED_INPUT(mem_op);
-
- CONNECT_REGISTERED_INPUT(valid);
+ CONNECT_REGISTERED_CLEN_INPUT(pc, clear, enable);
+ CONNECT_REGISTERED_CLEN_INPUT(pc4, clear, enable);
+ CONNECT_REGISTERED_CLEN_INPUT(alures, clear, enable);
+ CONNECT_REGISTERED_CLEN_INPUT(r2, clear, enable);
+
+ CONNECT_REGISTERED_CLEN_INPUT(reg_wr_src_ctrl, clear, enable);
+ CONNECT_REGISTERED_CLEN_INPUT(wr_reg_idx, clear, enable);
+ CONNECT_REGISTERED_CLEN_INPUT(reg_do_write, clear, enable);
+ CONNECT_REGISTERED_CLEN_INPUT(mem_do_write, clear, enable);
+ CONNECT_REGISTERED_CLEN_INPUT(mem_do_read, clear, enable);
+ CONNECT_REGISTERED_CLEN_INPUT(mem_op, clear, enable);
+
+ CONNECT_REGISTERED_CLEN_INPUT(valid, clear, enable);
}
// Data
- REGISTERED_INPUT(pc, RV_REG_WIDTH);
- REGISTERED_INPUT(pc4, RV_REG_WIDTH);
- REGISTERED_INPUT(alures, RV_REG_WIDTH);
- REGISTERED_INPUT(r2, RV_REG_WIDTH);
+ REGISTERED_CLEN_INPUT(pc, RV_REG_WIDTH);
+ REGISTERED_CLEN_INPUT(pc4, RV_REG_WIDTH);
+ REGISTERED_CLEN_INPUT(alures, RV_REG_WIDTH);
+ REGISTERED_CLEN_INPUT(r2, RV_REG_WIDTH);
// Control
- REGISTERED_INPUT(reg_wr_src_ctrl, RegWrSrc::width());
- REGISTERED_INPUT(wr_reg_idx, RV_REGS_BITS);
- REGISTERED_INPUT(reg_do_write, 1);
- REGISTERED_INPUT(mem_do_write, 1);
- REGISTERED_INPUT(mem_op, MemOp::width());
+ REGISTERED_CLEN_INPUT(reg_wr_src_ctrl, RegWrSrc::width());
+ REGISTERED_CLEN_INPUT(wr_reg_idx, RV_REGS_BITS);
+ REGISTERED_CLEN_INPUT(reg_do_write, 1);
+ REGISTERED_CLEN_INPUT(mem_do_write, 1);
+ REGISTERED_CLEN_INPUT(mem_do_read, 1);
+ REGISTERED_CLEN_INPUT(mem_op, MemOp::width());
+
+ // Register bank controls
+ INPUTPORT(enable, 1);
+ INPUTPORT(clear, 1);
// Valid signal. False when the register bank has been cleared. May be used by UI to determine whether the NOP in
// the stage is a user-inserted nop or the result of some pipeline action.
- REGISTERED_INPUT(valid, 1);
+ REGISTERED_CLEN_INPUT(valid, 1);
};
} // namespace core

MODIFY
src/processors/RISC-V/rv5swof/rv5swof_idex.h
src/processors/RISC-V/rv5swof/rv5swof_idex.h
@@ -24,6 +24,7 @@ public:
CONNECT_REGISTERED_CLEN_INPUT(alu_op1_ctrl, clear, enable);
CONNECT_REGISTERED_CLEN_INPUT(alu_op2_ctrl, clear, enable);
CONNECT_REGISTERED_CLEN_INPUT(mem_do_write, clear, enable);
+ CONNECT_REGISTERED_CLEN_INPUT(mem_do_read, clear, enable);
CONNECT_REGISTERED_CLEN_INPUT(alu_ctrl, clear, enable);
CONNECT_REGISTERED_CLEN_INPUT(mem_op, clear, enable);
CONNECT_REGISTERED_CLEN_INPUT(br_op, clear, enable);
@@ -48,6 +49,7 @@ public:
REGISTERED_CLEN_INPUT(alu_op2_ctrl, AluSrc2::width());
REGISTERED_CLEN_INPUT(alu_ctrl, ALUOp::width());
REGISTERED_CLEN_INPUT(mem_do_write, 1);
+ REGISTERED_CLEN_INPUT(mem_do_read, 1);
REGISTERED_CLEN_INPUT(mem_op, MemOp::width());
REGISTERED_CLEN_INPUT(br_op, CompOp::width());
REGISTERED_CLEN_INPUT(do_br, 1);

MODIFY
src/processors/RISC-V/rv_control.h
src/processors/RISC-V/rv_control.h
@@ -210,6 +210,14 @@ public:
}
};
+ mem_do_read_ctrl << [=] {
+ switch(opcode.uValue()) {
+ case RVInstr::LB: case RVInstr::LH: case RVInstr::LW:
+ return 1;
+ default: return 0;
+ }
+ };
+
// clang-format on
}
@@ -217,6 +225,7 @@ public:
OUTPUTPORT(reg_do_write_ctrl, 1);
OUTPUTPORT(mem_do_write_ctrl, 1);
+ OUTPUTPORT(mem_do_read_ctrl, 1);
OUTPUTPORT(do_branch, 1);
OUTPUTPORT(do_jump, 1);
OUTPUTPORT_ENUM(comp_ctrl, CompOp);

MODIFY
src/processors/RISC-V/rv_ecallchecker.h
src/processors/RISC-V/rv_ecallchecker.h
@@ -13,7 +13,7 @@ class EcallChecker : public Component {
public:
EcallChecker(std::string name, SimComponent* parent) : Component(name, parent) {
dummy << [=] {
- if (opcode.uValue() == RVInstr::ECALL) {
+ if (opcode.uValue() == RVInstr::ECALL && !stallEcallHandling.uValue()) {
m_signal->Emit();
}
return 0;
@@ -33,6 +33,12 @@ public:
Gallant::Signal0<>* m_signal;
INPUTPORT_ENUM(opcode, RVInstr);
+
+ // Handling an ecall puts an implicit dependence on all outstanding writes to any register being performed before
+ // the ecall is handled. As such, the hazard unit may stall handling of the Ecall until all outstanding writes have
+ // been perfomed.
+ INPUTPORT(stallEcallHandling, 1);
+
OUTPUTPORT(dummy, 1);
OUTPUTPORT(syscallExit, 1);

MODIFY
src/processors/RISC-V/rvss/rvss.h
src/processors/RISC-V/rvss/rvss.h
@@ -109,6 +109,7 @@ public:
// Ecall checker
decode->opcode >> ecallChecker->opcode;
ecallChecker->setSysCallSignal(&handleSysCall);
+ 0 >> ecallChecker->stallEcallHandling;
}
// Design subcomponents

[WIP] Add hazard unit to RV5S