diff --git a/Readme.md b/Readme.md index 3439be2..b10ef46 100644 --- a/Readme.md +++ b/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. + diff --git a/hdl/hazard3_core.v b/hdl/hazard3_core.v index 3337bf4..6547a85 100644 --- a/hdl/hazard3_core.v +++ b/hdl/hazard3_core.v @@ -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 diff --git a/hdl/hazard3_csr.v b/hdl/hazard3_csr.v index 9a6ca2a..a57d1f8 100644 --- a/hdl/hazard3_csr.v +++ b/hdl/hazard3_csr.v @@ -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 diff --git a/hdl/hazard3_decode.v b/hdl/hazard3_decode.v index 0466812..65c04bc 100644 --- a/hdl/hazard3_decode.v +++ b/hdl/hazard3_decode.v @@ -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" diff --git a/hdl/hazard3_frontend.v b/hdl/hazard3_frontend.v index 62dad53..95b88de 100644 --- a/hdl/hazard3_frontend.v +++ b/hdl/hazard3_frontend.v @@ -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; diff --git a/hdl/hazard3_ops.vh b/hdl/hazard3_ops.vh index 38a62da..f562ed4 100644 --- a/hdl/hazard3_ops.vh +++ b/hdl/hazard3_ops.vh @@ -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]) diff --git a/hdl/hazard3_width_const.vh b/hdl/hazard3_width_const.vh index a7a4801..7a472f8 100644 --- a/hdl/hazard3_width_const.vh +++ b/hdl/hazard3_width_const.vh @@ -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 diff --git a/test/sim/ecall_simple/Makefile b/test/sim/ecall_simple/Makefile new file mode 100644 index 0000000..9baf65a --- /dev/null +++ b/test/sim/ecall_simple/Makefile @@ -0,0 +1,5 @@ +SRCS := ../common/init.S main.c +APP := ecall_simple +CCFLAGS = -march=rv32ic + +include ../common/src_only_app.mk diff --git a/test/sim/ecall_simple/ecall_simple_run.gtkw b/test/sim/ecall_simple/ecall_simple_run.gtkw new file mode 100644 index 0000000..ffee99e --- /dev/null +++ b/test/sim/ecall_simple/ecall_simple_run.gtkw @@ -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 diff --git a/test/sim/ecall_simple/main.c b/test/sim/ecall_simple/main.c new file mode 100644 index 0000000..8a20958 --- /dev/null +++ b/test/sim/ecall_simple/main.c @@ -0,0 +1,37 @@ +#include "tb_cxxrtl_io.h" + + +#include + +#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); +} diff --git a/test/sim/tb_cxxrtl/Makefile b/test/sim/tb_cxxrtl/Makefile index 709a7c5..dd6dfc0 100644 --- a/test/sim/tb_cxxrtl/Makefile +++ b/test/sim/tb_cxxrtl/Makefile @@ -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);