Significant overhaul of trap handling. Exceptions now taken from stage 3 instead of stage 2
This commit is contained in:
parent
5e61c9f9ac
commit
1b252d4bda
25
Readme.md
25
Readme.md
|
@ -36,3 +36,28 @@ On Hazard3 the expectation is for all jumps and taken branches to take 2 cycles,
|
|||
- Don't half-ass exceptions -- particularly things like instruction fetch memory fault
|
||||
- Debug
|
||||
- Don't half-ass CSRs
|
||||
- WFI instruction
|
||||
|
||||
|
||||
# Exceptions
|
||||
|
||||
Exceptions have a number of sources:
|
||||
|
||||
- Instruction fetch (hresp captured and piped through to decode, flushed if fetch speculation was incorrect)
|
||||
- Instruction decode (invalid instructions, or exception-causing instructions like ecall)
|
||||
- CSR address decode
|
||||
- Load/store address alignment (address phase)
|
||||
- Load/store bus error (data phase)
|
||||
- External interrupts
|
||||
- Internal interrupts (timers etc)
|
||||
- Debugger breakpoints
|
||||
- Debugger single-step
|
||||
|
||||
Out of these the most troublesome is probably load/store bus error, as it *must* be associated with stage 3 of the pipeline, not with stage 2.
|
||||
|
||||
Therefore it may be best to take the exception branch from stage 3, kind of like a branch mispredict. This flush signal would inhibit side-effecting instructions in stage 2. In particular, the case of a load/store in stage 2 with a faulting load/store in stage 3. The sequence of events there is probably:
|
||||
|
||||
- Cycles m through n (maybe): data phase stall of instruction in stage 3
|
||||
- Cycle n + 1: first cycle of error response. Error is registered locally.
|
||||
- Cycle n + 2: Second cycle of error response. Exception branch is generated, and load/store in stage 2 is suppressed based on the registered flag.
|
||||
|
||||
|
|
|
@ -57,21 +57,6 @@ module hazard3_core #(
|
|||
|
||||
`include "hazard3_ops.vh"
|
||||
|
||||
`ifdef FORMAL
|
||||
// Only yosys-smtbmc seems to support immediate assertions
|
||||
`ifdef RISCV_FORMAL
|
||||
`define ASSERT(x)
|
||||
`else
|
||||
`define ASSERT(x) assert(x)
|
||||
`endif
|
||||
`else
|
||||
`define ASSERT(x)
|
||||
//synthesis translate_off
|
||||
`undef ASSERT
|
||||
`define ASSERT(x) if (!x) begin $display("Assertion failed!"); $finish(1); end
|
||||
//synthesis translate_on
|
||||
`endif
|
||||
|
||||
wire d_stall;
|
||||
wire x_stall;
|
||||
wire m_stall;
|
||||
|
@ -235,10 +220,9 @@ wire [W_DATA-1:0] x_alu_result;
|
|||
wire [W_DATA-1:0] x_alu_add;
|
||||
wire x_alu_cmp;
|
||||
|
||||
wire [W_DATA-1:0] x_trap_addr;
|
||||
wire [W_DATA-1:0] x_mepc;
|
||||
wire x_trap_enter;
|
||||
wire x_trap_exit;
|
||||
wire [W_DATA-1:0] m_trap_addr;
|
||||
wire m_trap_enter_vld;
|
||||
wire m_trap_enter_rdy = f_jump_rdy;
|
||||
|
||||
reg [W_REGADDR-1:0] xm_rs1;
|
||||
reg [W_REGADDR-1:0] xm_rs2;
|
||||
|
@ -246,15 +230,17 @@ reg [W_REGADDR-1:0] xm_rd;
|
|||
reg [W_DATA-1:0] xm_result;
|
||||
reg [W_DATA-1:0] xm_store_data;
|
||||
reg [W_MEMOP-1:0] xm_memop;
|
||||
reg [W_EXCEPT-1:0] xm_except;
|
||||
|
||||
reg x_stall_raw;
|
||||
wire x_stall_muldiv;
|
||||
wire x_jump_req;
|
||||
|
||||
assign x_stall =
|
||||
m_stall ||
|
||||
x_stall_raw || x_stall_muldiv ||
|
||||
bus_aph_req_d && !bus_aph_ready_d ||
|
||||
f_jump_req && !f_jump_rdy;
|
||||
x_jump_req && !f_jump_rdy;
|
||||
|
||||
wire m_fast_mul_result_vld;
|
||||
wire m_generating_result = xm_memop < MEMOP_SW || m_fast_mul_result_vld;
|
||||
|
@ -328,9 +314,6 @@ wire x_unaligned_addr =
|
|||
bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] ||
|
||||
bus_hsize_d == HSIZE_HWORD && bus_haddr_d[0];
|
||||
|
||||
wire x_except_load_misaligned = x_memop_vld && x_unaligned_addr && !x_memop_write;
|
||||
wire x_except_store_misaligned = x_memop_vld && x_unaligned_addr && x_memop_write;
|
||||
|
||||
always @ (*) begin
|
||||
// Need to be careful not to use anything hready-sourced to gate htrans!
|
||||
bus_haddr_d = x_alu_add;
|
||||
|
@ -343,70 +326,9 @@ always @ (*) begin
|
|||
MEMOP_SH: bus_hsize_d = HSIZE_HWORD;
|
||||
default: bus_hsize_d = HSIZE_BYTE;
|
||||
endcase
|
||||
bus_aph_req_d = x_memop_vld && !(x_stall_raw || x_trap_enter);
|
||||
bus_aph_req_d = x_memop_vld && !(x_stall_raw || m_trap_enter_vld);
|
||||
end
|
||||
|
||||
// CSRs and Trap Handling
|
||||
|
||||
wire x_except_ecall = d_except == EXCEPT_ECALL;
|
||||
wire x_except_breakpoint = d_except == EXCEPT_EBREAK;
|
||||
wire x_except_invalid_instr = d_except == EXCEPT_INSTR_ILLEGAL;
|
||||
assign x_trap_exit = d_except == EXCEPT_MRET;
|
||||
wire x_trap_enter_rdy = !(x_stall || x_trap_exit);
|
||||
wire x_trap_is_exception; // diagnostic
|
||||
|
||||
`ifdef FORMAL
|
||||
always @ (posedge clk) begin
|
||||
if (x_trap_exit)
|
||||
assert(!bus_aph_req_d);
|
||||
end
|
||||
`endif
|
||||
|
||||
wire [W_DATA-1:0] x_csr_wdata = d_csr_w_imm ?
|
||||
{{W_DATA-5{1'b0}}, d_rs1} : x_rs1_bypass;
|
||||
|
||||
wire [W_DATA-1:0] x_csr_rdata;
|
||||
|
||||
hazard3_csr #(
|
||||
.XLEN (W_DATA),
|
||||
`include "hazard3_config_inst.vh"
|
||||
) inst_hazard3_csr (
|
||||
.clk (clk),
|
||||
.rst_n (rst_n),
|
||||
// CSR access port
|
||||
// *en_soon are early access strobes which are not a function of bus stall.
|
||||
// Can generate access faults (hence traps), but do not actually perform access.
|
||||
.addr (d_imm[11:0]), // todo could just connect this to the instruction bits
|
||||
.wdata (x_csr_wdata),
|
||||
.wen_soon (d_csr_wen),
|
||||
.wen (d_csr_wen && !x_stall),
|
||||
.wtype (d_csr_wtype),
|
||||
.rdata (x_csr_rdata),
|
||||
.ren_soon (d_csr_ren),
|
||||
.ren (d_csr_ren && !x_stall),
|
||||
// Trap signalling
|
||||
.trap_addr (x_trap_addr),
|
||||
.trap_enter_vld (x_trap_enter),
|
||||
.trap_enter_rdy (x_trap_enter_rdy),
|
||||
.trap_exit (x_trap_exit && !x_stall),
|
||||
.trap_is_exception (x_trap_is_exception),
|
||||
.mepc_in (d_pc),
|
||||
.mepc_out (x_mepc),
|
||||
// IRQ and exception requests
|
||||
.irq (irq),
|
||||
.except_instr_misaligned (1'b0), // TODO
|
||||
.except_instr_fault (1'b0), // TODO
|
||||
.except_instr_invalid (x_except_invalid_instr),
|
||||
.except_breakpoint (x_except_breakpoint),
|
||||
.except_load_misaligned (x_except_load_misaligned),
|
||||
.except_load_fault (1'b0), // TODO
|
||||
.except_store_misaligned (x_except_store_misaligned),
|
||||
.except_store_fault (1'b0), // TODO
|
||||
.except_ecall (x_except_ecall),
|
||||
// Other CSR-specific signalling
|
||||
.instr_ret (1'b0) // TODO
|
||||
);
|
||||
|
||||
// Multiply/divide
|
||||
|
||||
wire [W_DATA-1:0] x_muldiv_result;
|
||||
|
@ -427,7 +349,7 @@ if (EXTENSION_M) begin: has_muldiv
|
|||
else
|
||||
x_muldiv_posted <= (x_muldiv_posted || (x_muldiv_op_vld && x_muldiv_op_rdy)) && x_stall;
|
||||
|
||||
wire x_muldiv_kill = x_trap_enter; // TODO this takes an extra cycle to kill muldiv before trap entry
|
||||
wire x_muldiv_kill = m_trap_enter_vld;
|
||||
|
||||
wire x_use_fast_mul = MUL_FAST && d_aluop == ALUOP_MULDIV && d_mulop == M_OP_MUL;
|
||||
|
||||
|
@ -501,21 +423,86 @@ end else begin: no_muldiv
|
|||
end
|
||||
endgenerate
|
||||
|
||||
// State machine
|
||||
// CSRs and Trap Handling
|
||||
|
||||
wire [W_DATA-1:0] x_csr_wdata = d_csr_w_imm ?
|
||||
{{W_DATA-5{1'b0}}, d_rs1} : x_rs1_bypass;
|
||||
|
||||
wire [W_DATA-1:0] x_csr_rdata;
|
||||
wire x_csr_illegal_access;
|
||||
|
||||
reg prev_instr_was_32_bit;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
prev_instr_was_32_bit <= 1'b0;
|
||||
end else if (!x_stall) begin
|
||||
prev_instr_was_32_bit <= df_cir_use == 2'd2;
|
||||
end
|
||||
end
|
||||
|
||||
hazard3_csr #(
|
||||
.XLEN (W_DATA),
|
||||
`include "hazard3_config_inst.vh"
|
||||
) inst_hazard3_csr (
|
||||
.clk (clk),
|
||||
.rst_n (rst_n),
|
||||
// CSR access port
|
||||
// *en_soon are early access strobes which are not a function of bus stall.
|
||||
// Can generate access faults (hence traps), but do not actually perform access.
|
||||
.addr (d_imm[11:0]), // todo could just connect this to the instruction bits
|
||||
.wdata (x_csr_wdata),
|
||||
.wen_soon (d_csr_wen && !m_trap_enter_vld),
|
||||
.wen (d_csr_wen && !m_trap_enter_vld && !x_stall),
|
||||
.wtype (d_csr_wtype),
|
||||
.rdata (x_csr_rdata),
|
||||
.ren_soon (d_csr_ren && !m_trap_enter_vld),
|
||||
.ren (d_csr_ren && !m_trap_enter_vld && !x_stall),
|
||||
.illegal (x_csr_illegal_access),
|
||||
// Trap signalling
|
||||
.trap_addr (m_trap_addr),
|
||||
.trap_enter_vld (m_trap_enter_vld),
|
||||
.trap_enter_rdy (m_trap_enter_rdy),
|
||||
.mepc_in (d_pc - (prev_instr_was_32_bit ? 32'h4 : 32'h2)),
|
||||
// IRQ and exception requests
|
||||
.irq (irq),
|
||||
.except (xm_except),
|
||||
// Other CSR-specific signalling
|
||||
.instr_ret (|df_cir_use)
|
||||
);
|
||||
|
||||
wire [W_EXCEPT-1:0] x_except =
|
||||
x_csr_illegal_access ? EXCEPT_INSTR_ILLEGAL :
|
||||
x_unaligned_addr && x_memop_write ? EXCEPT_STORE_ALIGN :
|
||||
x_unaligned_addr && !x_memop_write ? EXCEPT_LOAD_ALIGN : d_except;
|
||||
|
||||
// Pipe register
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
xm_memop <= MEMOP_NONE;
|
||||
xm_except <= EXCEPT_NONE;
|
||||
{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
|
||||
end else begin
|
||||
if (!m_stall) begin
|
||||
{xm_rs1, xm_rs2, xm_rd} <= {d_rs1, d_rs2, d_rd};
|
||||
// If the transfer is unaligned, make sure it is completely NOP'd on the bus
|
||||
xm_memop <= d_memop | {x_unaligned_addr, 3'h0};
|
||||
if (x_stall || x_trap_enter) begin
|
||||
if (x_stall || m_trap_enter_vld) begin
|
||||
// Insert bubble
|
||||
xm_rd <= {W_REGADDR{1'b0}};
|
||||
xm_memop <= MEMOP_NONE;
|
||||
xm_except <= EXCEPT_NONE;
|
||||
end
|
||||
xm_except <= x_except;
|
||||
end else if (bus_dph_err_d) begin
|
||||
// First phase of 2-phase AHBL error response. Pass the exception along on
|
||||
// this cycle, and on the next cycle the trap entry will be asserted,
|
||||
// suppressing any load/store that may currently be in stage X.
|
||||
`ifdef FORMAL
|
||||
assert(!xm_memop[3]); // Not NONE
|
||||
`endif
|
||||
xm_except <= xm_memop <= MEMOP_LBU ? EXCEPT_LOAD_FAULT : EXCEPT_STORE_FAULT;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -533,25 +520,15 @@ always @ (posedge clk)
|
|||
// Branch handling
|
||||
|
||||
// For JALR, the LSB of the result must be cleared by hardware
|
||||
wire [W_ADDR-1:0] x_taken_jump_target = ((d_jump_is_regoffs ? x_rs1_bypass : d_pc) + d_jump_offs) & ~32'h1;
|
||||
|
||||
wire [W_ADDR-1:0] x_jump_target =
|
||||
x_trap_exit ? x_mepc : // Note precedence -- it's possible to have enter && exit, but in this case enter_rdy is false.
|
||||
x_trap_enter ? x_trap_addr :
|
||||
x_taken_jump_target;
|
||||
wire [W_ADDR-1:0] x_jump_target = ((d_jump_is_regoffs ? x_rs1_bypass : d_pc) + d_jump_offs) & ~32'h1;
|
||||
|
||||
// Be careful not to take branches whose comparisons depend on a load result
|
||||
wire x_jump_req = x_trap_enter || x_trap_exit || !x_stall_raw && (
|
||||
assign x_jump_req = !x_stall_raw && (
|
||||
d_branchcond == BCOND_ALWAYS ||
|
||||
d_branchcond == BCOND_ZERO && !x_alu_cmp ||
|
||||
d_branchcond == BCOND_NZERO && x_alu_cmp
|
||||
);
|
||||
|
||||
assign f_jump_req = x_jump_req;
|
||||
assign f_jump_target = x_jump_target;
|
||||
assign x_jump_not_except = !(x_trap_enter || x_trap_exit);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Pipe Stage M
|
||||
|
||||
|
@ -559,9 +536,11 @@ reg [W_DATA-1:0] m_rdata_shift;
|
|||
reg [W_DATA-1:0] m_wdata;
|
||||
reg [W_DATA-1:0] m_result;
|
||||
|
||||
assign m_stall = !xm_memop[3] && !bus_dph_ready_d;
|
||||
assign f_jump_req = x_jump_req || m_trap_enter_vld;
|
||||
assign f_jump_target = m_trap_enter_vld ? m_trap_addr : x_jump_target;
|
||||
assign x_jump_not_except = !m_trap_enter_vld;
|
||||
|
||||
wire m_except_bus_fault = bus_dph_err_d; // TODO: handle differently for LSU/ifetch?
|
||||
assign m_stall = (!xm_memop[3] && !bus_dph_ready_d) || (m_trap_enter_vld && !m_trap_enter_rdy);
|
||||
|
||||
always @ (*) begin
|
||||
// Local forwarding of store data
|
||||
|
@ -607,11 +586,6 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
mw_rd <= {W_REGADDR{1'b0}};
|
||||
end else if (!m_stall) begin
|
||||
//synthesis translate_off
|
||||
// TODO: proper exception support
|
||||
if (m_except_bus_fault) begin
|
||||
$display("Bus fault!");
|
||||
$finish;
|
||||
end
|
||||
if (^bus_wdata_d === 1'bX) begin
|
||||
$display("Writing Xs to memory!");
|
||||
$finish;
|
||||
|
@ -632,7 +606,7 @@ always @ (posedge clk)
|
|||
// mw_result and mw_rd register the most recent write to the register file,
|
||||
// so that X can bypass them in.
|
||||
|
||||
wire w_reg_wen = |xm_rd && !m_stall;
|
||||
wire w_reg_wen = |xm_rd && !m_stall && xm_except == EXCEPT_NONE;
|
||||
|
||||
//synthesis translate_off
|
||||
always @ (posedge clk) begin
|
||||
|
|
|
@ -25,6 +25,8 @@ module hazard3_csr #(
|
|||
// The full 64 bits is writeable, so high-word increment can
|
||||
// be implemented in software, and a narrower hw counter used
|
||||
`include "hazard3_config.vh"
|
||||
,
|
||||
`include "hazard3_width_const.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
@ -37,14 +39,15 @@ module hazard3_csr #(
|
|||
// - Illegal CSR accesses produce trap entry
|
||||
// - Trap entry (not necessarily caused by CSR access) gates outgoing bus accesses
|
||||
// - Through-paths from e.g. hready to htrans are problematic for timing/implementation
|
||||
input wire [11:0] addr,
|
||||
input wire [XLEN-1:0] wdata,
|
||||
input wire wen,
|
||||
input wire wen_soon, // wen will be asserted once some stall condition clears
|
||||
input wire [1:0] wtype,
|
||||
output reg [XLEN-1:0] rdata,
|
||||
input wire ren,
|
||||
input wire ren_soon, // ren will be asserted once some stall condition clears
|
||||
input wire [11:0] addr,
|
||||
input wire [XLEN-1:0] wdata,
|
||||
input wire wen,
|
||||
input wire wen_soon, // wen will be asserted once some stall condition clears
|
||||
input wire [1:0] wtype,
|
||||
output reg [XLEN-1:0] rdata,
|
||||
input wire ren,
|
||||
input wire ren_soon, // ren will be asserted once some stall condition clears
|
||||
output wire illegal,
|
||||
|
||||
// Trap signalling
|
||||
// *We* tell the core that we are taking a trap, and where to, based on:
|
||||
|
@ -58,32 +61,17 @@ module hazard3_csr #(
|
|||
//
|
||||
// Note that an exception input can go away, e.g. if the pipe gets flushed. In this
|
||||
// case we lower trap_enter_vld.
|
||||
//
|
||||
// The core tells *us* that we are leaving the trap, by putting a 1-clock pulse on
|
||||
// trap_exit. The core will simultaneously produce a jump (specifically a mispredict)
|
||||
// to mepc_out.
|
||||
output wire [XLEN-1:0] trap_addr,
|
||||
output wire trap_enter_vld,
|
||||
input wire trap_enter_rdy,
|
||||
input wire trap_exit,
|
||||
output wire trap_is_exception, // diagnostic
|
||||
input wire [XLEN-1:0] mepc_in,
|
||||
output wire [XLEN-1:0] mepc_out,
|
||||
output wire [XLEN-1:0] trap_addr,
|
||||
output wire trap_enter_vld,
|
||||
input wire trap_enter_rdy,
|
||||
input wire [XLEN-1:0] mepc_in,
|
||||
|
||||
// Exceptions must *not* be a function of bus stall.
|
||||
input wire [15:0] irq,
|
||||
input wire except_instr_misaligned,
|
||||
input wire except_instr_fault,
|
||||
input wire except_instr_invalid,
|
||||
input wire except_breakpoint,
|
||||
input wire except_load_misaligned,
|
||||
input wire except_load_fault,
|
||||
input wire except_store_misaligned,
|
||||
input wire except_store_fault,
|
||||
input wire except_ecall,
|
||||
input wire [15:0] irq,
|
||||
input wire [W_EXCEPT-1:0] except,
|
||||
|
||||
// Other CSR-specific signalling
|
||||
input wire instr_ret
|
||||
input wire instr_ret
|
||||
);
|
||||
|
||||
// TODO block CSR access when entering trap?
|
||||
|
@ -262,11 +250,13 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
mstatus_mie <= 1'b0;
|
||||
end else if (CSR_M_TRAP) begin
|
||||
if (trap_enter_vld && trap_enter_rdy) begin
|
||||
mstatus_mpie <= mstatus_mie;
|
||||
mstatus_mie <= 1'b0;
|
||||
end else if (trap_exit) begin
|
||||
mstatus_mpie <= 1'b1;
|
||||
mstatus_mie <= mstatus_mpie;
|
||||
if (except == EXCEPT_MRET) begin
|
||||
mstatus_mpie <= 1'b1;
|
||||
mstatus_mie <= mstatus_mpie;
|
||||
end else begin
|
||||
mstatus_mpie <= mstatus_mie;
|
||||
mstatus_mie <= 1'b0;
|
||||
end
|
||||
end else if (wen && addr == MSTATUS) begin
|
||||
{mstatus_mpie, mstatus_mie} <=
|
||||
wtype == CSR_WTYPE_C ? {mstatus_mpie, mstatus_mie} & ~{wdata[7], wdata[3]} :
|
||||
|
@ -310,7 +300,7 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
if (!rst_n) begin
|
||||
mepc <= X0;
|
||||
end else if (CSR_M_TRAP) begin
|
||||
if (trap_enter_vld && trap_enter_rdy) begin
|
||||
if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET) begin
|
||||
mepc <= mepc_in & MEPC_MASK;
|
||||
end else if (wen && addr == MEPC) begin
|
||||
mepc <= update(mepc) & MEPC_MASK;
|
||||
|
@ -354,7 +344,7 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
mcause_irq <= 1'b0;
|
||||
mcause_code <= 5'h0;
|
||||
end else if (CSR_M_TRAP) begin
|
||||
if (trap_enter_vld && trap_enter_rdy) begin
|
||||
if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET) begin
|
||||
mcause_irq <= mcause_irq_next;
|
||||
mcause_code <= mcause_code_next;
|
||||
end else if (wen && addr == MCAUSE) begin
|
||||
|
@ -657,49 +647,13 @@ always @ (*) begin
|
|||
endcase
|
||||
end
|
||||
|
||||
wire csr_access_error = (wen_soon || ren_soon) && !decode_match;
|
||||
assign illegal = (wen_soon || ren_soon) && !decode_match;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Trap request generation
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Keep track of whether we are in a trap; we do not permit exception nesting.
|
||||
// TODO lockup condition?
|
||||
reg in_trap;
|
||||
|
||||
always @ (posedge clk or negedge rst_n)
|
||||
if (!rst_n)
|
||||
in_trap <= 1'b0;
|
||||
else
|
||||
in_trap <= (in_trap || (trap_enter_vld && trap_enter_rdy)) && !trap_exit;
|
||||
|
||||
// Exception selection
|
||||
|
||||
// Most-significant is lowest priority
|
||||
// FIXME: this is different from the priority order given in the spec, but will get us off the ground
|
||||
wire [15:0] exception_req = {
|
||||
4'h0, // reserved by spec
|
||||
except_ecall,
|
||||
3'h0, // nonimplemented privileges
|
||||
except_store_fault,
|
||||
except_store_misaligned,
|
||||
except_load_fault,
|
||||
except_load_misaligned,
|
||||
except_breakpoint,
|
||||
except_instr_invalid || csr_access_error,
|
||||
except_instr_fault,
|
||||
except_instr_misaligned
|
||||
};
|
||||
|
||||
wire exception_req_any = |exception_req && !in_trap;
|
||||
wire [3:0] exception_req_num;
|
||||
|
||||
hazard3_priority_encode #(
|
||||
.W_REQ(16)
|
||||
) except_priority (
|
||||
.req (exception_req),
|
||||
.gnt (exception_req_num)
|
||||
);
|
||||
wire exception_req_any = except != EXCEPT_NONE;
|
||||
|
||||
// Interrupt masking and selection
|
||||
|
||||
|
@ -735,13 +689,12 @@ hazard3_priority_encode #(
|
|||
);
|
||||
|
||||
wire [11:0] mtvec_offs = (exception_req_any ?
|
||||
{8'h0, exception_req_num} :
|
||||
{8'h0, except} :
|
||||
12'h10 + {7'h0, irq_num}
|
||||
) << 2;
|
||||
|
||||
assign trap_addr = mtvec | {20'h0, mtvec_offs};
|
||||
assign trap_addr = except == EXCEPT_MRET ? mepc : mtvec | {20'h0, mtvec_offs};
|
||||
assign trap_enter_vld = CSR_M_TRAP && (exception_req_any || irq_any);
|
||||
assign trap_is_exception = exception_req_any;
|
||||
|
||||
assign mcause_irq_next = !exception_req_any;
|
||||
assign mcause_code_next = exception_req_any ? exception_req_num : {1'b0, irq_num};
|
||||
|
@ -749,22 +702,33 @@ assign mcause_code_next = exception_req_any ? exception_req_num : {1'b0, irq_num
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
`ifdef RISCV_FORMAL
|
||||
|
||||
// Keep track of whether we are in a trap (only for formal property purposes)
|
||||
reg in_trap;
|
||||
|
||||
always @ (posedge clk or negedge rst_n)
|
||||
if (!rst_n)
|
||||
in_trap <= 1'b0;
|
||||
else
|
||||
in_trap <= (in_trap || (trap_enter_vld && trap_enter_rdy))
|
||||
&& !(trap_enter_vld && trap_enter_rdy && except == EXCEPT_MRET);
|
||||
|
||||
always @ (posedge clk) begin
|
||||
// We disallow double exceptions -- this causes riscv-formal to complain that
|
||||
// loads/stores don't trap inside of traps. Therefore assume this doesn't happen
|
||||
// Assume there are no nested exceptions, to stop risc-formal from doing
|
||||
// annoying things like stopping instructions from retiring by repeatedly
|
||||
// feeding in invalid instructions
|
||||
|
||||
if (in_trap)
|
||||
assume(!(except_load_misaligned || except_store_misaligned));
|
||||
assume(except == EXCEPT_NONE);
|
||||
|
||||
// Something is screwed up if this happens
|
||||
if ($past(trap_enter_vld && trap_enter_rdy))
|
||||
assert(!wen);
|
||||
// Don't do this
|
||||
assert(!(trap_enter_vld && trap_enter_rdy && trap_exit));
|
||||
// Should be impossible to get into the trap and exit it so quickly:
|
||||
if (in_trap && !$past(in_trap))
|
||||
assert(!trap_exit);
|
||||
assert(except != EXCEPT_MRET);
|
||||
// Should be impossible to get to another mret so soon after exiting:
|
||||
assert(!(trap_exit && $past(trap_exit)));
|
||||
assert(!(except == EXCEPT_MRET && $past(except == EXCEPT_MRET)));
|
||||
end
|
||||
|
||||
`endif
|
||||
|
|
|
@ -52,7 +52,7 @@ module hazard3_decode #(
|
|||
output reg [W_BCOND-1:0] d_branchcond,
|
||||
output reg [W_ADDR-1:0] d_jump_offs,
|
||||
output reg d_jump_is_regoffs,
|
||||
output reg [2:0] d_except
|
||||
output reg [W_EXCEPT-1:0] d_except
|
||||
);
|
||||
|
||||
`include "rv_opcodes.vh"
|
||||
|
|
|
@ -49,13 +49,6 @@ module hazard3_frontend #(
|
|||
output wire next_regs_vld
|
||||
);
|
||||
|
||||
`undef ASSERT
|
||||
`ifdef HAZARD3_FRONTEND_ASSERTIONS
|
||||
`define ASSERT(x) assert(x);
|
||||
`else
|
||||
`define ASSERT(x)
|
||||
`endif
|
||||
|
||||
localparam W_BUNDLE = W_DATA / 2;
|
||||
parameter W_FIFO_LEVEL = $clog2(FIFO_DEPTH + 1);
|
||||
|
||||
|
@ -124,10 +117,12 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
pending_fetches <= 2'h0;
|
||||
ctr_flush_pending <= 2'h0;
|
||||
end else begin
|
||||
`ASSERT(ctr_flush_pending <= pending_fetches)
|
||||
`ASSERT(pending_fetches < 2'd3)
|
||||
`ASSERT(!(mem_data_vld && !pending_fetches))
|
||||
// `ASSERT(!($past(mem_addr_hold) && $past(mem_addr_vld) && !$stable(mem_addr)))
|
||||
`ifdef FORMAL
|
||||
assert(ctr_flush_pending <= pending_fetches);
|
||||
assert(pending_fetches < 2'd3);
|
||||
assert(!(mem_data_vld && !pending_fetches));
|
||||
// assert(!($past(mem_addr_hold) && $past(mem_addr_vld) && !$stable(mem_addr)));
|
||||
`endif
|
||||
mem_addr_hold <= mem_addr_vld && !mem_addr_rdy;
|
||||
pending_fetches <= pending_fetches_next;
|
||||
if (jump_now) begin
|
||||
|
@ -175,9 +170,11 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
unaligned_jump_aph <= 1'b0;
|
||||
unaligned_jump_dph <= 1'b0;
|
||||
end else if (EXTENSION_C) begin
|
||||
`ASSERT(!(unaligned_jump_aph && !unaligned_jump_dph))
|
||||
`ASSERT(!($past(jump_now && !jump_target[1]) && unaligned_jump_aph))
|
||||
`ASSERT(!($past(jump_now && !jump_target[1]) && unaligned_jump_dph))
|
||||
`ifdef FORMAL
|
||||
assert(!(unaligned_jump_aph && !unaligned_jump_dph));
|
||||
assert(!($past(jump_now && !jump_target[1]) && unaligned_jump_aph));
|
||||
assert(!($past(jump_now && !jump_target[1]) && unaligned_jump_dph));
|
||||
`endif
|
||||
if (mem_addr_rdy || (jump_now && !unaligned_jump_now)) begin
|
||||
unaligned_jump_aph <= 1'b0;
|
||||
end
|
||||
|
@ -277,10 +274,12 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
hwbuf_vld <= 1'b0;
|
||||
cir_vld <= 2'h0;
|
||||
end else begin
|
||||
`ASSERT(cir_vld <= 2)
|
||||
`ASSERT(cir_use <= 2)
|
||||
`ASSERT(cir_use <= cir_vld)
|
||||
`ASSERT(cir_vld <= buf_level || $past(cir_lock))
|
||||
`ifdef FORMAL
|
||||
assert(cir_vld <= 2);
|
||||
assert(cir_use <= 2);
|
||||
assert(cir_use <= cir_vld);
|
||||
assert(cir_vld <= buf_level || $past(cir_lock));
|
||||
`endif
|
||||
// Update CIR flags
|
||||
buf_level <= buf_level_next;
|
||||
hwbuf_vld <= &buf_level_next;
|
||||
|
|
|
@ -46,14 +46,20 @@ localparam CSR_WTYPE_C = 2'h2;
|
|||
// Exceptional condition signals which travel alongside (or instead of)
|
||||
// instructions in the pipeline. These are speculative and can be flushed
|
||||
// on e.g. branch mispredict
|
||||
// These mostly align with mcause values.
|
||||
|
||||
localparam EXCEPT_NONE = 3'h0;
|
||||
localparam EXCEPT_ECALL = 3'h1;
|
||||
localparam EXCEPT_EBREAK = 3'h2;
|
||||
localparam EXCEPT_MRET = 3'h3; // separate, but handled similarly
|
||||
localparam EXCEPT_INSTR_ILLEGAL = 3'h4;
|
||||
localparam EXCEPT_INSTR_MISALIGN = 3'h5;
|
||||
localparam EXCEPT_INSTR_FAULT = 3'h6;
|
||||
localparam EXCEPT_NONE = 4'hf;
|
||||
|
||||
localparam EXCEPT_INSTR_MISALIGN = 4'h0;
|
||||
localparam EXCEPT_INSTR_FAULT = 4'h1;
|
||||
localparam EXCEPT_INSTR_ILLEGAL = 4'h2;
|
||||
localparam EXCEPT_EBREAK = 4'h3;
|
||||
localparam EXCEPT_LOAD_ALIGN = 4'h4;
|
||||
localparam EXCEPT_LOAD_FAULT = 4'h5;
|
||||
localparam EXCEPT_STORE_ALIGN = 4'h6;
|
||||
localparam EXCEPT_STORE_FAULT = 4'h7;
|
||||
localparam EXCEPT_MRET = 4'ha; // Not really an exception, but handled like one
|
||||
localparam EXCEPT_ECALL = 4'hb;
|
||||
|
||||
// Operations for M extension (these are just instr[14:12])
|
||||
|
||||
|
|
|
@ -10,5 +10,5 @@ parameter W_ALUSRC = 2,
|
|||
parameter W_MEMOP = 4,
|
||||
parameter W_BCOND = 2,
|
||||
|
||||
parameter W_EXCEPT = 3,
|
||||
parameter W_EXCEPT = 4,
|
||||
parameter W_MULOP = 3
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
SRCS := ../common/init.S main.c
|
||||
APP := ecall_simple
|
||||
CCFLAGS = -march=rv32ic
|
||||
|
||||
include ../common/src_only_app.mk
|
|
@ -0,0 +1,89 @@
|
|||
[*]
|
||||
[*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI
|
||||
[*] Sat May 22 07:37:18 2021
|
||||
[*]
|
||||
[dumpfile] "/home/luke/proj/hazard3/test/ecall_simple/ecall_simple_run.vcd"
|
||||
[dumpfile_mtime] "Sat May 22 07:33:26 2021"
|
||||
[dumpfile_size] 1269546
|
||||
[savefile] "/home/luke/proj/hazard3/test/ecall_simple/ecall_simple_run.gtkw"
|
||||
[timestart] 314
|
||||
[size] 2560 1403
|
||||
[pos] -1 -1
|
||||
*-2.000000 330 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
|
||||
[treeopen] core.
|
||||
[sst_width] 593
|
||||
[signals_width] 214
|
||||
[sst_expanded] 1
|
||||
[sst_vpaned_height] 423
|
||||
@200
|
||||
-I Bus
|
||||
@22
|
||||
i_haddr[31:0]
|
||||
@28
|
||||
i_htrans[1:0]
|
||||
i_hsize[2:0]
|
||||
i_hready
|
||||
@22
|
||||
i_hrdata[31:0]
|
||||
@200
|
||||
-
|
||||
-Frontend
|
||||
@28
|
||||
core.f_jump_req
|
||||
core.f_jump_rdy
|
||||
@22
|
||||
core.frontend.jump_target[31:0]
|
||||
@200
|
||||
-
|
||||
-PC/CIR
|
||||
@28
|
||||
core.inst_hazard3_decode.d_starved
|
||||
@22
|
||||
core.inst_hazard3_decode.pc[31:0]
|
||||
core.frontend.cir[31:0]
|
||||
@200
|
||||
-
|
||||
-Exceptions
|
||||
@28
|
||||
core.inst_hazard3_csr.except_breakpoint
|
||||
core.inst_hazard3_csr.except_ecall
|
||||
core.inst_hazard3_csr.except_instr_invalid
|
||||
core.inst_hazard3_csr.except_load_misaligned
|
||||
core.inst_hazard3_csr.except_store_misaligned
|
||||
core.inst_hazard3_csr.exception_req_any
|
||||
@22
|
||||
core.inst_hazard3_csr.exception_req_num[3:0]
|
||||
@29
|
||||
core.inst_hazard3_csr.trap_enter_rdy
|
||||
core.inst_hazard3_csr.trap_enter_vld
|
||||
@200
|
||||
-
|
||||
-Reg Read
|
||||
@22
|
||||
core.f_rs1[4:0]
|
||||
core.f_rs2[4:0]
|
||||
core.d_rs1[4:0]
|
||||
core.d_rs2[4:0]
|
||||
core.inst_regfile_1w2r.rdata1[31:0]
|
||||
core.inst_regfile_1w2r.rdata2[31:0]
|
||||
@200
|
||||
-
|
||||
-Reg Write
|
||||
@22
|
||||
core.xm_rd[4:0]
|
||||
core.xm_result[31:0]
|
||||
@200
|
||||
-
|
||||
-D Bus
|
||||
@22
|
||||
d_haddr[31:0]
|
||||
@28
|
||||
d_htrans[1:0]
|
||||
d_hsize[2:0]
|
||||
d_hwrite
|
||||
d_hready
|
||||
@22
|
||||
d_hwdata[31:0]
|
||||
d_hrdata[31:0]
|
||||
[pattern_trace] 1
|
||||
[pattern_trace] 0
|
|
@ -0,0 +1,37 @@
|
|||
#include "tb_cxxrtl_io.h"
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define read_csr(csrname) ({ \
|
||||
uint32_t __csr_tmp_u32; \
|
||||
__asm__ ("csrr %0, " #csrname : "=r" (__csr_tmp_u32)); \
|
||||
__csr_tmp_u32; \
|
||||
})
|
||||
|
||||
#define write_csr(csrname, val) __asm__ ("csrw " #csrname ", %0" : : "r" (val))
|
||||
|
||||
void __attribute__((interrupt)) handle_ecall() {
|
||||
uint32_t call_num;
|
||||
asm volatile ("mv %0, a7" : "=r" (call_num));
|
||||
tb_puts("Handling ecall. Call number:\n");
|
||||
tb_put_u32(call_num);
|
||||
write_csr(mepc, read_csr(mepc) + 4);
|
||||
}
|
||||
|
||||
static inline void make_ecall(uint32_t call) {
|
||||
asm volatile ("mv a7, %0 \n ecall" : : "r" (call));
|
||||
}
|
||||
|
||||
const uint32_t call_nums[] = {
|
||||
0x123,
|
||||
0x456,
|
||||
0xdeadbeef
|
||||
};
|
||||
|
||||
void main() {
|
||||
for (int i = 0; i < sizeof(call_nums) / sizeof(*call_nums); ++i)
|
||||
make_ecall(call_nums[i]);
|
||||
tb_puts("Finished making calls.\n");
|
||||
tb_exit(0);
|
||||
}
|
|
@ -12,7 +12,7 @@ REDUCED_BYPASS := 0
|
|||
|
||||
all: tb
|
||||
|
||||
SYNTH_CMD += read_verilog -I ../../hdl $(shell listfiles ../../hdl/hazard3.f);
|
||||
SYNTH_CMD += read_verilog -I ../../../hdl $(shell listfiles ../../../hdl/hazard3.f);
|
||||
SYNTH_CMD += chparam -set EXTENSION_C $(EXTENSION_C) $(TOP);
|
||||
SYNTH_CMD += chparam -set EXTENSION_M $(EXTENSION_M) $(TOP);
|
||||
SYNTH_CMD += chparam -set CSR_COUNTER 1 $(TOP);
|
||||
|
|
Loading…
Reference in New Issue