Hazard3/hdl/hazard3_csr.v

1541 lines
51 KiB
Verilog

/*****************************************************************************\
| Copyright (C) 2021-2022 Luke Wren |
| SPDX-License-Identifier: Apache-2.0 |
\*****************************************************************************/
`default_nettype none
// Control and Status Registers (CSRs)
// Also includes CSR-related logic like interrupt enable/masking,
// trap vector calculation.
module hazard3_csr #(
parameter XLEN = 32, // Must be 32
parameter W_COUNTER = 64, // This *should* be 64, but can be reduced to save gates.
// The full 64 bits is writeable, so high-word increment can
// be implemented in software, and a narrower hw counter used
`include "hazard3_config.vh"
,
`include "hazard3_width_const.vh"
) (
input wire clk,
input wire rst_n,
// Debug signalling
output reg debug_mode,
input wire dbg_req_halt,
input wire dbg_req_halt_on_reset,
input wire dbg_req_resume,
output wire dbg_instr_caught_exception,
output wire dbg_instr_caught_ebreak,
input wire [W_DATA-1:0] dbg_data0_rdata,
output wire [W_DATA-1:0] dbg_data0_wdata,
output wire dbg_data0_wen,
// Read port is combinatorial.
// Write port is synchronous, and write effects will be observed on the next clock cycle.
// The *_soon strobes are versions which the core does not gate with its stall signal.
// These are needed because:
// - Core stall is a function of bus stall
// - Illegal CSR accesses produce trap entry
// - Trap entry (not necessarily caused by CSR access) gates outgoing bus accesses
// - Through-paths from e.g. hready to htrans are problematic for timing/implementation
input wire [11:0] addr,
input wire [XLEN-1:0] wdata,
input wire wen,
input wire wen_soon, // wen will be asserted once some stall condition clears
input wire [1:0] wtype,
output reg [XLEN-1:0] rdata,
input wire ren,
input wire ren_soon, // ren will be asserted once some stall condition clears
output wire illegal,
// Trap signalling
// *We* tell the core that we are taking a trap, and where to, based on:
// - Synchronous exception inputs from the core
// - External IRQ signals
// - Masking etc based on the state of CSRs like mie
//
// We do this by raising trap_enter_vld, and keeping it raised until trap_enter_rdy
// goes high. trap_addr has the absolute value of trap target address.
// Once trap_enter_vld && _rdy, mepc_in is copied to mepc, and other trap state is set.
//
// Note that an exception input can go away, e.g. if the pipe gets flushed. In this
// case we lower trap_enter_vld.
output wire [XLEN-1:0] trap_addr,
output wire trap_is_irq,
output wire trap_is_debug_entry,
output wire trap_enter_vld,
input wire trap_enter_rdy,
// True when we are about to trap, but are waiting for an excepting or
// potentially-excepting instruction to clear M first. The instruction in X
// is suppressed, X PC does not increment but still tracks exception
// addresses.
output wire trap_enter_soon,
// We need to know about load/stores in data phase because their exception
// status is still unknown, so we fence off on them before entering debug
// mode.
input wire loadstore_dphase_pending,
input wire [XLEN-1:0] mepc_in,
output wire wfi_stall_clear,
// Each of these may be performed at a different privilege level from the others:
output wire m_mode_execution,
output wire m_mode_trap_entry,
output wire m_mode_loadstore,
// Exceptions must *not* be a function of bus stall.
input wire [W_EXCEPT-1:0] except,
input wire except_to_d_mode,
// Level-sensitive interrupt sources
input wire delay_irq_entry,
input wire [NUM_IRQS-1:0] irq,
input wire irq_software,
input wire irq_timer,
// PMP config interface
output wire [11:0] pmp_cfg_addr,
output reg pmp_cfg_wen,
output wire [W_DATA-1:0] pmp_cfg_wdata,
input wire [W_DATA-1:0] pmp_cfg_rdata,
// Trigger config interface
output wire [11:0] trig_cfg_addr,
output reg trig_cfg_wen,
output wire [W_DATA-1:0] trig_cfg_wdata,
input wire [W_DATA-1:0] trig_cfg_rdata,
output wire trig_m_en,
// Other CSR-specific signalling
output wire permit_wfi,
input wire instr_ret
);
`include "hazard3_ops.vh"
`include "hazard3_csr_addr.vh"
localparam X0 = {XLEN{1'b0}};
// ----------------------------------------------------------------------------
// CSR state + update logic
// ----------------------------------------------------------------------------
// Names are (reg)_(field)
// Generic update logic for write/set/clear of an entire CSR:
wire [XLEN-1:0] wdata_update =
wtype == CSR_WTYPE_C ? rdata & ~wdata :
wtype == CSR_WTYPE_S ? rdata | wdata : wdata;
function [XLEN-1:0] update_nonconst;
input [XLEN-1:0] prev;
input [XLEN-1:0] nonconst;
begin
update_nonconst = (wdata_update & nonconst) | (prev & ~nonconst) ;
end
endfunction
wire enter_debug_mode;
wire exit_debug_mode;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
debug_mode <= 1'b0;
end else if (DEBUG_SUPPORT) begin
debug_mode <= (debug_mode && !exit_debug_mode) || enter_debug_mode;
end
end
// Core execution state, 1 -> M-mode, 0 -> U-mode (if implemented)
reg m_mode;
wire wen_m_mode = wen && (m_mode || debug_mode);
wire ren_m_mode = ren && (m_mode || debug_mode);
// ----------------------------------------------------------------------------
// Standard trap-handling CSRs
wire debug_suppresses_trap_update = DEBUG_SUPPORT && (debug_mode || enter_debug_mode);
wire trapreg_update = trap_enter_vld && trap_enter_rdy && !debug_suppresses_trap_update;
wire trapreg_update_enter = trapreg_update && except != EXCEPT_MRET;
wire trapreg_update_exit = trapreg_update && except == EXCEPT_MRET;
reg mstatus_mpie;
reg mstatus_mie;
reg mstatus_mpp; // only MSB is implemented
reg mstatus_mprv;
reg mstatus_tw;
// Spec text from priv-1.12:
// "An MRET or SRET instruction is used to return from a trap in M-mode or
// S-mode respectively. When executing an xRET instruction, supposing xPP
// holds the value y, xIE is set to xPIE; the privilege mode is changed to
// y; xPIE is set to 1; and xPP is set to the least-privileged supported
// mode (U if U-mode is implemented, else M). If xPP=M, xRET also sets
// MPRV=0."
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
m_mode <= 1'b1;
mstatus_mpie <= 1'b0;
mstatus_mie <= 1'b0;
mstatus_mpp <= 1'b1;
mstatus_mprv <= 1'b0;
mstatus_tw <= 1'b0;
end else if (CSR_M_TRAP) begin
if (trapreg_update_exit) begin
mstatus_mpie <= 1'b1;
mstatus_mie <= mstatus_mpie;
if (U_MODE) begin
m_mode <= mstatus_mpp;
mstatus_mpp <= 1'b0;
if (!mstatus_mpp) begin
mstatus_mprv <= 1'b0;
end
end
end else if (trapreg_update_enter) begin
mstatus_mpie <= mstatus_mie;
mstatus_mie <= 1'b0;
if (U_MODE) begin
m_mode <= 1'b1;
mstatus_mpp <= m_mode;
end
end else if (wen_m_mode && addr == MSTATUS) begin
mstatus_mpie <= wdata_update[7];
mstatus_mie <= wdata_update[3];
mstatus_mprv <= wdata_update[17];
// Note only the MSB of MPP is implemented. It reads back as 11 or 00.
mstatus_mpp <= wdata_update[12] || !U_MODE;
mstatus_tw <= wdata_update[21] && U_MODE;
end else if (wen && debug_mode && addr == DCSR && U_MODE && DEBUG_SUPPORT) begin
// Debugger can change/observe core state directly through
// dcsr.prv (this doesn't affect debugger operation, as all
// operations have an effective level of M-mode whilst the core
// is halted for debug.)
m_mode <= wdata_update[1];
end
end
end
// Simply trap all U-mode WFIs if timeout bit is set
assign permit_wfi = m_mode || !mstatus_tw;
reg [XLEN-1:0] mscratch;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
mscratch <= X0;
end else if (CSR_M_TRAP) begin
if (wen_m_mode && addr == MSCRATCH)
mscratch <= wdata_update;
end
end
// Trap vector base
reg [XLEN-1:0] mtvec_reg;
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
if (!rst_n) begin
mtvec_reg <= MTVEC_INIT;
end else if (CSR_M_TRAP) begin
if (wen_m_mode && addr == MTVEC)
mtvec_reg <= update_nonconst(mtvec_reg, MTVEC_WMASK);
end
end
// Exception program counter
reg [XLEN-1:0] mepc;
// mepc only holds values aligned to instruction alignment
localparam MEPC_MASK = {{XLEN-2{1'b1}}, |EXTENSION_C, 1'b0};
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
mepc <= X0;
end else if (CSR_M_TRAP) begin
if (trapreg_update_enter) begin
mepc <= mepc_in & MEPC_MASK;
end else if (wen_m_mode && addr == MEPC) begin
mepc <= wdata_update & MEPC_MASK;
end
end
end
// Interrupt enable (reserved bits are tied to 0)
reg [XLEN-1:0] mie;
localparam MIE_WMASK = 32'h00000888; // meie, mtie, msie
wire meicontext_clearts = wen_m_mode && wtype != CSR_WTYPE_C && addr == MEICONTEXT && wdata[1];
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
mie <= X0;
end else if (CSR_M_TRAP) begin
if (wen_m_mode && addr == MIE) begin
mie <= update_nonconst(mie, MIE_WMASK);
end else if (wen_m_mode && addr == MEICONTEXT) begin
// meicontext.clearts/mtiesave/msiesave can be used to clear and
// save standard timer and soft IRQ enables, simultaneously with
// saving external interrupt context.
mie[7] <= (mie[7] || wdata_update[3]) && !meicontext_clearts;
mie[3] <= (mie[3] || wdata_update[2]) && !meicontext_clearts;
end
end
end
// Interrupt pending register (assigned later). In our implementation this
// register is entirely read-only.
wire [XLEN-1:0] mip;
// Trap cause registers. The non-constant bits can be written by software, and
// update automatically on trap entry. The `code` field need only be large
// enough to support causes that will be set by hardware, in our case 4 bits.
// (Note this is different to scause which always has a hard minimum of 5
// bits.)
reg mcause_irq;
reg [3:0] mcause_code;
wire mcause_irq_next;
wire [3:0] mcause_code_next;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
mcause_irq <= 1'b0;
mcause_code <= 4'h0;
end else if (CSR_M_TRAP) begin
if (trapreg_update_enter) begin
mcause_irq <= mcause_irq_next;
mcause_code <= mcause_code_next;
end else if (wen_m_mode && addr == MCAUSE) begin
{mcause_irq, mcause_code} <= {wdata_update[31], wdata_update[3:0]};
end
end
end
// ----------------------------------------------------------------------------
// Custom external IRQ handling CSRs
localparam MAX_IRQS = 512;
localparam [MAX_IRQS-1:0] IRQ_IMPL_MASK = ~({MAX_IRQS{1'b1}} << NUM_IRQS);
localparam [3:0] IRQ_PRIORITY_MASK = ~(4'hf >> IRQ_PRIORITY_BITS);
// Assigned later:
wire [MAX_IRQS-1:0] meipa;
wire [8:0] meinext_irq;
wire meinext_noirq;
reg [3:0] eirq_highest_priority;
reg [MAX_IRQS-1:0] meiea;
reg [MAX_IRQS-1:0] meifa;
reg [4*MAX_IRQS-1:0] meipra;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
meiea <= {MAX_IRQS{1'b0}};
meifa <= {MAX_IRQS{1'b0}};
meipra <= {4*MAX_IRQS{1'b0}};
end else begin
// Tie off unimplemented fields with a constant mask, then rely on
// further constant folding. Otherwise subsequent RTL will get a bit
// out of hand.
if (wen_m_mode && addr == MEIEA) begin
meiea[16 * wdata[4:0] +: 16] <= IRQ_IMPL_MASK[16 * wdata[4:0] +: 16] & wdata_update[31:16];
end else if (wen_m_mode && addr == MEIFA) begin
meifa[16 * wdata[4:0] +: 16] <= IRQ_IMPL_MASK[16 * wdata[4:0] +: 16] & wdata_update[31:16];
end else if (wen_m_mode && addr == MEIPRA) begin
meipra[16 * wdata[6:0] +: 16] <= {
{4{IRQ_IMPL_MASK[4 * wdata[6:0] + 3]}},
{4{IRQ_IMPL_MASK[4 * wdata[6:0] + 2]}},
{4{IRQ_IMPL_MASK[4 * wdata[6:0] + 1]}},
{4{IRQ_IMPL_MASK[4 * wdata[6:0] + 0]}}
} & {4{IRQ_PRIORITY_MASK}} & wdata_update[31:16];
end
// Clear IRQ force when the corresponding IRQ is sample from meinext
// (so that an IRQ can be posted *once* without modifying the ISR source)
if (ren_m_mode && addr == MEINEXT && !meinext_noirq) begin
meifa[meinext_irq] <= 1'b0;
end
end
end
reg [3:0] meicontext_pppreempt;
reg [3:0] meicontext_ppreempt;
reg [4:0] meicontext_preempt;
reg meicontext_noirq;
reg [8:0] meicontext_irq;
reg meicontext_mreteirq;
wire [4:0] preempt_level_next = meinext_noirq ? 5'h10 : (
(5'd1 << (4 - IRQ_PRIORITY_BITS)) + {1'b0, eirq_highest_priority}
);
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
meicontext_pppreempt <= 4'h0;
meicontext_ppreempt <= 4'h0;
meicontext_preempt <= 5'h0;
meicontext_noirq <= 1'b1;
meicontext_irq <= 9'h0;
meicontext_mreteirq <= 1'b0;
end else if (trapreg_update_enter) begin
if (mcause_irq_next && mcause_code_next == 4'hb) begin
// Priority save. Note the MSB of preempt needn't be saved since,
// when it is set, preemption is impossible, so we won't be here.
meicontext_pppreempt <= meicontext_ppreempt & IRQ_PRIORITY_MASK;
meicontext_ppreempt <= meicontext_preempt[3:0] & IRQ_PRIORITY_MASK;
// Setting preempt isn't strictly necessary, since an updating read
// of meinext ought to be performed before re-enabling IRQs via
// mstatus.mie, but it seems the least surprising thing to do:
meicontext_preempt <= preempt_level_next & {1'b1, IRQ_PRIORITY_MASK};
meicontext_mreteirq <= 1'b1;
end else begin
meicontext_mreteirq <= 1'b0;
end
end else if (trapreg_update_exit) begin
meicontext_mreteirq <= 1'b0;
if (meicontext_mreteirq) begin
// Priority restore
meicontext_pppreempt <= 4'h0;
meicontext_ppreempt <= meicontext_pppreempt & IRQ_PRIORITY_MASK;
meicontext_preempt <= {1'b0, meicontext_ppreempt & IRQ_PRIORITY_MASK};
end
end else if (wen_m_mode && addr == MEICONTEXT) begin
meicontext_pppreempt <= wdata_update[31:28] & IRQ_PRIORITY_MASK;
meicontext_ppreempt <= wdata_update[27:24] & IRQ_PRIORITY_MASK;
meicontext_preempt <= wdata_update[20:16] & {1'b1, IRQ_PRIORITY_MASK};
meicontext_noirq <= wdata_update[15];
meicontext_irq <= wdata_update[12:4];
meicontext_mreteirq <= wdata_update[0];
end else if (wen_m_mode && addr == MEINEXT && wdata_update[0]) begin
// Interrupt has been sampled, with the update request set, so update
// the context (including preemption level) appropriately.
meicontext_preempt <= preempt_level_next & {1'b1, IRQ_PRIORITY_MASK};
meicontext_noirq <= meinext_noirq;
meicontext_irq <= meinext_irq;
end
end
// ----------------------------------------------------------------------------
// External interrupt logic
// Signal to standard IRQ logic (mip etc) that at least one of the external IRQ
// signals should cause a trap to the mip.meip vector:
wire external_irq_pending;
// Register external IRQ signals (mainly to avoid a through-path from IRQs to
// bus request signals)
reg [NUM_IRQS-1:0] irq_r;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
irq_r <= {NUM_IRQS{1'b0}};
end else begin
irq_r <= irq;
end
end
// Trap request is asserted when there is an interrupt at or above our current
// preemption level. meinext displays interrupts at or above our *previous*
// preemption level: this masking helps avoid re-taking IRQs in frames that you
// have preempted.
assign meipa = {{MAX_IRQS-NUM_IRQS{1'b0}}, irq_r} | meifa;
reg [NUM_IRQS-1:0] eirq_active_above_preempt;
reg [NUM_IRQS-1:0] eirq_active_above_ppreempt;
always @ (*) begin: eirq_compare
integer i;
for (i = 0; i < NUM_IRQS; i = i + 1) begin
eirq_active_above_preempt[i] = meipa[i] && meiea[i] && {1'b0, meipra[i * 4 +: 4]} >= meicontext_preempt;
eirq_active_above_ppreempt[i] = meipa[i] && meiea[i] && meipra[i * 4 +: 4] >= meicontext_ppreempt;
end
end
assign external_irq_pending = |eirq_active_above_preempt;
assign meinext_noirq = ~|eirq_active_above_ppreempt;
// Two things remaining to calculate:
//
// - What is the IRQ number of the highest-priority pending IRQ that is above
// meicontext.ppreempt
// - What is the priority of that IRQ
//
// In the second case we can relax the calculation to ignore ppreempt, since it
// only needs to be valid if such an IRQ exists. Currently we choose to reuse
// the same priority selector (possibly longer critpath while saving area), but
// we could use a second priority selector that ignores ppreempt masking.
wire [NUM_IRQS-1:0] highest_eirq_onehot;
wire [8:0] meinext_irq_unmasked;
hazard3_onehot_priority_dynamic #(
.W_REQ (NUM_IRQS),
.N_PRIORITIES (16),
.PRIORITY_HIGHEST_WINS (1),
.TIEBREAK_HIGHEST_WINS (0)
) eirq_priority_u (
.pri (meipra[4*NUM_IRQS-1:0] & {NUM_IRQS{IRQ_PRIORITY_MASK}}),
.req (eirq_active_above_ppreempt),
.gnt (highest_eirq_onehot)
);
always @ (*) begin: get_highest_eirq_priority
integer i;
eirq_highest_priority = 4'h0;
for (i = 0; i < NUM_IRQS; i = i + 1) begin
eirq_highest_priority = eirq_highest_priority | (
meipra[4 * i +: 4] & {4{highest_eirq_onehot[i]}}
);
end
end
hazard3_onehot_encode #(
.W_REQ (NUM_IRQS),
.W_GNT (9)
) eirq_encode_u (
.req (highest_eirq_onehot),
.gnt (meinext_irq_unmasked)
);
assign meinext_irq = meinext_irq_unmasked & {9{!meinext_noirq}};
// ----------------------------------------------------------------------------
// Counters
reg mcountinhibit_cy;
reg mcountinhibit_ir;
reg [XLEN-1:0] mcycleh;
reg [XLEN-1:0] mcycle;
reg [XLEN-1:0] minstreth;
reg [XLEN-1:0] minstret;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
mcycleh <= X0;
mcycle <= X0;
minstreth <= X0;
minstret <= X0;
// Counters inhibited by default to save energy
mcountinhibit_cy <= 1'b1;
mcountinhibit_ir <= 1'b1;
end else if (CSR_COUNTER) begin
// Optionally hold the top (2 * XLEN - W_COUNTER) bits constant to
// save gates (noncompliant if enabled)
if (!(mcountinhibit_cy || debug_mode))
{mcycleh, mcycle} <= (({mcycleh, mcycle} + 1'b1) & ~({2*XLEN{1'b1}} << W_COUNTER))
| ({mcycleh, mcycle} & ({2*XLEN{1'b1}} << W_COUNTER));
if (!(mcountinhibit_ir || debug_mode) && instr_ret)
{minstreth, minstret} <= (({minstreth, minstret} + 1'b1) & ~({2*XLEN{1'b1}} << W_COUNTER))
| ({minstreth, minstret} & ({2*XLEN{1'b1}} << W_COUNTER));
if (wen_m_mode) begin
if (addr == MCYCLEH)
mcycleh <= wdata_update;
if (addr == MCYCLE)
mcycle <= wdata_update;
if (addr == MINSTRETH)
minstreth <= wdata_update;
if (addr == MINSTRET)
minstret <= wdata_update;
if (addr == MCOUNTINHIBIT) begin
{mcountinhibit_ir, mcountinhibit_cy} <= {wdata_update[2], wdata_update[0]};
end
end
end
end
// Note we still implement tm, even though the time/timeh CSRs are
// unimplemented. The tm bit provides a standard way to configure whether
// M-mode software permits the trapped access to reach the timer registers.
// (This is in fact required by the RVM-CSI platform spec.)
reg mcounteren_cy;
reg mcounteren_tm;
reg mcounteren_ir;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
mcounteren_cy <= 1'b0;
mcounteren_tm <= 1'b0;
mcounteren_ir <= 1'b0;
end else if (CSR_COUNTER && U_MODE && wen_m_mode && addr == MCOUNTEREN) begin
// Note this register only exists when U mode is implemented.
mcounteren_cy <= wdata_update[0];
mcounteren_tm <= wdata_update[1];
mcounteren_ir <= wdata_update[2];
end
end
// ----------------------------------------------------------------------------
// Debug-mode CSRs
// The following DCSR bits are read/writable as normal:
// - ebreakm (bit 15)
// - ebreaku (bit 12) if U-mode is supported
// - step (bit 2)
// The following are read-only volatile:
// - cause (bits 8:6)
// All others are hardwired constants.
reg dcsr_ebreakm;
reg dcsr_ebreaku;
reg dcsr_step;
reg [2:0] dcsr_cause;
wire [2:0] dcause_next;
localparam DCSR_CAUSE_EBREAK = 3'h1;
localparam DCSR_CAUSE_TRIGGER = 3'h2;
localparam DCSR_CAUSE_HALTREQ = 3'h3;
localparam DCSR_CAUSE_STEP = 3'h4;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
dcsr_ebreakm <= 1'b0;
dcsr_ebreaku <= 1'b0;
dcsr_step <= 1'b0;
dcsr_cause <= 3'h0;
end else if (DEBUG_SUPPORT) begin
if (debug_mode && wen && addr == DCSR) begin
{dcsr_ebreakm, dcsr_ebreaku, dcsr_step} <= {
wdata_update[15],
wdata_update[12] && U_MODE,
wdata_update[2]
};
end
if (enter_debug_mode) begin
dcsr_cause <= dcause_next;
end
end
end
reg [XLEN-1:0] dpc;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
dpc <= X0;
end else if (DEBUG_SUPPORT) begin
if (enter_debug_mode)
dpc <= mepc_in;
else if (debug_mode && wen && addr == DPC)
// 1 or 2 LSBs are hardwired to 0, depending on IALIGN.
dpc <= wdata_update & (~X0 << 2 - EXTENSION_C);
end
end
assign dbg_data0_wdata = wdata;
assign dbg_data0_wen = debug_mode && wen && addr == DMDATA0;
reg tcontrol_mte;
reg tcontrol_mpte;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
tcontrol_mpte <= 1'b0;
tcontrol_mte <= 1'b0;
end else if (wen_m_mode && addr == TCONTROL) begin
tcontrol_mpte <= wdata_update[7] && DEBUG_SUPPORT;
tcontrol_mte <= wdata_update[3] && DEBUG_SUPPORT;
end else if (DEBUG_SUPPORT && trapreg_update_enter) begin
tcontrol_mte <= 1'b0;
tcontrol_mpte <= tcontrol_mte;
end else if (DEBUG_SUPPORT && trapreg_update_exit) begin
tcontrol_mte <= tcontrol_mpte;
tcontrol_mpte <= 1'b1;
end
end
// ----------------------------------------------------------------------------
// Read port + detect addressing of unmapped CSRs
// ----------------------------------------------------------------------------
reg decode_match;
// Address match conditions -- certain CSRs are inaccessible if their
// conditions are not met:
wire match_drw = DEBUG_SUPPORT && debug_mode;
wire match_mrw = m_mode || debug_mode;
wire match_mro = (m_mode || debug_mode) && !wen_soon;
wire match_urw = U_MODE && 1'b1;
wire match_uro = U_MODE && !wen_soon;
always @ (*) begin
decode_match = 1'b0;
rdata = {XLEN{1'b0}};
pmp_cfg_wen = 1'b0;
trig_cfg_wen = 1'b0;
case (addr)
// ------------------------------------------------------------------------
// Mandatory CSRs
MISA: if (CSR_M_MANDATORY) begin
// WARL, so it is legal to be tied constant
decode_match = match_mrw;
rdata = {
2'h1, // MXL: 32-bit
{XLEN-28{1'b0}}, // WLRL
2'd0, // Z, Y, no
|{ // X is set for any custom extensions
|CSR_M_TRAP,
|EXTENSION_XH3B
},
2'd0, // V, W, no
|U_MODE,
7'd0, // T...N, no
|EXTENSION_M,
3'd0, // L...J, no
1'b1, // Integer ISA
5'd0, // H...D, no
|EXTENSION_C,
1'b0,
|EXTENSION_A
};
end
MVENDORID: if (CSR_M_MANDATORY) begin
decode_match = match_mro;
rdata = MVENDORID_VAL;
end
MARCHID: if (CSR_M_MANDATORY) begin
decode_match = match_mro;
// Hazard3's open source architecture ID
rdata = 32'd27;
end
MIMPID: if (CSR_M_MANDATORY) begin
decode_match = match_mro;
rdata = MIMPID_VAL;
end
MHARTID: if (CSR_M_MANDATORY) begin
decode_match = match_mro;
rdata = MHARTID_VAL;
end
MCONFIGPTR: if (CSR_M_MANDATORY) begin
decode_match = match_mro;
rdata = MCONFIGPTR_VAL;
end
MSTATUS: if (CSR_M_MANDATORY || CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = {
1'b0, // Never any dirty state besides GPRs
8'd0, // (WPRI)
1'b0, // TSR (Trap SRET), tied 0 if no S mode.
mstatus_tw, // TW (Timeout Wait)
1'b0, // TVM (trap virtual memory), tied 0 if no S mode.
1'b0, // MXR (Make eXecutable Readable), tied 0 if no S mode.
1'b0, // SUM, tied 0 if no S mode
mstatus_mprv, // MPRV (modify privilege)
4'd0, // XS, FS always "off" (no extension state to clear!)
{2{mstatus_mpp}}, // MPP (M-mode previous privilege), only M and U supported
2'd0, // (WPRI)
1'b0, // SPP, tied 0 if S mode not supported
mstatus_mpie, // Previous interrupt enable
3'd0, // No S, U
mstatus_mie, // Interrupt enable
3'd0 // No S, U
};
end
// MSTATUSH is all zeroes (fields are MBE and SBE, which are zero because
// we are pure little-endian.) Prior to priv-1.12 MSTATUSH could be left
// unimplemented in this case, but now it must be decoded even if
// hardwired to 0.
MSTATUSH: if (CSR_M_MANDATORY || CSR_M_TRAP) begin
decode_match = match_mrw;
end
// MEDELEG, MIDELEG should not exist for no-S-mode implementations. Will
// raise illegal instruction exception if accessed.
// MENVCFG is seemingly mandatory as of M-mode v1.12, if U-mode is
// implemented. All of its fields are tied to 0 in our implementation.
MENVCFGH: if (U_MODE) begin
decode_match = match_mrw;
end
MENVCFG: if (U_MODE) begin
decode_match = match_mrw;
end
// ------------------------------------------------------------------------
// Trap-handling CSRs
// This is a 32 bit synthesised register with set/clear/write/read, don't
// turn it on unless we really have to
MSCRATCH: if (CSR_M_TRAP && CSR_M_MANDATORY) begin
decode_match = match_mrw;
rdata = mscratch;
end
MEPC: if (CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = mepc;
end
MCAUSE: if (CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = {
mcause_irq, // Sign bit is 1 for IRQ, 0 for exception
{27{1'b0}}, // Padding
mcause_code[3:0] // Enough for 16 external IRQs, which is all we have room for in mip/mie
};
end
MTVAL: if (CSR_M_TRAP) begin
decode_match = match_mrw;
// Hardwired to 0
end
MIE: if (CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = mie;
end
MIP: if (CSR_M_TRAP) begin
// Writes are permitted, but ignored.
decode_match = match_mrw;
rdata = mip;
end
MTVEC: if (CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = {
mtvec[XLEN-1:2], // BASE
1'b0, // Reserved mode bit gets WARL'd to 0
irq_vector_enable // MODE is vectored (2'h1) or direct (2'h0)
};
end
// ------------------------------------------------------------------------
// Counter CSRs
// Get the tied WARLs out the way first
MHPMCOUNTER3: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER4: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER5: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER6: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER7: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER8: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER9: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER10: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER11: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER12: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER13: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER14: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER15: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER16: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER17: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER18: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER19: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER20: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER21: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER22: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER23: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER24: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER25: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER26: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER27: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER28: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER29: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER30: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER31: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER3H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER4H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER5H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER6H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER7H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER8H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER9H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER10H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER11H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER12H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER13H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER14H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER15H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER16H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER17H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER18H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER19H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER20H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER21H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER22H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER23H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER24H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER25H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER26H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER27H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER28H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER29H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER30H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMCOUNTER31H: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT3: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT4: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT5: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT6: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT7: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT8: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT9: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT10: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT11: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT12: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT13: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT14: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT15: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT16: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT17: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT18: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT19: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT20: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT21: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT22: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT23: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT24: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT25: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT26: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT27: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT28: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT29: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT30: if (CSR_COUNTER) begin decode_match = match_mrw; end
MHPMEVENT31: if (CSR_COUNTER) begin decode_match = match_mrw; end
MCOUNTINHIBIT: if (CSR_COUNTER) begin
decode_match = match_mrw;
rdata = {
29'd0,
mcountinhibit_ir,
1'b0,
mcountinhibit_cy
};
end
MCYCLE: if (CSR_COUNTER) begin
decode_match = match_mrw;
rdata = mcycle;
end
MINSTRET: if (CSR_COUNTER) begin
decode_match = match_mrw;
rdata = minstret;
end
MCYCLEH: if (CSR_COUNTER) begin
decode_match = match_mrw;
rdata = mcycleh;
end
MINSTRETH: if (CSR_COUNTER) begin
decode_match = match_mrw;
rdata = minstreth;
end
MCOUNTEREN: if (CSR_COUNTER && U_MODE) begin
decode_match = match_mrw;
rdata = {
29'd0,
mcounteren_ir,
mcounteren_tm,
mcounteren_cy
};
end
// ------------------------------------------------------------------------
// PMP CSRs (bridge to PMP config interface)
// If PMP is present, all 16 registers are present, but some may be WARL'd
// to 0 depending on how many regions are actually implemented.
PMPCFG0: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPCFG1: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPCFG2: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPCFG3: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR0: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR1: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR2: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR3: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR4: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR5: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR6: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR7: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR8: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR9: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR10: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR11: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR12: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR13: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR14: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
PMPADDR15: if (PMP_REGIONS > 0) begin
decode_match = match_mrw;
pmp_cfg_wen = match_mrw && wen;
rdata = pmp_cfg_rdata;
end
// MSECCFG is strictly optional, and we don't implement any of its
// features (ePMP etc) so we don't decode it.
// ------------------------------------------------------------------------
// U-mode CSRs
// The read-only counters are always visible to M mode, and are visible to
// U mode if the corresponding mcounteren bit is set.
CYCLE: if (CSR_COUNTER) begin
decode_match = mcounteren_cy ? match_uro : match_mro;
rdata = mcycle;
end
CYCLEH: if (CSR_COUNTER) begin
decode_match = mcounteren_cy ? match_uro : match_mro;
rdata = mcycleh;
end
INSTRET: if (CSR_COUNTER) begin
decode_match = mcounteren_ir ? match_uro : match_mro;
rdata = minstret;
end
INSTRETH: if (CSR_COUNTER) begin
decode_match = mcounteren_ir ? match_uro : match_mro;
rdata = minstreth;
end
// ------------------------------------------------------------------------
// Trigger Module CSRs
// If triggers aren't supported, we implement tselect as hardwired 0,
// tinfo as hardwired 1 (meaning type=0 always) and tdata as hardwired 0
// (meaning type=0). The debugger will see the first trigger as
// unimplemented, and immediately halt discovery.
TSELECT: if (DEBUG_SUPPORT) begin
decode_match = match_mrw;
if (BREAKPOINT_TRIGGERS > 0) begin
trig_cfg_wen = match_mrw && wen;
rdata = trig_cfg_rdata;
end else begin
rdata = 32'h00000000;
end
end
TDATA1: if (DEBUG_SUPPORT) begin
decode_match = match_mrw;
if (BREAKPOINT_TRIGGERS > 0) begin
trig_cfg_wen = match_mrw && wen;
rdata = trig_cfg_rdata;
end else begin
rdata = 32'h00000000;
end
end
TDATA2: if (DEBUG_SUPPORT) begin
decode_match = match_mrw;
if (BREAKPOINT_TRIGGERS > 0) begin
trig_cfg_wen = match_mrw && wen;
rdata = trig_cfg_rdata;
end else begin
rdata = 32'h00000000;
end
end
TINFO: if (DEBUG_SUPPORT) begin
// Note tinfo is a read-write CSR (writes don't trap) even though it
// is entirely read-only.
decode_match = match_mrw;
if (BREAKPOINT_TRIGGERS > 0) begin
trig_cfg_wen = match_mrw && wen;
rdata = trig_cfg_rdata;
end else begin
rdata = 32'h00000001;
end
end
TCONTROL: if (DEBUG_SUPPORT && BREAKPOINT_TRIGGERS > 0) begin
decode_match = match_mrw;
rdata = {
24'h0,
tcontrol_mpte,
3'h0,
tcontrol_mte,
3'h0
};
end
// ------------------------------------------------------------------------
// Debug CSRs
DCSR: if (DEBUG_SUPPORT) begin
decode_match = match_drw;
rdata = {
4'h4, // xdebugver = 4, for 0.13.2 debug spec
12'd0, // reserved
dcsr_ebreakm,
2'h0, // No mode 2/1
dcsr_ebreaku,
1'b0, // stepie = 0, no interrupts in single-step mode
1'b1, // stopcount = 1, no counter increment in debug mode
1'b1, // stoptime = 0, no core-local timer increment in debug mode
dcsr_cause,
1'b0, // reserved
1'b0, // mprven = 0, debugger always acts as M-mode
1'b0, // nmip = 0, we have no NMI
dcsr_step,
{2{m_mode}} // priv = M or U, report the core state directly
};
end
DPC: if (DEBUG_SUPPORT) begin
decode_match = match_drw;
rdata = dpc;
end
DMDATA0: if (DEBUG_SUPPORT) begin
decode_match = match_drw;
rdata = dbg_data0_rdata;
end
// ------------------------------------------------------------------------
// Custom CSRs
MEIEA: if (CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = {
meiea[wdata[4:0] * 16 +: 16],
16'h0
};
end
MEIPA: if (CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = {
meipa[wdata[4:0] * 16 +: 16],
16'h0
};
end
MEIFA: if (CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = {
meifa[wdata[4:0] * 16 +: 16],
16'h0
};
end
MEIPRA: if (CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = {
meipra[wdata[6:0] * 16 +: 16],
16'h0
};
end
MEINEXT: if (CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = {
meinext_noirq,
20'h0,
meinext_irq,
2'h0
};
end
MEICONTEXT: if (CSR_M_TRAP) begin
decode_match = match_mrw;
rdata = {
meicontext_pppreempt,
meicontext_ppreempt,
3'h0,
meicontext_preempt,
meicontext_noirq,
2'h0,
meicontext_irq,
mie[7] && meicontext_clearts,
mie[3] && meicontext_clearts,
1'b0,
meicontext_mreteirq
};
end
default: begin end
endcase
end
assign illegal = (wen_soon || ren_soon) && !decode_match;
// ----------------------------------------------------------------------------
// Debug run/halt
// req_resume_prev is to cut an in->out path from request to trap addr.
reg have_just_reset;
reg step_halt_req;
reg dbg_req_resume_prev;
reg dbg_req_halt_prev;
reg pending_dbg_resume_prev;
wire pending_dbg_resume = (pending_dbg_resume_prev || dbg_req_resume_prev) && debug_mode;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
have_just_reset <= |DEBUG_SUPPORT;
step_halt_req <= 1'b0;
dbg_req_resume_prev <= 1'b0;
dbg_req_halt_prev <= 1'b0;
pending_dbg_resume_prev <= 1'b0;
end else if (DEBUG_SUPPORT) begin
if (instr_ret)
have_just_reset <= 1'b0;
// Just a delayed version of the request from outside of the core.
// Delay is fine because the DM awaits ack before deasserting.
dbg_req_resume_prev <= dbg_req_resume;
dbg_req_halt_prev <= dbg_req_halt;
if (debug_mode) begin
step_halt_req <= 1'b0;
end else if (dcsr_step && (instr_ret || (trap_enter_vld && trap_enter_rdy))) begin
// Note exception entry is also considered a step -- in this case
// we are supposed to take the trap and then break to debug mode
// before executing the first trap instruction. *IRQ* entry does
// not occur when step is 1 (because we hardwire dcsr.stepie to 0)
step_halt_req <= 1'b1;
end
pending_dbg_resume_prev <= pending_dbg_resume;
end
end
// We can enter the halted state in an IRQ-like manner (squeeze in between the
// instructions in stage 2 and stage 3) or in an exception-like manner
// (replace the instruction in stage 3).
//
// Halt request and step-break are IRQ-like. We need to be careful when a halt
// request lines up with an instruction in M which either has generated an
// exception (e.g. an ecall) or may yet generate an exception (a load). In
// this case the correct thing to do is to:
//
// - Squash whatever instruction may be in X, and inhibit X PC increment
// - Wait until after the exception entry is taken (or the load/store
// completes successfully)
// - Immediately trigger an IRQ-like debug entry.
//
// This ensures the debugger sees mcause/mepc set correctly, with dpc pointing
// to the handler entry point, if the instruction excepts.
wire exception_req_any;
wire halt_delayed_by_exception = exception_req_any || loadstore_dphase_pending;
wire want_halt_except = DEBUG_SUPPORT && !debug_mode && (
dcsr_ebreakm && m_mode && except == EXCEPT_EBREAK ||
dcsr_ebreaku && !m_mode && except == EXCEPT_EBREAK ||
except_to_d_mode && except == EXCEPT_EBREAK
);
// Note exception-like causes (trigger, ebreak) are higher priority than
// IRQ-like.
//
// We must mask halt_req with delay_irq_entry (true on cycle 2 and beyond of
// load/store address phase) because at that point we can't suppress the bus
// access..
wire want_halt_irq_if_no_exception = DEBUG_SUPPORT && !debug_mode && !want_halt_except && (
(dbg_req_halt_prev && !delay_irq_entry) ||
(dbg_req_halt_on_reset && have_just_reset) ||
step_halt_req
);
// Exception (or potential exception due to load/store) in M delays halt
// entry. The halt intention still blocks X, so we can't get blocked forever
// by a string of load/stores. This is just here to get sequencing between an
// exception (or potential exception) in M, and debug mode entry.
wire want_halt_irq = want_halt_irq_if_no_exception && !halt_delayed_by_exception;
assign dcause_next =
except == EXCEPT_EBREAK && except_to_d_mode ? 3'h2 : // trigger (priority 4)
except == EXCEPT_EBREAK ? 3'h1 : // ebreak (priority 3)
dbg_req_halt_prev || (dbg_req_halt_on_reset && have_just_reset) ? 3'h3 : // halt or reset-halt (priority 1, 2)
3'h4; // single-step (priority 0)
assign trap_is_debug_entry = |DEBUG_SUPPORT && !debug_mode && (want_halt_irq || want_halt_except);
assign enter_debug_mode = trap_is_debug_entry && trap_enter_rdy;
assign exit_debug_mode = debug_mode && pending_dbg_resume && trap_enter_rdy;
// Report back to DM instruction injector to tell it its instruction sequence
// has finished (ebreak) or crashed out
assign dbg_instr_caught_ebreak = debug_mode && except == EXCEPT_EBREAK && trap_enter_rdy;
// Note we exclude ebreak from here regardless of dcsr.ebreakm, since we are
// already in debug mode at this point
assign dbg_instr_caught_exception = debug_mode && except != EXCEPT_NONE && except != EXCEPT_EBREAK && trap_enter_rdy;
// ----------------------------------------------------------------------------
// Trap request generation
reg irq_software_r;
reg irq_timer_r;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
irq_software_r <= 1'b0;
irq_timer_r <= 1'b0;
end else begin
irq_software_r <= irq_software;
irq_timer_r <= irq_timer;
end
end
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
};
wire irq_active = |(mip & mie) && mstatus_mie && !dcsr_step;
// WFI clear respects individual interrupt enables but ignores mstatus.mie.
// Additionally, wfi is treated as a nop during single-stepping and D-mode.
assign wfi_stall_clear = |(mip & mie) || dcsr_step || debug_mode || want_halt_irq_if_no_exception;
// Priority order from priv spec: external > software > timer
wire [3:0] standard_irq_num =
mip[11] && mie[11] ? 4'd11 :
mip[3] && mie[3] ? 4'd3 :
mip[7] && mie[7] ? 4'd7 : 4'd0;
// ebreak may be treated as a halt-to-debugger or a regular M-mode exception,
// depending on dcsr.ebreakm.
assign exception_req_any = except != EXCEPT_NONE && !(
except == EXCEPT_EBREAK && (m_mode ? dcsr_ebreakm : dcsr_ebreaku)
);
wire [3:0] mcause_irq_num = irq_active ? standard_irq_num : 4'd0;
wire [3:0] vector_sel = !exception_req_any && irq_vector_enable ? mcause_irq_num : 4'd0;
assign trap_addr =
except == EXCEPT_MRET ? mepc :
pending_dbg_resume ? dpc : mtvec | {26'h0, vector_sel, 2'h0};
// Check for exception-like or IRQ-like trap entry; any debug mode entry takes
// priority over any regular trap.
assign trap_is_irq = DEBUG_SUPPORT && (want_halt_except || want_halt_irq) ?
!want_halt_except : !exception_req_any;
// When there is a loadstore dphase pending, we block off the IRQ trap
// request, but still assert the trap_enter_soon flag. This blocks issue of
// further load/stores which have not yet been locked into aphase, so the IRQ
// can eventually go through. It's not safe to enter an IRQ when a dphase is
// in progress, because that dphase could subsequently except, and sample the
// IRQ vector PC instead of the load/store instruction PC.
// delay_irq_entry also applies to IRQ-like debug entries.
assign trap_enter_vld =
CSR_M_TRAP && (exception_req_any ||
!delay_irq_entry && !debug_mode && irq_active && !loadstore_dphase_pending) ||
DEBUG_SUPPORT && (
(!delay_irq_entry && want_halt_irq) || want_halt_except || pending_dbg_resume);
assign trap_enter_soon = trap_enter_vld || (
|DEBUG_SUPPORT && want_halt_irq_if_no_exception ||
!delay_irq_entry && !debug_mode && irq_active && loadstore_dphase_pending
);
assign mcause_irq_next = !exception_req_any;
assign mcause_code_next = exception_req_any ? except : mcause_irq_num;
// ----------------------------------------------------------------------------
// Privilege state outputs
assign pmp_cfg_addr = addr;
assign pmp_cfg_wdata = wdata_update;
assign trig_cfg_addr = addr;
assign trig_cfg_wdata = wdata_update;
assign trig_m_en = tcontrol_mte;
// Effective privilege for execution. Used for:
// - Privilege level of branch target fetches (frontend keeps fetch privilege
// constant during sequential fetch)
// - Checking PC against PMP execute permission
assign m_mode_execution = !U_MODE || debug_mode || m_mode;
// Effective privilege for trap entry. Used for:
// - Privilege level of trap target fetches (frontend keeps fetch privilege
// constant during sequential fetch)
assign m_mode_trap_entry = !U_MODE || (
except == EXCEPT_MRET ? mstatus_mpp : 1'b1
);
// Effective privilege for load/stores. Used for:
// - Privilege level of load/stores on the bus
// - Checking load/stores against PMP read/write permission
assign m_mode_loadstore = !U_MODE || debug_mode || ( // FIXME how does this interact with load/store racing against debug entry?
mstatus_mprv ? mstatus_mpp : m_mode
);
// ----------------------------------------------------------------------------
// Properties
`ifdef HAZARD3_ASSERTIONS
// Keep track of whether we are in a trap (only for formal property purposes)
reg in_trap;
always @ (posedge clk or negedge rst_n)
if (!rst_n)
in_trap <= 1'b0;
else
in_trap <= (in_trap || (trap_enter_vld && trap_enter_rdy))
&& !(trap_enter_vld && trap_enter_rdy && except == EXCEPT_MRET);
always @ (posedge clk) begin
`ifdef RISCV_FORMAL
// Assume there are no nested exceptions, to stop riscv-formal from doing
// annoying things like stopping instructions from retiring by repeatedly
// feeding in invalid instructions
if (in_trap)
assume(except == EXCEPT_NONE || except == EXCEPT_MRET);
// Assume IRQs are not deasserted on cycles where exception entry does not
// take place
if (!trap_enter_rdy)
assume(~|(irq_r & ~irq));
`endif
// Make sure CSR accesses are flushed
if (trap_enter_vld && trap_enter_rdy)
assert(!(wen || ren));
// Writing to CSR on cycle after trap entry -- should be impossible, a CSR
// access instruction should have been flushed or moved down to stage 3, and
// no fetch could have arrived by now
if ($past(trap_enter_vld && trap_enter_rdy))
assert(!wen);
// Should be impossible to get into the trap and exit immediately:
if (in_trap && !$past(in_trap))
assert(except != EXCEPT_MRET);
// Should be impossible to get to another mret so soon after exiting:
if ($past(except == EXCEPT_MRET && trap_enter_vld && trap_enter_rdy))
assert(except != EXCEPT_MRET);
// Must be impossible to enter two traps on consecutive cycles. Most importantly:
//
// - IRQ -> Except: no new instruction could have been fetched by this point,
// and an exception by e.g. a left-behind store data phase would sample the
// wrong PC.
//
// - Except -> IRQ: would need to re-set mstatus.mie first, shouldn't happen
//
// Sole exclusion is mret. We can return from an interrupt/exception and take
// a new interrupt on the next cycle.
if ($past(trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET))
assert(!(trap_enter_vld && trap_enter_rdy));
// Just to stress that this is the the only case:
if (trap_enter_vld && trap_enter_rdy && $past(trap_enter_vld && trap_enter_rdy))
assert($past(except == EXCEPT_MRET));
if (rst_n && $past(trap_enter_vld && !trap_enter_rdy && !trap_is_irq)) begin
// Exception which didn't go through should not disappear
assert(trap_enter_vld);
// Exception should not be replaced by IRQ
assert(!trap_is_irq);
end
// This must be a breakpoint exception (presumably from the trigger unit).
if (except_to_d_mode) begin
assert(except == EXCEPT_EBREAK);
end
end
`endif
endmodule
`ifndef YOSYS
`default_nettype wire
`endif