mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-22 13:17:41 -04:00
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:
parent
f2b188776f
commit
ab2b75a3ff
7 changed files with 202 additions and 203 deletions
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
// ---------------
|
||||
|
|
|
@ -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 ),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 hart’s 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 hart’s current privilege
|
||||
// mode equals the delegated privilege mode (S or U) and that mode’s 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;
|
||||
|
|
|
@ -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 hart’s current privilege
|
||||
// mode equals the delegated privilege mode (S or U) and that mode’s 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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue