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"
|
`include "hazard3_width_const.vh"
|
||||||
) (
|
) (
|
||||||
// Global signals
|
// Global signals
|
||||||
input wire clk,
|
input wire clk,
|
||||||
input wire clk_always_on,
|
input wire clk_always_on,
|
||||||
input wire rst_n,
|
input wire rst_n,
|
||||||
|
|
||||||
// Power control signals
|
// Power control signals
|
||||||
output wire pwrup_req,
|
output wire pwrup_req,
|
||||||
input wire pwrup_ack,
|
input wire pwrup_ack,
|
||||||
output wire clk_en,
|
output wire clk_en,
|
||||||
output wire unblock_out,
|
output reg unblock_out,
|
||||||
input wire unblock_in,
|
input wire unblock_in,
|
||||||
|
|
||||||
`ifdef RISCV_FORMAL
|
`ifdef RISCV_FORMAL
|
||||||
`RVFI_OUTPUTS ,
|
`RVFI_OUTPUTS ,
|
||||||
`endif
|
`endif
|
||||||
|
|
||||||
// Instruction fetch port
|
// Instruction fetch port
|
||||||
output wire bus_aph_req_i,
|
output wire bus_aph_req_i,
|
||||||
output wire bus_aph_panic_i, // e.g. branch mispredict + flush
|
output wire bus_aph_panic_i, // e.g. branch mispredict + flush
|
||||||
input wire bus_aph_ready_i,
|
input wire bus_aph_ready_i,
|
||||||
input wire bus_dph_ready_i,
|
input wire bus_dph_ready_i,
|
||||||
input wire bus_dph_err_i,
|
input wire bus_dph_err_i,
|
||||||
|
|
||||||
output wire [W_ADDR-1:0] bus_haddr_i,
|
output wire [W_ADDR-1:0] bus_haddr_i,
|
||||||
output wire [2:0] bus_hsize_i,
|
output wire [2:0] bus_hsize_i,
|
||||||
output wire bus_priv_i,
|
output wire bus_priv_i,
|
||||||
input wire [W_DATA-1:0] bus_rdata_i,
|
input wire [W_DATA-1:0] bus_rdata_i,
|
||||||
|
|
||||||
// Load/store port
|
// Load/store port
|
||||||
output reg bus_aph_req_d,
|
output reg bus_aph_req_d,
|
||||||
output wire bus_aph_excl_d,
|
output wire bus_aph_excl_d,
|
||||||
input wire bus_aph_ready_d,
|
input wire bus_aph_ready_d,
|
||||||
input wire bus_dph_ready_d,
|
input wire bus_dph_ready_d,
|
||||||
input wire bus_dph_err_d,
|
input wire bus_dph_err_d,
|
||||||
input wire bus_dph_exokay_d,
|
input wire bus_dph_exokay_d,
|
||||||
|
|
||||||
output reg [W_ADDR-1:0] bus_haddr_d,
|
output reg [W_ADDR-1:0] bus_haddr_d,
|
||||||
output reg [2:0] bus_hsize_d,
|
output reg [2:0] bus_hsize_d,
|
||||||
output reg bus_priv_d,
|
output reg bus_priv_d,
|
||||||
output reg bus_hwrite_d,
|
output reg bus_hwrite_d,
|
||||||
output reg [W_DATA-1:0] bus_wdata_d,
|
output reg [W_DATA-1:0] bus_wdata_d,
|
||||||
input wire [W_DATA-1:0] bus_rdata_d,
|
input wire [W_DATA-1:0] bus_rdata_d,
|
||||||
|
|
||||||
// Debugger run/halt control
|
// Debugger run/halt control
|
||||||
input wire dbg_req_halt,
|
input wire dbg_req_halt,
|
||||||
input wire dbg_req_halt_on_reset,
|
input wire dbg_req_halt_on_reset,
|
||||||
input wire dbg_req_resume,
|
input wire dbg_req_resume,
|
||||||
output wire dbg_halted,
|
output wire dbg_halted,
|
||||||
output wire dbg_running,
|
output wire dbg_running,
|
||||||
// Debugger access to data0 CSR
|
// Debugger access to data0 CSR
|
||||||
input wire [W_DATA-1:0] dbg_data0_rdata,
|
input wire [W_DATA-1:0] dbg_data0_rdata,
|
||||||
output wire [W_DATA-1:0] dbg_data0_wdata,
|
output wire [W_DATA-1:0] dbg_data0_wdata,
|
||||||
output wire dbg_data0_wen,
|
output wire dbg_data0_wen,
|
||||||
// Debugger instruction injection
|
// Debugger instruction injection
|
||||||
input wire [W_DATA-1:0] dbg_instr_data,
|
input wire [W_DATA-1:0] dbg_instr_data,
|
||||||
input wire dbg_instr_data_vld,
|
input wire dbg_instr_data_vld,
|
||||||
output wire dbg_instr_data_rdy,
|
output wire dbg_instr_data_rdy,
|
||||||
output wire dbg_instr_caught_exception,
|
output wire dbg_instr_caught_exception,
|
||||||
output wire dbg_instr_caught_ebreak,
|
output wire dbg_instr_caught_ebreak,
|
||||||
|
|
||||||
// Level-sensitive interrupt sources
|
// Level-sensitive interrupt sources
|
||||||
input wire [NUM_IRQS-1:0] irq, // -> mip.meip
|
input wire [NUM_IRQS-1:0] irq, // -> mip.meip
|
||||||
input wire soft_irq, // -> mip.msip
|
input wire soft_irq, // -> mip.msip
|
||||||
input wire timer_irq // -> mip.mtip
|
input wire timer_irq // -> mip.mtip
|
||||||
);
|
);
|
||||||
|
|
||||||
`include "hazard3_ops.vh"
|
`include "hazard3_ops.vh"
|
||||||
|
@ -222,7 +222,7 @@ wire d_csr_w_imm;
|
||||||
|
|
||||||
wire x_jump_not_except;
|
wire x_jump_not_except;
|
||||||
wire x_mmode_execution;
|
wire x_mmode_execution;
|
||||||
wire x_permit_wfi;
|
wire x_trap_wfi;
|
||||||
|
|
||||||
hazard3_decode #(
|
hazard3_decode #(
|
||||||
`include "hazard3_config_inst.vh"
|
`include "hazard3_config_inst.vh"
|
||||||
|
@ -241,7 +241,7 @@ hazard3_decode #(
|
||||||
|
|
||||||
.debug_mode (debug_mode),
|
.debug_mode (debug_mode),
|
||||||
.m_mode (x_mmode_execution),
|
.m_mode (x_mmode_execution),
|
||||||
.permit_wfi (x_permit_wfi),
|
.trap_wfi (x_trap_wfi),
|
||||||
|
|
||||||
.d_starved (d_starved),
|
.d_starved (d_starved),
|
||||||
.x_stall (x_stall),
|
.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 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_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_pwr_allow_sleep;
|
||||||
wire m_allow_power_down;
|
wire m_pwr_allow_power_down;
|
||||||
wire m_allow_sleep_on_block;
|
wire m_pwr_allow_sleep_on_block;
|
||||||
wire m_wfi_wakeup_req;
|
wire m_wfi_wakeup_req;
|
||||||
|
|
||||||
hazard3_csr #(
|
hazard3_csr #(
|
||||||
|
@ -984,9 +987,9 @@ hazard3_csr #(
|
||||||
.loadstore_dphase_pending (m_dphase_in_flight),
|
.loadstore_dphase_pending (m_dphase_in_flight),
|
||||||
.mepc_in (m_exception_return_addr),
|
.mepc_in (m_exception_return_addr),
|
||||||
|
|
||||||
.pwr_allow_sleep (m_allow_sleep),
|
.pwr_allow_sleep (m_pwr_allow_sleep),
|
||||||
.pwr_allow_power_down (m_allow_power_down),
|
.pwr_allow_power_down (m_pwr_allow_power_down),
|
||||||
.pwr_allow_sleep_on_block (m_allow_sleep_on_block),
|
.pwr_allow_sleep_on_block (m_pwr_allow_sleep_on_block),
|
||||||
.pwr_wfi_wakeup_req (m_wfi_wakeup_req),
|
.pwr_wfi_wakeup_req (m_wfi_wakeup_req),
|
||||||
|
|
||||||
.m_mode_execution (x_mmode_execution),
|
.m_mode_execution (x_mmode_execution),
|
||||||
|
@ -1013,7 +1016,7 @@ hazard3_csr #(
|
||||||
.trig_m_en (x_trig_m_en),
|
.trig_m_en (x_trig_m_en),
|
||||||
|
|
||||||
// Other CSR-specific signalling
|
// Other CSR-specific signalling
|
||||||
.permit_wfi (x_permit_wfi),
|
.trap_wfi (x_trap_wfi),
|
||||||
.instr_ret (x_instr_ret)
|
.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_except_to_d_mode <= 1'b0;
|
||||||
xm_sleep_wfi <= 1'b0;
|
xm_sleep_wfi <= 1'b0;
|
||||||
xm_sleep_block <= 1'b0;
|
xm_sleep_block <= 1'b0;
|
||||||
|
unblock_out <= 1'b0;
|
||||||
{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
|
{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
|
||||||
end else begin
|
end else begin
|
||||||
|
unblock_out <= 1'b0;
|
||||||
if (!m_stall) begin
|
if (!m_stall) begin
|
||||||
{xm_rs1, xm_rs2, xm_rd} <= {d_rs1, d_rs2, d_rd};
|
{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.
|
// 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_memop <= x_except != EXCEPT_NONE ? MEMOP_NONE : d_memop;
|
||||||
xm_except <= x_except;
|
xm_except <= x_except;
|
||||||
xm_except_to_d_mode <= x_trig_break_d_mode;
|
xm_except_to_d_mode <= x_trig_break_d_mode;
|
||||||
xm_sleep_wfi <= d_sleep_wfi && !x_exec_pmp_fail;
|
xm_sleep_wfi <= x_except == EXCEPT_NONE && d_sleep_wfi;
|
||||||
xm_sleep_block <= d_sleep_block && !x_exec_pmp_fail;
|
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
|
// Note the d_starved term is required because it is possible
|
||||||
// (e.g. PMP X permission fail) to except when the frontend is
|
// (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:
|
// starved, and we get a bad mepc if we let this jump ahead:
|
||||||
if (x_stall || d_starved || m_trap_enter_soon) begin
|
if (x_stall || d_starved || m_trap_enter_soon) begin
|
||||||
// Insert bubble
|
// Insert bubble
|
||||||
xm_rd <= {W_REGADDR{1'b0}};
|
xm_rd <= {W_REGADDR{1'b0}};
|
||||||
xm_memop <= MEMOP_NONE;
|
xm_memop <= MEMOP_NONE;
|
||||||
xm_except <= EXCEPT_NONE;
|
xm_except <= EXCEPT_NONE;
|
||||||
xm_except_to_d_mode <= 1'b0;
|
xm_except_to_d_mode <= 1'b0;
|
||||||
xm_sleep_wfi <= 1'b0;
|
xm_sleep_wfi <= 1'b0;
|
||||||
xm_sleep_block <= 1'b0;
|
xm_sleep_block <= 1'b0;
|
||||||
|
unblock_out <= 1'b0;
|
||||||
end
|
end
|
||||||
end else if (bus_dph_err_d) begin
|
end else if (bus_dph_err_d) begin
|
||||||
// First phase of 2-phase AHB5 error response. Pass the exception along on
|
// 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_memop != MEMOP_NONE);
|
||||||
assert(!xm_sleep_wfi);
|
assert(!xm_sleep_wfi);
|
||||||
assert(!xm_sleep_block);
|
assert(!xm_sleep_block);
|
||||||
|
assert(!unblock_out);
|
||||||
`endif
|
`endif
|
||||||
xm_except <=
|
xm_except <=
|
||||||
|EXTENSION_A && xm_memop == MEMOP_LR_W ? EXCEPT_LOAD_FAULT :
|
|EXTENSION_A && xm_memop == MEMOP_LR_W ? EXCEPT_LOAD_FAULT :
|
||||||
|
@ -1142,15 +1150,15 @@ hazard3_power_ctrl power_ctrl (
|
||||||
.pwrup_ack (pwrup_ack),
|
.pwrup_ack (pwrup_ack),
|
||||||
.clk_en (clk_en),
|
.clk_en (clk_en),
|
||||||
|
|
||||||
.allow_sleep (m_allow_sleep),
|
.allow_sleep (m_pwr_allow_sleep),
|
||||||
.allow_power_down (m_allow_power_down),
|
.allow_power_down (m_pwr_allow_power_down),
|
||||||
.allow_sleep_on_block (m_allow_sleep_on_block),
|
.allow_sleep_on_block (m_pwr_allow_sleep_on_block),
|
||||||
|
|
||||||
.frontend_pwrdown_ok (f_frontend_pwrdown_ok),
|
.frontend_pwrdown_ok (f_frontend_pwrdown_ok),
|
||||||
|
|
||||||
.sleeping_on_wfi (xm_sleep_wfi),
|
.sleeping_on_wfi (xm_sleep_wfi),
|
||||||
.wfi_wakeup_req (m_wfi_wakeup_req),
|
.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),
|
.block_wakeup_req_pulse (unblock_in),
|
||||||
.stall_release (m_sleep_stall_release)
|
.stall_release (m_sleep_stall_release)
|
||||||
);
|
);
|
||||||
|
|
|
@ -117,7 +117,8 @@ module hazard3_csr #(
|
||||||
output wire trig_m_en,
|
output wire trig_m_en,
|
||||||
|
|
||||||
// Other CSR-specific signalling
|
// Other CSR-specific signalling
|
||||||
output wire permit_wfi,
|
output wire trap_wfi,
|
||||||
|
output wire wfi_is_nop,
|
||||||
input wire instr_ret
|
input wire instr_ret
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -226,8 +227,11 @@ always @ (posedge clk or negedge rst_n) begin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// Simply trap all U-mode WFIs if timeout bit is set
|
// Trap all U-mode WFIs if timeout bit is set. Note that the debug spec
|
||||||
assign permit_wfi = m_mode || !mstatus_tw;
|
// 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;
|
reg [XLEN-1:0] mscratch;
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ module hazard3_decode #(
|
||||||
|
|
||||||
input wire debug_mode,
|
input wire debug_mode,
|
||||||
input wire m_mode,
|
input wire m_mode,
|
||||||
input wire permit_wfi,
|
input wire trap_wfi,
|
||||||
|
|
||||||
output wire d_starved,
|
output wire d_starved,
|
||||||
input wire x_stall,
|
input wire x_stall,
|
||||||
|
@ -233,7 +233,6 @@ always @ (*) begin
|
||||||
`RVOPC_ADD: begin d_aluop = ALUOP_ADD; end
|
`RVOPC_ADD: begin d_aluop = ALUOP_ADD; end
|
||||||
`RVOPC_SUB: begin d_aluop = ALUOP_SUB; end
|
`RVOPC_SUB: begin d_aluop = ALUOP_SUB; end
|
||||||
`RVOPC_SLL: begin d_aluop = ALUOP_SLL; 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_SLTU: begin d_aluop = ALUOP_LTU; end
|
||||||
`RVOPC_XOR: begin d_aluop = ALUOP_XOR; end
|
`RVOPC_XOR: begin d_aluop = ALUOP_XOR; end
|
||||||
`RVOPC_SRL: begin d_aluop = ALUOP_SRL; 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_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_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_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_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
|
`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_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: 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_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_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_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_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_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_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_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_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_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_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_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
|
default: begin d_invalid_32bit = 1'b1; end
|
||||||
endcase
|
endcase
|
||||||
|
|
||||||
if (d_invalid || d_starved || d_except_instr_bus_fault || partial_predicted_branch) begin
|
if (d_invalid || d_starved || d_except_instr_bus_fault || partial_predicted_branch) begin
|
||||||
d_rs1 = {W_REGADDR{1'b0}};
|
d_rs1 = {W_REGADDR{1'b0}};
|
||||||
d_rs2 = {W_REGADDR{1'b0}};
|
d_rs2 = {W_REGADDR{1'b0}};
|
||||||
d_rd = {W_REGADDR{1'b0}};
|
d_rd = {W_REGADDR{1'b0}};
|
||||||
d_memop = MEMOP_NONE;
|
d_memop = MEMOP_NONE;
|
||||||
d_branchcond = BCOND_NEVER;
|
d_branchcond = BCOND_NEVER;
|
||||||
d_csr_ren = 1'b0;
|
d_csr_ren = 1'b0;
|
||||||
d_csr_wen = 1'b0;
|
d_csr_wen = 1'b0;
|
||||||
d_except = EXCEPT_NONE;
|
d_except = EXCEPT_NONE;
|
||||||
d_sleep_wfi = 1'b0;
|
d_sleep_wfi = 1'b0;
|
||||||
|
d_sleep_block = 1'b0;
|
||||||
|
d_sleep_unblock = 1'b0;
|
||||||
if (EXTENSION_M)
|
if (EXTENSION_M)
|
||||||
d_aluop = ALUOP_ADD;
|
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 "tb_cxxrtl_io.h"
|
||||||
|
#include "hazard3_instr.h"
|
||||||
|
|
||||||
// Smoke test for instructions in the Xh3b extension (Hazard3 custom
|
// Smoke test for instructions in the Xh3b extension (Hazard3 custom
|
||||||
// bitmanip). Currently these are:
|
// bitmanip). Currently these are:
|
||||||
|
@ -6,27 +7,6 @@
|
||||||
// - h3_bextm: multiple bit version of the bext instruction from Zbs (1 to 8 bits)
|
// - 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)
|
// - 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...
|
// 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) {
|
__attribute__((noinline)) uint32_t bextm_dynamic_width(uint nbits, uint32_t rs1, uint32_t rs2) {
|
||||||
switch (nbits) {
|
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