From 15cb21ae436931170fc93ff20be22aaf3340c92d Mon Sep 17 00:00:00 2001 From: Luke Wren Date: Sun, 7 Aug 2022 20:51:12 +0100 Subject: [PATCH] First pass at implementing the new IRQ controls. Works well enough that the old tests pass :) --- example_soc/soc/example_soc.v | 2 +- hdl/arith/hazard3_alu.v | 3 +- hdl/arith/hazard3_onehot_encode.v | 33 ++ hdl/arith/hazard3_onehot_priority.v | 33 ++ hdl/arith/hazard3_onehot_priority_dynamic.v | 76 ++++ hdl/arith/hazard3_priority_encode.v | 47 +-- hdl/hazard3.f | 5 +- hdl/hazard3_config.vh | 15 +- hdl/hazard3_config_inst.vh | 3 +- hdl/hazard3_core.v | 78 ++-- hdl/hazard3_cpu_1port.v | 80 ++-- hdl/hazard3_cpu_2port.v | 104 ++--- hdl/hazard3_csr.v | 396 ++++++++++++------ hdl/hazard3_csr_addr.vh | 20 +- test/sim/common/hazard3_csr.h | 43 +- test/sim/common/hazard3_irq.h | 31 ++ test/sim/sw_testcases/csr_readable.c | 9 +- test/sim/sw_testcases/csr_readable_umode.c | 27 +- test/sim/sw_testcases/csr_writable.c | 14 +- test/sim/sw_testcases/irq_individual_enable.c | 154 +++---- test/sim/sw_testcases/irq_individual_pend.c | 155 +++---- test/sim/sw_testcases/irq_set_all.c | 154 +++---- test/sim/tb_cxxrtl/Makefile | 47 ++- test/sim/tb_cxxrtl/tb.v | 72 ++-- test/sim/tb_cxxrtl/tb_multicore.v | 72 ++-- 25 files changed, 1035 insertions(+), 638 deletions(-) create mode 100644 hdl/arith/hazard3_onehot_encode.v create mode 100644 hdl/arith/hazard3_onehot_priority.v create mode 100644 hdl/arith/hazard3_onehot_priority_dynamic.v create mode 100644 test/sim/common/hazard3_irq.h diff --git a/example_soc/soc/example_soc.v b/example_soc/soc/example_soc.v index 729f732..9207fb4 100644 --- a/example_soc/soc/example_soc.v +++ b/example_soc/soc/example_soc.v @@ -245,7 +245,7 @@ hazard3_cpu_1port #( .CSR_M_MANDATORY (1), .CSR_M_TRAP (1), .DEBUG_SUPPORT (1), - .NUM_IRQ (1), + .NUM_IRQS (1), .RESET_REGFILE (0), // Can be overridden from the defaults in hazard3_config.vh during // instantiation of example_soc(): diff --git a/hdl/arith/hazard3_alu.v b/hdl/arith/hazard3_alu.v index 90d922c..1bb871e 100644 --- a/hdl/arith/hazard3_alu.v +++ b/hdl/arith/hazard3_alu.v @@ -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; hazard3_priority_encode #( - .W_REQ (W_DATA) + .W_REQ (W_DATA), + .HIGHEST_WINS (0) ) ctz_priority_encode ( .req (ctz_search_mask), .gnt (ctz_clz[W_SHAMT-1:0]) diff --git a/hdl/arith/hazard3_onehot_encode.v b/hdl/arith/hazard3_onehot_encode.v new file mode 100644 index 0000000..b08fd26 --- /dev/null +++ b/hdl/arith/hazard3_onehot_encode.v @@ -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 diff --git a/hdl/arith/hazard3_onehot_priority.v b/hdl/arith/hazard3_onehot_priority.v new file mode 100644 index 0000000..65cba6d --- /dev/null +++ b/hdl/arith/hazard3_onehot_priority.v @@ -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 diff --git a/hdl/arith/hazard3_onehot_priority_dynamic.v b/hdl/arith/hazard3_onehot_priority_dynamic.v new file mode 100644 index 0000000..5dedc07 --- /dev/null +++ b/hdl/arith/hazard3_onehot_priority_dynamic.v @@ -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 diff --git a/hdl/arith/hazard3_priority_encode.v b/hdl/arith/hazard3_priority_encode.v index 4e0f634..172e72a 100644 --- a/hdl/arith/hazard3_priority_encode.v +++ b/hdl/arith/hazard3_priority_encode.v @@ -3,44 +3,39 @@ | SPDX-License-Identifier: Apache-2.0 | \*****************************************************************************/ -// Really something like this should be in a utility library (or the language!), -// but Hazard3 is supposed to be self-contained +// req: bitmap +// gnt: index of least set bit (HIGHEST_WINS=0) or most set bit (HIGHEST_WINS=1) `default_nettype none module hazard3_priority_encode #( - parameter W_REQ = 16, - parameter W_GNT = $clog2(W_REQ) // do not modify + parameter W_REQ = 16, + parameter HIGHEST_WINS = 0, + parameter W_GNT = $clog2(W_REQ) // do not modify ) ( input wire [W_REQ-1:0] req, 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 - integer i; - for (i = 0; i < W_REQ; i = i + 1) - gnt_onehot[i] = req[i] && ~|(req & ~({W_REQ{1'b1}} << i)); -end - -// 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; +hazard3_onehot_encode #( + .W_REQ (W_REQ) +) encode_u ( + .req (gnt_onehot), + .gnt (gnt) +); endmodule +`ifndef YOSYS `default_nettype wire +`endif diff --git a/hdl/hazard3.f b/hdl/hazard3.f index 3346461..e104df4 100644 --- a/hdl/hazard3.f +++ b/hdl/hazard3.f @@ -3,8 +3,11 @@ file hazard3_cpu_1port.v file hazard3_cpu_2port.v file arith/hazard3_alu.v file arith/hazard3_branchcmp.v -file arith/hazard3_muldiv_seq.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_shift_barrel.v file hazard3_frontend.v diff --git a/hdl/hazard3_config.vh b/hdl/hazard3_config.vh index 9756cb5..50d7d84 100644 --- a/hdl/hazard3_config.vh +++ b/hdl/hazard3_config.vh @@ -120,9 +120,18 @@ parameter PMP_HARDWIRED_CFG = PMP_REGIONS > 0 ? {PMP_REGIONS{8'h00}} : 1'b0, // Requires: CSR_M_MANDATORY, CSR_M_TRAP. parameter DEBUG_SUPPORT = 0, -// NUM_IRQ: Number of external IRQs implemented in meie0 and meip0. -// Minimum 1 (if CSR_M_TRAP = 1), maximum 128. -parameter NUM_IRQ = 32, +// ---------------------------------------------------------------------------- +// External interrupt support + +// 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 diff --git a/hdl/hazard3_config_inst.vh b/hdl/hazard3_config_inst.vh index c650f85..55526c1 100644 --- a/hdl/hazard3_config_inst.vh +++ b/hdl/hazard3_config_inst.vh @@ -34,7 +34,8 @@ .PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR), .PMP_HARDWIRED_CFG (PMP_HARDWIRED_CFG), .DEBUG_SUPPORT (DEBUG_SUPPORT), -.NUM_IRQ (NUM_IRQ), +.NUM_IRQS (NUM_IRQS), +.IRQ_PRIORITY_BITS (IRQ_PRIORITY_BITS), .MVENDORID_VAL (MVENDORID_VAL), .MIMPID_VAL (MIMPID_VAL), `ifndef HAZARD3_CONFIG_INST_NO_MHARTID diff --git a/hdl/hazard3_core.v b/hdl/hazard3_core.v index 0a84749..08fd95d 100644 --- a/hdl/hazard3_core.v +++ b/hdl/hazard3_core.v @@ -11,61 +11,61 @@ module hazard3_core #( `include "hazard3_width_const.vh" ) ( // Global signals - input wire clk, - input wire rst_n, + input wire clk, + input wire rst_n, `ifdef RISCV_FORMAL `RVFI_OUTPUTS , `endif // Instruction fetch port - output wire bus_aph_req_i, - output wire bus_aph_panic_i, // e.g. branch mispredict + flush - input wire bus_aph_ready_i, - input wire bus_dph_ready_i, - input wire bus_dph_err_i, + output wire bus_aph_req_i, + output wire bus_aph_panic_i, // e.g. branch mispredict + flush + input wire bus_aph_ready_i, + input wire bus_dph_ready_i, + input wire bus_dph_err_i, - output wire [W_ADDR-1:0] bus_haddr_i, - output wire [2:0] bus_hsize_i, - output wire bus_priv_i, - input wire [W_DATA-1:0] bus_rdata_i, + output wire [W_ADDR-1:0] bus_haddr_i, + output wire [2:0] bus_hsize_i, + output wire bus_priv_i, + input wire [W_DATA-1:0] bus_rdata_i, // Load/store port - output reg bus_aph_req_d, - output wire bus_aph_excl_d, - input wire bus_aph_ready_d, - input wire bus_dph_ready_d, - input wire bus_dph_err_d, - input wire bus_dph_exokay_d, + output reg bus_aph_req_d, + output wire bus_aph_excl_d, + input wire bus_aph_ready_d, + input wire bus_dph_ready_d, + input wire bus_dph_err_d, + input wire bus_dph_exokay_d, - output reg [W_ADDR-1:0] bus_haddr_d, - output reg [2:0] bus_hsize_d, - output reg bus_priv_d, - output reg bus_hwrite_d, - output reg [W_DATA-1:0] bus_wdata_d, - input wire [W_DATA-1:0] bus_rdata_d, + output reg [W_ADDR-1:0] bus_haddr_d, + output reg [2:0] bus_hsize_d, + output reg bus_priv_d, + output reg bus_hwrite_d, + output reg [W_DATA-1:0] bus_wdata_d, + input wire [W_DATA-1:0] bus_rdata_d, // Debugger run/halt control - input wire dbg_req_halt, - input wire dbg_req_halt_on_reset, - input wire dbg_req_resume, - output wire dbg_halted, - output wire dbg_running, + input wire dbg_req_halt, + input wire dbg_req_halt_on_reset, + input wire dbg_req_resume, + output wire dbg_halted, + output wire dbg_running, // Debugger access to data0 CSR - input wire [W_DATA-1:0] dbg_data0_rdata, - output wire [W_DATA-1:0] dbg_data0_wdata, - output wire dbg_data0_wen, + input wire [W_DATA-1:0] dbg_data0_rdata, + output wire [W_DATA-1:0] dbg_data0_wdata, + output wire dbg_data0_wen, // Debugger instruction injection - input wire [W_DATA-1:0] dbg_instr_data, - input wire dbg_instr_data_vld, - output wire dbg_instr_data_rdy, - output wire dbg_instr_caught_exception, - output wire dbg_instr_caught_ebreak, + input wire [W_DATA-1:0] dbg_instr_data, + input wire dbg_instr_data_vld, + output wire dbg_instr_data_rdy, + output wire dbg_instr_caught_exception, + output wire dbg_instr_caught_ebreak, // Level-sensitive interrupt sources - input wire [NUM_IRQ-1:0] irq, // -> mip.meip - input wire soft_irq, // -> mip.msip - input wire timer_irq // -> mip.mtip + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire soft_irq, // -> mip.msip + input wire timer_irq // -> mip.mtip ); `include "hazard3_ops.vh" diff --git a/hdl/hazard3_cpu_1port.v b/hdl/hazard3_cpu_1port.v index 5951b2d..788fa45 100644 --- a/hdl/hazard3_cpu_1port.v +++ b/hdl/hazard3_cpu_1port.v @@ -13,60 +13,60 @@ module hazard3_cpu_1port #( `include "hazard3_config.vh" ) ( // Global signals - input wire clk, - input wire rst_n, + input wire clk, + input wire rst_n, `ifdef RISCV_FORMAL `RVFI_OUTPUTS , `endif // AHB-lite Master port - output reg [W_ADDR-1:0] ahblm_haddr, - output reg ahblm_hwrite, - output reg [1:0] ahblm_htrans, - output reg [2:0] ahblm_hsize, - output wire [2:0] ahblm_hburst, - output reg [3:0] ahblm_hprot, - output wire ahblm_hmastlock, - output reg [7:0] ahblm_hmaster, - output reg ahblm_hexcl, - input wire ahblm_hready, - input wire ahblm_hresp, - input wire ahblm_hexokay, - output wire [W_DATA-1:0] ahblm_hwdata, - input wire [W_DATA-1:0] ahblm_hrdata, + output reg [W_ADDR-1:0] ahblm_haddr, + output reg ahblm_hwrite, + output reg [1:0] ahblm_htrans, + output reg [2:0] ahblm_hsize, + output wire [2:0] ahblm_hburst, + output reg [3:0] ahblm_hprot, + output wire ahblm_hmastlock, + output reg [7:0] ahblm_hmaster, + output reg ahblm_hexcl, + input wire ahblm_hready, + input wire ahblm_hresp, + input wire ahblm_hexokay, + output wire [W_DATA-1:0] ahblm_hwdata, + input wire [W_DATA-1:0] ahblm_hrdata, // Debugger run/halt control - input wire dbg_req_halt, - input wire dbg_req_halt_on_reset, - input wire dbg_req_resume, - output wire dbg_halted, - output wire dbg_running, + input wire dbg_req_halt, + input wire dbg_req_halt_on_reset, + input wire dbg_req_resume, + output wire dbg_halted, + output wire dbg_running, // Debugger access to data0 CSR - input wire [W_DATA-1:0] dbg_data0_rdata, - output wire [W_DATA-1:0] dbg_data0_wdata, - output wire dbg_data0_wen, + input wire [W_DATA-1:0] dbg_data0_rdata, + output wire [W_DATA-1:0] dbg_data0_wdata, + output wire dbg_data0_wen, // Debugger instruction injection - input wire [W_DATA-1:0] dbg_instr_data, - input wire dbg_instr_data_vld, - output wire dbg_instr_data_rdy, - output wire dbg_instr_caught_exception, - output wire dbg_instr_caught_ebreak, + input wire [W_DATA-1:0] dbg_instr_data, + input wire dbg_instr_data_vld, + output wire dbg_instr_data_rdy, + output wire dbg_instr_caught_exception, + output wire dbg_instr_caught_ebreak, // Optional debug system bus access patch-through - input wire [W_ADDR-1:0] dbg_sbus_addr, - input wire dbg_sbus_write, - input wire [1:0] dbg_sbus_size, - input wire dbg_sbus_vld, - output wire dbg_sbus_rdy, - output wire dbg_sbus_err, - input wire [W_DATA-1:0] dbg_sbus_wdata, - output wire [W_DATA-1:0] dbg_sbus_rdata, + input wire [W_ADDR-1:0] dbg_sbus_addr, + input wire dbg_sbus_write, + input wire [1:0] dbg_sbus_size, + input wire dbg_sbus_vld, + output wire dbg_sbus_rdy, + output wire dbg_sbus_err, + input wire [W_DATA-1:0] dbg_sbus_wdata, + output wire [W_DATA-1:0] dbg_sbus_rdata, // Level-sensitive interrupt sources - input wire [NUM_IRQ-1:0] irq, // -> mip.meip - input wire soft_irq, // -> mip.msip - input wire timer_irq // -> mip.mtip + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire soft_irq, // -> mip.msip + input wire timer_irq // -> mip.mtip ); // ---------------------------------------------------------------------------- diff --git a/hdl/hazard3_cpu_2port.v b/hdl/hazard3_cpu_2port.v index 51483f7..6ef3173 100644 --- a/hdl/hazard3_cpu_2port.v +++ b/hdl/hazard3_cpu_2port.v @@ -13,73 +13,73 @@ module hazard3_cpu_2port #( `include "hazard3_config.vh" ) ( // Global signals - input wire clk, - input wire rst_n, + input wire clk, + input wire rst_n, `ifdef RISCV_FORMAL `RVFI_OUTPUTS , `endif // Instruction fetch port - output wire [W_ADDR-1:0] i_haddr, - output wire i_hwrite, - output wire [1:0] i_htrans, - output wire [2:0] i_hsize, - output wire [2:0] i_hburst, - output wire [3:0] i_hprot, - output wire i_hmastlock, - output wire [7:0] i_hmaster, - input wire i_hready, - input wire i_hresp, - output wire [W_DATA-1:0] i_hwdata, - input wire [W_DATA-1:0] i_hrdata, + output wire [W_ADDR-1:0] i_haddr, + output wire i_hwrite, + output wire [1:0] i_htrans, + output wire [2:0] i_hsize, + output wire [2:0] i_hburst, + output wire [3:0] i_hprot, + output wire i_hmastlock, + output wire [7:0] i_hmaster, + input wire i_hready, + input wire i_hresp, + output wire [W_DATA-1:0] i_hwdata, + input wire [W_DATA-1:0] i_hrdata, // Load/store port - output wire [W_ADDR-1:0] d_haddr, - output wire d_hwrite, - output wire [1:0] d_htrans, - output wire [2:0] d_hsize, - output wire [2:0] d_hburst, - output wire [3:0] d_hprot, - output wire d_hmastlock, - output wire [7:0] d_hmaster, - output wire d_hexcl, - input wire d_hready, - input wire d_hresp, - input wire d_hexokay, - output wire [W_DATA-1:0] d_hwdata, - input wire [W_DATA-1:0] d_hrdata, + output wire [W_ADDR-1:0] d_haddr, + output wire d_hwrite, + output wire [1:0] d_htrans, + output wire [2:0] d_hsize, + output wire [2:0] d_hburst, + output wire [3:0] d_hprot, + output wire d_hmastlock, + output wire [7:0] d_hmaster, + output wire d_hexcl, + input wire d_hready, + input wire d_hresp, + input wire d_hexokay, + output wire [W_DATA-1:0] d_hwdata, + input wire [W_DATA-1:0] d_hrdata, // Debugger run/halt control - input wire dbg_req_halt, - input wire dbg_req_halt_on_reset, - input wire dbg_req_resume, - output wire dbg_halted, - output wire dbg_running, + input wire dbg_req_halt, + input wire dbg_req_halt_on_reset, + input wire dbg_req_resume, + output wire dbg_halted, + output wire dbg_running, // Debugger access to data0 CSR - input wire [W_DATA-1:0] dbg_data0_rdata, - output wire [W_DATA-1:0] dbg_data0_wdata, - output wire dbg_data0_wen, + input wire [W_DATA-1:0] dbg_data0_rdata, + output wire [W_DATA-1:0] dbg_data0_wdata, + output wire dbg_data0_wen, // Debugger instruction injection - input wire [W_DATA-1:0] dbg_instr_data, - input wire dbg_instr_data_vld, - output wire dbg_instr_data_rdy, - output wire dbg_instr_caught_exception, - output wire dbg_instr_caught_ebreak, + input wire [W_DATA-1:0] dbg_instr_data, + input wire dbg_instr_data_vld, + output wire dbg_instr_data_rdy, + output wire dbg_instr_caught_exception, + output wire dbg_instr_caught_ebreak, // Optional debug system bus access patch-through - input wire [W_ADDR-1:0] dbg_sbus_addr, - input wire dbg_sbus_write, - input wire [1:0] dbg_sbus_size, - input wire dbg_sbus_vld, - output wire dbg_sbus_rdy, - output wire dbg_sbus_err, - input wire [W_DATA-1:0] dbg_sbus_wdata, - output wire [W_DATA-1:0] dbg_sbus_rdata, + input wire [W_ADDR-1:0] dbg_sbus_addr, + input wire dbg_sbus_write, + input wire [1:0] dbg_sbus_size, + input wire dbg_sbus_vld, + output wire dbg_sbus_rdy, + output wire dbg_sbus_err, + input wire [W_DATA-1:0] dbg_sbus_wdata, + output wire [W_DATA-1:0] dbg_sbus_rdata, // Level-sensitive interrupt sources - input wire [NUM_IRQ-1:0] irq, // -> mip.meip - input wire soft_irq, // -> mip.msip - input wire timer_irq // -> mip.mtip + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire soft_irq, // -> mip.msip + input wire timer_irq // -> mip.mtip ); // ---------------------------------------------------------------------------- diff --git a/hdl/hazard3_csr.v b/hdl/hazard3_csr.v index fb53d91..b77b448 100644 --- a/hdl/hazard3_csr.v +++ b/hdl/hazard3_csr.v @@ -92,7 +92,7 @@ module hazard3_csr #( // Level-sensitive interrupt sources 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_timer, @@ -112,7 +112,6 @@ module hazard3_csr #( localparam X0 = {XLEN{1'b0}}; - // ---------------------------------------------------------------------------- // CSR state + update logic // ---------------------------------------------------------------------------- @@ -142,14 +141,19 @@ always @ (posedge clk or negedge rst_n) begin 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); -// Core execution state, 1 -> M-mode, 0 -> U-mode (if implemented) -reg m_mode; +wire trapreg_update = trap_enter_vld && trap_enter_rdy && !debug_suppresses_trap_update; +wire trapreg_update_enter = trapreg_update && except != EXCEPT_MRET; +wire trapreg_update_exit = trapreg_update && except == EXCEPT_MRET; reg mstatus_mpie; reg mstatus_mie; @@ -157,8 +161,6 @@ reg mstatus_mpp; // only MSB is implemented reg mstatus_mprv; reg mstatus_tw; -wire wen_m_mode = wen && (m_mode || debug_mode); - // Spec text from priv-1.12: // "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 @@ -176,25 +178,23 @@ always @ (posedge clk or negedge rst_n) begin mstatus_mprv <= 1'b0; mstatus_tw <= 1'b0; end else if (CSR_M_TRAP) begin - if (trap_enter_vld && trap_enter_rdy && !debug_suppresses_trap_update) begin - if (except == EXCEPT_MRET) begin - mstatus_mpie <= 1'b1; - mstatus_mie <= mstatus_mpie; - if (U_MODE) begin - m_mode <= mstatus_mpp; - mstatus_mpp <= 1'b0; - if (!mstatus_mpp) begin - 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; + if (trapreg_update_exit) begin + mstatus_mpie <= 1'b1; + mstatus_mie <= mstatus_mpie; + if (U_MODE) begin + m_mode <= mstatus_mpp; + mstatus_mpp <= 1'b0; + if (!mstatus_mpp) begin + mstatus_mprv <= 1'b0; 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 mstatus_mpie <= wdata_update[7]; mstatus_mie <= wdata_update[3]; @@ -249,7 +249,7 @@ always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin mepc <= X0; 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; end else if (wen_m_mode && addr == MEPC) begin mepc <= wdata_update & MEPC_MASK; @@ -265,81 +265,234 @@ always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin mie <= X0; 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); + 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 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; -// 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, -// and update automatically on trap entry. (bits 30:0 are WLRL, so we tie most off) +// Trap cause registers. The non-constant bits can be written by software, and +// 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 [5:0] mcause_code; +reg [3:0] mcause_code; wire mcause_irq_next; -wire [5:0] mcause_code_next; +wire [3:0] mcause_code_next; always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin mcause_irq <= 1'b0; - mcause_code <= 6'h0; + mcause_code <= 4'h0; 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_code <= mcause_code_next; 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 -// 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 N_IRQ_REG1 = NUM_IRQ >= 64 ? 32 : NUM_IRQ <= 32 ? 0 : NUM_IRQ - 32; -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 MAX_IRQS = 512; +localparam [MAX_IRQS-1:0] IRQ_IMPL_MASK = ~({MAX_IRQS{1'b1}} << NUM_IRQS); -localparam MEIE0_WMASK = ~({XLEN{1'b1}} << N_IRQ_REG0); -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); +localparam IRQ_PRIORITY_MASK = ~(4'hf >> IRQ_PRIORITY_BITS); -reg [XLEN-1:0] meie0; -reg [XLEN-1:0] meie1; -reg [XLEN-1:0] meie2; -reg [XLEN-1:0] meie3; +// Assigned later: +wire [MAX_IRQS-1:0] meipa; +wire [8:0] meinext_irq; +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 if (!rst_n) begin - meie0 <= X0; - meie1 <= X0; - meie2 <= X0; - meie3 <= X0; - end else if (wen_m_mode && addr == MEIE0) begin - meie0 <= update_nonconst(meie0, MEIE0_WMASK); - end else if (wen_m_mode && addr == MEIE1) begin - meie1 <= update_nonconst(meie1, MEIE1_WMASK); - end else if (wen_m_mode && addr == MEIE2) begin - meie2 <= update_nonconst(meie2, MEIE2_WMASK); - end else if (wen_m_mode && addr == MEIE3) begin - meie3 <= update_nonconst(meie3, MEIE3_WMASK); + meiea <= {MAX_IRQS{1'b0}}; + meifa <= {MAX_IRQS{1'b0}}; + meipra <= {4*MAX_IRQS{1'b0}}; + end else begin + // Tie off unimplemented fields with a constant mask, then rely on + // further constant folding. Otherwise subsequent RTL will get a bit + // out of hand. + if (wen_m_mode && addr == MEIEA) begin + meiea[16 * wdata[4:0] +: 16] <= IRQ_IMPL_MASK[16 * wdata[4:0] +: 16] & wdata_update[31:16]; + end else if (wen_m_mode && addr == MEIFA) begin + meifa[16 * wdata[4:0] +: 16] <= IRQ_IMPL_MASK[16 * wdata[4:0] +: 16] & wdata_update[31:16]; + 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 -// Assigned later: -wire [XLEN-1:0] meip0; -wire [XLEN-1:0] meip1; -wire [XLEN-1:0] meip2; -wire [XLEN-1:0] meip3; -wire [6:0] mlei; +reg [3:0] meicontext_pppreempt; +reg [3:0] meicontext_ppreempt; +reg [4:0] meicontext_preempt; +reg meicontext_noirq; +reg [8:0] meicontext_irq; +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 @@ -597,8 +750,8 @@ always @ (*) begin decode_match = match_mrw; rdata = { mcause_irq, // Sign bit is 1 for IRQ, 0 for exception - {26{1'b0}}, // Padding - mcause_code[4:0] // Enough for 16 external IRQs, which is all we have room for in mip/mie + {27{1'b0}}, // Padding + mcause_code[3:0] // Enough for 16 external IRQs, which is all we have room for in mip/mie }; end @@ -938,49 +1091,63 @@ always @ (*) begin // ------------------------------------------------------------------------ // Custom CSRs - MEIE0: if (CSR_M_TRAP && N_IRQ_REG0 > 0) begin + MEIEA: if (CSR_M_TRAP) begin decode_match = match_mrw; - rdata = meie0; + rdata = { + meiea[wdata[4:0] * 16 +: 16], + 16'h0 + }; end - MEIE1: if (CSR_M_TRAP && N_IRQ_REG1 > 0) begin + MEIPA: if (CSR_M_TRAP) begin decode_match = match_mrw; - rdata = meie1; + rdata = { + meipa[wdata[4:0] * 16 +: 16], + 16'h0 + }; end - MEIE2: if (CSR_M_TRAP && N_IRQ_REG2 > 0) begin + MEIFA: if (CSR_M_TRAP) begin decode_match = match_mrw; - rdata = meie2; + rdata = { + meifa[wdata[4:0] * 16 +: 16], + 16'h0 + }; end - MEIE3: if (CSR_M_TRAP && N_IRQ_REG3 > 0) begin + MEIPRA: if (CSR_M_TRAP) begin decode_match = match_mrw; - rdata = meie3; + rdata = { + meipra[wdata[6:0] * 16 +: 16], + 16'h0 + }; end - MEIP0: if (CSR_M_TRAP && N_IRQ_REG0 > 0) begin - decode_match = match_mro; - rdata = meip0; + MEINEXT: if (CSR_M_TRAP) begin + decode_match = match_mrw; + rdata = { + meinext_noirq, + 20'h0, + meinext_irq, + 2'h0 + }; end - MEIP1: if (CSR_M_TRAP && N_IRQ_REG1 > 0) begin - decode_match = match_mro; - rdata = meip1; - end - - MEIP2: if (CSR_M_TRAP && N_IRQ_REG2 > 0) begin - decode_match = match_mro; - rdata = meip2; - end - - MEIP3: if (CSR_M_TRAP && N_IRQ_REG3 > 0) begin - decode_match = match_mro; - rdata = meip3; - end - - MLEI: if (CSR_M_TRAP) begin - decode_match = match_mro; - rdata = {{XLEN-9{1'b0}}, mlei, 2'b00}; + MEICONTEXT: if (CSR_M_TRAP) begin + decode_match = match_mrw; + rdata = { + meicontext_pppreempt, + meicontext_ppreempt, + 3'h0, + meicontext_preempt, + meicontext_noirq, + 2'h0, + meicontext_irq, + mie[7] && wdata_update[1], + mie[3] && wdata_update[1], + 1'b0, + meicontext_mreteirq + }; end default: begin end @@ -1097,30 +1264,19 @@ assign dbg_instr_caught_exception = debug_mode && except != EXCEPT_NONE && excep // ---------------------------------------------------------------------------- // Trap request generation -reg [NUM_IRQ-1:0] irq_r; reg irq_software_r; reg irq_timer_r; always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin - irq_r <= {NUM_IRQ{1'b0}}; irq_software_r <= 1'b0; irq_timer_r <= 1'b0; end else begin - irq_r <= irq; irq_software_r <= irq_software; irq_timer_r <= irq_timer; 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 = { 20'h0, // Reserved 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. 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 wire [3:0] standard_irq_num = 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) ); -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 = 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 // 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_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 diff --git a/hdl/hazard3_csr_addr.vh b/hdl/hazard3_csr_addr.vh index 0023079..ba6a510 100644 --- a/hdl/hazard3_csr_addr.vh +++ b/hdl/hazard3_csr_addr.vh @@ -158,15 +158,14 @@ localparam MENVCFG = 12'h30a; localparam MENVCFGH = 12'h31a; // Custom M-mode CSRs: -localparam MEIE0 = 12'hbe0; // External interrupt enable register 0 -localparam MEIE1 = 12'hbe1; // External interrupt enable register 1 -localparam MEIE2 = 12'hbe2; // External interrupt enable register 2 -localparam MEIE3 = 12'hbe3; // External interrupt enable register 3 -localparam MEIP0 = 12'hfe0; // External interrupt pending register 0 -localparam MEIP1 = 12'hfe1; // External interrupt pending register 1 -localparam MEIP2 = 12'hfe2; // External interrupt pending register 2 -localparam MEIP3 = 12'hfe3; // External interrupt pending register 3 -localparam MLEI = 12'hfe4; // Lowest external interrupt number +localparam MEIEA = 12'hbe0; // External interrupt pending array +localparam MEIPA = 12'hbe1; // External interrupt enable array +localparam MEIFA = 12'hbe2; // External interrupt force array +localparam MEIPRA = 12'hbe3; // External interrupt priority array +localparam MEINEXT = 12'hbe4; // Next external interrupt +localparam MEICONTEXT = 12'hbe5; // External interrupt context register + +localparam MSLEEP = 12'hbf0; // M-mode sleep control register // ---------------------------------------------------------------------------- // U-mode CSRs @@ -179,6 +178,9 @@ localparam CYCLEH = 12'hc80; localparam TIMEH = 12'hc81; localparam INSTRETH = 12'hc82; +// Custom U-mode CSRs +localparam SLEEP = 12'h8f0; // U-mode subset of M-mode sleep control + // ---------------------------------------------------------------------------- // Trigger Module diff --git a/test/sim/common/hazard3_csr.h b/test/sim/common/hazard3_csr.h index 3005c86..eb3ef20 100644 --- a/test/sim/common/hazard3_csr.h +++ b/test/sim/common/hazard3_csr.h @@ -5,10 +5,16 @@ #include "stdint.h" #endif -#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_mlei 0xfe4 // Lowest external interrupt (pending & enabled) +#define hazard3_csr_dmdata0 0xbff // Debug-mode shadow CSR for DM data transfer + +#define hazard3_csr_meiea 0xbe0 // External interrupt pending array +#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) ({ \ uint32_t __csr_tmp_u32; \ @@ -28,10 +34,31 @@ 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 -#define read_csr(csrname) _read_csr(csrname) -#define write_csr(csrname, data) _write_csr(csrname, data) -#define set_csr(csrname, data) _set_csr(csrname, data) -#define clear_csr(csrname, data) _clear_csr(csrname, data) +#define read_csr(csrname) _read_csr(csrname) +#define write_csr(csrname, data) _write_csr(csrname, data) +#define set_csr(csrname, data) _set_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 diff --git a/test/sim/common/hazard3_irq.h b/test/sim/common/hazard3_irq.h new file mode 100644 index 0000000..81d442a --- /dev/null +++ b/test/sim/common/hazard3_irq.h @@ -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 diff --git a/test/sim/sw_testcases/csr_readable.c b/test/sim/sw_testcases/csr_readable.c index 1b8d303..89ab2c8 100644 --- a/test/sim/sw_testcases/csr_readable.c +++ b/test/sim/sw_testcases/csr_readable.c @@ -137,9 +137,12 @@ int main() { (void)read_csr(dscratch0); (void)read_csr(dscratch1); (void)read_csr(hazard3_csr_dmdata0); - (void)read_csr(hazard3_csr_meie0); - (void)read_csr(hazard3_csr_meip0); - (void)read_csr(hazard3_csr_mlei); + (void)read_csr(hazard3_csr_meiea); + (void)read_csr(hazard3_csr_meipa); + (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; } diff --git a/test/sim/sw_testcases/csr_readable_umode.c b/test/sim/sw_testcases/csr_readable_umode.c index b04b042..3878399 100644 --- a/test/sim/sw_testcases/csr_readable_umode.c +++ b/test/sim/sw_testcases/csr_readable_umode.c @@ -184,14 +184,20 @@ CSR was 7b1 CSR was 7b2 -> exception, mcause = 2, mpp = 0 // dscratch1 CSR was 7b3 --> exception, mcause = 2, mpp = 0 // hazard3 dmdata0 +-> exception, mcause = 2, mpp = 0 // hazard3_csr_dmdata0 CSR was bff --> exception, mcause = 2, mpp = 0 // hazard3 meie0 +-> exception, mcause = 2, mpp = 0 // hazard3_csr_meiea CSR was be0 --> exception, mcause = 2, mpp = 0 // hazard3 meip0 -CSR was fe0 --> exception, mcause = 2, mpp = 0 // hazard3 mlei -CSR was fe4 +-> exception, mcause = 2, mpp = 0 // hazard3_csr_meipa +CSR was be1 +-> exception, mcause = 2, mpp = 0 // hazard3_csr_meifa +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 *******************************************************************************/ @@ -295,9 +301,12 @@ void read_all_csrs() { (void)read_csr(dscratch0); (void)read_csr(dscratch1); (void)read_csr(hazard3_csr_dmdata0); - (void)read_csr(hazard3_csr_meie0); - (void)read_csr(hazard3_csr_meip0); - (void)read_csr(hazard3_csr_mlei); + (void)read_csr(hazard3_csr_meiea); + (void)read_csr(hazard3_csr_meipa); + (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() { diff --git a/test/sim/sw_testcases/csr_writable.c b/test/sim/sw_testcases/csr_writable.c index f93d014..813c330 100644 --- a/test/sim/sw_testcases/csr_writable.c +++ b/test/sim/sw_testcases/csr_writable.c @@ -64,10 +64,6 @@ CSR was 7b3 CSR was bff -> exception, mcause = 2 // write to dmdata0, D-mode 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(dscratch1, read_csr(dscratch1 )); 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_mlei, read_csr(hazard3_csr_mlei )); + + write_csr(hazard3_csr_meiea, read_csr(hazard3_csr_meiea )); + 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; } diff --git a/test/sim/sw_testcases/irq_individual_enable.c b/test/sim/sw_testcases/irq_individual_enable.c index 5bf22ab..6133849 100644 --- a/test/sim/sw_testcases/irq_individual_enable.c +++ b/test/sim/sw_testcases/irq_individual_enable.c @@ -1,5 +1,6 @@ #include "tb_cxxrtl_io.h" #include "hazard3_csr.h" +#include "hazard3_irq.h" // Pend all IRQs, enable them one-by-one and log their firing. @@ -7,14 +8,13 @@ int main() { asm volatile ("csrsi mstatus, 0x8"); - write_csr(hazard3_csr_meie0, 0u); write_csr(mie, mie_meie); tb_set_irq_masked(-1u); for (int i = 31; i >= 0; --i) { tb_printf("Enabling IRQ %d\n", i); - write_csr(hazard3_csr_meie0, 1u << i); + h3irq_enable(i, true); } return 0; @@ -23,148 +23,154 @@ int main() { void __attribute__((interrupt)) isr_external_irq() { tb_puts("-> external irq handler\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. - uint32_t mlei = read_csr(hazard3_csr_mlei); - tb_printf("mlei = %u\n", mlei); - tb_printf("meip0 = %08x\n", read_csr(hazard3_csr_meip0)); + uint32_t meinext = read_csr(hazard3_csr_meinext); + tb_printf("meinext = %u\n", meinext); + 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. - tb_assert(read_csr(hazard3_csr_meip0) & (1u << (mlei >> 2)), "IRQ indicated by mlei is not pending\n"); - tb_clr_irq_masked(1u << (mlei >> 2)); + // meinext is scaled by 4 to make it cheaper to index a software vector table. + tb_assert(h3irq_pending(meinext >> 2), "IRQ indicated by meinext is not pending\n"); + tb_clr_irq_masked(1u << (meinext >> 2)); } /*EXPECTED-OUTPUT*************************************************************** Enabling IRQ 31 -> external irq handler -mlei = 124 -meip0 = ffffffff +meinext = 124 +meipa = ffffffff Enabling IRQ 30 -> external irq handler -mlei = 120 -meip0 = 7fffffff +meinext = 120 +meipa = 7fffffff Enabling IRQ 29 -> external irq handler -mlei = 116 -meip0 = 3fffffff +meinext = 116 +meipa = 3fffffff Enabling IRQ 28 -> external irq handler -mlei = 112 -meip0 = 1fffffff +meinext = 112 +meipa = 1fffffff Enabling IRQ 27 -> external irq handler -mlei = 108 -meip0 = 0fffffff +meinext = 108 +meipa = 0fffffff Enabling IRQ 26 -> external irq handler -mlei = 104 -meip0 = 07ffffff +meinext = 104 +meipa = 07ffffff Enabling IRQ 25 -> external irq handler -mlei = 100 -meip0 = 03ffffff +meinext = 100 +meipa = 03ffffff Enabling IRQ 24 -> external irq handler -mlei = 96 -meip0 = 01ffffff +meinext = 96 +meipa = 01ffffff Enabling IRQ 23 -> external irq handler -mlei = 92 -meip0 = 00ffffff +meinext = 92 +meipa = 00ffffff Enabling IRQ 22 -> external irq handler -mlei = 88 -meip0 = 007fffff +meinext = 88 +meipa = 007fffff Enabling IRQ 21 -> external irq handler -mlei = 84 -meip0 = 003fffff +meinext = 84 +meipa = 003fffff Enabling IRQ 20 -> external irq handler -mlei = 80 -meip0 = 001fffff +meinext = 80 +meipa = 001fffff Enabling IRQ 19 -> external irq handler -mlei = 76 -meip0 = 000fffff +meinext = 76 +meipa = 000fffff Enabling IRQ 18 -> external irq handler -mlei = 72 -meip0 = 0007ffff +meinext = 72 +meipa = 0007ffff Enabling IRQ 17 -> external irq handler -mlei = 68 -meip0 = 0003ffff +meinext = 68 +meipa = 0003ffff Enabling IRQ 16 -> external irq handler -mlei = 64 -meip0 = 0001ffff +meinext = 64 +meipa = 0001ffff Enabling IRQ 15 -> external irq handler -mlei = 60 -meip0 = 0000ffff +meinext = 60 +meipa = 0000ffff Enabling IRQ 14 -> external irq handler -mlei = 56 -meip0 = 00007fff +meinext = 56 +meipa = 00007fff Enabling IRQ 13 -> external irq handler -mlei = 52 -meip0 = 00003fff +meinext = 52 +meipa = 00003fff Enabling IRQ 12 -> external irq handler -mlei = 48 -meip0 = 00001fff +meinext = 48 +meipa = 00001fff Enabling IRQ 11 -> external irq handler -mlei = 44 -meip0 = 00000fff +meinext = 44 +meipa = 00000fff Enabling IRQ 10 -> external irq handler -mlei = 40 -meip0 = 000007ff +meinext = 40 +meipa = 000007ff Enabling IRQ 9 -> external irq handler -mlei = 36 -meip0 = 000003ff +meinext = 36 +meipa = 000003ff Enabling IRQ 8 -> external irq handler -mlei = 32 -meip0 = 000001ff +meinext = 32 +meipa = 000001ff Enabling IRQ 7 -> external irq handler -mlei = 28 -meip0 = 000000ff +meinext = 28 +meipa = 000000ff Enabling IRQ 6 -> external irq handler -mlei = 24 -meip0 = 0000007f +meinext = 24 +meipa = 0000007f Enabling IRQ 5 -> external irq handler -mlei = 20 -meip0 = 0000003f +meinext = 20 +meipa = 0000003f Enabling IRQ 4 -> external irq handler -mlei = 16 -meip0 = 0000001f +meinext = 16 +meipa = 0000001f Enabling IRQ 3 -> external irq handler -mlei = 12 -meip0 = 0000000f +meinext = 12 +meipa = 0000000f Enabling IRQ 2 -> external irq handler -mlei = 8 -meip0 = 00000007 +meinext = 8 +meipa = 00000007 Enabling IRQ 1 -> external irq handler -mlei = 4 -meip0 = 00000003 +meinext = 4 +meipa = 00000003 Enabling IRQ 0 -> external irq handler -mlei = 0 -meip0 = 00000001 +meinext = 0 +meipa = 00000001 *******************************************************************************/ diff --git a/test/sim/sw_testcases/irq_individual_pend.c b/test/sim/sw_testcases/irq_individual_pend.c index 6d206cb..48e80e8 100644 --- a/test/sim/sw_testcases/irq_individual_pend.c +++ b/test/sim/sw_testcases/irq_individual_pend.c @@ -1,5 +1,6 @@ #include "tb_cxxrtl_io.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 // firing. @@ -8,7 +9,9 @@ int main() { 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); for (int i = 0; i < 32; ++i) { @@ -22,148 +25,154 @@ int main() { void __attribute__((interrupt)) isr_external_irq() { tb_puts("-> external irq handler\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. - uint32_t mlei = read_csr(hazard3_csr_mlei); - tb_printf("mlei = %u\n", mlei); - tb_printf("meip0 = %08x\n", read_csr(hazard3_csr_meip0)); + uint32_t meinext = read_csr(hazard3_csr_meinext); + tb_printf("meinext = %u\n", meinext); + 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. - tb_assert(read_csr(hazard3_csr_meip0) & (1u << (mlei >> 2)), "IRQ indicated by mlei is not pending\n"); - tb_clr_irq_masked(1u << (mlei >> 2)); + // meinext is scaled by 4 to make it cheaper to index a software vector table. + tb_assert(h3irq_pending(meinext >> 2), "IRQ indicated by meinext is not pending\n"); + tb_clr_irq_masked(1u << (meinext >> 2)); } /*EXPECTED-OUTPUT*************************************************************** Setting IRQ 0 -> external irq handler -mlei = 0 -meip0 = 00000001 +meinext = 0 +meipa = 00000001 Setting IRQ 1 -> external irq handler -mlei = 4 -meip0 = 00000002 +meinext = 4 +meipa = 00000002 Setting IRQ 2 -> external irq handler -mlei = 8 -meip0 = 00000004 +meinext = 8 +meipa = 00000004 Setting IRQ 3 -> external irq handler -mlei = 12 -meip0 = 00000008 +meinext = 12 +meipa = 00000008 Setting IRQ 4 -> external irq handler -mlei = 16 -meip0 = 00000010 +meinext = 16 +meipa = 00000010 Setting IRQ 5 -> external irq handler -mlei = 20 -meip0 = 00000020 +meinext = 20 +meipa = 00000020 Setting IRQ 6 -> external irq handler -mlei = 24 -meip0 = 00000040 +meinext = 24 +meipa = 00000040 Setting IRQ 7 -> external irq handler -mlei = 28 -meip0 = 00000080 +meinext = 28 +meipa = 00000080 Setting IRQ 8 -> external irq handler -mlei = 32 -meip0 = 00000100 +meinext = 32 +meipa = 00000100 Setting IRQ 9 -> external irq handler -mlei = 36 -meip0 = 00000200 +meinext = 36 +meipa = 00000200 Setting IRQ 10 -> external irq handler -mlei = 40 -meip0 = 00000400 +meinext = 40 +meipa = 00000400 Setting IRQ 11 -> external irq handler -mlei = 44 -meip0 = 00000800 +meinext = 44 +meipa = 00000800 Setting IRQ 12 -> external irq handler -mlei = 48 -meip0 = 00001000 +meinext = 48 +meipa = 00001000 Setting IRQ 13 -> external irq handler -mlei = 52 -meip0 = 00002000 +meinext = 52 +meipa = 00002000 Setting IRQ 14 -> external irq handler -mlei = 56 -meip0 = 00004000 +meinext = 56 +meipa = 00004000 Setting IRQ 15 -> external irq handler -mlei = 60 -meip0 = 00008000 +meinext = 60 +meipa = 00008000 Setting IRQ 16 -> external irq handler -mlei = 64 -meip0 = 00010000 +meinext = 64 +meipa = 00010000 Setting IRQ 17 -> external irq handler -mlei = 68 -meip0 = 00020000 +meinext = 68 +meipa = 00020000 Setting IRQ 18 -> external irq handler -mlei = 72 -meip0 = 00040000 +meinext = 72 +meipa = 00040000 Setting IRQ 19 -> external irq handler -mlei = 76 -meip0 = 00080000 +meinext = 76 +meipa = 00080000 Setting IRQ 20 -> external irq handler -mlei = 80 -meip0 = 00100000 +meinext = 80 +meipa = 00100000 Setting IRQ 21 -> external irq handler -mlei = 84 -meip0 = 00200000 +meinext = 84 +meipa = 00200000 Setting IRQ 22 -> external irq handler -mlei = 88 -meip0 = 00400000 +meinext = 88 +meipa = 00400000 Setting IRQ 23 -> external irq handler -mlei = 92 -meip0 = 00800000 +meinext = 92 +meipa = 00800000 Setting IRQ 24 -> external irq handler -mlei = 96 -meip0 = 01000000 +meinext = 96 +meipa = 01000000 Setting IRQ 25 -> external irq handler -mlei = 100 -meip0 = 02000000 +meinext = 100 +meipa = 02000000 Setting IRQ 26 -> external irq handler -mlei = 104 -meip0 = 04000000 +meinext = 104 +meipa = 04000000 Setting IRQ 27 -> external irq handler -mlei = 108 -meip0 = 08000000 +meinext = 108 +meipa = 08000000 Setting IRQ 28 -> external irq handler -mlei = 112 -meip0 = 10000000 +meinext = 112 +meipa = 10000000 Setting IRQ 29 -> external irq handler -mlei = 116 -meip0 = 20000000 +meinext = 116 +meipa = 20000000 Setting IRQ 30 -> external irq handler -mlei = 120 -meip0 = 40000000 +meinext = 120 +meipa = 40000000 Setting IRQ 31 -> external irq handler -mlei = 124 -meip0 = 80000000 +meinext = 124 +meipa = 80000000 *******************************************************************************/ diff --git a/test/sim/sw_testcases/irq_set_all.c b/test/sim/sw_testcases/irq_set_all.c index ad954b7..679ba65 100644 --- a/test/sim/sw_testcases/irq_set_all.c +++ b/test/sim/sw_testcases/irq_set_all.c @@ -1,5 +1,6 @@ #include "tb_cxxrtl_io.h" #include "hazard3_csr.h" +#include "hazard3_irq.h" // Fire all IRQs simultaneously, and log the resulting handler calls @@ -8,7 +9,8 @@ int main() { asm volatile ("csrsi mstatus, 0x8"); 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_set_irq_masked(-1u); @@ -20,118 +22,124 @@ int main() { void __attribute__((interrupt)) isr_external_irq() { tb_puts("-> external irq handler\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. - uint32_t mlei = read_csr(hazard3_csr_mlei); - tb_printf("mlei = %u\n", mlei); - tb_printf("meip0 = %08x\n", read_csr(hazard3_csr_meip0)); + uint32_t meinext = read_csr(hazard3_csr_meinext); + tb_printf("meinext = %u\n", meinext); + 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. - tb_assert(read_csr(hazard3_csr_meip0) & (1u << (mlei >> 2)), "IRQ indicated by mlei is not pending\n"); - tb_clr_irq_masked(1u << (mlei >> 2)); + // meinext is scaled by 4 to make it cheaper to index a software vector table. + tb_assert(h3irq_pending(meinext >> 2), "IRQ indicated by meinext is not pending\n"); + tb_clr_irq_masked(1u << (meinext >> 2)); } /*EXPECTED-OUTPUT*************************************************************** Firing all IRQs -> external irq handler -mlei = 0 -meip0 = ffffffff +meinext = 0 +meipa = ffffffff -> external irq handler -mlei = 4 -meip0 = fffffffe +meinext = 4 +meipa = fffffffe -> external irq handler -mlei = 8 -meip0 = fffffffc +meinext = 8 +meipa = fffffffc -> external irq handler -mlei = 12 -meip0 = fffffff8 +meinext = 12 +meipa = fffffff8 -> external irq handler -mlei = 16 -meip0 = fffffff0 +meinext = 16 +meipa = fffffff0 -> external irq handler -mlei = 20 -meip0 = ffffffe0 +meinext = 20 +meipa = ffffffe0 -> external irq handler -mlei = 24 -meip0 = ffffffc0 +meinext = 24 +meipa = ffffffc0 -> external irq handler -mlei = 28 -meip0 = ffffff80 +meinext = 28 +meipa = ffffff80 -> external irq handler -mlei = 32 -meip0 = ffffff00 +meinext = 32 +meipa = ffffff00 -> external irq handler -mlei = 36 -meip0 = fffffe00 +meinext = 36 +meipa = fffffe00 -> external irq handler -mlei = 40 -meip0 = fffffc00 +meinext = 40 +meipa = fffffc00 -> external irq handler -mlei = 44 -meip0 = fffff800 +meinext = 44 +meipa = fffff800 -> external irq handler -mlei = 48 -meip0 = fffff000 +meinext = 48 +meipa = fffff000 -> external irq handler -mlei = 52 -meip0 = ffffe000 +meinext = 52 +meipa = ffffe000 -> external irq handler -mlei = 56 -meip0 = ffffc000 +meinext = 56 +meipa = ffffc000 -> external irq handler -mlei = 60 -meip0 = ffff8000 +meinext = 60 +meipa = ffff8000 -> external irq handler -mlei = 64 -meip0 = ffff0000 +meinext = 64 +meipa = ffff0000 -> external irq handler -mlei = 68 -meip0 = fffe0000 +meinext = 68 +meipa = fffe0000 -> external irq handler -mlei = 72 -meip0 = fffc0000 +meinext = 72 +meipa = fffc0000 -> external irq handler -mlei = 76 -meip0 = fff80000 +meinext = 76 +meipa = fff80000 -> external irq handler -mlei = 80 -meip0 = fff00000 +meinext = 80 +meipa = fff00000 -> external irq handler -mlei = 84 -meip0 = ffe00000 +meinext = 84 +meipa = ffe00000 -> external irq handler -mlei = 88 -meip0 = ffc00000 +meinext = 88 +meipa = ffc00000 -> external irq handler -mlei = 92 -meip0 = ff800000 +meinext = 92 +meipa = ff800000 -> external irq handler -mlei = 96 -meip0 = ff000000 +meinext = 96 +meipa = ff000000 -> external irq handler -mlei = 100 -meip0 = fe000000 +meinext = 100 +meipa = fe000000 -> external irq handler -mlei = 104 -meip0 = fc000000 +meinext = 104 +meipa = fc000000 -> external irq handler -mlei = 108 -meip0 = f8000000 +meinext = 108 +meipa = f8000000 -> external irq handler -mlei = 112 -meip0 = f0000000 +meinext = 112 +meipa = f0000000 -> external irq handler -mlei = 116 -meip0 = e0000000 +meinext = 116 +meipa = e0000000 -> external irq handler -mlei = 120 -meip0 = c0000000 +meinext = 120 +meipa = c0000000 -> external irq handler -mlei = 124 -meip0 = 80000000 +meinext = 124 +meipa = 80000000 Returned OK diff --git a/test/sim/tb_cxxrtl/Makefile b/test/sim/tb_cxxrtl/Makefile index b72095c..96db795 100644 --- a/test/sim/tb_cxxrtl/Makefile +++ b/test/sim/tb_cxxrtl/Makefile @@ -1,29 +1,32 @@ -TOP := tb -DOTF := tb.f +TOP := tb +DOTF := tb.f -CPU_RESET_VECTOR := 32'h40 +CPU_RESET_VECTOR := 32'h40 -EXTENSION_C := 1 -EXTENSION_M := 1 -EXTENSION_ZBA := 1 -EXTENSION_ZBB := 1 -EXTENSION_ZBC := 1 -EXTENSION_ZBS := 1 +EXTENSION_C := 1 +EXTENSION_M := 1 +EXTENSION_ZBA := 1 +EXTENSION_ZBB := 1 +EXTENSION_ZBC := 1 +EXTENSION_ZBS := 1 -DEBUG_SUPPORT := 1 -U_MODE := 1 -PMP_REGIONS := 4 +DEBUG_SUPPORT := 1 +U_MODE := 1 +PMP_REGIONS := 4 -MULDIV_UNROLL := 2 -MUL_FAST := 1 -MUL_FASTER := 1 -MULH_FAST := 1 -FAST_BRANCHCMP := 1 -REDUCED_BYPASS := 0 +NUM_IRQS := 32 +IRQ_PRIORITY_BITS := 4 -MVENDORID_VAL := 32'hdeadbeef -MIMPID_VAL := 32'h12345678 -MCONFIGPTR_VAL := 32'h9abcdef0 +MULDIV_UNROLL := 2 +MUL_FAST := 1 +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 @@ -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 U_MODE $(U_MODE) $(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 RESET_VECTOR $(CPU_RESET_VECTOR) $(TOP); SYNTH_CMD += chparam -set REDUCED_BYPASS $(REDUCED_BYPASS) $(TOP); diff --git a/test/sim/tb_cxxrtl/tb.v b/test/sim/tb_cxxrtl/tb.v index 99a2851..77d2d58 100644 --- a/test/sim/tb_cxxrtl/tb.v +++ b/test/sim/tb_cxxrtl/tb.v @@ -7,50 +7,50 @@ module tb #( `include "hazard3_config.vh" ) ( // Global signals - input wire clk, - input wire rst_n, + input wire clk, + input wire rst_n, // JTAG port - input wire tck, - input wire trst_n, - input wire tms, - input wire tdi, - output wire tdo, + input wire tck, + input wire trst_n, + input wire tms, + input wire tdi, + output wire tdo, // Instruction fetch port - output wire [W_ADDR-1:0] i_haddr, - output wire i_hwrite, - output wire [1:0] i_htrans, - output wire i_hexcl, - output wire [2:0] i_hsize, - output wire [2:0] i_hburst, - output wire [3:0] i_hprot, - output wire i_hmastlock, - input wire i_hready, - input wire i_hresp, - input wire i_hexokay, - output wire [W_DATA-1:0] i_hwdata, - input wire [W_DATA-1:0] i_hrdata, + output wire [W_ADDR-1:0] i_haddr, + output wire i_hwrite, + output wire [1:0] i_htrans, + output wire i_hexcl, + output wire [2:0] i_hsize, + output wire [2:0] i_hburst, + output wire [3:0] i_hprot, + output wire i_hmastlock, + input wire i_hready, + input wire i_hresp, + input wire i_hexokay, + output wire [W_DATA-1:0] i_hwdata, + input wire [W_DATA-1:0] i_hrdata, // Load/store port - output wire [W_ADDR-1:0] d_haddr, - output wire d_hwrite, - output wire [1:0] d_htrans, - output wire d_hexcl, - output wire [2:0] d_hsize, - output wire [2:0] d_hburst, - output wire [3:0] d_hprot, - output wire d_hmastlock, - input wire d_hready, - input wire d_hresp, - input wire d_hexokay, - output wire [W_DATA-1:0] d_hwdata, - input wire [W_DATA-1:0] d_hrdata, + output wire [W_ADDR-1:0] d_haddr, + output wire d_hwrite, + output wire [1:0] d_htrans, + output wire d_hexcl, + output wire [2:0] d_hsize, + output wire [2:0] d_hburst, + output wire [3:0] d_hprot, + output wire d_hmastlock, + input wire d_hready, + input wire d_hresp, + input wire d_hexokay, + output wire [W_DATA-1:0] d_hwdata, + input wire [W_DATA-1:0] d_hrdata, // Level-sensitive interrupt sources - input wire [NUM_IRQ-1:0] irq, // -> mip.meip - input wire [1:0] soft_irq, // -> mip.msip - input wire timer_irq // -> mip.mtip + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire [1:0] soft_irq, // -> mip.msip + input wire timer_irq // -> mip.mtip ); // JTAG-DTM IDCODE, selected after TAP reset, would normally be a diff --git a/test/sim/tb_cxxrtl/tb_multicore.v b/test/sim/tb_cxxrtl/tb_multicore.v index 9f36a26..216da3e 100644 --- a/test/sim/tb_cxxrtl/tb_multicore.v +++ b/test/sim/tb_cxxrtl/tb_multicore.v @@ -7,50 +7,50 @@ module tb #( `include "hazard3_config.vh" ) ( // Global signals - input wire clk, - input wire rst_n, + input wire clk, + input wire rst_n, // JTAG port - input wire tck, - input wire trst_n, - input wire tms, - input wire tdi, - output wire tdo, + input wire tck, + input wire trst_n, + input wire tms, + input wire tdi, + output wire tdo, // Core 0 bus (named I for consistency with 1-core 2-port tb) - output wire [W_ADDR-1:0] i_haddr, - output wire i_hwrite, - output wire [1:0] i_htrans, - output wire i_hexcl, - output wire [2:0] i_hsize, - output wire [2:0] i_hburst, - output wire [3:0] i_hprot, - output wire i_hmastlock, - input wire i_hready, - input wire i_hresp, - input wire i_hexokay, - output wire [W_DATA-1:0] i_hwdata, - input wire [W_DATA-1:0] i_hrdata, + output wire [W_ADDR-1:0] i_haddr, + output wire i_hwrite, + output wire [1:0] i_htrans, + output wire i_hexcl, + output wire [2:0] i_hsize, + output wire [2:0] i_hburst, + output wire [3:0] i_hprot, + output wire i_hmastlock, + input wire i_hready, + input wire i_hresp, + input wire i_hexokay, + output wire [W_DATA-1:0] i_hwdata, + input wire [W_DATA-1:0] i_hrdata, // Core 1 bus (named D for consistency with 1-core 2-port tb) - output wire [W_ADDR-1:0] d_haddr, - output wire d_hwrite, - output wire [1:0] d_htrans, - output wire d_hexcl, - output wire [2:0] d_hsize, - output wire [2:0] d_hburst, - output wire [3:0] d_hprot, - output wire d_hmastlock, - input wire d_hready, - input wire d_hresp, - input wire d_hexokay, - output wire [W_DATA-1:0] d_hwdata, - input wire [W_DATA-1:0] d_hrdata, + output wire [W_ADDR-1:0] d_haddr, + output wire d_hwrite, + output wire [1:0] d_htrans, + output wire d_hexcl, + output wire [2:0] d_hsize, + output wire [2:0] d_hburst, + output wire [3:0] d_hprot, + output wire d_hmastlock, + input wire d_hready, + input wire d_hresp, + input wire d_hexokay, + output wire [W_DATA-1:0] d_hwdata, + input wire [W_DATA-1:0] d_hrdata, // Level-sensitive interrupt sources - input wire [NUM_IRQ-1:0] irq, // -> mip.meip - input wire [1:0] soft_irq, // -> mip.msip - input wire timer_irq // -> mip.mtip + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire [1:0] soft_irq, // -> mip.msip + input wire timer_irq // -> mip.mtip ); // JTAG-DTM IDCODE, selected after TAP reset, would normally be a