Implement new IRQ behaviour, and change mip.meip to be masked by individual enables in meip0
This commit is contained in:
parent
4053458485
commit
5f8d217395
|
@ -1 +0,0 @@
|
|||
*.pdf
|
File diff suppressed because it is too large
Load Diff
|
@ -72,9 +72,9 @@ External IRQ pending register 0. Contains a read-only bit for each external inte
|
|||
|
||||
Addresses `0xfe1` through `0xfe3` are reserved for further `meip` registers, supporting up to 128 external interrupts.
|
||||
|
||||
When any bit is set in `meip0`, the standard external interrupt pending bit `mip.meip` is also set. An external interrupt is taken when all of the following are true:
|
||||
When any bit is set in both `meip0` and `meie0`, the standard external interrupt pending bit `mip.meip` is also set. In other words, `meip0` is filtered by `meie0` to generate the standard `mip.meip` flag. So, an external interrupt is taken when _all_ of the following are true:
|
||||
|
||||
* The interrupt is currently asserted in `meip0`
|
||||
* An interrupt is currently asserted in `meip0`
|
||||
* The matching interrupt enable bit is set in `meie0`
|
||||
* The standard M-mode interrupt enable `mstatus.mie` is set
|
||||
* The standard M-mode global external interrupt enable `mie.meie` is set
|
||||
|
@ -84,7 +84,7 @@ In this case, the processor jumps to either:
|
|||
* `mtvec` directly, if vectoring is disabled (`mtvec[0]` is 0)
|
||||
* `mtvec + 0x2c`, if vectoring is enabled (`mtvec[0]` is 1) and modified external IRQ vectoring is disabled (`midcr.eivect` is 0)
|
||||
* `mtvect + (mlei + 16) * 4`, if vectoring is enabled (`mtvec[0]` is 1) and modified external IRQ vectoring is enabled (`midcr.eivect` is 1). `
|
||||
** `mlei` is a read-only CSR containing the lowest-numbered
|
||||
** `mlei` is a read-only CSR containing the lowest-numbered pending-and-enabled external interrupt.
|
||||
|
||||
==== mlei
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
// your top-level instantiation, it's up to you. These parameters are all
|
||||
// plumbed through Hazard3's internal hierarchy to the appropriate places.
|
||||
|
||||
// If you add a parameter here, you should add a matching line to
|
||||
// hazard3_config_inst.vh to propagate the parameter through module
|
||||
// instantiations.
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Reset state configuration
|
||||
|
||||
|
@ -41,7 +45,11 @@ parameter CSR_M_MANDATORY = 1,
|
|||
parameter CSR_M_TRAP = 1,
|
||||
|
||||
// CSR_COUNTER: Include performance counters and relevant M-mode CSRs
|
||||
parameter CSR_COUNTER = 0,
|
||||
parameter CSR_COUNTER = 1,
|
||||
|
||||
// NUM_IRQ: Number of external IRQs implemented in meie0 and meip0.
|
||||
// Minimum 1 (if CSR_M_TRAP = 1), maximum 32.
|
||||
parameter NUM_IRQ = 32,
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ID registers
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
.CSR_M_MANDATORY (CSR_M_MANDATORY),
|
||||
.CSR_M_TRAP (CSR_M_TRAP),
|
||||
.CSR_COUNTER (CSR_COUNTER),
|
||||
.NUM_IRQ (NUM_IRQ),
|
||||
.MVENDORID_VAL (MVENDORID_VAL),
|
||||
.MARCHID_VAL (MARCHID_VAL),
|
||||
.MIMPID_VAL (MIMPID_VAL),
|
||||
.REDUCED_BYPASS (REDUCED_BYPASS),
|
||||
.MULDIV_UNROLL (MULDIV_UNROLL),
|
||||
.MUL_FAST (MUL_FAST),
|
||||
|
|
|
@ -53,8 +53,10 @@ module hazard3_core #(
|
|||
output reg [W_DATA-1:0] bus_wdata_d,
|
||||
input wire [W_DATA-1:0] bus_rdata_d,
|
||||
|
||||
// External level-sensitive interrupt sources (tie 0 if unused)
|
||||
input wire [15:0] irq
|
||||
// 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
|
||||
);
|
||||
|
||||
`include "hazard3_ops.vh"
|
||||
|
@ -493,6 +495,8 @@ hazard3_csr #(
|
|||
// IRQ and exception requests
|
||||
.delay_irq_entry (xm_delay_irq_entry),
|
||||
.irq (irq),
|
||||
.irq_software (soft_irq),
|
||||
.irq_timer (timer_irq),
|
||||
.except (xm_except),
|
||||
|
||||
// Other CSR-specific signalling
|
||||
|
|
|
@ -43,8 +43,10 @@ module hazard3_cpu_1port #(
|
|||
output wire [W_DATA-1:0] ahblm_hwdata,
|
||||
input wire [W_DATA-1:0] ahblm_hrdata,
|
||||
|
||||
// External level-sensitive interrupt sources (tie 0 if unused)
|
||||
input wire [15:0] irq
|
||||
// Level-sensitive interrupt sources
|
||||
input wire [NUM_IRQ-1:0] irq, // -> mip.meip
|
||||
input wire irq_software, // -> mip.msip
|
||||
input wire irq_timer // -> mip.mtip
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -104,7 +106,9 @@ hazard3_core #(
|
|||
.bus_wdata_d (core_wdata_d),
|
||||
.bus_rdata_d (core_rdata_d),
|
||||
|
||||
.irq (irq)
|
||||
.irq (irq),
|
||||
.soft_irq (soft_irq),
|
||||
.timer_irq (timer_irq)
|
||||
);
|
||||
|
||||
|
||||
|
|
|
@ -56,8 +56,10 @@ module hazard3_cpu_2port #(
|
|||
output wire [W_DATA-1:0] d_hwdata,
|
||||
input wire [W_DATA-1:0] d_hrdata,
|
||||
|
||||
// External level-sensitive interrupt sources (tie 0 if unused)
|
||||
input wire [15:0] irq
|
||||
// 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
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -117,7 +119,9 @@ hazard3_core #(
|
|||
.bus_wdata_d (core_wdata_d),
|
||||
.bus_rdata_d (core_rdata_d),
|
||||
|
||||
.irq (irq)
|
||||
.irq (irq),
|
||||
.soft_irq (soft_irq),
|
||||
.timer_irq (timer_irq)
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
|
@ -70,10 +70,14 @@ module hazard3_csr #(
|
|||
input wire [XLEN-1:0] mepc_in,
|
||||
|
||||
// Exceptions must *not* be a function of bus stall.
|
||||
input wire delay_irq_entry,
|
||||
input wire [15:0] irq,
|
||||
input wire [W_EXCEPT-1:0] except,
|
||||
|
||||
// Level-sensitive interrupt sources
|
||||
input wire delay_irq_entry,
|
||||
input wire [NUM_IRQ-1:0] irq,
|
||||
input wire irq_software,
|
||||
input wire irq_timer,
|
||||
|
||||
// Other CSR-specific signalling
|
||||
input wire instr_ret
|
||||
);
|
||||
|
@ -215,6 +219,12 @@ localparam MHPMEVENT29 = 12'h33d; // WARL (we tie to 0)
|
|||
localparam MHPMEVENT30 = 12'h33e; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT31 = 12'h33f; // WARL (we tie to 0)
|
||||
|
||||
// Custom M-mode CSRs:
|
||||
localparam MIDCR = 12'hbc0; // Implementation-defined control register (bag of bits)
|
||||
localparam MEIE0 = 12'hbe0; // External interrupt enable register 0
|
||||
localparam MEIP0 = 12'hfe0; // External interrupt pending register 0
|
||||
localparam MLEI = 12'hfe4; // Lowest external interrupt number
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// CSR state + update logic
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -231,8 +241,38 @@ begin
|
|||
end
|
||||
endfunction
|
||||
|
||||
function [XLEN-1:0] update_nonconst;
|
||||
input [XLEN-1:0] prev;
|
||||
input [XLEN-1:0] nonconst;
|
||||
begin
|
||||
update_nonconst = ((
|
||||
wtype == CSR_WTYPE_C ? prev & ~wdata :
|
||||
wtype == CSR_WTYPE_S ? prev | wdata :
|
||||
wdata) & nonconst) | (prev & ~nonconst) ;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Trap-handling
|
||||
// Implementation-defined control register
|
||||
|
||||
localparam MIDCR_INIT = X0;
|
||||
localparam MIDCR_WMASK = 32'h00000001;
|
||||
|
||||
reg [XLEN-1:0] midcr;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
midcr <= MIDCR_INIT;
|
||||
end else if (wen && addr == MIDCR) begin
|
||||
midcr <= update_nonconst(midcr, MIDCR_WMASK);
|
||||
end
|
||||
end
|
||||
|
||||
// Modified external interrupt vectoring
|
||||
wire midcr_eivect = midcr[0];
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Trap-handling CSRs
|
||||
|
||||
// Two-level interrupt enable stack, shuffled on entry/exit:
|
||||
reg mstatus_mpie;
|
||||
|
@ -273,7 +313,7 @@ end
|
|||
|
||||
// Trap vector base
|
||||
reg [XLEN-1:0] mtvec_reg;
|
||||
wire [XLEN-1:0] mtvec = ((mtvec_reg & MTVEC_WMASK) | (MTVEC_INIT & ~MTVEC_WMASK)) & ({XLEN{1'b1}} << 2);
|
||||
wire [XLEN-1:0] mtvec = mtvec_reg & ({XLEN{1'b1}} << 2);
|
||||
wire irq_vector_enable = mtvec_reg[0];
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
|
@ -281,7 +321,7 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
mtvec_reg <= MTVEC_INIT;
|
||||
end else if (CSR_M_TRAP) begin
|
||||
if (wen && addr == MTVEC)
|
||||
mtvec_reg <= update(mtvec_reg);
|
||||
mtvec_reg <= update_nonconst(mtvec_reg, MTVEC_WMASK);
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -304,21 +344,18 @@ end
|
|||
|
||||
// Interrupt enable (reserved bits are tied to 0)
|
||||
reg [XLEN-1:0] mie;
|
||||
localparam MIE_CONST_MASK = 32'h0000f777;
|
||||
localparam MIE_WMASK = 32'h00000888; // meie, mtip, msip
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
mie <= X0;
|
||||
end else if (CSR_M_TRAP) begin
|
||||
if (wen && addr == MIE)
|
||||
mie <= update(mie) & ~MIE_CONST_MASK;
|
||||
mie <= update_nonconst(mie, MIE_WMASK);
|
||||
end
|
||||
end
|
||||
|
||||
wire [15:0] mie_irq = mie[31:16]; // Per-IRQ mask. Nonstandard, but legal.
|
||||
wire mie_meie = mie[11]; // Global external IRQ enable. This is ANDed over our per-IRQ mask
|
||||
wire mie_mtie = mie[7]; // Timer interrupt enable
|
||||
wire mie_msie = mie[3]; // Software interrupt enable
|
||||
wire mie_meie = mie[11];
|
||||
|
||||
// Interrupt status ("pending") register, handled later
|
||||
wire [XLEN-1:0] mip;
|
||||
|
@ -329,27 +366,47 @@ wire [XLEN-1:0] mip;
|
|||
// 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)
|
||||
reg mcause_irq;
|
||||
reg [4:0] mcause_code;
|
||||
reg [5:0] mcause_code;
|
||||
wire mcause_irq_next;
|
||||
wire [4:0] mcause_code_next;
|
||||
wire [5:0] mcause_code_next;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
mcause_irq <= 1'b0;
|
||||
mcause_code <= 5'h0;
|
||||
mcause_code <= 6'h0;
|
||||
end else if (CSR_M_TRAP) begin
|
||||
if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET) begin
|
||||
mcause_irq <= mcause_irq_next;
|
||||
mcause_code <= mcause_code_next;
|
||||
end else if (wen && addr == MCAUSE) begin
|
||||
{mcause_irq, mcause_code} <=
|
||||
wtype == CSR_WTYPE_C ? {mcause_irq, mcause_code} & ~{wdata[31], wdata[4:0]} :
|
||||
wtype == CSR_WTYPE_S ? {mcause_irq, mcause_code} | {wdata[31], wdata[4:0]} :
|
||||
{wdata[31], wdata[4:0]} ;
|
||||
wtype == CSR_WTYPE_C ? {mcause_irq, mcause_code} & ~{wdata[31], wdata[5:0]} :
|
||||
wtype == CSR_WTYPE_S ? {mcause_irq, mcause_code} | {wdata[31], wdata[5:0]} :
|
||||
{wdata[31], wdata[5:0]} ;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Custom external interrupt enable register (would be at top of mie, but that
|
||||
// only leaves room for 16 external interrupts)
|
||||
|
||||
localparam MEIE0_WMASK = ~({XLEN{1'b1}} << NUM_IRQ);
|
||||
|
||||
reg [XLEN-1:0] meie0;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
// All-ones for implemented IRQs
|
||||
meie0 <= MEIE0_WMASK;
|
||||
end else if (wen && addr == MEIE0) begin
|
||||
meie0 <= update_nonconst(meie0, MEIE0_WMASK);
|
||||
end
|
||||
end
|
||||
|
||||
// Assigned later:
|
||||
wire [XLEN-1:0] meip0;
|
||||
wire [4:0] mlei;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Counters
|
||||
|
||||
|
@ -517,6 +574,7 @@ always @ (*) begin
|
|||
end
|
||||
|
||||
MIP: if (CSR_M_TRAP) begin
|
||||
// Writes are permitted, but ignored.
|
||||
decode_match = 1'b1;
|
||||
rdata = mip;
|
||||
end
|
||||
|
@ -652,6 +710,29 @@ always @ (*) begin
|
|||
rdata = minstreth;
|
||||
end
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Custom CSRs
|
||||
|
||||
MIDCR: if (CSR_M_TRAP) begin
|
||||
decode_match = 1'b1;
|
||||
rdata = midcr;
|
||||
end
|
||||
|
||||
MEIE0: if (CSR_M_TRAP) begin
|
||||
decode_match = 1'b1;
|
||||
rdata = meie0;
|
||||
end
|
||||
|
||||
MEIP0: if (CSR_M_TRAP) begin
|
||||
decode_match = !wen_soon;
|
||||
rdata = meip0;
|
||||
end
|
||||
|
||||
MLEI: if (CSR_M_TRAP) begin
|
||||
decode_match = !wen_soon;
|
||||
rdata = {{XLEN-5{1'b0}}, mlei};
|
||||
end
|
||||
|
||||
default: begin end
|
||||
endcase
|
||||
end
|
||||
|
@ -660,53 +741,74 @@ assign illegal = (wen_soon || ren_soon) && !decode_match;
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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
|
||||
|
||||
assign meip0 = {{XLEN-NUM_IRQ{1'b0}}, irq_r};
|
||||
wire external_irq_pending = |(meie0 & meip0);
|
||||
|
||||
assign mip = {
|
||||
20'h0, // Reserved
|
||||
external_irq_pending, // meip, Global pending bit for external IRQs
|
||||
3'h0, // Reserved
|
||||
irq_timer_r, // mtip, interrupt from memory-mapped timer peripheral
|
||||
3'h0, // Reserved
|
||||
irq_software_r, // msip, software interrupt from memory-mapped register
|
||||
3'h0 // Reserved
|
||||
};
|
||||
|
||||
// When eivect = 1, mip.meip is masked from the standard IRQs, so that the
|
||||
// platform-specific causes and vectors are used instead.
|
||||
wire [31:0] mip_no_global = mip & ~(32'h800 & ~{XLEN{midcr_eivect}});
|
||||
wire standard_irq_active = |(mip_no_global & mie) && mstatus_mie;
|
||||
wire external_irq_active = external_irq_pending && mstatus_mie && mie_meie;
|
||||
|
||||
wire [4:0] external_irq_num;
|
||||
wire [3:0] standard_irq_num;
|
||||
assign mlei = external_irq_num;
|
||||
|
||||
hazard3_priority_encode #(
|
||||
.W_REQ (32)
|
||||
) mlei_priority_encode (
|
||||
.req (meie0 & meip0),
|
||||
.gnt (external_irq_num)
|
||||
);
|
||||
|
||||
hazard3_priority_encode #(
|
||||
.W_REQ (16)
|
||||
) irq_priority (
|
||||
.req (mip_no_global[15:0] & mie[15:0]),
|
||||
.gnt (standard_irq_num)
|
||||
);
|
||||
|
||||
wire exception_req_any = except != EXCEPT_NONE;
|
||||
|
||||
// Interrupt masking and selection
|
||||
wire [5:0] vector_sel =
|
||||
exception_req_any || !irq_vector_enable ? 6'd0 :
|
||||
standard_irq_active ? standard_irq_num :
|
||||
external_irq_active ? 6'd16 + external_irq_num : 6'd0;
|
||||
|
||||
reg [15:0] irq_r;
|
||||
|
||||
always @ (posedge clk or negedge rst_n)
|
||||
if (!rst_n)
|
||||
irq_r <= 16'h0;
|
||||
else
|
||||
irq_r <= irq;
|
||||
|
||||
assign mip = {
|
||||
irq_r, // Our nonstandard bits for per-IRQ status
|
||||
4'h0, // Reserved
|
||||
|irq_r, // Global pending bit for external IRQs
|
||||
3'h0, // Reserved
|
||||
1'b0, // Timer (FIXME)
|
||||
3'h0, // Reserved
|
||||
1'b0, // Software interrupt (FIXME)
|
||||
3'h0 // Reserved
|
||||
};
|
||||
|
||||
// We don't actually trap the aggregate IRQ, just provide it for software info
|
||||
wire [31:0] mip_no_global = mip & 32'hffff_f7ff;
|
||||
wire irq_any = |(mip_no_global & {{16{mie_meie}}, {16{1'b1}}}) && mstatus_mie && !delay_irq_entry;
|
||||
wire [4:0] irq_num;
|
||||
|
||||
hazard3_priority_encode #(
|
||||
.W_REQ(32)
|
||||
) irq_priority (
|
||||
.req (mip_no_global),
|
||||
.gnt (irq_num)
|
||||
);
|
||||
|
||||
wire [11:0] mtvec_offs = (
|
||||
exception_req_any || !irq_vector_enable ? 12'h0 : {7'h0, irq_num}
|
||||
) << 2;
|
||||
|
||||
assign trap_addr = except == EXCEPT_MRET ? mepc : mtvec | {20'h0, mtvec_offs};
|
||||
assign trap_addr = except == EXCEPT_MRET ? mepc : mtvec | {24'h0, vector_sel, 2'h0};
|
||||
assign trap_is_irq = !exception_req_any;
|
||||
assign trap_enter_vld = CSR_M_TRAP && (exception_req_any || irq_any);
|
||||
assign trap_enter_vld = CSR_M_TRAP && (exception_req_any ||
|
||||
!delay_irq_entry && (standard_irq_active || external_irq_active));
|
||||
|
||||
assign mcause_irq_next = !exception_req_any;
|
||||
assign mcause_code_next = exception_req_any ? except : {1'b0, irq_num};
|
||||
assign mcause_code_next = exception_req_any ? {2'h0, except} : vector_sel;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -736,6 +838,9 @@ always @ (posedge clk) begin
|
|||
if (!trap_enter_rdy)
|
||||
assume(~|(irq_r & ~irq));
|
||||
|
||||
// Make sure CSR accesses are flushed
|
||||
if (trap_enter_vld && trap_enter_rdy)
|
||||
assert(!(wen || ren));
|
||||
// Something is screwed up if this happens
|
||||
if ($past(trap_enter_vld && trap_enter_rdy))
|
||||
assert(!wen);
|
||||
|
|
Loading…
Reference in New Issue