[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 2020-04-23 14:25:16 +02:00
parent 2c340849ac
commit e65a27e14d
7 changed files with 127 additions and 2 deletions

View file

@ -67,6 +67,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,
@ -131,6 +135,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;
@ -272,6 +278,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 //
/////////////////////
@ -315,6 +357,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
// just wait for fetch_enable
@ -460,6 +505,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

@ -65,6 +65,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,
@ -233,6 +236,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;
@ -251,6 +255,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;
@ -412,6 +417,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
@ -536,6 +542,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 ),
@ -819,6 +828,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 ),
@ -835,9 +845,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

@ -59,6 +59,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,
@ -148,6 +151,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

@ -39,6 +39,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,
@ -55,9 +56,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,
@ -183,6 +186,11 @@ module ibex_cs_registers #(
logic [31:0] dscratch0_q, dscratch0_d;
logic [31:0] dscratch1_q, dscratch1_d;
// CLINTx
logic [31:0] miex_q, miex_d;
logic [31:0] mipx;
logic [31:0] mtvecx_q, mtvecx_d;
// CSRs for recoverable NMIs
// NOTE: these CSRS are nonstandard, see https://github.com/riscv/riscv-isa-manual/issues/261
StatusStk_t mstack_q, mstack_d;
@ -253,6 +261,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;
@ -420,6 +431,11 @@ module ibex_cs_registers #(
csr_rdata_int = {cpuctrl_rdata};
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
@ -451,6 +467,9 @@ module ibex_cs_registers #(
mhpmcounter_we = '0;
mhpmcounterh_we = '0;
miex_d = miex_q;
mtvecx_d = csr_mtvec_init_i ? {boot_addr_i[31:8], 6'b0, 2'b01} : mtvecx_q;
if (csr_we_int) begin
unique case (csr_addr_i)
// mstatus: IE bit
@ -547,6 +566,14 @@ module ibex_cs_registers #(
mhpmcounterh_we[mhpmcounter_idx] = 1'b1;
end
// CLINTx
CSR_MIEX: miex_d = csr_wdata_int;
// mtvecx
// mtvecx.MODE set to vectored
// mtvecx.BASE must be 256-byte aligned
CSR_MTVECX: mtvecx_d = {csr_wdata_int[31:8], 6'b0, 2'b01};
default:;
endcase
end
@ -656,7 +683,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;
// actual registers
always_ff @(posedge clk_i or negedge rst_ni) begin
@ -692,6 +726,9 @@ module ibex_cs_registers #(
mstack_epc_q <= '0;
mstack_cause_q <= '0;
miex_q <= '0;
mtvecx_q <= 32'b01;
end else begin
// update CSRs
priv_lvl_q <= priv_lvl_d;
@ -711,6 +748,8 @@ module ibex_cs_registers #(
mstack_epc_q <= mstack_epc_d;
mstack_cause_q <= mstack_cause_d;
miex_q <= miex_d;
mtvecx_q <= mtvecx_d;
end
end

View file

@ -124,6 +124,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,
@ -559,6 +562,9 @@ module ibex_id_stage #(
.csr_mtval_o ( csr_mtval_o ),
.priv_mode_i ( priv_mode_i ),
.csr_mstatus_tw_i ( csr_mstatus_tw_i ),
.irqs_x_i ( irqs_x_i ),
.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_o ),

View file

@ -70,6 +70,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
@ -105,9 +106,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};
@ -118,6 +121,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

@ -198,9 +198,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;
@ -327,6 +328,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,