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:
parent
b352d3878d
commit
954bae5cf1
|
@ -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)
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue