Implement new IRQ behaviour, and change mip.meip to be masked by individual enables in meip0

This commit is contained in:
Luke Wren 2021-05-31 17:54:12 +01:00
parent 4053458485
commit 5f8d217395
9 changed files with 14708 additions and 70 deletions

1
doc/.gitignore vendored
View File

@ -1 +0,0 @@
*.pdf

14510
doc/hazard3.pdf Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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