[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:
Pirmin Vogel 2021-01-19 09:53:27 +01:00
parent 57108c3184
commit 0d6e220d79
7 changed files with 158 additions and 3 deletions

View file

@ -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,

View file

@ -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 ),

View file

@ -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,

View file

@ -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 //

View file

@ -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 ),

View file

@ -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 };

View file

@ -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,