From 954bae5cf1d020af5943dc85ab8e114a18e9689e Mon Sep 17 00:00:00 2001 From: Luke Wren Date: Mon, 29 Aug 2022 14:51:19 +0100 Subject: [PATCH] Implement block/unblock instructions, and fix questionable partial masking of sleep signals on exceptions. Add simple test for self-block/unblock with loopback in tb. --- hdl/hazard3_core.v | 144 +++++++++--------- hdl/hazard3_csr.v | 10 +- hdl/hazard3_decode.v | 60 +++++--- test/sim/common/hazard3_instr.h | 32 ++++ test/sim/sw_testcases/extension_xh3b.c | 22 +-- test/sim/sw_testcases/unblock_self_latching.c | 70 +++++++++ 6 files changed, 224 insertions(+), 114 deletions(-) create mode 100644 test/sim/common/hazard3_instr.h create mode 100644 test/sim/sw_testcases/unblock_self_latching.c diff --git a/hdl/hazard3_core.v b/hdl/hazard3_core.v index 1a29456..7ea5078 100644 --- a/hdl/hazard3_core.v +++ b/hdl/hazard3_core.v @@ -11,69 +11,69 @@ module hazard3_core #( `include "hazard3_width_const.vh" ) ( // Global signals - input wire clk, - input wire clk_always_on, - input wire rst_n, + input wire clk, + input wire clk_always_on, + input wire rst_n, // Power control signals - output wire pwrup_req, - input wire pwrup_ack, - output wire clk_en, - output wire unblock_out, - input wire unblock_in, + output wire pwrup_req, + input wire pwrup_ack, + output wire clk_en, + output reg unblock_out, + input wire unblock_in, `ifdef RISCV_FORMAL `RVFI_OUTPUTS , `endif // Instruction fetch port - output wire bus_aph_req_i, - output wire bus_aph_panic_i, // e.g. branch mispredict + flush - input wire bus_aph_ready_i, - input wire bus_dph_ready_i, - input wire bus_dph_err_i, + output wire bus_aph_req_i, + output wire bus_aph_panic_i, // e.g. branch mispredict + flush + input wire bus_aph_ready_i, + input wire bus_dph_ready_i, + input wire bus_dph_err_i, - output wire [W_ADDR-1:0] bus_haddr_i, - output wire [2:0] bus_hsize_i, - output wire bus_priv_i, - input wire [W_DATA-1:0] bus_rdata_i, + output wire [W_ADDR-1:0] bus_haddr_i, + output wire [2:0] bus_hsize_i, + output wire bus_priv_i, + input wire [W_DATA-1:0] bus_rdata_i, // Load/store port - output reg bus_aph_req_d, - output wire bus_aph_excl_d, - input wire bus_aph_ready_d, - input wire bus_dph_ready_d, - input wire bus_dph_err_d, - input wire bus_dph_exokay_d, + output reg bus_aph_req_d, + output wire bus_aph_excl_d, + input wire bus_aph_ready_d, + input wire bus_dph_ready_d, + input wire bus_dph_err_d, + input wire bus_dph_exokay_d, - output reg [W_ADDR-1:0] bus_haddr_d, - output reg [2:0] bus_hsize_d, - output reg bus_priv_d, - output reg bus_hwrite_d, - output reg [W_DATA-1:0] bus_wdata_d, - input wire [W_DATA-1:0] bus_rdata_d, + output reg [W_ADDR-1:0] bus_haddr_d, + output reg [2:0] bus_hsize_d, + output reg bus_priv_d, + output reg bus_hwrite_d, + output reg [W_DATA-1:0] bus_wdata_d, + input wire [W_DATA-1:0] bus_rdata_d, // Debugger run/halt control - input wire dbg_req_halt, - input wire dbg_req_halt_on_reset, - input wire dbg_req_resume, - output wire dbg_halted, - output wire dbg_running, + input wire dbg_req_halt, + input wire dbg_req_halt_on_reset, + input wire dbg_req_resume, + output wire dbg_halted, + output wire dbg_running, // Debugger access to data0 CSR - input wire [W_DATA-1:0] dbg_data0_rdata, - output wire [W_DATA-1:0] dbg_data0_wdata, - output wire dbg_data0_wen, + input wire [W_DATA-1:0] dbg_data0_rdata, + output wire [W_DATA-1:0] dbg_data0_wdata, + output wire dbg_data0_wen, // Debugger instruction injection - input wire [W_DATA-1:0] dbg_instr_data, - input wire dbg_instr_data_vld, - output wire dbg_instr_data_rdy, - output wire dbg_instr_caught_exception, - output wire dbg_instr_caught_ebreak, + input wire [W_DATA-1:0] dbg_instr_data, + input wire dbg_instr_data_vld, + output wire dbg_instr_data_rdy, + output wire dbg_instr_caught_exception, + output wire dbg_instr_caught_ebreak, // Level-sensitive interrupt sources - input wire [NUM_IRQS-1:0] irq, // -> mip.meip - input wire soft_irq, // -> mip.msip - input wire timer_irq // -> mip.mtip + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire soft_irq, // -> mip.msip + input wire timer_irq // -> mip.mtip ); `include "hazard3_ops.vh" @@ -222,7 +222,7 @@ wire d_csr_w_imm; wire x_jump_not_except; wire x_mmode_execution; -wire x_permit_wfi; +wire x_trap_wfi; hazard3_decode #( `include "hazard3_config_inst.vh" @@ -241,7 +241,7 @@ hazard3_decode #( .debug_mode (debug_mode), .m_mode (x_mmode_execution), - .permit_wfi (x_permit_wfi), + .trap_wfi (x_trap_wfi), .d_starved (d_starved), .x_stall (x_stall), @@ -933,11 +933,14 @@ wire x_except_counts_as_retire = wire x_instr_ret = |df_cir_use && (x_except == EXCEPT_NONE || x_except_counts_as_retire); wire m_dphase_in_flight = xm_memop != MEMOP_NONE && xm_memop != MEMOP_AMO; -wire m_delay_irq_entry = xm_delay_irq_entry_on_ls_dphase || ((xm_sleep_wfi || xm_sleep_block) && !m_sleep_stall_release); +// Need to delay IRQ entry on sleep exit because, for deep sleep states, we +// can't access the bus until the power handshake has completed. +wire m_delay_irq_entry = xm_delay_irq_entry_on_ls_dphase || + ((xm_sleep_wfi || xm_sleep_block) && !m_sleep_stall_release); -wire m_allow_sleep; -wire m_allow_power_down; -wire m_allow_sleep_on_block; +wire m_pwr_allow_sleep; +wire m_pwr_allow_power_down; +wire m_pwr_allow_sleep_on_block; wire m_wfi_wakeup_req; hazard3_csr #( @@ -984,9 +987,9 @@ hazard3_csr #( .loadstore_dphase_pending (m_dphase_in_flight), .mepc_in (m_exception_return_addr), - .pwr_allow_sleep (m_allow_sleep), - .pwr_allow_power_down (m_allow_power_down), - .pwr_allow_sleep_on_block (m_allow_sleep_on_block), + .pwr_allow_sleep (m_pwr_allow_sleep), + .pwr_allow_power_down (m_pwr_allow_power_down), + .pwr_allow_sleep_on_block (m_pwr_allow_sleep_on_block), .pwr_wfi_wakeup_req (m_wfi_wakeup_req), .m_mode_execution (x_mmode_execution), @@ -1013,7 +1016,7 @@ hazard3_csr #( .trig_m_en (x_trig_m_en), // Other CSR-specific signalling - .permit_wfi (x_permit_wfi), + .trap_wfi (x_trap_wfi), .instr_ret (x_instr_ret) ); @@ -1026,27 +1029,31 @@ always @ (posedge clk or negedge rst_n) begin xm_except_to_d_mode <= 1'b0; xm_sleep_wfi <= 1'b0; xm_sleep_block <= 1'b0; + unblock_out <= 1'b0; {xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}}; end else begin + unblock_out <= 1'b0; if (!m_stall) begin {xm_rs1, xm_rs2, xm_rd} <= {d_rs1, d_rs2, d_rd}; // If some X-sourced exception has squashed the address phase, need to squash the data phase too. - xm_memop <= x_unaligned_addr || x_exec_pmp_fail || x_loadstore_pmp_fail ? MEMOP_NONE : d_memop; - xm_except <= x_except; + xm_memop <= x_except != EXCEPT_NONE ? MEMOP_NONE : d_memop; + xm_except <= x_except; xm_except_to_d_mode <= x_trig_break_d_mode; - xm_sleep_wfi <= d_sleep_wfi && !x_exec_pmp_fail; - xm_sleep_block <= d_sleep_block && !x_exec_pmp_fail; + xm_sleep_wfi <= x_except == EXCEPT_NONE && d_sleep_wfi; + xm_sleep_block <= x_except == EXCEPT_NONE && d_sleep_block; + unblock_out <= x_except == EXCEPT_NONE && d_sleep_unblock; // Note the d_starved term is required because it is possible // (e.g. PMP X permission fail) to except when the frontend is // starved, and we get a bad mepc if we let this jump ahead: if (x_stall || d_starved || m_trap_enter_soon) begin // Insert bubble - xm_rd <= {W_REGADDR{1'b0}}; - xm_memop <= MEMOP_NONE; - xm_except <= EXCEPT_NONE; + xm_rd <= {W_REGADDR{1'b0}}; + xm_memop <= MEMOP_NONE; + xm_except <= EXCEPT_NONE; xm_except_to_d_mode <= 1'b0; - xm_sleep_wfi <= 1'b0; - xm_sleep_block <= 1'b0; + xm_sleep_wfi <= 1'b0; + xm_sleep_block <= 1'b0; + unblock_out <= 1'b0; end end else if (bus_dph_err_d) begin // First phase of 2-phase AHB5 error response. Pass the exception along on @@ -1056,6 +1063,7 @@ always @ (posedge clk or negedge rst_n) begin assert(xm_memop != MEMOP_NONE); assert(!xm_sleep_wfi); assert(!xm_sleep_block); + assert(!unblock_out); `endif xm_except <= |EXTENSION_A && xm_memop == MEMOP_LR_W ? EXCEPT_LOAD_FAULT : @@ -1142,15 +1150,15 @@ hazard3_power_ctrl power_ctrl ( .pwrup_ack (pwrup_ack), .clk_en (clk_en), - .allow_sleep (m_allow_sleep), - .allow_power_down (m_allow_power_down), - .allow_sleep_on_block (m_allow_sleep_on_block), + .allow_sleep (m_pwr_allow_sleep), + .allow_power_down (m_pwr_allow_power_down), + .allow_sleep_on_block (m_pwr_allow_sleep_on_block), .frontend_pwrdown_ok (f_frontend_pwrdown_ok), .sleeping_on_wfi (xm_sleep_wfi), .wfi_wakeup_req (m_wfi_wakeup_req), - .sleeping_on_block (xm_sleep_wfi), + .sleeping_on_block (xm_sleep_block), .block_wakeup_req_pulse (unblock_in), .stall_release (m_sleep_stall_release) ); diff --git a/hdl/hazard3_csr.v b/hdl/hazard3_csr.v index f33ad3b..e389cfd 100644 --- a/hdl/hazard3_csr.v +++ b/hdl/hazard3_csr.v @@ -117,7 +117,8 @@ module hazard3_csr #( output wire trig_m_en, // Other CSR-specific signalling - output wire permit_wfi, + output wire trap_wfi, + output wire wfi_is_nop, input wire instr_ret ); @@ -226,8 +227,11 @@ always @ (posedge clk or negedge rst_n) begin end end -// Simply trap all U-mode WFIs if timeout bit is set -assign permit_wfi = m_mode || !mstatus_tw; +// Trap all U-mode WFIs if timeout bit is set. Note that the debug spec +// says "The `wfi` instruction acts as a `nop`" during program buffer +// execution, and nops do not trap, so in debug mode we uncondi allow WFIs but we inhibit their sleep signal. +assign trap_wfi = mstatus_tw && !(debug_mode || m_mode); +assign wfi_is_nop = debug_mode; reg [XLEN-1:0] mscratch; diff --git a/hdl/hazard3_decode.v b/hdl/hazard3_decode.v index 5bf478e..0eae47f 100644 --- a/hdl/hazard3_decode.v +++ b/hdl/hazard3_decode.v @@ -23,7 +23,7 @@ module hazard3_decode #( input wire debug_mode, input wire m_mode, - input wire permit_wfi, + input wire trap_wfi, output wire d_starved, input wire x_stall, @@ -233,7 +233,6 @@ always @ (*) begin `RVOPC_ADD: begin d_aluop = ALUOP_ADD; end `RVOPC_SUB: begin d_aluop = ALUOP_SUB; end `RVOPC_SLL: begin d_aluop = ALUOP_SLL; end - `RVOPC_SLT: begin d_aluop = ALUOP_LT; end `RVOPC_SLTU: begin d_aluop = ALUOP_LTU; end `RVOPC_XOR: begin d_aluop = ALUOP_XOR; end `RVOPC_SRL: begin d_aluop = ALUOP_SRL; end @@ -249,6 +248,21 @@ always @ (*) begin `RVOPC_SH: begin d_addr_is_regoffs = 1'b1; d_aluop = ALUOP_RS2; d_memop = MEMOP_SH; d_rd = X0; end `RVOPC_SW: begin d_addr_is_regoffs = 1'b1; d_aluop = ALUOP_RS2; d_memop = MEMOP_SW; d_rd = X0; end + `RVOPC_SLT: begin + d_aluop = ALUOP_LT; + if (|EXTENSION_XH3POWER && ~|d_rd && ~|d_rs1) begin + if (d_rs2 == 5'h00) begin + // h3.block (power management hint) + d_invalid_32bit = trap_wfi; + d_sleep_block = !trap_wfi; + end else if (d_rs2 == 5'h01) begin + // h3.unblock (power management hint) + d_invalid_32bit = trap_wfi; + d_sleep_unblock = !trap_wfi; + end + end + end + `RVOPC_MUL: if (EXTENSION_M) begin d_aluop = ALUOP_MULDIV; d_mulop = M_OP_MUL; end else begin d_invalid_32bit = 1'b1; end `RVOPC_MULH: if (EXTENSION_M) begin d_aluop = ALUOP_MULDIV; d_mulop = M_OP_MULH; end else begin d_invalid_32bit = 1'b1; end `RVOPC_MULHSU: if (EXTENSION_M) begin d_aluop = ALUOP_MULDIV; d_mulop = M_OP_MULHSU; end else begin d_invalid_32bit = 1'b1; end @@ -318,31 +332,33 @@ always @ (*) begin `RVOPC_H3_BEXTMI: if (EXTENSION_XH3BEXTM) begin d_aluop = ALUOP_BEXTM; d_rs2 = X0; d_imm = d_imm_i; d_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end `RVOPC_FENCE: begin d_rs2 = X0; end // NOP, note rs1/rd are zero in instruction - `RVOPC_FENCE_I: if (EXTENSION_ZIFENCEI) begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_branchcond = BCOND_ALWAYS; d_fence_i = 1'b1; end else begin d_invalid_32bit = 1'b1; end // note rs1/rs2/rd are zero in instruction - `RVOPC_CSRRW: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = 1'b1 ; d_csr_ren = |d_rd; d_csr_wtype = CSR_WTYPE_W; end else begin d_invalid_32bit = 1'b1; end - `RVOPC_CSRRS: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = |d_rs1; d_csr_ren = 1'b1 ; d_csr_wtype = CSR_WTYPE_S; end else begin d_invalid_32bit = 1'b1; end - `RVOPC_CSRRC: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = |d_rs1; d_csr_ren = 1'b1 ; d_csr_wtype = CSR_WTYPE_C; end else begin d_invalid_32bit = 1'b1; end - `RVOPC_CSRRWI: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = 1'b1 ; d_csr_ren = |d_rd; d_csr_wtype = CSR_WTYPE_W; d_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end - `RVOPC_CSRRSI: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = |d_rs1; d_csr_ren = 1'b1 ; d_csr_wtype = CSR_WTYPE_S; d_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end - `RVOPC_CSRRCI: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = |d_rs1; d_csr_ren = 1'b1 ; d_csr_wtype = CSR_WTYPE_C; d_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end - `RVOPC_ECALL: if (HAVE_CSR) begin d_except = m_mode || !U_MODE ? EXCEPT_ECALL_M : EXCEPT_ECALL_U; d_rs2 = X0; d_rs1 = X0; d_rd = X0; end else begin d_invalid_32bit = 1'b1; end - `RVOPC_EBREAK: if (HAVE_CSR) begin d_except = EXCEPT_EBREAK; d_rs2 = X0; d_rs1 = X0; d_rd = X0; end else begin d_invalid_32bit = 1'b1; end - `RVOPC_MRET: if (HAVE_CSR && m_mode) begin d_except = EXCEPT_MRET; d_rs2 = X0; d_rs1 = X0; d_rd = X0; end else begin d_invalid_32bit = 1'b1; end - `RVOPC_WFI: if (HAVE_CSR && permit_wfi) begin d_sleep_wfi = 1'b1; d_rs2 = X0; d_rs1 = X0; d_rd = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_FENCE_I: if (EXTENSION_ZIFENCEI) begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_branchcond = BCOND_ALWAYS; d_fence_i = 1'b1; end else begin d_invalid_32bit = 1'b1; end // note rs1/rs2/rd are zero in instruction + `RVOPC_CSRRW: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = 1'b1 ; d_csr_ren = |d_rd; d_csr_wtype = CSR_WTYPE_W; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRS: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = |d_rs1; d_csr_ren = 1'b1 ; d_csr_wtype = CSR_WTYPE_S; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRC: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = |d_rs1; d_csr_ren = 1'b1 ; d_csr_wtype = CSR_WTYPE_C; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRWI: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = 1'b1 ; d_csr_ren = |d_rd; d_csr_wtype = CSR_WTYPE_W; d_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRSI: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = |d_rs1; d_csr_ren = 1'b1 ; d_csr_wtype = CSR_WTYPE_S; d_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRCI: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = |d_rs1; d_csr_ren = 1'b1 ; d_csr_wtype = CSR_WTYPE_C; d_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_ECALL: if (HAVE_CSR) begin d_except = m_mode || !U_MODE ? EXCEPT_ECALL_M : EXCEPT_ECALL_U; d_rs2 = X0; d_rs1 = X0; d_rd = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_EBREAK: if (HAVE_CSR) begin d_except = EXCEPT_EBREAK; d_rs2 = X0; d_rs1 = X0; d_rd = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_MRET: if (HAVE_CSR && m_mode) begin d_except = EXCEPT_MRET; d_rs2 = X0; d_rs1 = X0; d_rd = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_WFI: if (HAVE_CSR && !trap_wfi) begin d_sleep_wfi = 1'b1; d_rs2 = X0; d_rs1 = X0; d_rd = X0; end else begin d_invalid_32bit = 1'b1; end default: begin d_invalid_32bit = 1'b1; end endcase if (d_invalid || d_starved || d_except_instr_bus_fault || partial_predicted_branch) begin - d_rs1 = {W_REGADDR{1'b0}}; - d_rs2 = {W_REGADDR{1'b0}}; - d_rd = {W_REGADDR{1'b0}}; - d_memop = MEMOP_NONE; - d_branchcond = BCOND_NEVER; - d_csr_ren = 1'b0; - d_csr_wen = 1'b0; - d_except = EXCEPT_NONE; - d_sleep_wfi = 1'b0; + d_rs1 = {W_REGADDR{1'b0}}; + d_rs2 = {W_REGADDR{1'b0}}; + d_rd = {W_REGADDR{1'b0}}; + d_memop = MEMOP_NONE; + d_branchcond = BCOND_NEVER; + d_csr_ren = 1'b0; + d_csr_wen = 1'b0; + d_except = EXCEPT_NONE; + d_sleep_wfi = 1'b0; + d_sleep_block = 1'b0; + d_sleep_unblock = 1'b0; if (EXTENSION_M) d_aluop = ALUOP_ADD; diff --git a/test/sim/common/hazard3_instr.h b/test/sim/common/hazard3_instr.h new file mode 100644 index 0000000..f360d7e --- /dev/null +++ b/test/sim/common/hazard3_instr.h @@ -0,0 +1,32 @@ +#ifndef _HAZARD3_INSTR_H +#define _HAZARD3_INSTR_H + +#include + +// C macros for Hazard3 custom instructions + +// nbits must be a constant expression +#define __hazard3_bextm(nbits, rs1, rs2) ({\ + uint32_t __h3_bextm_rd; \ + asm (".insn r 0x0b, 0, %3, %0, %1, %2"\ + : "=r" (__h3_bextm_rd) \ + : "r" (rs1), "r" (rs2), "i" ((((nbits) - 1) & 0x7) << 1)\ + ); \ + __h3_bextm_rd; \ +}) + +// nbits and shamt must be constant expressions +#define __hazard3_bextmi(nbits, rs1, shamt) ({\ + uint32_t __h3_bextmi_rd; \ + asm (".insn i 0x0b, 0x4, %0, %1, %2"\ + : "=r" (__h3_bextmi_rd) \ + : "r" (rs1), "i" ((((nbits) - 1) & 0x7) << 6 | ((shamt) & 0x1f)) \ + ); \ + __h3_bextmi_rd; \ +}) + +#define __hazard3_block() asm ("slt x0, x0, x0" : : : "memory") + +#define __hazard3_unblock() asm ("slt x0, x0, x1" : : : "memory") + +#endif diff --git a/test/sim/sw_testcases/extension_xh3b.c b/test/sim/sw_testcases/extension_xh3b.c index 08e1cb1..11d4628 100644 --- a/test/sim/sw_testcases/extension_xh3b.c +++ b/test/sim/sw_testcases/extension_xh3b.c @@ -1,4 +1,5 @@ #include "tb_cxxrtl_io.h" +#include "hazard3_instr.h" // Smoke test for instructions in the Xh3b extension (Hazard3 custom // bitmanip). Currently these are: @@ -6,27 +7,6 @@ // - h3_bextm: multiple bit version of the bext instruction from Zbs (1 to 8 bits) // - h3_bextmi: immediate version of the above (as bexti is to bext) -// nbits must be a constant expression -#define __hazard3_bextm(nbits, rs1, rs2) ({\ - uint32_t __h3_bextm_rd; \ - asm (".insn r 0x0b, 0, %3, %0, %1, %2"\ - : "=r" (__h3_bextm_rd) \ - : "r" (rs1), "r" (rs2), "i" ((((nbits) - 1) & 0x7) << 1)\ - ); \ - __h3_bextm_rd; \ -}) - -// nbits and shamt must be constant expressions -#define __hazard3_bextmi(nbits, rs1, shamt) ({\ - uint32_t __h3_bextmi_rd; \ - asm (".insn i 0x0b, 0x4, %0, %1, %2"\ - : "=r" (__h3_bextmi_rd) \ - : "r" (rs1), "i" ((((nbits) - 1) & 0x7) << 6 | ((shamt) & 0x1f)) \ - ); \ - __h3_bextmi_rd; \ -}) - - // The instruction is just supposed to take a single static size... __attribute__((noinline)) uint32_t bextm_dynamic_width(uint nbits, uint32_t rs1, uint32_t rs2) { switch (nbits) { diff --git a/test/sim/sw_testcases/unblock_self_latching.c b/test/sim/sw_testcases/unblock_self_latching.c new file mode 100644 index 0000000..2c0e35b --- /dev/null +++ b/test/sim/sw_testcases/unblock_self_latching.c @@ -0,0 +1,70 @@ +#include "tb_cxxrtl_io.h" +#include "hazard3_instr.h" +#include "hazard3_csr.h" +#include + +// In the single core testbench, the unblock_out signal is just looped back to +// unblock_in. Use this to confirm that the unblock is latching, i.e. an +// earlier unblock will cause the next block to fall through immediately. +// +// Also check that a block *without* an earlier unblock will timeout. Testing +// of unblock-while-blocked needs two processors. + +/*EXPECTED-OUTPUT*************************************************************** + +Test 1: block without prior unblock +Timed out +Test 2: block with prior unblock +Unblocked +Test 3: block without prior unblock, again +Timed out +Test 4: unblock, wfi, block +Unblocked + +*******************************************************************************/ + +void set_timer_wfi_timeout(unsigned int t) { + // Use the machine timer to wake if the block instruction gets stuck. This + // relies on the fact that WFI wake respects the individual IRQ enables + // in mie, but ignores mstatus.mie, so we can use the timer to wake + // without getting an interrupt. + clear_csr(mstatus, 0x8); + set_csr(mie, 0x80); + + // Set timer in future, and ensure the pending bit has cleared (may not be immediate) + mm_timer->mtimecmp = mm_timer->mtime + 1000; + while (read_csr(mip) & 0x80) + ; +} + +bool block_with_timeout() { + set_timer_wfi_timeout(1000); + __hazard3_block(); + bool timed_out = !!(read_csr(mip) & 0x80); + clear_csr(mie, 0x80); + return timed_out; +} + +int main() { + tb_puts("Test 1: block without prior unblock\n"); + tb_puts(block_with_timeout() ? "Timed out\n" : "Unblocked\n"); + + // Make sure the unblock latch sets. + tb_puts("Test 2: block with prior unblock\n"); + __hazard3_unblock(); + tb_puts(block_with_timeout() ? "Timed out\n" : "Unblocked\n"); + + // Make sure the unblock latch clears + tb_puts("Test 3: block without prior unblock, again\n"); + tb_puts(block_with_timeout() ? "Timed out\n" : "Unblocked\n"); + + // Make sure a WFI does not clear the latch + tb_puts("Test 4: unblock, wfi, block\n"); + __hazard3_unblock(); + set_timer_wfi_timeout(1000); + asm ("wfi"); + clear_csr(mie, 0x80); + tb_puts(block_with_timeout() ? "Timed out\n" : "Unblocked\n"); + + return 0; +}