irq_subsystem: Move interrupts to id_stage

This make execution more deterministic as we can decide early
whether the remaining pipeline is speculative or not. Furthermore it
removes a couple of logic gates during commit and clean-s up the tight
integration between `commit_stage` and `csr_regfile` which often lead
to combinational loops.

This patch is in preparation to fixing speculative reads to I/O regions.
This commit is contained in:
Florian Zaruba 2019-04-07 12:40:14 +02:00
parent f2b188776f
commit ab2b75a3ff
7 changed files with 202 additions and 203 deletions

View file

@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Changed
- Re-work interrupt and debug subsystem to associate requests during decode. This improves stability on for non-idempotent loads.
- Fix RISC-V PK simulation bug caused due to insufficient time to init the `a0` and `a1` registers via the bootrom
- Fix bug in wt_axi_adapter (only appeared when dcache lines were wider than icache lines)
- Fix potentially long timing path in `axi_lite_interface`

View file

@ -267,6 +267,19 @@ package ariane_pkg;
localparam EXC_OFF_RST = 8'h80;
localparam SupervisorIrq = 1;
localparam MachineIrq = 0;
// All information needed to determine whether we need to associate an interrupt
// with the corresponding instruction or not.
typedef struct packed {
logic [63:0] mie;
logic [63:0] mip;
logic [63:0] mideleg;
logic sie;
logic global_enable;
} irq_ctrl_t;
// ---------------
// Cache config
// ---------------

View file

@ -171,6 +171,7 @@ module ariane #(
logic tvm_csr_id;
logic tw_csr_id;
logic tsr_csr_id;
irq_ctrl_t irq_ctrl_csr_id;
logic dcache_en_csr_nbdcache;
logic csr_write_fflags_commit_cs;
logic icache_en_csr;
@ -277,6 +278,8 @@ module ariane #(
.tvm_i ( tvm_csr_id ),
.tw_i ( tw_csr_id ),
.tsr_i ( tsr_csr_id ),
.irq_i ( irq_i ),
.irq_ctrl_i ( irq_ctrl_csr_id ),
.*
);
@ -489,6 +492,7 @@ module ariane #(
.fflags_o ( fflags_csr_commit ),
.frm_o ( frm_csr_id_issue_ex ),
.fprec_o ( fprec_csr_ex ),
.irq_ctrl_o ( irq_ctrl_csr_id ),
.ld_st_priv_lvl_o ( ld_st_priv_lvl_csr_ex ),
.en_translation_o ( enable_translation_csr_ex ),
.en_ld_st_translation_o ( en_ld_st_translation_csr_ex ),

View file

@ -84,7 +84,6 @@ module commit_stage #(
// -------------------
// write register file or commit instruction in LSU or CSR Buffer
always_comb begin : commit
// default assignments
commit_ack_o[0] = 1'b0;
commit_ack_o[1] = 1'b0;
@ -109,99 +108,85 @@ module commit_stage #(
// we will not commit the instruction if we took an exception
// and we do not commit the instruction if we requested a halt
// furthermore if the debugger is requesting to debug do not commit this instruction if we are not yet in debug mode
// also check that there is no atomic memory operation committing, right now this is the only operation
// which will take longer than one cycle to commit
if (commit_instr_i[0].valid && !halt_i) begin
// we have to exclude the AMOs from debug mode as we are not jumping to debug
// while committing an AMO
if (!debug_req_i || debug_mode_i) begin
if (commit_instr_i[0].valid && !commit_instr_i[0].ex.valid && !halt_i) begin
// we can definitely write the register file
// if the instruction is not committing anything the destination
commit_ack_o[0] = 1'b1;
if (is_rd_fpr(commit_instr_i[0].op)) begin
we_fpr_o[0] = 1'b1;
end else begin
we_gpr_o[0] = 1'b1;
end
// check whether the instruction we retire was a store
if (commit_instr_i[0].fu == STORE && !instr_0_is_amo) begin
// check if the LSU is ready to accept another commit entry (e.g.: a non-speculative store)
if (commit_lsu_ready_i) begin
commit_ack_o[0] = 1'b1;
commit_lsu_o = 1'b1;
// stall in case the store buffer is not able to accept anymore instructions
end else begin
commit_ack_o[0] = 1'b0;
end
end
// ---------
// FPU Flags
// ---------
if (commit_instr_i[0].fu inside {FPU, FPU_VEC}) begin
// write the CSR with potential exception flags from retiring floating point instruction
csr_wdata_o = {59'b0, commit_instr_i[0].ex.cause[4:0]};
csr_write_fflags_o = 1'b1;
commit_ack_o[0] = 1'b1;
// register will be the all zero register.
// and also acknowledge the instruction, this is mainly done for the instruction tracer
// as it will listen on the instruction ack signal. For the overall result it does not make any
// difference as the whole pipeline is going to be flushed anyway.
if (!exception_o.valid) begin
// we can definitely write the register file
// if the instruction is not committing anything the destination
if (is_rd_fpr(commit_instr_i[0].op))
we_fpr_o[0] = 1'b1;
else
we_gpr_o[0] = 1'b1;
// check whether the instruction we retire was a store
// do not commit the instruction if we got an exception since the store buffer will be cleared
// by the subsequent flush triggered by an exception
if (commit_instr_i[0].fu == STORE && !instr_0_is_amo) begin
// check if the LSU is ready to accept another commit entry (e.g.: a non-speculative store)
if (commit_lsu_ready_i)
commit_lsu_o = 1'b1;
else // if the LSU buffer is not ready - do not commit, wait
commit_ack_o[0] = 1'b0;
end
// ---------
// FPU Flags
// ---------
if (commit_instr_i[0].fu inside {FPU, FPU_VEC}) begin
// write the CSR with potential exception flags from retiring floating point instruction
csr_wdata_o = {59'b0, commit_instr_i[0].ex.cause[4:0]};
csr_write_fflags_o = 1'b1;
end
end
// ---------
// CSR Logic
// ---------
// check whether the instruction we retire was a CSR instruction
// interrupts are never taken on CSR instructions
if (commit_instr_i[0].fu == CSR) begin
// write the CSR file
commit_csr_o = 1'b1;
wdata_o[0] = csr_rdata_i;
csr_op_o = commit_instr_i[0].op;
csr_wdata_o = commit_instr_i[0].result;
end
// ------------------
// SFENCE.VMA Logic
// ------------------
// sfence.vma is idempotent so we can safely re-execute it after returning
// from interrupt service routine
// check if this instruction was a SFENCE_VMA
if (commit_instr_i[0].op == SFENCE_VMA) begin
// no store pending so we can flush the TLBs and pipeline
sfence_vma_o = no_st_pending_i;
// wait for the store buffer to drain until flushing the pipeline
commit_ack_o[0] = no_st_pending_i;
end
// ------------------
// FENCE.I Logic
// ------------------
// fence.i is idempotent so we can safely re-execute it after returning
// from interrupt service routine
// Fence synchronizes data and instruction streams. That means that we need to flush the private icache
// and the private dcache. This is the most expensive instruction.
if (commit_instr_i[0].op == FENCE_I || (flush_dcache_i && commit_instr_i[0].fu != STORE)) begin
commit_ack_o[0] = no_st_pending_i;
// tell the controller to flush the I$
fence_i_o = no_st_pending_i;
end
// ------------------
// FENCE Logic
// ------------------
// fence is idempotent so we can safely re-execute it after returning
// from interrupt service routine
if (commit_instr_i[0].op == FENCE) begin
commit_ack_o[0] = no_st_pending_i;
// tell the controller to flush the D$
fence_o = no_st_pending_i;
end
// ---------
// CSR Logic
// ---------
// check whether the instruction we retire was a CSR instruction and it did not
// throw an exception
if (commit_instr_i[0].fu == CSR) begin
// write the CSR file
csr_op_o = commit_instr_i[0].op;
csr_wdata_o = commit_instr_i[0].result;
if (!csr_exception_i.valid) begin
commit_csr_o = 1'b1;
wdata_o[0] = csr_rdata_i;
commit_ack_o[0] = 1'b1;
end else begin
commit_ack_o[0] = 1'b0;
we_gpr_o[0] = 1'b0;
end
end
// ------------------
// SFENCE.VMA Logic
// ------------------
// check if this instruction was a SFENCE_VMA
if (commit_instr_i[0].op == SFENCE_VMA) begin
// no store pending so we can flush the TLBs and pipeline
sfence_vma_o = no_st_pending_i;
// wait for the store buffer to drain until flushing the pipeline
commit_ack_o[0] = no_st_pending_i;
end
// ------------------
// FENCE.I Logic
// ------------------
// Fence synchronizes data and instruction streams. That means that we need to flush the private icache
// and the private dcache. This is the most expensive instruction.
if (commit_instr_i[0].op == FENCE_I || (flush_dcache_i && commit_instr_i[0].fu != STORE)) begin
commit_ack_o[0] = no_st_pending_i;
// tell the controller to flush the I$
fence_i_o = no_st_pending_i;
end
// ------------------
// FENCE Logic
// ------------------
if (commit_instr_i[0].op == FENCE) begin
commit_ack_o[0] = no_st_pending_i;
// tell the controller to flush the D$
fence_o = no_st_pending_i;
end
// ------------------
// AMO
// ------------------
if (RVA && instr_0_is_amo && !commit_instr_i[0].ex.valid) begin
if (RVA && instr_0_is_amo) begin
// AMO finished
commit_ack_o[0] = amo_resp_i.ack;
// flush the pipeline
@ -251,7 +236,6 @@ module commit_stage #(
// -----------------------------
// Exception & Interrupt Logic
// -----------------------------
// TODO(zarubaf): Move interrupt handling to commit stage.
// here we know for sure that we are taking the exception
always_comb begin : exception_handling
// Multiple simultaneous interrupts and traps at the same privilege level are handled in the following decreasing
@ -260,45 +244,26 @@ module commit_stage #(
exception_o.valid = 1'b0;
exception_o.cause = 64'b0;
exception_o.tval = 64'b0;
// we need a valid instruction in the commit stage, otherwise we might loose the PC in case of interrupts as they
// can happen anywhere in the execution flow and might just happen between two legal instructions - the PC would then
// be outdated. The solution here is to defer any exception/interrupt until we get a valid PC again (from where we cane
// resume execution afterwards).
// we need a valid instruction in the commit stage
if (commit_instr_i[0].valid) begin
// ------------------------
// check for CSR exception
// ------------------------
if (csr_exception_i.valid && !csr_exception_i.cause[63]) begin
if (csr_exception_i.valid) begin
exception_o = csr_exception_i;
// if no earlier exception happened the commit instruction will still contain
// the instruction data from the ID stage. If a earlier exception happened we don't care
// the instruction bits from the ID stage. If a earlier exception happened we don't care
// as we will overwrite it anyway in the next IF bl
exception_o.tval = commit_instr_i[0].ex.tval;
end
// ------------------------
// Earlier Exceptions
// ------------------------
// but we give precedence to exceptions which happened earlier
// but we give precedence to exceptions which happened earlier e.g.: instruction page
// faults for example
if (commit_instr_i[0].ex.valid) begin
exception_o = commit_instr_i[0].ex;
end
// ------------------------
// Interrupts
// ------------------------
// check for CSR interrupts (e.g.: normal interrupts which get triggered here)
// by putting interrupts here we give them precedence over any other exception
// Don't take the interrupt if we are committing an AMO or a CSR.
// - Atomics because they are atomic in their nature and should not be interrupted
// - CSRs because it makes the implementation easier as CSRs are figured out at the same
// time as interrupts (here in the commit stage). By not allowing them on CSRs we
// reduce the potential critical path length. As all CSRs are single-cycle (plus a
// potential pipeline flush) this only impacts interrupt latency in a couple of cycles.
if (csr_exception_i.valid && csr_exception_i.cause[63]
&& !amo_valid_commit_o
&& commit_instr_i[0].fu != CSR) begin
exception_o = csr_exception_i;
exception_o.tval = commit_instr_i[0].ex.tval;
end
end
// Don't take any exceptions iff:
// - If we halted the processor
@ -306,5 +271,4 @@ module commit_stage #(
exception_o.valid = 1'b0;
end
end
endmodule

View file

@ -32,7 +32,7 @@ module csr_regfile #(
input logic [63:0] boot_addr_i, // Address from which to start booting, mtvec is set to the same address
input logic [63:0] hart_id_i, // Hart id in a multicore environment (reflected in a CSR)
// we are taking an exception
input exception_t ex_i, // We've got an exception from the commit stage, take its
input exception_t ex_i, // We've got an exception from the commit stage, take it
input fu_op csr_op_i, // Operation to perform on the CSR file
input logic [11:0] csr_addr_i, // Address of the register to read/write
@ -54,6 +54,8 @@ module csr_regfile #(
output logic [4:0] fflags_o, // Floating-Point Accured Exceptions
output logic [2:0] frm_o, // Floating-Point Dynamic Rounding Mode
output logic [6:0] fprec_o, // Floating-Point Precision Control
// Decoder
output irq_ctrl_t irq_ctrl_o, // interrupt management to id stage
// MMU
output logic en_translation_o, // enable VA translation
output logic en_ld_st_translation_o, // enable VA translation for load and stores
@ -83,7 +85,7 @@ module csr_regfile #(
output logic perf_we_o
);
// internal signal to keep track of access exceptions
logic read_access_exception, update_access_exception;
logic read_access_exception, update_access_exception, privilege_violation;
logic csr_we, csr_read;
logic [63:0] csr_wdata, csr_rdata;
riscv::priv_lvl_t trap_to_priv_lvl;
@ -785,96 +787,50 @@ module csr_regfile #(
csr_read = 1'b0;
end
endcase
// if we are retiring an exception do not return from exception
if (ex_i.valid) begin
mret = 1'b0;
sret = 1'b0;
dret = 1'b0;
// if we are violating our privilges do not update the architectural state
if (privilege_violation) begin
csr_we = 1'b0;
csr_read = 1'b0;
end
end
logic interrupt_global_enable;
// --------------------------------------
// Exception Control & Interrupt Control
// --------------------------------------
always_comb begin : exception_ctrl
automatic logic [63:0] interrupt_cause;
interrupt_cause = '0;
// wait for interrupt register
wfi_d = wfi_q;
csr_exception_o = {
64'b0, 64'b0, 1'b0
};
// -----------------
// Interrupt Control
// -----------------
// TODO(zarubaf): Move interrupt handling to commit stage.
// we decode an interrupt the same as an exception, hence it will be taken if the instruction did not
// throw any previous exception.
// we have three interrupt sources: external interrupts, software interrupts, timer interrupts (order of precedence)
// for two privilege levels: Supervisor and Machine Mode
// Supervisor Timer Interrupt
if (mie_q[riscv::S_TIMER_INTERRUPT[5:0]] && mip_q[riscv::S_TIMER_INTERRUPT[5:0]])
interrupt_cause = riscv::S_TIMER_INTERRUPT;
// Supervisor Software Interrupt
if (mie_q[riscv::S_SW_INTERRUPT[5:0]] && mip_q[riscv::S_SW_INTERRUPT[5:0]])
interrupt_cause = riscv::S_SW_INTERRUPT;
// Supervisor External Interrupt
// The logical-OR of the software-writable bit and the signal from the external interrupt controller is
// used to generate external interrupts to the supervisor
if (mie_q[riscv::S_EXT_INTERRUPT[5:0]] && (mip_q[riscv::S_EXT_INTERRUPT[5:0]] | irq_i[1]))
interrupt_cause = riscv::S_EXT_INTERRUPT;
// Machine Timer Interrupt
if (mip_q[riscv::M_TIMER_INTERRUPT[5:0]] && mie_q[riscv::M_TIMER_INTERRUPT[5:0]])
interrupt_cause = riscv::M_TIMER_INTERRUPT;
// Machine Mode Software Interrupt
if (mip_q[riscv::M_SW_INTERRUPT[5:0]] && mie_q[riscv::M_SW_INTERRUPT[5:0]])
interrupt_cause = riscv::M_SW_INTERRUPT;
// Machine Mode External Interrupt
if (mip_q[riscv::M_EXT_INTERRUPT[5:0]] && mie_q[riscv::M_EXT_INTERRUPT[5:0]])
interrupt_cause = riscv::M_EXT_INTERRUPT;
// An interrupt i will be taken if bit i is set in both mip and mie, and if interrupts are globally enabled.
// By default, M-mode interrupts are globally enabled if the harts current privilege mode is less
// than M, or if the current privilege mode is M and the MIE bit in the mstatus register is set.
// All interrupts are masked in debug mode
interrupt_global_enable = (~debug_mode_q)
// interrupts are enabled during single step or we are not stepping
& (~dcsr_q.step | dcsr_q.stepie)
& ((mstatus_q.mie & (priv_lvl_o == riscv::PRIV_LVL_M))
| (priv_lvl_o != riscv::PRIV_LVL_M));
if (interrupt_cause[63] && interrupt_global_enable) begin
// we can set the cause here
csr_exception_o.cause = interrupt_cause;
// However, if bit i in mideleg is set, interrupts are considered to be globally enabled if the harts current privilege
// mode equals the delegated privilege mode (S or U) and that modes interrupt enable bit
// (SIE or UIE in mstatus) is set, or if the current privilege mode is less than the delegated privilege mode.
if (mideleg_q[interrupt_cause[5:0]]) begin
if ((mstatus_q.sie && priv_lvl_o == riscv::PRIV_LVL_S) || priv_lvl_o == riscv::PRIV_LVL_U)
csr_exception_o.valid = 1'b1;
end else begin
csr_exception_o.valid = 1'b1;
end
end
assign irq_ctrl_o.mie = mie_q;
assign irq_ctrl_o.mip = mip_q;
assign irq_ctrl_o.sie = mstatus_q.sie;
assign irq_ctrl_o.mideleg = mideleg_q;
assign irq_ctrl_o.global_enable = (~debug_mode_q)
// interrupts are enabled during single step or we are not stepping
& (~dcsr_q.step | dcsr_q.stepie)
& ((mstatus_q.mie & (priv_lvl_o == riscv::PRIV_LVL_M))
| (priv_lvl_o != riscv::PRIV_LVL_M));
always_comb begin : privilege_check
// -----------------
// Privilege Check
// -----------------
privilege_violation = 1'b0;
// if we are reading or writing, check for the correct privilege level this has
// precedence over interrupts
if (csr_we || csr_read) begin
if (csr_op_i inside {CSR_WRITE, CSR_SET, CSR_CLEAR, CSR_READ}) begin
if ((riscv::priv_lvl_t'(priv_lvl_o & csr_addr.csr_decode.priv_lvl) != csr_addr.csr_decode.priv_lvl)) begin
csr_exception_o.cause = riscv::ILLEGAL_INSTR;
csr_exception_o.valid = 1'b1;
privilege_violation = 1'b1;
end
// check access to debug mode only CSRs
if (csr_addr_i[11:4] == 8'h7b && !debug_mode_q) begin
csr_exception_o.cause = riscv::ILLEGAL_INSTR;
csr_exception_o.valid = 1'b1;
privilege_violation = 1'b1;
end
end
end
// ----------------------
// CSR Exception Control
// ----------------------
always_comb begin : exception_ctrl
csr_exception_o = {
64'b0, 64'b0, 1'b0
};
// ----------------------------------
// Illegal Access (decode exception)
// ----------------------------------
// we got an exception in one of the processes above
// throw an illegal instruction exception
if (update_access_exception || read_access_exception) begin
@ -883,9 +839,19 @@ module csr_regfile #(
// this spares the extra wiring from commit to CSR and back to commit
csr_exception_o.valid = 1'b1;
end
// -------------------
// Wait for Interrupt
// -------------------
if (privilege_violation) begin
csr_exception_o.cause = riscv::ILLEGAL_INSTR;
csr_exception_o.valid = 1'b1;
end
end
// -------------------
// Wait for Interrupt
// -------------------
always_comb begin : wfi_ctrl
// wait for interrupt register
wfi_d = wfi_q;
// if there is any interrupt pending un-stall the core
// also un-stall if we want to enter debug mode
if (|mip_q || debug_req_i || irq_i[1]) begin
@ -913,8 +879,8 @@ module csr_regfile #(
// check if we are in vectored mode, if yes then do BASE + 4 * cause
// we are imposing an additional alignment-constraint of 64 * 4 bytes since
// we want to spare the costly addition
if ((mtvec_q[0] || stvec_q[0]) && csr_exception_o.cause[63]) begin
trap_vector_base_o[7:2] = csr_exception_o.cause[5:0];
if ((mtvec_q[0] || stvec_q[0]) && ex_i.cause[63]) begin
trap_vector_base_o[7:2] = ex_i.cause[5:0];
end
epc_o = mepc_q;

View file

@ -28,6 +28,8 @@ module decoder (
input logic [31:0] instruction_i, // instruction from IF
input branchpredict_sbe_t branch_predict_i,
input exception_t ex_i, // if an exception occured in if
input logic [1:0] irq_i, // external interrupt
input irq_ctrl_t irq_ctrl_i, // interrupt control and status information from CSRs
// From CSR
input riscv::priv_lvl_t priv_lvl_i, // current privilege level
input logic debug_mode_i, // we are in debug mode
@ -1048,9 +1050,14 @@ module decoder (
// ---------------------
// Exception handling
// ---------------------
logic [63:0] interrupt_cause;
// this instruction has already executed if the exception is valid
assign instruction_o.valid = instruction_o.ex.valid;
always_comb begin : exception_handling
interrupt_cause = '0;
instruction_o.ex = ex_i;
instruction_o.valid = ex_i.valid;
// look if we didn't already get an exception in any previous
// stage - we should not overwrite it as we retain order regarding the exception
if (~ex_i.valid) begin
@ -1062,14 +1069,11 @@ module decoder (
// check here if we decoded an invalid instruction or if the compressed decoder already decoded
// a invalid instruction
if (illegal_instr || is_illegal_i) begin
instruction_o.valid = 1'b1;
instruction_o.ex.valid = 1'b1;
// we decoded an illegal exception here
instruction_o.ex.cause = riscv::ILLEGAL_INSTR;
// we got an ecall, set the correct cause depending on the current privilege level
end else if (ecall) begin
// this instruction has already executed
instruction_o.valid = 1'b1;
// this exception is valid
instruction_o.ex.valid = 1'b1;
// depending on the privilege mode, set the appropriate cause
@ -1080,13 +1084,59 @@ module decoder (
default:; // this should not happen
endcase
end else if (ebreak) begin
// this instruction has already executed
instruction_o.valid = 1'b1;
// this exception is valid
instruction_o.ex.valid = 1'b1;
// set breakpoint cause
instruction_o.ex.cause = riscv::BREAKPOINT;
end
// -----------------
// Interrupt Control
// -----------------
// we decode an interrupt the same as an exception, hence it will be taken if the instruction did not
// throw any previous exception.
// we have three interrupt sources: external interrupts, software interrupts, timer interrupts (order of precedence)
// for two privilege levels: Supervisor and Machine Mode
// Supervisor Timer Interrupt
if (irq_ctrl_i.mie[riscv::S_TIMER_INTERRUPT[5:0]] && irq_ctrl_i.mip[riscv::S_TIMER_INTERRUPT[5:0]]) begin
interrupt_cause = riscv::S_TIMER_INTERRUPT;
end
// Supervisor Software Interrupt
if (irq_ctrl_i.mie[riscv::S_SW_INTERRUPT[5:0]] && irq_ctrl_i.mip[riscv::S_SW_INTERRUPT[5:0]]) begin
interrupt_cause = riscv::S_SW_INTERRUPT;
end
// Supervisor External Interrupt
// The logical-OR of the software-writable bit and the signal from the external interrupt controller is
// used to generate external interrupts to the supervisor
if (irq_ctrl_i.mie[riscv::S_EXT_INTERRUPT[5:0]] && (irq_ctrl_i.mip[riscv::S_EXT_INTERRUPT[5:0]] | irq_i[ariane_pkg::SupervisorIrq])) begin
interrupt_cause = riscv::S_EXT_INTERRUPT;
end
// Machine Timer Interrupt
if (irq_ctrl_i.mip[riscv::M_TIMER_INTERRUPT[5:0]] && irq_ctrl_i.mie[riscv::M_TIMER_INTERRUPT[5:0]]) begin
interrupt_cause = riscv::M_TIMER_INTERRUPT;
end
// Machine Mode Software Interrupt
if (irq_ctrl_i.mip[riscv::M_SW_INTERRUPT[5:0]] && irq_ctrl_i.mie[riscv::M_SW_INTERRUPT[5:0]]) begin
interrupt_cause = riscv::M_SW_INTERRUPT;
end
// Machine Mode External Interrupt
if (irq_ctrl_i.mip[riscv::M_EXT_INTERRUPT[5:0]] && irq_ctrl_i.mie[riscv::M_EXT_INTERRUPT[5:0]]) begin
interrupt_cause = riscv::M_EXT_INTERRUPT;
end
if (interrupt_cause[63] && irq_ctrl_i.global_enable) begin
// However, if bit i in mideleg is set, interrupts are considered to be globally enabled if the harts current privilege
// mode equals the delegated privilege mode (S or U) and that modes interrupt enable bit
// (SIE or UIE in mstatus) is set, or if the current privilege mode is less than the delegated privilege mode.
if (irq_ctrl_i.mideleg[interrupt_cause[5:0]]) begin
if ((irq_ctrl_i.sie && priv_lvl_i == riscv::PRIV_LVL_S) || priv_lvl_i == riscv::PRIV_LVL_U) begin
instruction_o.ex.valid = 1'b1;
instruction_o.ex.cause = interrupt_cause;
end
end else begin
instruction_o.ex.valid = 1'b1;
instruction_o.ex.cause = interrupt_cause;
end
end
end
end
endmodule

View file

@ -34,7 +34,8 @@ module id_stage (
input riscv::priv_lvl_t priv_lvl_i, // current privilege level
input riscv::xs_t fs_i, // floating point extension status
input logic [2:0] frm_i, // floating-point dynamic rounding mode
input logic [1:0] irq_i,
input irq_ctrl_t irq_ctrl_i,
input logic debug_mode_i, // we are in debug mode
input logic tvm_i,
input logic tw_i,