mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
[rtl] Add CLINTx: 32 fast interrupts and custom CSRs + ACK signaling
This commit adds 32 non-standard, custom, fast interrupts to Ibex. These interrupts are managed via 3 custom CSRs: `miex`, `mipx` and `mtvecx`. `mtvecx` is initialized just like `mtvec`. Upon handling such a custom interrupt, the core uses two signals (ACK + ID) to inform a possible platform-level interrupt controller accordingly. The purpose of adding these non-standard interrupts is to have an interrupt framework compatible with RI5CY/CV32E40P for platforms that can use the two cores interchangeably (PULP/PULPissmio). Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
This commit is contained in:
parent
57108c3184
commit
0d6e220d79
7 changed files with 158 additions and 3 deletions
|
@ -75,6 +75,10 @@ module ibex_controller #(
|
|||
// mie CSR
|
||||
input logic irq_nm_i, // non-maskeable interrupt
|
||||
output logic nmi_mode_o, // core executing NMI handler
|
||||
input logic [31:0] irqs_x_i, // x interrupt requests qualified with
|
||||
// miex CSR
|
||||
output logic irq_x_ack_o, // to ext. int controller
|
||||
output logic [4:0] irq_x_ack_id_o, // to ext. int controller
|
||||
|
||||
// debug signals
|
||||
input logic debug_req_i,
|
||||
|
@ -150,6 +154,8 @@ module ibex_controller #(
|
|||
logic [3:0] mfip_id;
|
||||
logic unused_irq_timer;
|
||||
|
||||
logic [4:0] irq_x_id;
|
||||
|
||||
logic ecall_insn;
|
||||
logic mret_insn;
|
||||
logic dret_insn;
|
||||
|
@ -353,6 +359,42 @@ module ibex_controller #(
|
|||
|
||||
assign unused_irq_timer = irqs_i.irq_timer;
|
||||
|
||||
// generate ID of custom interrupts, highest priority to highest ID
|
||||
always_comb begin : gen_irq_x_id
|
||||
if (irqs_x_i[31]) irq_x_id = 5'd31;
|
||||
else if (irqs_x_i[30]) irq_x_id = 5'd30;
|
||||
else if (irqs_x_i[29]) irq_x_id = 5'd29;
|
||||
else if (irqs_x_i[28]) irq_x_id = 5'd28;
|
||||
else if (irqs_x_i[27]) irq_x_id = 5'd27;
|
||||
else if (irqs_x_i[26]) irq_x_id = 5'd26;
|
||||
else if (irqs_x_i[25]) irq_x_id = 5'd25;
|
||||
else if (irqs_x_i[24]) irq_x_id = 5'd24;
|
||||
else if (irqs_x_i[23]) irq_x_id = 5'd23;
|
||||
else if (irqs_x_i[22]) irq_x_id = 5'd22;
|
||||
else if (irqs_x_i[21]) irq_x_id = 5'd21;
|
||||
else if (irqs_x_i[20]) irq_x_id = 5'd20;
|
||||
else if (irqs_x_i[19]) irq_x_id = 5'd19;
|
||||
else if (irqs_x_i[18]) irq_x_id = 5'd18;
|
||||
else if (irqs_x_i[17]) irq_x_id = 5'd17;
|
||||
else if (irqs_x_i[16]) irq_x_id = 5'd16;
|
||||
else if (irqs_x_i[15]) irq_x_id = 5'd15;
|
||||
else if (irqs_x_i[14]) irq_x_id = 5'd14;
|
||||
else if (irqs_x_i[13]) irq_x_id = 5'd13;
|
||||
else if (irqs_x_i[12]) irq_x_id = 5'd12;
|
||||
else if (irqs_x_i[11]) irq_x_id = 5'd11;
|
||||
else if (irqs_x_i[10]) irq_x_id = 5'd10;
|
||||
else if (irqs_x_i[ 9]) irq_x_id = 5'd9;
|
||||
else if (irqs_x_i[ 8]) irq_x_id = 5'd8;
|
||||
else if (irqs_x_i[ 7]) irq_x_id = 5'd7;
|
||||
else if (irqs_x_i[ 6]) irq_x_id = 5'd6;
|
||||
else if (irqs_x_i[ 5]) irq_x_id = 5'd5;
|
||||
else if (irqs_x_i[ 4]) irq_x_id = 5'd4;
|
||||
else if (irqs_x_i[ 3]) irq_x_id = 5'd3;
|
||||
else if (irqs_x_i[ 2]) irq_x_id = 5'd2;
|
||||
else if (irqs_x_i[ 1]) irq_x_id = 5'd1;
|
||||
else irq_x_id = 5'd0;
|
||||
end
|
||||
|
||||
/////////////////////
|
||||
// Core controller //
|
||||
/////////////////////
|
||||
|
@ -399,6 +441,9 @@ module ibex_controller #(
|
|||
|
||||
controller_run_o = 1'b0;
|
||||
|
||||
irq_x_ack_o = 1'b0;
|
||||
irq_x_ack_id_o = 5'b0;
|
||||
|
||||
unique case (ctrl_fsm_cs)
|
||||
RESET: begin
|
||||
instr_req_o = 1'b0;
|
||||
|
@ -569,6 +614,13 @@ module ibex_controller #(
|
|||
if (irq_nm_i && !nmi_mode_q) begin
|
||||
exc_cause_o = EXC_CAUSE_IRQ_NM;
|
||||
nmi_mode_d = 1'b1; // enter NMI mode
|
||||
end else if (irqs_x_i != 32'b0) begin
|
||||
// generate exception cause ID from fast interrupt ID:
|
||||
// - first bit distinguishes interrupts from exceptions,
|
||||
exc_cause_o = exc_cause_e'({1'b1, irq_x_id});
|
||||
exc_pc_mux_o = EXC_PC_IRQ_X;
|
||||
irq_x_ack_o = 1'b1;
|
||||
irq_x_ack_id_o = irq_x_id;
|
||||
end else if (irqs_i.irq_fast != 15'b0) begin
|
||||
// generate exception cause ID from fast interrupt ID:
|
||||
// - first bit distinguishes interrupts from exceptions,
|
||||
|
|
|
@ -67,6 +67,9 @@ module ibex_core #(
|
|||
input logic irq_external_i,
|
||||
input logic [14:0] irq_fast_i,
|
||||
input logic irq_nm_i, // non-maskeable interrupt
|
||||
input logic [31:0] irq_x_i,
|
||||
output logic irq_x_ack_o,
|
||||
output logic [4:0] irq_x_ack_id_o,
|
||||
|
||||
// Debug Interface
|
||||
input logic debug_req_i,
|
||||
|
@ -264,6 +267,7 @@ module ibex_core #(
|
|||
logic irq_pending;
|
||||
logic nmi_mode;
|
||||
irqs_t irqs;
|
||||
logic [31:0] irqs_x;
|
||||
logic csr_mstatus_mie;
|
||||
logic [31:0] csr_mepc, csr_depc;
|
||||
|
||||
|
@ -282,6 +286,7 @@ module ibex_core #(
|
|||
logic csr_save_cause;
|
||||
logic csr_mtvec_init;
|
||||
logic [31:0] csr_mtvec;
|
||||
logic [31:0] csr_mtvecx;
|
||||
logic [31:0] csr_mtval;
|
||||
logic csr_mstatus_tw;
|
||||
priv_lvl_e priv_mode_id;
|
||||
|
@ -458,6 +463,7 @@ module ibex_core #(
|
|||
.csr_mepc_i ( csr_mepc ), // exception return address
|
||||
.csr_depc_i ( csr_depc ), // debug return address
|
||||
.csr_mtvec_i ( csr_mtvec ), // trap-vector base address
|
||||
.csr_mtvecx_i ( csr_mtvecx ), // x trap-vector base address
|
||||
.csr_mtvec_init_o ( csr_mtvec_init ),
|
||||
|
||||
// pipeline stalls
|
||||
|
@ -586,6 +592,9 @@ module ibex_core #(
|
|||
.irqs_i ( irqs ),
|
||||
.irq_nm_i ( irq_nm_i ),
|
||||
.nmi_mode_o ( nmi_mode ),
|
||||
.irqs_x_i ( irqs_x ),
|
||||
.irq_x_ack_o ( irq_x_ack_o ),
|
||||
.irq_x_ack_id_o ( irq_x_ack_id_o ),
|
||||
|
||||
// Debug Signal
|
||||
.debug_mode_o ( debug_mode ),
|
||||
|
@ -991,6 +1000,7 @@ module ibex_core #(
|
|||
|
||||
// mtvec
|
||||
.csr_mtvec_o ( csr_mtvec ),
|
||||
.csr_mtvecx_o ( csr_mtvecx ),
|
||||
.csr_mtvec_init_i ( csr_mtvec_init ),
|
||||
.boot_addr_i ( boot_addr_i ),
|
||||
|
||||
|
@ -1007,9 +1017,11 @@ module ibex_core #(
|
|||
.irq_timer_i ( irq_timer_i ),
|
||||
.irq_external_i ( irq_external_i ),
|
||||
.irq_fast_i ( irq_fast_i ),
|
||||
.irq_x_i ( irq_x_i ),
|
||||
.nmi_mode_i ( nmi_mode ),
|
||||
.irq_pending_o ( irq_pending ),
|
||||
.irqs_o ( irqs ),
|
||||
.irqs_x_o ( irqs_x ),
|
||||
.csr_mstatus_mie_o ( csr_mstatus_mie ),
|
||||
.csr_mstatus_tw_o ( csr_mstatus_tw ),
|
||||
.csr_mepc_o ( csr_mepc ),
|
||||
|
|
|
@ -61,6 +61,9 @@ module ibex_core_tracing #(
|
|||
input logic irq_external_i,
|
||||
input logic [14:0] irq_fast_i,
|
||||
input logic irq_nm_i, // non-maskeable interrupt
|
||||
output logic [31:0] irq_x_i,
|
||||
output logic irq_x_ack_o,
|
||||
output logic [3:0] irq_x_ack_id_o,
|
||||
|
||||
// Debug Interface
|
||||
input logic debug_req_i,
|
||||
|
@ -155,6 +158,9 @@ module ibex_core_tracing #(
|
|||
.irq_external_i,
|
||||
.irq_fast_i,
|
||||
.irq_nm_i,
|
||||
.irq_x_i,
|
||||
.irq_x_ack_o,
|
||||
.irq_x_ack_id_o,
|
||||
|
||||
.debug_req_i,
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ module ibex_cs_registers #(
|
|||
|
||||
// mtvec
|
||||
output logic [31:0] csr_mtvec_o,
|
||||
output logic [31:0] csr_mtvecx_o,
|
||||
input logic csr_mtvec_init_i,
|
||||
input logic [31:0] boot_addr_i,
|
||||
|
||||
|
@ -59,9 +60,11 @@ module ibex_cs_registers #(
|
|||
input logic irq_timer_i,
|
||||
input logic irq_external_i,
|
||||
input logic [14:0] irq_fast_i,
|
||||
input logic [31:0] irq_x_i,
|
||||
input logic nmi_mode_i,
|
||||
output logic irq_pending_o, // interrupt request pending
|
||||
output ibex_pkg::irqs_t irqs_o, // interrupt requests qualified with mie
|
||||
output logic [31:0] irqs_x_o, // custom interrupt requests qualified with miex
|
||||
output logic csr_mstatus_mie_o,
|
||||
output logic [31:0] csr_mepc_o,
|
||||
|
||||
|
@ -209,6 +212,14 @@ module ibex_cs_registers #(
|
|||
logic [31:0] dscratch1_q;
|
||||
logic dscratch0_en, dscratch1_en;
|
||||
|
||||
// CLINTx
|
||||
logic [31:0] miex_q;
|
||||
logic miex_en;
|
||||
logic [31:0] mipx;
|
||||
logic [31:0] mtvecx_q, mtvecx_d;
|
||||
logic mtvecx_err;
|
||||
logic mtvecx_en;
|
||||
|
||||
// CSRs for recoverable NMIs
|
||||
// NOTE: these CSRS are nonstandard, see https://github.com/riscv/riscv-isa-manual/issues/261
|
||||
status_stk_t mstack_q, mstack_d;
|
||||
|
@ -286,6 +297,9 @@ module ibex_cs_registers #(
|
|||
assign mip.irq_external = irq_external_i;
|
||||
assign mip.irq_fast = irq_fast_i;
|
||||
|
||||
// mipx CSR is purely combinational - must be able to re-enable the clock upon WFI
|
||||
assign mipx = irq_x_i;
|
||||
|
||||
// read logic
|
||||
always_comb begin
|
||||
csr_rdata_int = '0;
|
||||
|
@ -458,6 +472,11 @@ module ibex_cs_registers #(
|
|||
csr_rdata_int = '0;
|
||||
end
|
||||
|
||||
// CLINTx
|
||||
CSR_MIEX: csr_rdata_int = miex_q;
|
||||
CSR_MIPX: csr_rdata_int = mipx;
|
||||
CSR_MTVECX: csr_rdata_int = mtvecx_q;
|
||||
|
||||
default: begin
|
||||
illegal_csr = 1'b1;
|
||||
end
|
||||
|
@ -503,6 +522,13 @@ module ibex_cs_registers #(
|
|||
|
||||
cpuctrl_we = 1'b0;
|
||||
|
||||
miex_en = 1'b0;
|
||||
mtvecx_en = csr_mtvec_init_i;
|
||||
// mtvecx.MODE set to vectored
|
||||
// mtvecx.BASE must be 256-byte aligned
|
||||
mtvecx_d = csr_mtvec_init_i ? {boot_addr_i[31:8], 6'b0, 2'b01} :
|
||||
{csr_wdata_int[31:8], 6'b0, 2'b01};
|
||||
|
||||
if (csr_we_int) begin
|
||||
unique case (csr_addr_i)
|
||||
// mstatus: IE bit
|
||||
|
@ -599,6 +625,10 @@ module ibex_cs_registers #(
|
|||
|
||||
CSR_CPUCTRL: cpuctrl_we = 1'b1;
|
||||
|
||||
// CLINTx
|
||||
CSR_MIEX: miex_en = 1'b1;
|
||||
CSR_MTVECX: mtvecx_en = 1'b1;
|
||||
|
||||
default:;
|
||||
endcase
|
||||
end
|
||||
|
@ -730,7 +760,14 @@ module ibex_cs_registers #(
|
|||
// Qualify incoming interrupt requests in mip CSR with mie CSR for controller and to re-enable
|
||||
// clock upon WFI (must be purely combinational).
|
||||
assign irqs_o = mip & mie_q;
|
||||
assign irq_pending_o = |irqs_o;
|
||||
assign irq_pending_o = (|irqs_o) | (|irqs_x_o);
|
||||
|
||||
// Qualify incoming interrupt requests in mipx CSR with miex CSR for controller and to re-enable
|
||||
// clock upon WFI (must be purely combinational).
|
||||
assign irqs_x_o = mipx & miex_q;
|
||||
|
||||
// directly output mtvecx to IF stage
|
||||
assign csr_mtvecx_o = mtvecx_q;
|
||||
|
||||
////////////////////////
|
||||
// CSR instantiations //
|
||||
|
@ -1412,7 +1449,39 @@ module ibex_cs_registers #(
|
|||
.rd_error_o (cpuctrl_err)
|
||||
);
|
||||
|
||||
assign csr_shadow_err_o = mstatus_err | mtvec_err | pmp_csr_err | cpuctrl_err;
|
||||
assign csr_shadow_err_o = mstatus_err | mtvec_err | mtvecx_err | pmp_csr_err | cpuctrl_err;
|
||||
|
||||
////////////
|
||||
// CLINTx //
|
||||
////////////
|
||||
|
||||
// MIEX
|
||||
ibex_csr #(
|
||||
.Width (32),
|
||||
.ShadowCopy (1'b0),
|
||||
.ResetValue ('0)
|
||||
) u_miex_csr (
|
||||
.clk_i (clk_i),
|
||||
.rst_ni (rst_ni),
|
||||
.wr_data_i (csr_wdata_int),
|
||||
.wr_en_i (miex_en),
|
||||
.rd_data_o (miex_q),
|
||||
.rd_error_o ()
|
||||
);
|
||||
|
||||
// MTVECX
|
||||
ibex_csr #(
|
||||
.Width (32),
|
||||
.ShadowCopy (ShadowCSR),
|
||||
.ResetValue (32'd1)
|
||||
) u_mtvecx_csr (
|
||||
.clk_i (clk_i),
|
||||
.rst_ni (rst_ni),
|
||||
.wr_data_i (mtvecx_d),
|
||||
.wr_en_i (mtvecx_en),
|
||||
.rd_data_o (mtvecx_q),
|
||||
.rd_error_o (mtvecx_err)
|
||||
);
|
||||
|
||||
////////////////
|
||||
// Assertions //
|
||||
|
|
|
@ -127,6 +127,9 @@ module ibex_id_stage #(
|
|||
input ibex_pkg::irqs_t irqs_i,
|
||||
input logic irq_nm_i,
|
||||
output logic nmi_mode_o,
|
||||
input logic [31:0] irqs_x_i,
|
||||
output logic irq_x_ack_o,
|
||||
output logic [4:0] irq_x_ack_id_o,
|
||||
|
||||
input logic lsu_load_err_i,
|
||||
input logic lsu_store_err_i,
|
||||
|
@ -582,6 +585,9 @@ module ibex_id_stage #(
|
|||
.irqs_i ( irqs_i ),
|
||||
.irq_nm_i ( irq_nm_i ),
|
||||
.nmi_mode_o ( nmi_mode_o ),
|
||||
.irqs_x_i ( irqs_x_i ),
|
||||
.irq_x_ack_o ( irq_x_ack_o ),
|
||||
.irq_x_ack_id_o ( irq_x_ack_id_o ),
|
||||
|
||||
// CSR Controller Signals
|
||||
.csr_save_if_o ( csr_save_if_o ),
|
||||
|
|
|
@ -83,6 +83,7 @@ module ibex_if_stage #(
|
|||
input logic [31:0] csr_depc_i, // PC to restore after handling
|
||||
// the debug request
|
||||
input logic [31:0] csr_mtvec_i, // base PC to jump to on exception
|
||||
input logic [31:0] csr_mtvecx_i, // base PC to jump to on x interrupts
|
||||
output logic csr_mtvec_init_o, // tell CS regfile to init mtvec
|
||||
|
||||
// pipeline stall
|
||||
|
@ -139,9 +140,11 @@ module ibex_if_stage #(
|
|||
|
||||
logic [7:0] unused_boot_addr;
|
||||
logic [7:0] unused_csr_mtvec;
|
||||
logic [7:0] unused_csr_mtvecx;
|
||||
|
||||
assign unused_boot_addr = boot_addr_i[7:0];
|
||||
assign unused_csr_mtvec = csr_mtvec_i[7:0];
|
||||
assign unused_csr_mtvecx = csr_mtvecx_i[7:0];
|
||||
|
||||
// extract interrupt ID from exception cause
|
||||
assign irq_id = {exc_cause};
|
||||
|
@ -152,6 +155,7 @@ module ibex_if_stage #(
|
|||
unique case (exc_pc_mux_i)
|
||||
EXC_PC_EXC: exc_pc = { csr_mtvec_i[31:8], 8'h00 };
|
||||
EXC_PC_IRQ: exc_pc = { csr_mtvec_i[31:8], 1'b0, irq_id[4:0], 2'b00 };
|
||||
EXC_PC_IRQ_X: exc_pc = { csr_mtvecx_i[31:8],1'b0, irq_id[4:0], 2'b00 };
|
||||
EXC_PC_DBD: exc_pc = DmHaltAddr;
|
||||
EXC_PC_DBG_EXC: exc_pc = DmExceptionAddr;
|
||||
default: exc_pc = { csr_mtvec_i[31:8], 8'h00 };
|
||||
|
|
|
@ -257,9 +257,10 @@ typedef enum logic [2:0] {
|
|||
} pc_sel_e;
|
||||
|
||||
// Exception PC mux selection
|
||||
typedef enum logic [1:0] {
|
||||
typedef enum logic [2:0] {
|
||||
EXC_PC_EXC,
|
||||
EXC_PC_IRQ,
|
||||
EXC_PC_IRQ_X,
|
||||
EXC_PC_DBD,
|
||||
EXC_PC_DBG_EXC // Exception while in debug mode
|
||||
} exc_pc_sel_e;
|
||||
|
@ -386,6 +387,11 @@ typedef enum logic[11:0] {
|
|||
CSR_DSCRATCH0 = 12'h7b2, // optional
|
||||
CSR_DSCRATCH1 = 12'h7b3, // optional
|
||||
|
||||
// CLINTx
|
||||
CSR_MIEX = 12'h7D0,
|
||||
CSR_MTVECX = 12'h7D1,
|
||||
CSR_MIPX = 12'h7D2,
|
||||
|
||||
// Machine Counter/Timers
|
||||
CSR_MCOUNTINHIBIT = 12'h320,
|
||||
CSR_MHPMEVENT3 = 12'h323,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue