First pass at implementing the new IRQ controls. Works well enough that the old tests pass :)

This commit is contained in:
Luke Wren 2022-08-07 20:51:12 +01:00
parent 69917ccbbe
commit 15cb21ae43
25 changed files with 1035 additions and 638 deletions

View File

@ -245,7 +245,7 @@ hazard3_cpu_1port #(
.CSR_M_MANDATORY (1), .CSR_M_MANDATORY (1),
.CSR_M_TRAP (1), .CSR_M_TRAP (1),
.DEBUG_SUPPORT (1), .DEBUG_SUPPORT (1),
.NUM_IRQ (1), .NUM_IRQS (1),
.RESET_REGFILE (0), .RESET_REGFILE (0),
// Can be overridden from the defaults in hazard3_config.vh during // Can be overridden from the defaults in hazard3_config.vh during
// instantiation of example_soc(): // instantiation of example_soc():

View File

@ -94,7 +94,8 @@ wire [W_DATA-1:0] ctz_search_mask = aluop == ALUOP_CLZ ? op_a_rev : op_a;
wire [W_SHAMT:0] ctz_clz; wire [W_SHAMT:0] ctz_clz;
hazard3_priority_encode #( hazard3_priority_encode #(
.W_REQ (W_DATA) .W_REQ (W_DATA),
.HIGHEST_WINS (0)
) ctz_priority_encode ( ) ctz_priority_encode (
.req (ctz_search_mask), .req (ctz_search_mask),
.gnt (ctz_clz[W_SHAMT-1:0]) .gnt (ctz_clz[W_SHAMT-1:0])

View File

@ -0,0 +1,33 @@
/*****************************************************************************\
| Copyright (C) 2022 Luke Wren |
| SPDX-License-Identifier: Apache-2.0 |
\*****************************************************************************/
// req: one-hot bitmap
// idx: index of the sole set bit in req
`default_nettype none
module hazard3_onehot_encode #(
parameter W_REQ = 16,
parameter W_GNT = $clog2(W_REQ) // do not modify
) (
input wire [W_REQ-1:0] req,
output reg [W_GNT-1:0] gnt
);
reg [W_GNT-1:0] gnt;
always @ (*) begin: encode
reg [W_GNT:0] i;
gnt = {W_GNT{1'b0}};
for (i = 0; i < W_REQ; i = i + 1) begin
gnt = gnt | ({W_GNT{req[i]}} & i[W_GNT-1:0]);
end
end
endmodule
`ifndef YOSYS
`default_nettype wire
`endif

View File

@ -0,0 +1,33 @@
/*****************************************************************************\
| Copyright (C) 2022 Luke Wren |
| SPDX-License-Identifier: Apache-2.0 |
\*****************************************************************************/
// req: bitmap
// idx: bitmap with all bits clear except the least- (HIGHEST_WINS=0) or
// most- (HIGHEST_WINS=1) significant set bit in req.
`default_nettype none
module hazard3_onehot_priority #(
parameter W_REQ = 16,
parameter HIGHEST_WINS = 0
) (
input wire [W_REQ-1:0] req,
output reg [W_REQ-1:0] gnt
);
always @ (*) begin: select
integer i;
for (i = 0; i < W_REQ; i = i + 1) begin
gnt[i] = req[i] && ~|(req & (
HIGHEST_WINS ? ~({W_REQ{1'b1}} >> i) : ~({W_REQ{1'b1}} << i)
));
end
end
endmodule
`ifndef YOSYS
`default_nettype wire
`endif

View File

@ -0,0 +1,76 @@
/*****************************************************************************\
| Copyright (C) 2022 Luke Wren |
| SPDX-License-Identifier: Apache-2.0 |
\*****************************************************************************/
// req: bitmap of requests
// priority: packed array of dynamic priority level of each request
// gnt: one-hot bitmap with the highest-priority request.
`default_nettype none
module hazard3_onehot_priority_dynamic #(
parameter W_REQ = 8,
parameter N_PRIORITIES = 2,
parameter PRIORITY_HIGHEST_WINS = 1, // If 1, numerically highest level has greatest priority.
// Otherwise, numerically lowest wins.
parameter TIEBREAK_HIGHEST_WINS = 0, // If 1, highest-numbered request at the highest priority
// level wins the tiebreak. Otherwise, lowest-numbered.
// Do not modify:
parameter W_PRIORITY = $clog2(N_PRIORITIES)
) (
input wire [W_REQ*W_PRIORITY-1:0] priority,
input wire [W_REQ-1:0] req,
output wire [W_REQ-1:0] gnt
);
// 1. Stratify requests according to their level
reg [W_REQ-1:0] req_stratified [0:N_PRIORITIES-1];
reg [N_PRIORITIES-1:0] level_has_req;
always @ (*) begin: stratify
integer i, j;
for (i = 0; i < N_PRIORITIES; i = i + 1) begin
for (j = 0; j < W_REQ; j = j + 1) begin
req_stratified[i][j] = req[j] && priority[W_PRIORITY * j +: W_PRIORITY] == i;
end
level_has_req[i] = |req_stratified[i];
end
end
// 2. Select the highest level with active requests
wire [N_PRIORITIES-1:0] active_layer_sel;
hazard3_onehot_priority #(
.W_REQ (N_PRIORITIES),
.HIGHEST_WINS (PRIORITY_HIGHEST_WINS)
) prisel_layer (
.req (level_has_req),
.gnt (active_layer_sel)
);
// 3. Mask only those requests at this level
reg [W_REQ-1:0] reqs_from_highest_layer;
always @ (*) begin: mux_reqs_by_layer
integer i;
reqs_from_highest_layer = {W_REQ{1'b0}};
for (i = 0; i < N_PRIORITIES; i = i + 1)
reqs_from_highest_layer = reqs_from_highest_layer |
(req_stratified[i] & {W_REQ{active_layer_sel[i]}});
end
// 4. Do a standard priority select on those requests as a tie break
hazard3_onehot_priority #(
.W_REQ (W_REQ),
.HIGHEST_WINS (TIEBREAK_HIGHEST_WINS)
) prisel_tiebreak (
.req (reqs_from_highest_layer),
.gnt (gnt)
);
endmodule
`ifndef YOSYS
`default_nettype wire
`endif

View File

@ -3,44 +3,39 @@
| SPDX-License-Identifier: Apache-2.0 | | SPDX-License-Identifier: Apache-2.0 |
\*****************************************************************************/ \*****************************************************************************/
// Really something like this should be in a utility library (or the language!), // req: bitmap
// but Hazard3 is supposed to be self-contained // gnt: index of least set bit (HIGHEST_WINS=0) or most set bit (HIGHEST_WINS=1)
`default_nettype none `default_nettype none
module hazard3_priority_encode #( module hazard3_priority_encode #(
parameter W_REQ = 16, parameter W_REQ = 16,
parameter W_GNT = $clog2(W_REQ) // do not modify parameter HIGHEST_WINS = 0,
parameter W_GNT = $clog2(W_REQ) // do not modify
) ( ) (
input wire [W_REQ-1:0] req, input wire [W_REQ-1:0] req,
output wire [W_GNT-1:0] gnt output wire [W_GNT-1:0] gnt
); );
// First do a priority-select of the input bitmap. wire [W_REQ-1:0] gnt_onehot;
reg [W_REQ-1:0] gnt_onehot; hazard3_onehot_priority #(
.W_REQ (W_REQ),
.HIGHEST_WINS (HIGHEST_WINS)
) priority_u (
.req (req),
.gnt (gnt_onehot)
);
always @ (*) begin: smear hazard3_onehot_encode #(
integer i; .W_REQ (W_REQ)
for (i = 0; i < W_REQ; i = i + 1) ) encode_u (
gnt_onehot[i] = req[i] && ~|(req & ~({W_REQ{1'b1}} << i)); .req (gnt_onehot),
end .gnt (gnt)
);
// As the result is onehot, we can now just OR in the representation of each
// encoded integer.
reg [W_GNT-1:0] gnt_accum;
always @ (*) begin: encode
reg [W_GNT:0] i;
gnt_accum = {W_GNT{1'b0}};
for (i = 0; i < W_REQ; i = i + 1) begin
gnt_accum = gnt_accum | ({W_GNT{gnt_onehot[i]}} & i[W_GNT-1:0]);
end
end
assign gnt = gnt_accum;
endmodule endmodule
`ifndef YOSYS
`default_nettype wire `default_nettype wire
`endif

View File

@ -3,8 +3,11 @@ file hazard3_cpu_1port.v
file hazard3_cpu_2port.v file hazard3_cpu_2port.v
file arith/hazard3_alu.v file arith/hazard3_alu.v
file arith/hazard3_branchcmp.v file arith/hazard3_branchcmp.v
file arith/hazard3_muldiv_seq.v
file arith/hazard3_mul_fast.v file arith/hazard3_mul_fast.v
file arith/hazard3_muldiv_seq.v
file arith/hazard3_onehot_encode.v
file arith/hazard3_onehot_priority.v
file arith/hazard3_onehot_priority_dynamic.v
file arith/hazard3_priority_encode.v file arith/hazard3_priority_encode.v
file arith/hazard3_shift_barrel.v file arith/hazard3_shift_barrel.v
file hazard3_frontend.v file hazard3_frontend.v

View File

@ -120,9 +120,18 @@ parameter PMP_HARDWIRED_CFG = PMP_REGIONS > 0 ? {PMP_REGIONS{8'h00}} : 1'b0,
// Requires: CSR_M_MANDATORY, CSR_M_TRAP. // Requires: CSR_M_MANDATORY, CSR_M_TRAP.
parameter DEBUG_SUPPORT = 0, parameter DEBUG_SUPPORT = 0,
// NUM_IRQ: Number of external IRQs implemented in meie0 and meip0. // ----------------------------------------------------------------------------
// Minimum 1 (if CSR_M_TRAP = 1), maximum 128. // External interrupt support
parameter NUM_IRQ = 32,
// NUM_IRQS: Number of external IRQs implemented in meiea, meipa, meifa and
// meipra, if CSR_M_TRAP is enabled. Minimum 1, maximum 512.
parameter NUM_IRQS = 32,
// Number of priority bits implemented for each interrupt in meipra. The
// number of distinct levels is (1 << IRQ_PRIORITY_BITS). Minimum 0, max 4.
// Note that having more than 1 priority level with a large number of IRQs
// will have a severe effect on timing.
parameter IRQ_PRIORITY_BITS = 0,
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ID registers // ID registers

View File

@ -34,7 +34,8 @@
.PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR), .PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR),
.PMP_HARDWIRED_CFG (PMP_HARDWIRED_CFG), .PMP_HARDWIRED_CFG (PMP_HARDWIRED_CFG),
.DEBUG_SUPPORT (DEBUG_SUPPORT), .DEBUG_SUPPORT (DEBUG_SUPPORT),
.NUM_IRQ (NUM_IRQ), .NUM_IRQS (NUM_IRQS),
.IRQ_PRIORITY_BITS (IRQ_PRIORITY_BITS),
.MVENDORID_VAL (MVENDORID_VAL), .MVENDORID_VAL (MVENDORID_VAL),
.MIMPID_VAL (MIMPID_VAL), .MIMPID_VAL (MIMPID_VAL),
`ifndef HAZARD3_CONFIG_INST_NO_MHARTID `ifndef HAZARD3_CONFIG_INST_NO_MHARTID

View File

@ -11,61 +11,61 @@ 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 rst_n, input wire rst_n,
`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_IRQ-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"

View File

@ -13,60 +13,60 @@ module hazard3_cpu_1port #(
`include "hazard3_config.vh" `include "hazard3_config.vh"
) ( ) (
// Global signals // Global signals
input wire clk, input wire clk,
input wire rst_n, input wire rst_n,
`ifdef RISCV_FORMAL `ifdef RISCV_FORMAL
`RVFI_OUTPUTS , `RVFI_OUTPUTS ,
`endif `endif
// AHB-lite Master port // AHB-lite Master port
output reg [W_ADDR-1:0] ahblm_haddr, output reg [W_ADDR-1:0] ahblm_haddr,
output reg ahblm_hwrite, output reg ahblm_hwrite,
output reg [1:0] ahblm_htrans, output reg [1:0] ahblm_htrans,
output reg [2:0] ahblm_hsize, output reg [2:0] ahblm_hsize,
output wire [2:0] ahblm_hburst, output wire [2:0] ahblm_hburst,
output reg [3:0] ahblm_hprot, output reg [3:0] ahblm_hprot,
output wire ahblm_hmastlock, output wire ahblm_hmastlock,
output reg [7:0] ahblm_hmaster, output reg [7:0] ahblm_hmaster,
output reg ahblm_hexcl, output reg ahblm_hexcl,
input wire ahblm_hready, input wire ahblm_hready,
input wire ahblm_hresp, input wire ahblm_hresp,
input wire ahblm_hexokay, input wire ahblm_hexokay,
output wire [W_DATA-1:0] ahblm_hwdata, output wire [W_DATA-1:0] ahblm_hwdata,
input wire [W_DATA-1:0] ahblm_hrdata, input wire [W_DATA-1:0] ahblm_hrdata,
// 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,
// Optional debug system bus access patch-through // Optional debug system bus access patch-through
input wire [W_ADDR-1:0] dbg_sbus_addr, input wire [W_ADDR-1:0] dbg_sbus_addr,
input wire dbg_sbus_write, input wire dbg_sbus_write,
input wire [1:0] dbg_sbus_size, input wire [1:0] dbg_sbus_size,
input wire dbg_sbus_vld, input wire dbg_sbus_vld,
output wire dbg_sbus_rdy, output wire dbg_sbus_rdy,
output wire dbg_sbus_err, output wire dbg_sbus_err,
input wire [W_DATA-1:0] dbg_sbus_wdata, input wire [W_DATA-1:0] dbg_sbus_wdata,
output wire [W_DATA-1:0] dbg_sbus_rdata, output wire [W_DATA-1:0] dbg_sbus_rdata,
// Level-sensitive interrupt sources // Level-sensitive interrupt sources
input wire [NUM_IRQ-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
); );
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -13,73 +13,73 @@ module hazard3_cpu_2port #(
`include "hazard3_config.vh" `include "hazard3_config.vh"
) ( ) (
// Global signals // Global signals
input wire clk, input wire clk,
input wire rst_n, input wire rst_n,
`ifdef RISCV_FORMAL `ifdef RISCV_FORMAL
`RVFI_OUTPUTS , `RVFI_OUTPUTS ,
`endif `endif
// Instruction fetch port // Instruction fetch port
output wire [W_ADDR-1:0] i_haddr, output wire [W_ADDR-1:0] i_haddr,
output wire i_hwrite, output wire i_hwrite,
output wire [1:0] i_htrans, output wire [1:0] i_htrans,
output wire [2:0] i_hsize, output wire [2:0] i_hsize,
output wire [2:0] i_hburst, output wire [2:0] i_hburst,
output wire [3:0] i_hprot, output wire [3:0] i_hprot,
output wire i_hmastlock, output wire i_hmastlock,
output wire [7:0] i_hmaster, output wire [7:0] i_hmaster,
input wire i_hready, input wire i_hready,
input wire i_hresp, input wire i_hresp,
output wire [W_DATA-1:0] i_hwdata, output wire [W_DATA-1:0] i_hwdata,
input wire [W_DATA-1:0] i_hrdata, input wire [W_DATA-1:0] i_hrdata,
// Load/store port // Load/store port
output wire [W_ADDR-1:0] d_haddr, output wire [W_ADDR-1:0] d_haddr,
output wire d_hwrite, output wire d_hwrite,
output wire [1:0] d_htrans, output wire [1:0] d_htrans,
output wire [2:0] d_hsize, output wire [2:0] d_hsize,
output wire [2:0] d_hburst, output wire [2:0] d_hburst,
output wire [3:0] d_hprot, output wire [3:0] d_hprot,
output wire d_hmastlock, output wire d_hmastlock,
output wire [7:0] d_hmaster, output wire [7:0] d_hmaster,
output wire d_hexcl, output wire d_hexcl,
input wire d_hready, input wire d_hready,
input wire d_hresp, input wire d_hresp,
input wire d_hexokay, input wire d_hexokay,
output wire [W_DATA-1:0] d_hwdata, output wire [W_DATA-1:0] d_hwdata,
input wire [W_DATA-1:0] d_hrdata, input wire [W_DATA-1:0] d_hrdata,
// 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,
// Optional debug system bus access patch-through // Optional debug system bus access patch-through
input wire [W_ADDR-1:0] dbg_sbus_addr, input wire [W_ADDR-1:0] dbg_sbus_addr,
input wire dbg_sbus_write, input wire dbg_sbus_write,
input wire [1:0] dbg_sbus_size, input wire [1:0] dbg_sbus_size,
input wire dbg_sbus_vld, input wire dbg_sbus_vld,
output wire dbg_sbus_rdy, output wire dbg_sbus_rdy,
output wire dbg_sbus_err, output wire dbg_sbus_err,
input wire [W_DATA-1:0] dbg_sbus_wdata, input wire [W_DATA-1:0] dbg_sbus_wdata,
output wire [W_DATA-1:0] dbg_sbus_rdata, output wire [W_DATA-1:0] dbg_sbus_rdata,
// Level-sensitive interrupt sources // Level-sensitive interrupt sources
input wire [NUM_IRQ-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
); );
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -92,7 +92,7 @@ module hazard3_csr #(
// Level-sensitive interrupt sources // Level-sensitive interrupt sources
input wire delay_irq_entry, input wire delay_irq_entry,
input wire [NUM_IRQ-1:0] irq, input wire [NUM_IRQS-1:0] irq,
input wire irq_software, input wire irq_software,
input wire irq_timer, input wire irq_timer,
@ -112,7 +112,6 @@ module hazard3_csr #(
localparam X0 = {XLEN{1'b0}}; localparam X0 = {XLEN{1'b0}};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// CSR state + update logic // CSR state + update logic
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -142,14 +141,19 @@ always @ (posedge clk or negedge rst_n) begin
end end
end end
// Core execution state, 1 -> M-mode, 0 -> U-mode (if implemented)
reg m_mode;
wire wen_m_mode = wen && (m_mode || debug_mode);
wire ren_m_mode = ren && (m_mode || debug_mode);
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Trap-handling CSRs // Standard trap-handling CSRs
wire debug_suppresses_trap_update = DEBUG_SUPPORT && (debug_mode || enter_debug_mode); wire debug_suppresses_trap_update = DEBUG_SUPPORT && (debug_mode || enter_debug_mode);
// Core execution state, 1 -> M-mode, 0 -> U-mode (if implemented) wire trapreg_update = trap_enter_vld && trap_enter_rdy && !debug_suppresses_trap_update;
reg m_mode; wire trapreg_update_enter = trapreg_update && except != EXCEPT_MRET;
wire trapreg_update_exit = trapreg_update && except == EXCEPT_MRET;
reg mstatus_mpie; reg mstatus_mpie;
reg mstatus_mie; reg mstatus_mie;
@ -157,8 +161,6 @@ reg mstatus_mpp; // only MSB is implemented
reg mstatus_mprv; reg mstatus_mprv;
reg mstatus_tw; reg mstatus_tw;
wire wen_m_mode = wen && (m_mode || debug_mode);
// Spec text from priv-1.12: // Spec text from priv-1.12:
// "An MRET or SRET instruction is used to return from a trap in M-mode or // "An MRET or SRET instruction is used to return from a trap in M-mode or
// S-mode respectively. When executing an xRET instruction, supposing xPP // S-mode respectively. When executing an xRET instruction, supposing xPP
@ -176,25 +178,23 @@ always @ (posedge clk or negedge rst_n) begin
mstatus_mprv <= 1'b0; mstatus_mprv <= 1'b0;
mstatus_tw <= 1'b0; mstatus_tw <= 1'b0;
end else if (CSR_M_TRAP) begin end else if (CSR_M_TRAP) begin
if (trap_enter_vld && trap_enter_rdy && !debug_suppresses_trap_update) begin if (trapreg_update_exit) begin
if (except == EXCEPT_MRET) begin mstatus_mpie <= 1'b1;
mstatus_mpie <= 1'b1; mstatus_mie <= mstatus_mpie;
mstatus_mie <= mstatus_mpie; if (U_MODE) begin
if (U_MODE) begin m_mode <= mstatus_mpp;
m_mode <= mstatus_mpp; mstatus_mpp <= 1'b0;
mstatus_mpp <= 1'b0; if (!mstatus_mpp) begin
if (!mstatus_mpp) begin mstatus_mprv <= 1'b0;
mstatus_mprv <= 1'b0;
end
end
end else begin
mstatus_mpie <= mstatus_mie;
mstatus_mie <= 1'b0;
if (U_MODE) begin
m_mode <= 1'b1;
mstatus_mpp <= m_mode;
end end
end end
end else if (trapreg_update_enter) begin
mstatus_mpie <= mstatus_mie;
mstatus_mie <= 1'b0;
if (U_MODE) begin
m_mode <= 1'b1;
mstatus_mpp <= m_mode;
end
end else if (wen_m_mode && addr == MSTATUS) begin end else if (wen_m_mode && addr == MSTATUS) begin
mstatus_mpie <= wdata_update[7]; mstatus_mpie <= wdata_update[7];
mstatus_mie <= wdata_update[3]; mstatus_mie <= wdata_update[3];
@ -249,7 +249,7 @@ always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin if (!rst_n) begin
mepc <= X0; mepc <= X0;
end else if (CSR_M_TRAP) begin end else if (CSR_M_TRAP) begin
if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET && !debug_suppresses_trap_update) begin if (trapreg_update_enter) begin
mepc <= mepc_in & MEPC_MASK; mepc <= mepc_in & MEPC_MASK;
end else if (wen_m_mode && addr == MEPC) begin end else if (wen_m_mode && addr == MEPC) begin
mepc <= wdata_update & MEPC_MASK; mepc <= wdata_update & MEPC_MASK;
@ -265,81 +265,234 @@ always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin if (!rst_n) begin
mie <= X0; mie <= X0;
end else if (CSR_M_TRAP) begin end else if (CSR_M_TRAP) begin
if (wen_m_mode && addr == MIE) if (wen_m_mode && addr == MIE) begin
mie <= update_nonconst(mie, MIE_WMASK); mie <= update_nonconst(mie, MIE_WMASK);
end else if (wen_m_mode && addr == MEICONTEXT) begin
// meicontext.clearts/mtiesave/msiesave can be used to clear and
// save standard timer and soft IRQ enables, simultaneously with
// saving external interrupt context.
mie[7] <= (mie[7] || wdata_update[3]) && !wdata_update[1];
mie[3] <= (mie[3] || wdata_update[2]) && !wdata_update[1];
end
end end
end end
wire mie_meie = mie[11]; wire mie_meie = mie[11];
// Interrupt status ("pending") register, handled later // Interrupt pending register (assigned later). In our implementation this
// register is entirely read-only.
wire [XLEN-1:0] mip; wire [XLEN-1:0] mip;
// None of the bits we implement are directly writeable.
// MSIP is only writeable by a "platform-defined" mechanism, and we don't implement
// one!
// Trap cause registers. The non-constant bits can be written by software, // Trap cause registers. The non-constant bits can be written by software, and
// and update automatically on trap entry. (bits 30:0 are WLRL, so we tie most off) // update automatically on trap entry. The `code` field need only be large
// enough to support causes that will be set by hardware, in our case 4 bits.
// (Note this is different to scause which always has a hard minimum of 5
// bits.)
reg mcause_irq; reg mcause_irq;
reg [5:0] mcause_code; reg [3:0] mcause_code;
wire mcause_irq_next; wire mcause_irq_next;
wire [5:0] mcause_code_next; wire [3:0] mcause_code_next;
always @ (posedge clk or negedge rst_n) begin always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin if (!rst_n) begin
mcause_irq <= 1'b0; mcause_irq <= 1'b0;
mcause_code <= 6'h0; mcause_code <= 4'h0;
end else if (CSR_M_TRAP) begin end else if (CSR_M_TRAP) begin
if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET && !debug_suppresses_trap_update) begin if (trapreg_update_enter) begin
mcause_irq <= mcause_irq_next; mcause_irq <= mcause_irq_next;
mcause_code <= mcause_code_next; mcause_code <= mcause_code_next;
end else if (wen_m_mode && addr == MCAUSE) begin end else if (wen_m_mode && addr == MCAUSE) begin
{mcause_irq, mcause_code} <= {wdata_update[31], wdata_update[5:0]}; {mcause_irq, mcause_code} <= {wdata_update[31], wdata_update[3:0]};
end end
end end
end end
// Custom external interrupt enable registers (would be at top of mie, but that // ----------------------------------------------------------------------------
// only leaves room for 16 external interrupts) // Custom external IRQ handling CSRs
localparam N_IRQ_REG0 = NUM_IRQ >= 32 ? 32 : NUM_IRQ ; localparam MAX_IRQS = 512;
localparam N_IRQ_REG1 = NUM_IRQ >= 64 ? 32 : NUM_IRQ <= 32 ? 0 : NUM_IRQ - 32; localparam [MAX_IRQS-1:0] IRQ_IMPL_MASK = ~({MAX_IRQS{1'b1}} << NUM_IRQS);
localparam N_IRQ_REG2 = NUM_IRQ >= 96 ? 32 : NUM_IRQ <= 64 ? 0 : NUM_IRQ - 64;
localparam N_IRQ_REG3 = NUM_IRQ >= 128 ? 32 : NUM_IRQ <= 96 ? 0 : NUM_IRQ - 96;
localparam MEIE0_WMASK = ~({XLEN{1'b1}} << N_IRQ_REG0); localparam IRQ_PRIORITY_MASK = ~(4'hf >> IRQ_PRIORITY_BITS);
localparam MEIE1_WMASK = ~({XLEN{1'b1}} << N_IRQ_REG1);
localparam MEIE2_WMASK = ~({XLEN{1'b1}} << N_IRQ_REG2);
localparam MEIE3_WMASK = ~({XLEN{1'b1}} << N_IRQ_REG3);
reg [XLEN-1:0] meie0; // Assigned later:
reg [XLEN-1:0] meie1; wire [MAX_IRQS-1:0] meipa;
reg [XLEN-1:0] meie2; wire [8:0] meinext_irq;
reg [XLEN-1:0] meie3; wire meinext_noirq;
reg [3:0] eirq_highest_priority;
reg [MAX_IRQS-1:0] meiea;
reg [MAX_IRQS-1:0] meifa;
reg [4*MAX_IRQS-1:0] meipra;
always @ (posedge clk or negedge rst_n) begin always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin if (!rst_n) begin
meie0 <= X0; meiea <= {MAX_IRQS{1'b0}};
meie1 <= X0; meifa <= {MAX_IRQS{1'b0}};
meie2 <= X0; meipra <= {4*MAX_IRQS{1'b0}};
meie3 <= X0; end else begin
end else if (wen_m_mode && addr == MEIE0) begin // Tie off unimplemented fields with a constant mask, then rely on
meie0 <= update_nonconst(meie0, MEIE0_WMASK); // further constant folding. Otherwise subsequent RTL will get a bit
end else if (wen_m_mode && addr == MEIE1) begin // out of hand.
meie1 <= update_nonconst(meie1, MEIE1_WMASK); if (wen_m_mode && addr == MEIEA) begin
end else if (wen_m_mode && addr == MEIE2) begin meiea[16 * wdata[4:0] +: 16] <= IRQ_IMPL_MASK[16 * wdata[4:0] +: 16] & wdata_update[31:16];
meie2 <= update_nonconst(meie2, MEIE2_WMASK); end else if (wen_m_mode && addr == MEIFA) begin
end else if (wen_m_mode && addr == MEIE3) begin meifa[16 * wdata[4:0] +: 16] <= IRQ_IMPL_MASK[16 * wdata[4:0] +: 16] & wdata_update[31:16];
meie3 <= update_nonconst(meie3, MEIE3_WMASK); end else if (wen_m_mode && addr == MEIPRA) begin
meipra[16 * wdata[6:0] +: 16] <= {
{4{IRQ_IMPL_MASK[4 * wdata[6:0] + 0]}},
{4{IRQ_IMPL_MASK[4 * wdata[6:0] + 1]}},
{4{IRQ_IMPL_MASK[4 * wdata[6:0] + 2]}},
{4{IRQ_IMPL_MASK[4 * wdata[6:0] + 3]}}
} & {4{~(4'hf >> IRQ_PRIORITY_BITS)}} & wdata_update[31:16];
end
// Clear IRQ force when the corresponding IRQ is sample from meinext
// (so that an IRQ can be posted *once* without modifying the ISR source)
if (ren_m_mode && addr == MEINEXT && !meinext_noirq) begin
meifa[meinext_irq] <= 1'b0;
end
end end
end end
// Assigned later: reg [3:0] meicontext_pppreempt;
wire [XLEN-1:0] meip0; reg [3:0] meicontext_ppreempt;
wire [XLEN-1:0] meip1; reg [4:0] meicontext_preempt;
wire [XLEN-1:0] meip2; reg meicontext_noirq;
wire [XLEN-1:0] meip3; reg [8:0] meicontext_irq;
wire [6:0] mlei; reg meicontext_mreteirq;
wire [4:0] preempt_level_next = meinext_noirq ? 5'h10 : (
(5'd1 << (4 - IRQ_PRIORITY_BITS)) + {1'b0, eirq_highest_priority}
);
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
meicontext_pppreempt <= 4'h0;
meicontext_ppreempt <= 4'h0;
meicontext_preempt <= 5'h0;
meicontext_noirq <= 1'b1;
meicontext_irq <= 9'h0;
meicontext_mreteirq <= 1'b0;
end else if (trapreg_update_enter) begin
if (mcause_irq_next && mcause_code_next == 4'hb) begin
// Priority save. Note the MSB of preempt needn't be saved since,
// when it is set, preemption is impossible, so we won't be here.
meicontext_pppreempt <= meicontext_ppreempt & IRQ_PRIORITY_MASK;
meicontext_ppreempt <= meicontext_preempt[3:0] & IRQ_PRIORITY_MASK;
// Setting preempt isn't strictly necessary, since an updating read
// of meinext ought to be performed before re-enabling IRQs via
// mstatus.mie, but it seems the least surprising thing to do:
meicontext_preempt <= preempt_level_next & {1'b1, IRQ_PRIORITY_MASK};
meicontext_mreteirq <= 1'b1;
end else begin
meicontext_mreteirq <= 1'b0;
end
end else if (trapreg_update_exit) begin
meicontext_mreteirq <= 1'b0;
if (meicontext_mreteirq) begin
// Priority restore
meicontext_pppreempt <= 4'h0;
meicontext_ppreempt <= meicontext_pppreempt & IRQ_PRIORITY_MASK;
meicontext_preempt <= {1'b0, meicontext_ppreempt & IRQ_PRIORITY_MASK};
end
end else if (wen_m_mode && addr == MEICONTEXT) begin
meicontext_pppreempt <= wdata_update[31:28] & IRQ_PRIORITY_MASK;
meicontext_ppreempt <= wdata_update[27:24] & IRQ_PRIORITY_MASK;
meicontext_preempt <= wdata_update[20:16] & {1'b1, IRQ_PRIORITY_MASK};
meicontext_noirq <= wdata_update[15];
meicontext_irq <= wdata_update[12:4];
meicontext_mreteirq <= wdata_update[0];
end else if (wen_m_mode && addr == MEINEXT && wdata_update[0]) begin
// Interrupt has been sampled, with the update request set, so update
// the context (including preemption level) appropriately.
meicontext_preempt <= preempt_level_next & IRQ_PRIORITY_MASK;
meicontext_noirq <= meinext_noirq;
meicontext_irq <= meinext_irq;
end
end
// ----------------------------------------------------------------------------
// External interrupt logic
// Signal to standard IRQ logic (mip etc) that at least one of the external IRQ
// signals should cause a trap to the mip.meip vector:
wire external_irq_pending;
// Register external IRQ signals (mainly to avoid a through-path from IRQs to
// bus request signals)
reg [NUM_IRQS-1:0] irq_r;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
irq_r <= {NUM_IRQS{1'b0}};
end else begin
irq_r <= irq;
end
end
// Trap request is asserted when there is an interrupt at or above our current
// preemption level. meinext displays interrupts at or above our *previous*
// preemption level: this masking helps avoid re-taking IRQs in frames that you
// have preempted.
assign meipa = {{MAX_IRQS{1'b0}}, irq_r} | meifa;
reg [NUM_IRQS-1:0] eirq_active_above_preempt;
reg [NUM_IRQS-1:0] eirq_active_above_ppreempt;
always @ (*) begin: eirq_compare
integer i;
for (i = 0; i < NUM_IRQS; i = i + 1) begin
eirq_active_above_preempt[i] = meipa[i] && meiea[i] && meipra[i * 4 +: 4] >= meicontext_preempt;
eirq_active_above_ppreempt[i] = meipa[i] && meiea[i] && meipra[i * 4 +: 4] >= meicontext_ppreempt;
end
end
assign external_irq_pending = |eirq_active_above_preempt;
assign meinext_noirq = ~|eirq_active_above_ppreempt;
// Two things remaining to calculate:
//
// - What is the IRQ number of the highest-priority pending IRQ that is above
// meicontext.ppreempt
// - What is the priority of that IRQ
//
// In the second case we can relax the calculation to ignore ppreempt, since it
// only needs to be valid if such an IRQ exists. Currently we choose to reuse
// the same priority selector (possibly longer critpath while saving area), but
// we could use a second priority selector that ignores ppreempt masking.
wire [NUM_IRQS-1:0] highest_eirq_onehot;
wire [8:0] meinext_irq_unmasked;
hazard3_onehot_priority_dynamic #(
.W_REQ (NUM_IRQS),
.N_PRIORITIES (16),
.PRIORITY_HIGHEST_WINS (1),
.TIEBREAK_HIGHEST_WINS (0)
) eirq_priority_u (
.priority (meipra[NUM_IRQS-1:0] & {NUM_IRQS{IRQ_PRIORITY_MASK}}),
.req (eirq_active_above_ppreempt),
.gnt (highest_eirq_onehot)
);
always @ (*) begin: get_highest_eirq_priority
integer i;
eirq_highest_priority = 4'h0;
for (i = 0; i < NUM_IRQS; i = i + 1) begin
eirq_highest_priority = meipra[4 * i +: 4] & {4{highest_eirq_onehot[i]}};
end
end
hazard3_onehot_encode #(
.W_REQ (NUM_IRQS),
.W_GNT (9)
) eirq_encode_u (
.req (highest_eirq_onehot),
.gnt (meinext_irq_unmasked)
);
assign meinext_irq = meinext_irq_unmasked & {9{!meinext_noirq}};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Counters // Counters
@ -597,8 +750,8 @@ always @ (*) begin
decode_match = match_mrw; decode_match = match_mrw;
rdata = { rdata = {
mcause_irq, // Sign bit is 1 for IRQ, 0 for exception mcause_irq, // Sign bit is 1 for IRQ, 0 for exception
{26{1'b0}}, // Padding {27{1'b0}}, // Padding
mcause_code[4:0] // Enough for 16 external IRQs, which is all we have room for in mip/mie mcause_code[3:0] // Enough for 16 external IRQs, which is all we have room for in mip/mie
}; };
end end
@ -938,49 +1091,63 @@ always @ (*) begin
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Custom CSRs // Custom CSRs
MEIE0: if (CSR_M_TRAP && N_IRQ_REG0 > 0) begin MEIEA: if (CSR_M_TRAP) begin
decode_match = match_mrw; decode_match = match_mrw;
rdata = meie0; rdata = {
meiea[wdata[4:0] * 16 +: 16],
16'h0
};
end end
MEIE1: if (CSR_M_TRAP && N_IRQ_REG1 > 0) begin MEIPA: if (CSR_M_TRAP) begin
decode_match = match_mrw; decode_match = match_mrw;
rdata = meie1; rdata = {
meipa[wdata[4:0] * 16 +: 16],
16'h0
};
end end
MEIE2: if (CSR_M_TRAP && N_IRQ_REG2 > 0) begin MEIFA: if (CSR_M_TRAP) begin
decode_match = match_mrw; decode_match = match_mrw;
rdata = meie2; rdata = {
meifa[wdata[4:0] * 16 +: 16],
16'h0
};
end end
MEIE3: if (CSR_M_TRAP && N_IRQ_REG3 > 0) begin MEIPRA: if (CSR_M_TRAP) begin
decode_match = match_mrw; decode_match = match_mrw;
rdata = meie3; rdata = {
meipra[wdata[6:0] * 16 +: 16],
16'h0
};
end end
MEIP0: if (CSR_M_TRAP && N_IRQ_REG0 > 0) begin MEINEXT: if (CSR_M_TRAP) begin
decode_match = match_mro; decode_match = match_mrw;
rdata = meip0; rdata = {
meinext_noirq,
20'h0,
meinext_irq,
2'h0
};
end end
MEIP1: if (CSR_M_TRAP && N_IRQ_REG1 > 0) begin MEICONTEXT: if (CSR_M_TRAP) begin
decode_match = match_mro; decode_match = match_mrw;
rdata = meip1; rdata = {
end meicontext_pppreempt,
meicontext_ppreempt,
MEIP2: if (CSR_M_TRAP && N_IRQ_REG2 > 0) begin 3'h0,
decode_match = match_mro; meicontext_preempt,
rdata = meip2; meicontext_noirq,
end 2'h0,
meicontext_irq,
MEIP3: if (CSR_M_TRAP && N_IRQ_REG3 > 0) begin mie[7] && wdata_update[1],
decode_match = match_mro; mie[3] && wdata_update[1],
rdata = meip3; 1'b0,
end meicontext_mreteirq
};
MLEI: if (CSR_M_TRAP) begin
decode_match = match_mro;
rdata = {{XLEN-9{1'b0}}, mlei, 2'b00};
end end
default: begin end default: begin end
@ -1097,30 +1264,19 @@ assign dbg_instr_caught_exception = debug_mode && except != EXCEPT_NONE && excep
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Trap request generation // Trap request generation
reg [NUM_IRQ-1:0] irq_r;
reg irq_software_r; reg irq_software_r;
reg irq_timer_r; reg irq_timer_r;
always @ (posedge clk or negedge rst_n) begin always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin if (!rst_n) begin
irq_r <= {NUM_IRQ{1'b0}};
irq_software_r <= 1'b0; irq_software_r <= 1'b0;
irq_timer_r <= 1'b0; irq_timer_r <= 1'b0;
end else begin end else begin
irq_r <= irq;
irq_software_r <= irq_software; irq_software_r <= irq_software;
irq_timer_r <= irq_timer; irq_timer_r <= irq_timer;
end end
end end
localparam MAX_IRQ = 128;
wire [MAX_IRQ-1:0] meip = {{MAX_IRQ-NUM_IRQ{1'b0}}, irq_r};
wire [MAX_IRQ-1:0] meie = {meie3, meie2, meie1, meie0};
assign {meip3, meip2, meip1, meip0} = meip;
wire external_irq_pending = |(meie & meip);
assign mip = { assign mip = {
20'h0, // Reserved 20'h0, // Reserved
external_irq_pending, // meip, Global pending bit for external IRQs external_irq_pending, // meip, Global pending bit for external IRQs
@ -1137,16 +1293,6 @@ wire irq_active = |(mip & mie) && mstatus_mie && !dcsr_step;
// Additionally, wfi is treated as a nop during single-stepping and D-mode. // Additionally, wfi is treated as a nop during single-stepping and D-mode.
assign wfi_stall_clear = |(mip & mie) || dcsr_step || debug_mode || want_halt_irq_if_no_exception; assign wfi_stall_clear = |(mip & mie) || dcsr_step || debug_mode || want_halt_irq_if_no_exception;
wire [6:0] external_irq_num;
assign mlei = external_irq_num;
hazard3_priority_encode #(
.W_REQ (MAX_IRQ)
) mlei_priority_encode (
.req (meie & meip),
.gnt (external_irq_num)
);
// Priority order from priv spec: external > software > timer // Priority order from priv spec: external > software > timer
wire [3:0] standard_irq_num = wire [3:0] standard_irq_num =
mip[11] && mie[11] ? 4'd11 : mip[11] && mie[11] ? 4'd11 :
@ -1159,13 +1305,13 @@ assign exception_req_any = except != EXCEPT_NONE && !(
except == EXCEPT_EBREAK && (m_mode ? dcsr_ebreakm : dcsr_ebreaku) except == EXCEPT_EBREAK && (m_mode ? dcsr_ebreakm : dcsr_ebreaku)
); );
wire [5:0] mcause_irq_num = irq_active ? {2'h0, standard_irq_num} : 6'd0; wire [3:0] mcause_irq_num = irq_active ? standard_irq_num : 4'd0;
wire [5:0] vector_sel = !exception_req_any && irq_vector_enable ? mcause_irq_num : 6'd0; wire [3:0] vector_sel = !exception_req_any && irq_vector_enable ? mcause_irq_num : 4'd0;
assign trap_addr = assign trap_addr =
except == EXCEPT_MRET ? mepc : except == EXCEPT_MRET ? mepc :
pending_dbg_resume ? dpc : mtvec | {24'h0, vector_sel, 2'h0}; pending_dbg_resume ? dpc : mtvec | {26'h0, vector_sel, 2'h0};
// Check for exception-like or IRQ-like trap entry; any debug mode entry takes // Check for exception-like or IRQ-like trap entry; any debug mode entry takes
// priority over any regular trap. // priority over any regular trap.
@ -1192,7 +1338,7 @@ assign trap_enter_soon = trap_enter_vld || (
); );
assign mcause_irq_next = !exception_req_any; assign mcause_irq_next = !exception_req_any;
assign mcause_code_next = exception_req_any ? {2'h0, except} : mcause_irq_num; assign mcause_code_next = exception_req_any ? except : mcause_irq_num;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Privilege state outputs // Privilege state outputs

View File

@ -158,15 +158,14 @@ localparam MENVCFG = 12'h30a;
localparam MENVCFGH = 12'h31a; localparam MENVCFGH = 12'h31a;
// Custom M-mode CSRs: // Custom M-mode CSRs:
localparam MEIE0 = 12'hbe0; // External interrupt enable register 0 localparam MEIEA = 12'hbe0; // External interrupt pending array
localparam MEIE1 = 12'hbe1; // External interrupt enable register 1 localparam MEIPA = 12'hbe1; // External interrupt enable array
localparam MEIE2 = 12'hbe2; // External interrupt enable register 2 localparam MEIFA = 12'hbe2; // External interrupt force array
localparam MEIE3 = 12'hbe3; // External interrupt enable register 3 localparam MEIPRA = 12'hbe3; // External interrupt priority array
localparam MEIP0 = 12'hfe0; // External interrupt pending register 0 localparam MEINEXT = 12'hbe4; // Next external interrupt
localparam MEIP1 = 12'hfe1; // External interrupt pending register 1 localparam MEICONTEXT = 12'hbe5; // External interrupt context register
localparam MEIP2 = 12'hfe2; // External interrupt pending register 2
localparam MEIP3 = 12'hfe3; // External interrupt pending register 3 localparam MSLEEP = 12'hbf0; // M-mode sleep control register
localparam MLEI = 12'hfe4; // Lowest external interrupt number
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// U-mode CSRs // U-mode CSRs
@ -179,6 +178,9 @@ localparam CYCLEH = 12'hc80;
localparam TIMEH = 12'hc81; localparam TIMEH = 12'hc81;
localparam INSTRETH = 12'hc82; localparam INSTRETH = 12'hc82;
// Custom U-mode CSRs
localparam SLEEP = 12'h8f0; // U-mode subset of M-mode sleep control
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Trigger Module // Trigger Module

View File

@ -5,10 +5,16 @@
#include "stdint.h" #include "stdint.h"
#endif #endif
#define hazard3_csr_dmdata0 0xbff // Debug-mode shadow CSR for DM data transfer #define hazard3_csr_dmdata0 0xbff // Debug-mode shadow CSR for DM data transfer
#define hazard3_csr_meie0 0xbe0 // External interrupt enable IRQ0 -> 31
#define hazard3_csr_meip0 0xfe0 // External interrupt pending IRQ0 -> 31 #define hazard3_csr_meiea 0xbe0 // External interrupt pending array
#define hazard3_csr_mlei 0xfe4 // Lowest external interrupt (pending & enabled) #define hazard3_csr_meipa 0xbe1 // External interrupt enable array
#define hazard3_csr_meifa 0xbe2 // External interrupt force array
#define hazard3_csr_meipr 0xbe3 // External interrupt priority array
#define hazard3_csr_meinext 0xbe4 // Next external interrupt
#define hazard3_csr_meicontext 0xbe5 // External interrupt context register
#define hazard3_csr_msleep 0xbf0 // M-mode sleep control register
#define _read_csr(csrname) ({ \ #define _read_csr(csrname) ({ \
uint32_t __csr_tmp_u32; \ uint32_t __csr_tmp_u32; \
@ -28,10 +34,31 @@
asm volatile ("csrc " #csrname ", %0" : : "r" (data)); \ asm volatile ("csrc " #csrname ", %0" : : "r" (data)); \
}) })
#define _read_write_csr(csrname, data) ({ \
uint32_t __csr_tmp_u32; \
asm volatile ("csrrw %0, " #csrname ", %1" : "=r" (__csr_tmp_u32) : "r" (data)); \
__csr_tmp_u32; \
})
#define _read_set_csr(csrname, data) ({ \
uint32_t __csr_tmp_u32; \
asm volatile ("csrrs %0, " #csrname ", %1" : "=r" (__csr_tmp_u32) : "r" (data)); \
__csr_tmp_u32; \
})
#define _read_clear_csr(csrname, data) ({ \
uint32_t __csr_tmp_u32; \
asm volatile ("csrrc %0, " #csrname ", %1" : "=r" (__csr_tmp_u32) : "r" (data)); \
__csr_tmp_u32; \
})
// Argument macro expansion layer // Argument macro expansion layer
#define read_csr(csrname) _read_csr(csrname) #define read_csr(csrname) _read_csr(csrname)
#define write_csr(csrname, data) _write_csr(csrname, data) #define write_csr(csrname, data) _write_csr(csrname, data)
#define set_csr(csrname, data) _set_csr(csrname, data) #define set_csr(csrname, data) _set_csr(csrname, data)
#define clear_csr(csrname, data) _clear_csr(csrname, data) #define clear_csr(csrname, data) _clear_csr(csrname, data)
#define read_write_csr(csrname, data) _read_write_csr(csrname, data)
#define read_set_csr(csrname, data) _read_set_csr(csrname, data)
#define read_clear_csr(csrname, data) _read_clear_csr(csrname, data)
#endif #endif

View File

@ -0,0 +1,31 @@
#ifndef _HAZARD3_IRQ_H
#define _HAZARD3_IRQ_H
#include "hazard3_csr.h"
#include "stdint.h"
#include "stdbool.h"
#define h3irq_array_read(csr, index) (read_set_csr(csr, (index)) >> 16)
#define h3irq_array_write(csr, index, data) (write_csr(csr, (index) | ((uint32_t)(data) << 16)))
#define h3irq_array_set(csr, index, data) (set_csr(csr, (index) | ((uint32_t)(data) << 16)))
#define h3irq_array_clear(csr, index, data) (clear_csr(csr, (index) | ((uint32_t)(data) << 16)))
static inline void h3irq_enable(unsigned int irq, bool enable) {
if (enable) {
h3irq_array_set(hazard3_csr_meiea, irq >> 4, 1u << (irq & 0xfu));
}
else {
h3irq_array_clear(hazard3_csr_meiea, irq >> 4, 1u << (irq & 0xfu));
}
}
static inline bool h3irq_pending(unsigned int irq) {
return h3irq_array_read(hazard3_csr_meipa, irq >> 4) & (1u << (irq & 0xfu));
}
static inline bool h3irq_force_pending(unsigned int irq) {
h3irq_array_set(hazard3_csr_meifa, irq >> 4, 1u << (irq & 0xfu));
}
#endif

View File

@ -137,9 +137,12 @@ int main() {
(void)read_csr(dscratch0); (void)read_csr(dscratch0);
(void)read_csr(dscratch1); (void)read_csr(dscratch1);
(void)read_csr(hazard3_csr_dmdata0); (void)read_csr(hazard3_csr_dmdata0);
(void)read_csr(hazard3_csr_meie0); (void)read_csr(hazard3_csr_meiea);
(void)read_csr(hazard3_csr_meip0); (void)read_csr(hazard3_csr_meipa);
(void)read_csr(hazard3_csr_mlei); (void)read_csr(hazard3_csr_meifa);
(void)read_csr(hazard3_csr_meipr);
(void)read_csr(hazard3_csr_meinext);
(void)read_csr(hazard3_csr_meicontext);
return 0; return 0;
} }

View File

@ -184,14 +184,20 @@ CSR was 7b1
CSR was 7b2 CSR was 7b2
-> exception, mcause = 2, mpp = 0 // dscratch1 -> exception, mcause = 2, mpp = 0 // dscratch1
CSR was 7b3 CSR was 7b3
-> exception, mcause = 2, mpp = 0 // hazard3 dmdata0 -> exception, mcause = 2, mpp = 0 // hazard3_csr_dmdata0
CSR was bff CSR was bff
-> exception, mcause = 2, mpp = 0 // hazard3 meie0 -> exception, mcause = 2, mpp = 0 // hazard3_csr_meiea
CSR was be0 CSR was be0
-> exception, mcause = 2, mpp = 0 // hazard3 meip0 -> exception, mcause = 2, mpp = 0 // hazard3_csr_meipa
CSR was fe0 CSR was be1
-> exception, mcause = 2, mpp = 0 // hazard3 mlei -> exception, mcause = 2, mpp = 0 // hazard3_csr_meifa
CSR was fe4 CSR was be2
-> exception, mcause = 2, mpp = 0 // hazard3_csr_meipr
CSR was be3
-> exception, mcause = 2, mpp = 0 // hazard3_csr_meinext
CSR was be4
-> exception, mcause = 2, mpp = 0 // hazard3_csr_meicontext
CSR was be5
-> exception, mcause = 3, mpp = 0 // This is the ebreak that ends the test -> exception, mcause = 3, mpp = 0 // This is the ebreak that ends the test
*******************************************************************************/ *******************************************************************************/
@ -295,9 +301,12 @@ void read_all_csrs() {
(void)read_csr(dscratch0); (void)read_csr(dscratch0);
(void)read_csr(dscratch1); (void)read_csr(dscratch1);
(void)read_csr(hazard3_csr_dmdata0); (void)read_csr(hazard3_csr_dmdata0);
(void)read_csr(hazard3_csr_meie0); (void)read_csr(hazard3_csr_meiea);
(void)read_csr(hazard3_csr_meip0); (void)read_csr(hazard3_csr_meipa);
(void)read_csr(hazard3_csr_mlei); (void)read_csr(hazard3_csr_meifa);
(void)read_csr(hazard3_csr_meipr);
(void)read_csr(hazard3_csr_meinext);
(void)read_csr(hazard3_csr_meicontext);
} }
void __attribute__((naked)) ebreak_trampoline() { void __attribute__((naked)) ebreak_trampoline() {

View File

@ -64,10 +64,6 @@ CSR was 7b3
CSR was bff CSR was bff
-> exception, mcause = 2 // write to dmdata0, D-mode -> exception, mcause = 2 // write to dmdata0, D-mode
CSR was bff CSR was bff
-> exception, mcause = 2 // write to meip0, read-only
CSR was fe0
-> exception, mcause = 2 // write to mlei, read-only
CSR was fe4
*******************************************************************************/ *******************************************************************************/
@ -170,9 +166,13 @@ int main() {
write_csr(dscratch0, read_csr(dscratch0 )); write_csr(dscratch0, read_csr(dscratch0 ));
write_csr(dscratch1, read_csr(dscratch1 )); write_csr(dscratch1, read_csr(dscratch1 ));
write_csr(hazard3_csr_dmdata0, read_csr(hazard3_csr_dmdata0)); write_csr(hazard3_csr_dmdata0, read_csr(hazard3_csr_dmdata0));
write_csr(hazard3_csr_meie0, read_csr(hazard3_csr_meie0 ));
write_csr(hazard3_csr_meip0, read_csr(hazard3_csr_meip0 )); write_csr(hazard3_csr_meiea, read_csr(hazard3_csr_meiea ));
write_csr(hazard3_csr_mlei, read_csr(hazard3_csr_mlei )); write_csr(hazard3_csr_meipa, read_csr(hazard3_csr_meipa ));
write_csr(hazard3_csr_meifa, read_csr(hazard3_csr_meifa ));
write_csr(hazard3_csr_meipr, read_csr(hazard3_csr_meipr ));
write_csr(hazard3_csr_meinext, read_csr(hazard3_csr_meinext ));
write_csr(hazard3_csr_meicontext, read_csr(hazard3_csr_meicontext));
return 0; return 0;
} }

View File

@ -1,5 +1,6 @@
#include "tb_cxxrtl_io.h" #include "tb_cxxrtl_io.h"
#include "hazard3_csr.h" #include "hazard3_csr.h"
#include "hazard3_irq.h"
// Pend all IRQs, enable them one-by-one and log their firing. // Pend all IRQs, enable them one-by-one and log their firing.
@ -7,14 +8,13 @@
int main() { int main() {
asm volatile ("csrsi mstatus, 0x8"); asm volatile ("csrsi mstatus, 0x8");
write_csr(hazard3_csr_meie0, 0u);
write_csr(mie, mie_meie); write_csr(mie, mie_meie);
tb_set_irq_masked(-1u); tb_set_irq_masked(-1u);
for (int i = 31; i >= 0; --i) { for (int i = 31; i >= 0; --i) {
tb_printf("Enabling IRQ %d\n", i); tb_printf("Enabling IRQ %d\n", i);
write_csr(hazard3_csr_meie0, 1u << i); h3irq_enable(i, true);
} }
return 0; return 0;
@ -23,148 +23,154 @@ int main() {
void __attribute__((interrupt)) isr_external_irq() { void __attribute__((interrupt)) isr_external_irq() {
tb_puts("-> external irq handler\n"); tb_puts("-> external irq handler\n");
tb_assert(read_csr(mcause) == 0x8000000bu, "mcause should indicate external IRQ\n"); tb_assert(read_csr(mcause) == 0x8000000bu, "mcause should indicate external IRQ\n");
tb_assert(read_csr(mip) == 0x880u, "mip should indicate external + timer IRQ\n"); // The external IRQ pending bit should immediately clear due to the
// premption priority being boosted above the IRQ we took.
tb_assert(read_csr(mip) == 0x080u, "mip should indicate timer IRQ only\n");
// mlei updates dynamically, should be read exactly once at the start of an // meinext updates dynamically, should be read exactly once at the start of an
// IRQ handler. // IRQ handler.
uint32_t mlei = read_csr(hazard3_csr_mlei); uint32_t meinext = read_csr(hazard3_csr_meinext);
tb_printf("mlei = %u\n", mlei); tb_printf("meinext = %u\n", meinext);
tb_printf("meip0 = %08x\n", read_csr(hazard3_csr_meip0)); tb_printf(
"meipa = %08x\n",
h3irq_array_read(hazard3_csr_meipa, 0) |
((uint32_t)h3irq_array_read(hazard3_csr_meipa, 1) << 16)
);
// mlei is scaled by 4 to make it cheaper to index a software vector table. // meinext is scaled by 4 to make it cheaper to index a software vector table.
tb_assert(read_csr(hazard3_csr_meip0) & (1u << (mlei >> 2)), "IRQ indicated by mlei is not pending\n"); tb_assert(h3irq_pending(meinext >> 2), "IRQ indicated by meinext is not pending\n");
tb_clr_irq_masked(1u << (mlei >> 2)); tb_clr_irq_masked(1u << (meinext >> 2));
} }
/*EXPECTED-OUTPUT*************************************************************** /*EXPECTED-OUTPUT***************************************************************
Enabling IRQ 31 Enabling IRQ 31
-> external irq handler -> external irq handler
mlei = 124 meinext = 124
meip0 = ffffffff meipa = ffffffff
Enabling IRQ 30 Enabling IRQ 30
-> external irq handler -> external irq handler
mlei = 120 meinext = 120
meip0 = 7fffffff meipa = 7fffffff
Enabling IRQ 29 Enabling IRQ 29
-> external irq handler -> external irq handler
mlei = 116 meinext = 116
meip0 = 3fffffff meipa = 3fffffff
Enabling IRQ 28 Enabling IRQ 28
-> external irq handler -> external irq handler
mlei = 112 meinext = 112
meip0 = 1fffffff meipa = 1fffffff
Enabling IRQ 27 Enabling IRQ 27
-> external irq handler -> external irq handler
mlei = 108 meinext = 108
meip0 = 0fffffff meipa = 0fffffff
Enabling IRQ 26 Enabling IRQ 26
-> external irq handler -> external irq handler
mlei = 104 meinext = 104
meip0 = 07ffffff meipa = 07ffffff
Enabling IRQ 25 Enabling IRQ 25
-> external irq handler -> external irq handler
mlei = 100 meinext = 100
meip0 = 03ffffff meipa = 03ffffff
Enabling IRQ 24 Enabling IRQ 24
-> external irq handler -> external irq handler
mlei = 96 meinext = 96
meip0 = 01ffffff meipa = 01ffffff
Enabling IRQ 23 Enabling IRQ 23
-> external irq handler -> external irq handler
mlei = 92 meinext = 92
meip0 = 00ffffff meipa = 00ffffff
Enabling IRQ 22 Enabling IRQ 22
-> external irq handler -> external irq handler
mlei = 88 meinext = 88
meip0 = 007fffff meipa = 007fffff
Enabling IRQ 21 Enabling IRQ 21
-> external irq handler -> external irq handler
mlei = 84 meinext = 84
meip0 = 003fffff meipa = 003fffff
Enabling IRQ 20 Enabling IRQ 20
-> external irq handler -> external irq handler
mlei = 80 meinext = 80
meip0 = 001fffff meipa = 001fffff
Enabling IRQ 19 Enabling IRQ 19
-> external irq handler -> external irq handler
mlei = 76 meinext = 76
meip0 = 000fffff meipa = 000fffff
Enabling IRQ 18 Enabling IRQ 18
-> external irq handler -> external irq handler
mlei = 72 meinext = 72
meip0 = 0007ffff meipa = 0007ffff
Enabling IRQ 17 Enabling IRQ 17
-> external irq handler -> external irq handler
mlei = 68 meinext = 68
meip0 = 0003ffff meipa = 0003ffff
Enabling IRQ 16 Enabling IRQ 16
-> external irq handler -> external irq handler
mlei = 64 meinext = 64
meip0 = 0001ffff meipa = 0001ffff
Enabling IRQ 15 Enabling IRQ 15
-> external irq handler -> external irq handler
mlei = 60 meinext = 60
meip0 = 0000ffff meipa = 0000ffff
Enabling IRQ 14 Enabling IRQ 14
-> external irq handler -> external irq handler
mlei = 56 meinext = 56
meip0 = 00007fff meipa = 00007fff
Enabling IRQ 13 Enabling IRQ 13
-> external irq handler -> external irq handler
mlei = 52 meinext = 52
meip0 = 00003fff meipa = 00003fff
Enabling IRQ 12 Enabling IRQ 12
-> external irq handler -> external irq handler
mlei = 48 meinext = 48
meip0 = 00001fff meipa = 00001fff
Enabling IRQ 11 Enabling IRQ 11
-> external irq handler -> external irq handler
mlei = 44 meinext = 44
meip0 = 00000fff meipa = 00000fff
Enabling IRQ 10 Enabling IRQ 10
-> external irq handler -> external irq handler
mlei = 40 meinext = 40
meip0 = 000007ff meipa = 000007ff
Enabling IRQ 9 Enabling IRQ 9
-> external irq handler -> external irq handler
mlei = 36 meinext = 36
meip0 = 000003ff meipa = 000003ff
Enabling IRQ 8 Enabling IRQ 8
-> external irq handler -> external irq handler
mlei = 32 meinext = 32
meip0 = 000001ff meipa = 000001ff
Enabling IRQ 7 Enabling IRQ 7
-> external irq handler -> external irq handler
mlei = 28 meinext = 28
meip0 = 000000ff meipa = 000000ff
Enabling IRQ 6 Enabling IRQ 6
-> external irq handler -> external irq handler
mlei = 24 meinext = 24
meip0 = 0000007f meipa = 0000007f
Enabling IRQ 5 Enabling IRQ 5
-> external irq handler -> external irq handler
mlei = 20 meinext = 20
meip0 = 0000003f meipa = 0000003f
Enabling IRQ 4 Enabling IRQ 4
-> external irq handler -> external irq handler
mlei = 16 meinext = 16
meip0 = 0000001f meipa = 0000001f
Enabling IRQ 3 Enabling IRQ 3
-> external irq handler -> external irq handler
mlei = 12 meinext = 12
meip0 = 0000000f meipa = 0000000f
Enabling IRQ 2 Enabling IRQ 2
-> external irq handler -> external irq handler
mlei = 8 meinext = 8
meip0 = 00000007 meipa = 00000007
Enabling IRQ 1 Enabling IRQ 1
-> external irq handler -> external irq handler
mlei = 4 meinext = 4
meip0 = 00000003 meipa = 00000003
Enabling IRQ 0 Enabling IRQ 0
-> external irq handler -> external irq handler
mlei = 0 meinext = 0
meip0 = 00000001 meipa = 00000001
*******************************************************************************/ *******************************************************************************/

View File

@ -1,5 +1,6 @@
#include "tb_cxxrtl_io.h" #include "tb_cxxrtl_io.h"
#include "hazard3_csr.h" #include "hazard3_csr.h"
#include "hazard3_irq.h"
// Set IRQ mask (meie0) wide-open, then pend the IRQs one by one and log their // Set IRQ mask (meie0) wide-open, then pend the IRQs one by one and log their
// firing. // firing.
@ -8,7 +9,9 @@
int main() { int main() {
asm volatile ("csrsi mstatus, 0x8"); asm volatile ("csrsi mstatus, 0x8");
write_csr(hazard3_csr_meie0, -1u);
h3irq_array_write(hazard3_csr_meiea, 0, 0xffffu);
h3irq_array_write(hazard3_csr_meiea, 1, 0xffffu);
write_csr(mie, mie_meie); write_csr(mie, mie_meie);
for (int i = 0; i < 32; ++i) { for (int i = 0; i < 32; ++i) {
@ -22,148 +25,154 @@ int main() {
void __attribute__((interrupt)) isr_external_irq() { void __attribute__((interrupt)) isr_external_irq() {
tb_puts("-> external irq handler\n"); tb_puts("-> external irq handler\n");
tb_assert(read_csr(mcause) == 0x8000000bu, "mcause should indicate external IRQ\n"); tb_assert(read_csr(mcause) == 0x8000000bu, "mcause should indicate external IRQ\n");
tb_assert(read_csr(mip) == 0x880u, "mip should indicate external + timer IRQ\n"); // External IRQ does not appear pending because, after taking the
// interrupt, it no longer has sufficient priority to preempt.
tb_assert(read_csr(mip) == 0x080u, "mip should indicate timer IRQ only\n");
// mlei updates dynamically, should be read exactly once at the start of an // meinext updates dynamically, should be read exactly once at the start of an
// IRQ handler. // IRQ handler.
uint32_t mlei = read_csr(hazard3_csr_mlei); uint32_t meinext = read_csr(hazard3_csr_meinext);
tb_printf("mlei = %u\n", mlei); tb_printf("meinext = %u\n", meinext);
tb_printf("meip0 = %08x\n", read_csr(hazard3_csr_meip0)); tb_printf(
"meipa = %08x\n",
h3irq_array_read(hazard3_csr_meipa, 0) |
((uint32_t)h3irq_array_read(hazard3_csr_meipa, 1) << 16)
);
// mlei is scaled by 4 to make it cheaper to index a software vector table. // meinext is scaled by 4 to make it cheaper to index a software vector table.
tb_assert(read_csr(hazard3_csr_meip0) & (1u << (mlei >> 2)), "IRQ indicated by mlei is not pending\n"); tb_assert(h3irq_pending(meinext >> 2), "IRQ indicated by meinext is not pending\n");
tb_clr_irq_masked(1u << (mlei >> 2)); tb_clr_irq_masked(1u << (meinext >> 2));
} }
/*EXPECTED-OUTPUT*************************************************************** /*EXPECTED-OUTPUT***************************************************************
Setting IRQ 0 Setting IRQ 0
-> external irq handler -> external irq handler
mlei = 0 meinext = 0
meip0 = 00000001 meipa = 00000001
Setting IRQ 1 Setting IRQ 1
-> external irq handler -> external irq handler
mlei = 4 meinext = 4
meip0 = 00000002 meipa = 00000002
Setting IRQ 2 Setting IRQ 2
-> external irq handler -> external irq handler
mlei = 8 meinext = 8
meip0 = 00000004 meipa = 00000004
Setting IRQ 3 Setting IRQ 3
-> external irq handler -> external irq handler
mlei = 12 meinext = 12
meip0 = 00000008 meipa = 00000008
Setting IRQ 4 Setting IRQ 4
-> external irq handler -> external irq handler
mlei = 16 meinext = 16
meip0 = 00000010 meipa = 00000010
Setting IRQ 5 Setting IRQ 5
-> external irq handler -> external irq handler
mlei = 20 meinext = 20
meip0 = 00000020 meipa = 00000020
Setting IRQ 6 Setting IRQ 6
-> external irq handler -> external irq handler
mlei = 24 meinext = 24
meip0 = 00000040 meipa = 00000040
Setting IRQ 7 Setting IRQ 7
-> external irq handler -> external irq handler
mlei = 28 meinext = 28
meip0 = 00000080 meipa = 00000080
Setting IRQ 8 Setting IRQ 8
-> external irq handler -> external irq handler
mlei = 32 meinext = 32
meip0 = 00000100 meipa = 00000100
Setting IRQ 9 Setting IRQ 9
-> external irq handler -> external irq handler
mlei = 36 meinext = 36
meip0 = 00000200 meipa = 00000200
Setting IRQ 10 Setting IRQ 10
-> external irq handler -> external irq handler
mlei = 40 meinext = 40
meip0 = 00000400 meipa = 00000400
Setting IRQ 11 Setting IRQ 11
-> external irq handler -> external irq handler
mlei = 44 meinext = 44
meip0 = 00000800 meipa = 00000800
Setting IRQ 12 Setting IRQ 12
-> external irq handler -> external irq handler
mlei = 48 meinext = 48
meip0 = 00001000 meipa = 00001000
Setting IRQ 13 Setting IRQ 13
-> external irq handler -> external irq handler
mlei = 52 meinext = 52
meip0 = 00002000 meipa = 00002000
Setting IRQ 14 Setting IRQ 14
-> external irq handler -> external irq handler
mlei = 56 meinext = 56
meip0 = 00004000 meipa = 00004000
Setting IRQ 15 Setting IRQ 15
-> external irq handler -> external irq handler
mlei = 60 meinext = 60
meip0 = 00008000 meipa = 00008000
Setting IRQ 16 Setting IRQ 16
-> external irq handler -> external irq handler
mlei = 64 meinext = 64
meip0 = 00010000 meipa = 00010000
Setting IRQ 17 Setting IRQ 17
-> external irq handler -> external irq handler
mlei = 68 meinext = 68
meip0 = 00020000 meipa = 00020000
Setting IRQ 18 Setting IRQ 18
-> external irq handler -> external irq handler
mlei = 72 meinext = 72
meip0 = 00040000 meipa = 00040000
Setting IRQ 19 Setting IRQ 19
-> external irq handler -> external irq handler
mlei = 76 meinext = 76
meip0 = 00080000 meipa = 00080000
Setting IRQ 20 Setting IRQ 20
-> external irq handler -> external irq handler
mlei = 80 meinext = 80
meip0 = 00100000 meipa = 00100000
Setting IRQ 21 Setting IRQ 21
-> external irq handler -> external irq handler
mlei = 84 meinext = 84
meip0 = 00200000 meipa = 00200000
Setting IRQ 22 Setting IRQ 22
-> external irq handler -> external irq handler
mlei = 88 meinext = 88
meip0 = 00400000 meipa = 00400000
Setting IRQ 23 Setting IRQ 23
-> external irq handler -> external irq handler
mlei = 92 meinext = 92
meip0 = 00800000 meipa = 00800000
Setting IRQ 24 Setting IRQ 24
-> external irq handler -> external irq handler
mlei = 96 meinext = 96
meip0 = 01000000 meipa = 01000000
Setting IRQ 25 Setting IRQ 25
-> external irq handler -> external irq handler
mlei = 100 meinext = 100
meip0 = 02000000 meipa = 02000000
Setting IRQ 26 Setting IRQ 26
-> external irq handler -> external irq handler
mlei = 104 meinext = 104
meip0 = 04000000 meipa = 04000000
Setting IRQ 27 Setting IRQ 27
-> external irq handler -> external irq handler
mlei = 108 meinext = 108
meip0 = 08000000 meipa = 08000000
Setting IRQ 28 Setting IRQ 28
-> external irq handler -> external irq handler
mlei = 112 meinext = 112
meip0 = 10000000 meipa = 10000000
Setting IRQ 29 Setting IRQ 29
-> external irq handler -> external irq handler
mlei = 116 meinext = 116
meip0 = 20000000 meipa = 20000000
Setting IRQ 30 Setting IRQ 30
-> external irq handler -> external irq handler
mlei = 120 meinext = 120
meip0 = 40000000 meipa = 40000000
Setting IRQ 31 Setting IRQ 31
-> external irq handler -> external irq handler
mlei = 124 meinext = 124
meip0 = 80000000 meipa = 80000000
*******************************************************************************/ *******************************************************************************/

View File

@ -1,5 +1,6 @@
#include "tb_cxxrtl_io.h" #include "tb_cxxrtl_io.h"
#include "hazard3_csr.h" #include "hazard3_csr.h"
#include "hazard3_irq.h"
// Fire all IRQs simultaneously, and log the resulting handler calls // Fire all IRQs simultaneously, and log the resulting handler calls
@ -8,7 +9,8 @@
int main() { int main() {
asm volatile ("csrsi mstatus, 0x8"); asm volatile ("csrsi mstatus, 0x8");
write_csr(mie, mie_meie); write_csr(mie, mie_meie);
write_csr(hazard3_csr_meie0, -1u); h3irq_array_write(hazard3_csr_meiea, 0, 0xffffu);
h3irq_array_write(hazard3_csr_meiea, 1, 0xffffu);
tb_puts("Firing all IRQs\n"); tb_puts("Firing all IRQs\n");
tb_set_irq_masked(-1u); tb_set_irq_masked(-1u);
@ -20,118 +22,124 @@ int main() {
void __attribute__((interrupt)) isr_external_irq() { void __attribute__((interrupt)) isr_external_irq() {
tb_puts("-> external irq handler\n"); tb_puts("-> external irq handler\n");
tb_assert(read_csr(mcause) == 0x8000000bu, "mcause should indicate external IRQ\n"); tb_assert(read_csr(mcause) == 0x8000000bu, "mcause should indicate external IRQ\n");
tb_assert(read_csr(mip) == 0x880u, "mip should indicate external + timer IRQ\n"); // Once an interrupt has fired, it does not appear pending in mip since it
// can't preempt itself unless its priority is raised.
tb_assert(read_csr(mip) == 0x080u, "mip should indicate timer IRQ only\n");
// mlei updates dynamically, should be read exactly once at the start of an // meinext updates dynamically, should be read exactly once at the start of an
// IRQ handler. // IRQ handler.
uint32_t mlei = read_csr(hazard3_csr_mlei); uint32_t meinext = read_csr(hazard3_csr_meinext);
tb_printf("mlei = %u\n", mlei); tb_printf("meinext = %u\n", meinext);
tb_printf("meip0 = %08x\n", read_csr(hazard3_csr_meip0)); tb_printf(
"meipa = %08x\n",
h3irq_array_read(hazard3_csr_meipa, 0) |
((uint32_t)h3irq_array_read(hazard3_csr_meipa, 1) << 16)
);
// mlei is scaled by 4 to make it cheaper to index a software vector table. // meinext is scaled by 4 to make it cheaper to index a software vector table.
tb_assert(read_csr(hazard3_csr_meip0) & (1u << (mlei >> 2)), "IRQ indicated by mlei is not pending\n"); tb_assert(h3irq_pending(meinext >> 2), "IRQ indicated by meinext is not pending\n");
tb_clr_irq_masked(1u << (mlei >> 2)); tb_clr_irq_masked(1u << (meinext >> 2));
} }
/*EXPECTED-OUTPUT*************************************************************** /*EXPECTED-OUTPUT***************************************************************
Firing all IRQs Firing all IRQs
-> external irq handler -> external irq handler
mlei = 0 meinext = 0
meip0 = ffffffff meipa = ffffffff
-> external irq handler -> external irq handler
mlei = 4 meinext = 4
meip0 = fffffffe meipa = fffffffe
-> external irq handler -> external irq handler
mlei = 8 meinext = 8
meip0 = fffffffc meipa = fffffffc
-> external irq handler -> external irq handler
mlei = 12 meinext = 12
meip0 = fffffff8 meipa = fffffff8
-> external irq handler -> external irq handler
mlei = 16 meinext = 16
meip0 = fffffff0 meipa = fffffff0
-> external irq handler -> external irq handler
mlei = 20 meinext = 20
meip0 = ffffffe0 meipa = ffffffe0
-> external irq handler -> external irq handler
mlei = 24 meinext = 24
meip0 = ffffffc0 meipa = ffffffc0
-> external irq handler -> external irq handler
mlei = 28 meinext = 28
meip0 = ffffff80 meipa = ffffff80
-> external irq handler -> external irq handler
mlei = 32 meinext = 32
meip0 = ffffff00 meipa = ffffff00
-> external irq handler -> external irq handler
mlei = 36 meinext = 36
meip0 = fffffe00 meipa = fffffe00
-> external irq handler -> external irq handler
mlei = 40 meinext = 40
meip0 = fffffc00 meipa = fffffc00
-> external irq handler -> external irq handler
mlei = 44 meinext = 44
meip0 = fffff800 meipa = fffff800
-> external irq handler -> external irq handler
mlei = 48 meinext = 48
meip0 = fffff000 meipa = fffff000
-> external irq handler -> external irq handler
mlei = 52 meinext = 52
meip0 = ffffe000 meipa = ffffe000
-> external irq handler -> external irq handler
mlei = 56 meinext = 56
meip0 = ffffc000 meipa = ffffc000
-> external irq handler -> external irq handler
mlei = 60 meinext = 60
meip0 = ffff8000 meipa = ffff8000
-> external irq handler -> external irq handler
mlei = 64 meinext = 64
meip0 = ffff0000 meipa = ffff0000
-> external irq handler -> external irq handler
mlei = 68 meinext = 68
meip0 = fffe0000 meipa = fffe0000
-> external irq handler -> external irq handler
mlei = 72 meinext = 72
meip0 = fffc0000 meipa = fffc0000
-> external irq handler -> external irq handler
mlei = 76 meinext = 76
meip0 = fff80000 meipa = fff80000
-> external irq handler -> external irq handler
mlei = 80 meinext = 80
meip0 = fff00000 meipa = fff00000
-> external irq handler -> external irq handler
mlei = 84 meinext = 84
meip0 = ffe00000 meipa = ffe00000
-> external irq handler -> external irq handler
mlei = 88 meinext = 88
meip0 = ffc00000 meipa = ffc00000
-> external irq handler -> external irq handler
mlei = 92 meinext = 92
meip0 = ff800000 meipa = ff800000
-> external irq handler -> external irq handler
mlei = 96 meinext = 96
meip0 = ff000000 meipa = ff000000
-> external irq handler -> external irq handler
mlei = 100 meinext = 100
meip0 = fe000000 meipa = fe000000
-> external irq handler -> external irq handler
mlei = 104 meinext = 104
meip0 = fc000000 meipa = fc000000
-> external irq handler -> external irq handler
mlei = 108 meinext = 108
meip0 = f8000000 meipa = f8000000
-> external irq handler -> external irq handler
mlei = 112 meinext = 112
meip0 = f0000000 meipa = f0000000
-> external irq handler -> external irq handler
mlei = 116 meinext = 116
meip0 = e0000000 meipa = e0000000
-> external irq handler -> external irq handler
mlei = 120 meinext = 120
meip0 = c0000000 meipa = c0000000
-> external irq handler -> external irq handler
mlei = 124 meinext = 124
meip0 = 80000000 meipa = 80000000
Returned OK Returned OK

View File

@ -1,29 +1,32 @@
TOP := tb TOP := tb
DOTF := tb.f DOTF := tb.f
CPU_RESET_VECTOR := 32'h40 CPU_RESET_VECTOR := 32'h40
EXTENSION_C := 1 EXTENSION_C := 1
EXTENSION_M := 1 EXTENSION_M := 1
EXTENSION_ZBA := 1 EXTENSION_ZBA := 1
EXTENSION_ZBB := 1 EXTENSION_ZBB := 1
EXTENSION_ZBC := 1 EXTENSION_ZBC := 1
EXTENSION_ZBS := 1 EXTENSION_ZBS := 1
DEBUG_SUPPORT := 1 DEBUG_SUPPORT := 1
U_MODE := 1 U_MODE := 1
PMP_REGIONS := 4 PMP_REGIONS := 4
MULDIV_UNROLL := 2 NUM_IRQS := 32
MUL_FAST := 1 IRQ_PRIORITY_BITS := 4
MUL_FASTER := 1
MULH_FAST := 1
FAST_BRANCHCMP := 1
REDUCED_BYPASS := 0
MVENDORID_VAL := 32'hdeadbeef MULDIV_UNROLL := 2
MIMPID_VAL := 32'h12345678 MUL_FAST := 1
MCONFIGPTR_VAL := 32'h9abcdef0 MUL_FASTER := 1
MULH_FAST := 1
FAST_BRANCHCMP := 1
REDUCED_BYPASS := 0
MVENDORID_VAL := 32'hdeadbeef
MIMPID_VAL := 32'h12345678
MCONFIGPTR_VAL := 32'h9abcdef0
.PHONY: clean all .PHONY: clean all
@ -39,6 +42,8 @@ SYNTH_CMD += chparam -set EXTENSION_ZBS $(EXTENSION_ZBS) $(TOP);
SYNTH_CMD += chparam -set DEBUG_SUPPORT $(DEBUG_SUPPORT) $(TOP); SYNTH_CMD += chparam -set DEBUG_SUPPORT $(DEBUG_SUPPORT) $(TOP);
SYNTH_CMD += chparam -set U_MODE $(U_MODE) $(TOP); SYNTH_CMD += chparam -set U_MODE $(U_MODE) $(TOP);
SYNTH_CMD += chparam -set PMP_REGIONS $(PMP_REGIONS) $(TOP); SYNTH_CMD += chparam -set PMP_REGIONS $(PMP_REGIONS) $(TOP);
SYNTH_CMD += chparam -set NUM_IRQS $(NUM_IRQS) $(TOP);
SYNTH_CMD += chparam -set IRQ_PRIORITY_BITS $(IRQ_PRIORITY_BITS) $(TOP);
SYNTH_CMD += chparam -set CSR_COUNTER 1 $(TOP); SYNTH_CMD += chparam -set CSR_COUNTER 1 $(TOP);
SYNTH_CMD += chparam -set RESET_VECTOR $(CPU_RESET_VECTOR) $(TOP); SYNTH_CMD += chparam -set RESET_VECTOR $(CPU_RESET_VECTOR) $(TOP);
SYNTH_CMD += chparam -set REDUCED_BYPASS $(REDUCED_BYPASS) $(TOP); SYNTH_CMD += chparam -set REDUCED_BYPASS $(REDUCED_BYPASS) $(TOP);

View File

@ -7,50 +7,50 @@ module tb #(
`include "hazard3_config.vh" `include "hazard3_config.vh"
) ( ) (
// Global signals // Global signals
input wire clk, input wire clk,
input wire rst_n, input wire rst_n,
// JTAG port // JTAG port
input wire tck, input wire tck,
input wire trst_n, input wire trst_n,
input wire tms, input wire tms,
input wire tdi, input wire tdi,
output wire tdo, output wire tdo,
// Instruction fetch port // Instruction fetch port
output wire [W_ADDR-1:0] i_haddr, output wire [W_ADDR-1:0] i_haddr,
output wire i_hwrite, output wire i_hwrite,
output wire [1:0] i_htrans, output wire [1:0] i_htrans,
output wire i_hexcl, output wire i_hexcl,
output wire [2:0] i_hsize, output wire [2:0] i_hsize,
output wire [2:0] i_hburst, output wire [2:0] i_hburst,
output wire [3:0] i_hprot, output wire [3:0] i_hprot,
output wire i_hmastlock, output wire i_hmastlock,
input wire i_hready, input wire i_hready,
input wire i_hresp, input wire i_hresp,
input wire i_hexokay, input wire i_hexokay,
output wire [W_DATA-1:0] i_hwdata, output wire [W_DATA-1:0] i_hwdata,
input wire [W_DATA-1:0] i_hrdata, input wire [W_DATA-1:0] i_hrdata,
// Load/store port // Load/store port
output wire [W_ADDR-1:0] d_haddr, output wire [W_ADDR-1:0] d_haddr,
output wire d_hwrite, output wire d_hwrite,
output wire [1:0] d_htrans, output wire [1:0] d_htrans,
output wire d_hexcl, output wire d_hexcl,
output wire [2:0] d_hsize, output wire [2:0] d_hsize,
output wire [2:0] d_hburst, output wire [2:0] d_hburst,
output wire [3:0] d_hprot, output wire [3:0] d_hprot,
output wire d_hmastlock, output wire d_hmastlock,
input wire d_hready, input wire d_hready,
input wire d_hresp, input wire d_hresp,
input wire d_hexokay, input wire d_hexokay,
output wire [W_DATA-1:0] d_hwdata, output wire [W_DATA-1:0] d_hwdata,
input wire [W_DATA-1:0] d_hrdata, input wire [W_DATA-1:0] d_hrdata,
// Level-sensitive interrupt sources // Level-sensitive interrupt sources
input wire [NUM_IRQ-1:0] irq, // -> mip.meip input wire [NUM_IRQS-1:0] irq, // -> mip.meip
input wire [1:0] soft_irq, // -> mip.msip input wire [1:0] soft_irq, // -> mip.msip
input wire timer_irq // -> mip.mtip input wire timer_irq // -> mip.mtip
); );
// JTAG-DTM IDCODE, selected after TAP reset, would normally be a // JTAG-DTM IDCODE, selected after TAP reset, would normally be a

View File

@ -7,50 +7,50 @@ module tb #(
`include "hazard3_config.vh" `include "hazard3_config.vh"
) ( ) (
// Global signals // Global signals
input wire clk, input wire clk,
input wire rst_n, input wire rst_n,
// JTAG port // JTAG port
input wire tck, input wire tck,
input wire trst_n, input wire trst_n,
input wire tms, input wire tms,
input wire tdi, input wire tdi,
output wire tdo, output wire tdo,
// Core 0 bus (named I for consistency with 1-core 2-port tb) // Core 0 bus (named I for consistency with 1-core 2-port tb)
output wire [W_ADDR-1:0] i_haddr, output wire [W_ADDR-1:0] i_haddr,
output wire i_hwrite, output wire i_hwrite,
output wire [1:0] i_htrans, output wire [1:0] i_htrans,
output wire i_hexcl, output wire i_hexcl,
output wire [2:0] i_hsize, output wire [2:0] i_hsize,
output wire [2:0] i_hburst, output wire [2:0] i_hburst,
output wire [3:0] i_hprot, output wire [3:0] i_hprot,
output wire i_hmastlock, output wire i_hmastlock,
input wire i_hready, input wire i_hready,
input wire i_hresp, input wire i_hresp,
input wire i_hexokay, input wire i_hexokay,
output wire [W_DATA-1:0] i_hwdata, output wire [W_DATA-1:0] i_hwdata,
input wire [W_DATA-1:0] i_hrdata, input wire [W_DATA-1:0] i_hrdata,
// Core 1 bus (named D for consistency with 1-core 2-port tb) // Core 1 bus (named D for consistency with 1-core 2-port tb)
output wire [W_ADDR-1:0] d_haddr, output wire [W_ADDR-1:0] d_haddr,
output wire d_hwrite, output wire d_hwrite,
output wire [1:0] d_htrans, output wire [1:0] d_htrans,
output wire d_hexcl, output wire d_hexcl,
output wire [2:0] d_hsize, output wire [2:0] d_hsize,
output wire [2:0] d_hburst, output wire [2:0] d_hburst,
output wire [3:0] d_hprot, output wire [3:0] d_hprot,
output wire d_hmastlock, output wire d_hmastlock,
input wire d_hready, input wire d_hready,
input wire d_hresp, input wire d_hresp,
input wire d_hexokay, input wire d_hexokay,
output wire [W_DATA-1:0] d_hwdata, output wire [W_DATA-1:0] d_hwdata,
input wire [W_DATA-1:0] d_hrdata, input wire [W_DATA-1:0] d_hrdata,
// Level-sensitive interrupt sources // Level-sensitive interrupt sources
input wire [NUM_IRQ-1:0] irq, // -> mip.meip input wire [NUM_IRQS-1:0] irq, // -> mip.meip
input wire [1:0] soft_irq, // -> mip.msip input wire [1:0] soft_irq, // -> mip.msip
input wire timer_irq // -> mip.mtip input wire timer_irq // -> mip.mtip
); );
// JTAG-DTM IDCODE, selected after TAP reset, would normally be a // JTAG-DTM IDCODE, selected after TAP reset, would normally be a