Significant overhaul of trap handling. Exceptions now taken from stage 3 instead of stage 2

This commit is contained in:
Luke Wren 2021-05-23 11:59:46 +01:00
parent 5e61c9f9ac
commit 1b252d4bda
11 changed files with 320 additions and 221 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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;

View File

@ -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])

View File

@ -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

View File

@ -0,0 +1,5 @@
SRCS := ../common/init.S main.c
APP := ecall_simple
CCFLAGS = -march=rv32ic
include ../common/src_only_app.mk

View File

@ -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

View File

@ -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);
}

View File

@ -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);