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.

This commit is contained in:
Luke Wren 2022-08-29 14:51:19 +01:00
parent b352d3878d
commit 954bae5cf1
6 changed files with 224 additions and 114 deletions

View File

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

View File

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

View File

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

View File

@ -0,0 +1,32 @@
#ifndef _HAZARD3_INSTR_H
#define _HAZARD3_INSTR_H
#include <stdint.h>
// 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

View File

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

View File

@ -0,0 +1,70 @@
#include "tb_cxxrtl_io.h"
#include "hazard3_instr.h"
#include "hazard3_csr.h"
#include <stdbool.h>
// 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;
}