blob: 111c1f8cc82765d188d635e79ef6c1592ac2937c [file] [log] [blame]
// Copyright lowRISC contributors.
// Copyright 2018 ETH Zurich and University of Bologna, see also CREDITS.md.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
/**
* Control and Status Registers
*
* Control and Status Registers (CSRs) following the RISC-V Privileged
* Specification, draft version 1.11
*/
module ibex_cs_registers #(
parameter int unsigned MHPMCounterNum = 8,
parameter int unsigned MHPMCounterWidth = 40,
parameter bit PMPEnable = 0,
parameter int unsigned PMPGranularity = 0,
parameter int unsigned PMPNumRegions = 4,
parameter bit RV32E = 0,
parameter bit RV32M = 0
) (
// Clock and Reset
input logic clk_i,
input logic rst_ni,
// Hart ID
input logic [31:0] hart_id_i,
// Privilege mode
output ibex_pkg::priv_lvl_e priv_mode_id_o,
output ibex_pkg::priv_lvl_e priv_mode_if_o,
output ibex_pkg::priv_lvl_e priv_mode_lsu_o,
output logic csr_mstatus_tw_o,
// mtvec
output logic [31:0] csr_mtvec_o,
input logic csr_mtvec_init_i,
input logic [31:0] boot_addr_i,
// Interface to registers (SRAM like)
input logic csr_access_i,
input ibex_pkg::csr_num_e csr_addr_i,
input logic [31:0] csr_wdata_i,
input ibex_pkg::csr_op_e csr_op_i,
output logic [31:0] csr_rdata_o,
// interrupts
input logic irq_software_i,
input logic irq_timer_i,
input logic irq_external_i,
input logic [14:0] irq_fast_i,
output logic irq_pending_o, // interupt request pending
output logic csr_msip_o, // software interrupt pending
output logic csr_mtip_o, // timer interrupt pending
output logic csr_meip_o, // external interrupt pending
output logic [14:0] csr_mfip_o, // fast interrupt pending
output logic csr_mstatus_mie_o,
output logic [31:0] csr_mepc_o,
// PMP
output ibex_pkg::pmp_cfg_t csr_pmp_cfg_o [PMPNumRegions],
output logic [33:0] csr_pmp_addr_o [PMPNumRegions],
// debug
input logic debug_mode_i,
input ibex_pkg::dbg_cause_e debug_cause_i,
input logic debug_csr_save_i,
output logic [31:0] csr_depc_o,
output logic debug_single_step_o,
output logic debug_ebreakm_o,
output logic debug_ebreaku_o,
input logic [31:0] pc_if_i,
input logic [31:0] pc_id_i,
input logic csr_save_if_i,
input logic csr_save_id_i,
input logic csr_restore_mret_i,
input logic csr_save_cause_i,
input ibex_pkg::exc_cause_e csr_mcause_i,
input logic [31:0] csr_mtval_i,
output logic illegal_csr_insn_o, // access to non-existent CSR,
// with wrong priviledge level, or
// missing write permissions
input logic instr_new_id_i, // ID stage sees a new instr
// Performance Counters
input logic instr_ret_i, // instr retired in ID/EX stage
input logic instr_ret_compressed_i, // compressed instr retired
input logic imiss_i, // instr fetch
input logic pc_set_i, // PC was set to a new value
input logic jump_i, // jump instr seen (j, jr, jal, jalr)
input logic branch_i, // branch instr seen (bf, bnf)
input logic branch_taken_i, // branch was taken
input logic mem_load_i, // load from memory in this cycle
input logic mem_store_i, // store to memory in this cycle
input logic lsu_busy_i
);
import ibex_pkg::*;
// misa
localparam logic [1:0] MXL = 2'd1; // M-XLEN: XLEN in M-Mode for RV32
localparam logic [31:0] MISA_VALUE =
(0 << 0) // A - Atomic Instructions extension
| (1 << 2) // C - Compressed extension
| (0 << 3) // D - Double precision floating-point extension
| (32'(RV32E) << 4) // E - RV32E base ISA
| (0 << 5) // F - Single precision floating-point extension
| (1 << 8) // I - RV32I/64I/128I base ISA
| (32'(RV32M) << 12) // M - Integer Multiply/Divide extension
| (0 << 13) // N - User level interrupts supported
| (0 << 18) // S - Supervisor mode implemented
| (0 << 20) // U - User mode implemented
| (0 << 23) // X - Non-standard extensions present
| (32'(MXL) << 30); // M-XLEN
typedef struct packed {
logic mie;
logic mpie;
priv_lvl_e mpp;
logic mprv;
logic tw;
} Status_t;
typedef struct packed {
logic mpie;
priv_lvl_e mpp;
} StatusStk_t;
// struct for mip/mie CSRs
typedef struct packed {
logic irq_software;
logic irq_timer;
logic irq_external;
logic [14:0] irq_fast; // 15 fast interrupts,
// one interrupt is reserved for NMI (not visible through mip/mie)
} Interrupts_t;
typedef struct packed {
x_debug_ver_e xdebugver;
logic [11:0] zero2;
logic ebreakm;
logic zero1;
logic ebreaks;
logic ebreaku;
logic stepie;
logic stopcount;
logic stoptime;
dbg_cause_e cause;
logic zero0;
logic mprven;
logic nmip;
logic step;
priv_lvl_e prv;
} Dcsr_t;
// Interrupt and exception control signals
logic [31:0] exception_pc;
// CSRs
priv_lvl_e priv_lvl_q, priv_lvl_d;
Status_t mstatus_q, mstatus_d;
Interrupts_t mie_q, mie_d;
logic [31:0] mscratch_q, mscratch_d;
logic [31:0] mepc_q, mepc_d;
logic [5:0] mcause_q, mcause_d;
logic [31:0] mtval_q, mtval_d;
logic [31:0] mtvec_q, mtvec_d;
Interrupts_t mip;
Dcsr_t dcsr_q, dcsr_d;
logic [31:0] depc_q, depc_d;
logic [31:0] dscratch0_q, dscratch0_d;
logic [31:0] dscratch1_q, dscratch1_d;
// CSRs for recoverable NMIs
// NOTE: these CSRS are nonstandard, see https://github.com/riscv/riscv-isa-manual/issues/261
StatusStk_t mstack_q, mstack_d;
logic [31:0] mstack_epc_q, mstack_epc_d;
logic [5:0] mstack_cause_q, mstack_cause_d;
// PMP Signals
logic [31:0] pmp_addr_rdata [PMP_MAX_REGIONS];
logic [PMP_CFG_W-1:0] pmp_cfg_rdata [PMP_MAX_REGIONS];
// Hardware performance monitor signals
logic [31:0] mcountinhibit_d, mcountinhibit_q, mcountinhibit;
logic [31:0] mcountinhibit_force;
logic mcountinhibit_we;
logic [63:0] mhpmcounter_mask [32];
logic [63:0] mhpmcounter_d [32];
logic [63:0] mhpmcounter_q [32];
logic [31:0] mhpmcounter_we;
logic [31:0] mhpmcounterh_we;
logic [31:0] mhpmcounter_incr;
logic [31:0] mhpmevent [32];
logic [4:0] mhpmcounter_idx;
// CSR update logic
logic [31:0] csr_wdata_int;
logic [31:0] csr_rdata_int;
logic csr_we_int;
logic csr_wreq;
// Access violation signals
logic illegal_csr;
logic illegal_csr_priv;
logic illegal_csr_write;
logic [7:0] unused_boot_addr;
assign unused_boot_addr = boot_addr_i[7:0];
/////////////
// CSR reg //
/////////////
logic [$bits(csr_num_e)-1:0] csr_addr;
assign csr_addr = {csr_addr_i};
assign mhpmcounter_idx = csr_addr[4:0];
// See RISC-V Privileged Specification, version 1.11, Section 2.1
assign illegal_csr_priv = (csr_addr[9:8] > {priv_lvl_q});
assign illegal_csr_write = (csr_addr[11:10] == 2'b11) && csr_wreq;
assign illegal_csr_insn_o = csr_access_i & (illegal_csr | illegal_csr_write | illegal_csr_priv);
// mip CSR is purely combinational - must be able to re-enable the clock upon WFI
assign mip.irq_software = irq_software_i & mie_q.irq_software;
assign mip.irq_timer = irq_timer_i & mie_q.irq_timer;
assign mip.irq_external = irq_external_i & mie_q.irq_external;
assign mip.irq_fast = irq_fast_i & mie_q.irq_fast;
// read logic
always_comb begin
csr_rdata_int = '0;
illegal_csr = 1'b0;
unique case (csr_addr_i)
// mhartid: unique hardware thread id
CSR_MHARTID: csr_rdata_int = hart_id_i;
// mstatus: always M-mode, contains IE bit
CSR_MSTATUS: begin
csr_rdata_int = '0;
csr_rdata_int[CSR_MSTATUS_MIE_BIT] = mstatus_q.mie;
csr_rdata_int[CSR_MSTATUS_MPIE_BIT] = mstatus_q.mpie;
csr_rdata_int[CSR_MSTATUS_MPP_BIT_HIGH:CSR_MSTATUS_MPP_BIT_LOW] = mstatus_q.mpp;
csr_rdata_int[CSR_MSTATUS_MPRV_BIT] = mstatus_q.mprv;
csr_rdata_int[CSR_MSTATUS_TW_BIT] = mstatus_q.tw;
end
// misa
CSR_MISA: csr_rdata_int = MISA_VALUE;
// interrupt enable
CSR_MIE: begin
csr_rdata_int = '0;
csr_rdata_int[CSR_MSIX_BIT] = mie_q.irq_software;
csr_rdata_int[CSR_MTIX_BIT] = mie_q.irq_timer;
csr_rdata_int[CSR_MEIX_BIT] = mie_q.irq_external;
csr_rdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW] = mie_q.irq_fast;
end
CSR_MSCRATCH: csr_rdata_int = mscratch_q;
// mtvec: trap-vector base address
CSR_MTVEC: csr_rdata_int = mtvec_q;
// mepc: exception program counter
CSR_MEPC: csr_rdata_int = mepc_q;
// mcause: exception cause
CSR_MCAUSE: csr_rdata_int = {mcause_q[5], 26'b0, mcause_q[4:0]};
// mtval: trap value
CSR_MTVAL: csr_rdata_int = mtval_q;
// mip: interrupt pending
CSR_MIP: begin
csr_rdata_int = '0;
csr_rdata_int[CSR_MSIX_BIT] = mip.irq_software;
csr_rdata_int[CSR_MTIX_BIT] = mip.irq_timer;
csr_rdata_int[CSR_MEIX_BIT] = mip.irq_external;
csr_rdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW] = mip.irq_fast;
end
// PMP registers
CSR_PMPCFG0: csr_rdata_int = {pmp_cfg_rdata[3], pmp_cfg_rdata[2],
pmp_cfg_rdata[1], pmp_cfg_rdata[0]};
CSR_PMPCFG1: csr_rdata_int = {pmp_cfg_rdata[7], pmp_cfg_rdata[6],
pmp_cfg_rdata[5], pmp_cfg_rdata[4]};
CSR_PMPCFG2: csr_rdata_int = {pmp_cfg_rdata[11], pmp_cfg_rdata[10],
pmp_cfg_rdata[9], pmp_cfg_rdata[8]};
CSR_PMPCFG3: csr_rdata_int = {pmp_cfg_rdata[15], pmp_cfg_rdata[14],
pmp_cfg_rdata[13], pmp_cfg_rdata[12]};
CSR_PMPADDR0: csr_rdata_int = pmp_addr_rdata[0];
CSR_PMPADDR1: csr_rdata_int = pmp_addr_rdata[1];
CSR_PMPADDR2: csr_rdata_int = pmp_addr_rdata[2];
CSR_PMPADDR3: csr_rdata_int = pmp_addr_rdata[3];
CSR_PMPADDR4: csr_rdata_int = pmp_addr_rdata[4];
CSR_PMPADDR5: csr_rdata_int = pmp_addr_rdata[5];
CSR_PMPADDR6: csr_rdata_int = pmp_addr_rdata[6];
CSR_PMPADDR7: csr_rdata_int = pmp_addr_rdata[7];
CSR_PMPADDR8: csr_rdata_int = pmp_addr_rdata[8];
CSR_PMPADDR9: csr_rdata_int = pmp_addr_rdata[9];
CSR_PMPADDR10: csr_rdata_int = pmp_addr_rdata[10];
CSR_PMPADDR11: csr_rdata_int = pmp_addr_rdata[11];
CSR_PMPADDR12: csr_rdata_int = pmp_addr_rdata[12];
CSR_PMPADDR13: csr_rdata_int = pmp_addr_rdata[13];
CSR_PMPADDR14: csr_rdata_int = pmp_addr_rdata[14];
CSR_PMPADDR15: csr_rdata_int = pmp_addr_rdata[15];
CSR_DCSR: begin
csr_rdata_int = dcsr_q;
illegal_csr = ~debug_mode_i;
end
CSR_DPC: begin
csr_rdata_int = depc_q;
illegal_csr = ~debug_mode_i;
end
CSR_DSCRATCH0: begin
csr_rdata_int = dscratch0_q;
illegal_csr = ~debug_mode_i;
end
CSR_DSCRATCH1: begin
csr_rdata_int = dscratch1_q;
illegal_csr = ~debug_mode_i;
end
// machine counter/timers
CSR_MCOUNTINHIBIT: csr_rdata_int = mcountinhibit;
CSR_MCYCLE: csr_rdata_int = mhpmcounter_q[0][31: 0];
CSR_MCYCLEH: csr_rdata_int = mhpmcounter_q[0][63:32];
CSR_MINSTRET: csr_rdata_int = mhpmcounter_q[2][31: 0];
CSR_MINSTRETH: csr_rdata_int = mhpmcounter_q[2][63:32];
default: begin
if ((csr_addr & CSR_MASK_MCOUNTER) == CSR_OFF_MCOUNTER_SETUP) begin
csr_rdata_int = mhpmevent[mhpmcounter_idx];
// check access to non-existent or already covered CSRs
if ((csr_addr[4:0] == 5'b00000) || // CSR_MCOUNTINHIBIT
(csr_addr[4:0] == 5'b00001) ||
(csr_addr[4:0] == 5'b00010)) begin
illegal_csr = 1'b1;
end
end else if ((csr_addr & CSR_MASK_MCOUNTER) == CSR_OFF_MCOUNTER) begin
csr_rdata_int = mhpmcounter_q[mhpmcounter_idx][31: 0];
// check access to non-existent or already covered CSRs
if ((csr_addr[4:0] == 5'b00000) || // CSR_MCYCLE
(csr_addr[4:0] == 5'b00001) ||
(csr_addr[4:0] == 5'b00010)) begin // CSR_MINSTRET
illegal_csr = 1'b1;
end
end else if ((csr_addr & CSR_MASK_MCOUNTER) == CSR_OFF_MCOUNTERH) begin
csr_rdata_int = mhpmcounter_q[mhpmcounter_idx][63:32];
// check access to non-existent or already covered CSRs
if ((csr_addr[4:0] == 5'b00000) || // CSR_MCYCLEH
(csr_addr[4:0] == 5'b00001) ||
(csr_addr[4:0] == 5'b00010)) begin // CSR_MINSTRETH
illegal_csr = 1'b1;
end
end else begin
illegal_csr = 1'b1;
end
end
endcase
end
// write logic
always_comb begin
exception_pc = pc_id_i;
priv_lvl_d = priv_lvl_q;
mstatus_d = mstatus_q;
mie_d = mie_q;
mscratch_d = mscratch_q;
mepc_d = mepc_q;
mcause_d = mcause_q;
mtval_d = mtval_q;
mtvec_d = csr_mtvec_init_i ? {boot_addr_i[31:8], 6'b0, 2'b01} : mtvec_q;
dcsr_d = dcsr_q;
depc_d = depc_q;
dscratch0_d = dscratch0_q;
dscratch1_d = dscratch1_q;
mstack_d = mstack_q;
mstack_epc_d = mstack_epc_q;
mstack_cause_d = mstack_cause_q;
mcountinhibit_we = 1'b0;
mhpmcounter_we = '0;
mhpmcounterh_we = '0;
if (csr_we_int) begin
unique case (csr_addr_i)
// mstatus: IE bit
CSR_MSTATUS: begin
mstatus_d = '{
mie: csr_wdata_int[CSR_MSTATUS_MIE_BIT],
mpie: csr_wdata_int[CSR_MSTATUS_MPIE_BIT],
mpp: priv_lvl_e'(csr_wdata_int[CSR_MSTATUS_MPP_BIT_HIGH:CSR_MSTATUS_MPP_BIT_LOW]),
mprv: csr_wdata_int[CSR_MSTATUS_MPRV_BIT],
tw: csr_wdata_int[CSR_MSTATUS_TW_BIT]
};
// Convert illegal values to M-mode
if ((mstatus_d.mpp != PRIV_LVL_M) && (mstatus_d.mpp != PRIV_LVL_U)) begin
mstatus_d.mpp = PRIV_LVL_M;
end
end
// interrupt enable
CSR_MIE: begin
mie_d.irq_software = csr_wdata_int[CSR_MSIX_BIT];
mie_d.irq_timer = csr_wdata_int[CSR_MTIX_BIT];
mie_d.irq_external = csr_wdata_int[CSR_MEIX_BIT];
mie_d.irq_fast = csr_wdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW];
end
CSR_MSCRATCH: mscratch_d = csr_wdata_int;
// mepc: exception program counter
CSR_MEPC: mepc_d = {csr_wdata_int[31:1], 1'b0};
// mcause
CSR_MCAUSE: mcause_d = {csr_wdata_int[31], csr_wdata_int[4:0]};
// mtval: trap value
CSR_MTVAL: mtval_d = csr_wdata_int;
// mtvec
// mtvec.MODE set to vectored
// mtvec.BASE must be 256-byte aligned
CSR_MTVEC: mtvec_d = {csr_wdata_int[31:8], 6'b0, 2'b01};
CSR_DCSR: begin
dcsr_d = csr_wdata_int;
dcsr_d.xdebugver = XDEBUGVER_STD;
// Change to PRIV_LVL_M if sofware writes an unsupported value
if ((dcsr_d.prv != PRIV_LVL_M) && (dcsr_d.prv != PRIV_LVL_U)) begin
dcsr_d.prv = PRIV_LVL_M;
end
// currently not supported:
dcsr_d.nmip = 1'b0;
dcsr_d.mprven = 1'b0;
dcsr_d.stopcount = 1'b0;
dcsr_d.stoptime = 1'b0;
// forced to be zero
dcsr_d.zero0 = 1'b0;
dcsr_d.zero1 = 1'b0;
dcsr_d.zero2 = 12'h0;
end
CSR_DPC: begin
// Only valid PC addresses are allowed (half-word aligned with C ext.)
if (csr_wdata_int[0] == 1'b0) begin
depc_d = csr_wdata_int;
end
end
CSR_DSCRATCH0: dscratch0_d = csr_wdata_int;
CSR_DSCRATCH1: dscratch1_d = csr_wdata_int;
CSR_MCOUNTINHIBIT: mcountinhibit_we = 1'b1;
CSR_MCYCLE: mhpmcounter_we[0] = 1'b1;
CSR_MCYCLEH: mhpmcounterh_we[0] = 1'b1;
CSR_MINSTRET: mhpmcounter_we[2] = 1'b1;
CSR_MINSTRETH: mhpmcounterh_we[2] = 1'b1;
default: begin
// performance counters and event selector
if ((csr_addr & CSR_MASK_MCOUNTER) == CSR_OFF_MCOUNTER) begin
mhpmcounter_we[mhpmcounter_idx] = 1'b1;
end else if ((csr_addr & CSR_MASK_MCOUNTER) == CSR_OFF_MCOUNTERH) begin
mhpmcounterh_we[mhpmcounter_idx] = 1'b1;
end
end
endcase
end
// exception controller gets priority over other writes
unique case (1'b1)
csr_save_cause_i: begin
unique case (1'b1)
csr_save_if_i: begin
exception_pc = pc_if_i;
end
csr_save_id_i: begin
exception_pc = pc_id_i;
end
default:;
endcase
if (debug_csr_save_i) begin
// all interrupts are masked
// do not update cause, epc, tval, epc and status
dcsr_d.prv = priv_lvl_q;
dcsr_d.cause = debug_cause_i;
depc_d = exception_pc;
end else begin
priv_lvl_d = PRIV_LVL_M;
mtval_d = csr_mtval_i;
mstatus_d.mie = 1'b0; // disable interrupts
// save current status
mstatus_d.mpie = mstatus_q.mie;
mstatus_d.mpp = priv_lvl_q;
mepc_d = exception_pc;
mcause_d = {csr_mcause_i};
// save previous status for recoverable NMI
mstack_d.mpie = mstatus_q.mpie;
mstack_d.mpp = mstatus_q.mpp;
mstack_epc_d = mepc_q;
mstack_cause_d = mcause_q;
end
end // csr_save_cause_i
csr_restore_mret_i: begin // MRET
priv_lvl_d = mstatus_q.mpp;
mstatus_d.mie = mstatus_q.mpie; // re-enable interrupts
// restore previous status for recoverable NMI
mstatus_d.mpie = mstack_q.mpie;
mstatus_d.mpp = mstack_q.mpp;
mepc_d = mstack_epc_q;
mcause_d = mstack_cause_q;
mstack_d.mpie = 1'b1;
mstack_d.mpp = PRIV_LVL_U;
end // csr_restore_mret_i
default:;
endcase
end
// CSR operation logic
always_comb begin
csr_wreq = 1'b1;
unique case (csr_op_i)
CSR_OP_WRITE: csr_wdata_int = csr_wdata_i;
CSR_OP_SET: csr_wdata_int = csr_wdata_i | csr_rdata_o;
CSR_OP_CLEAR: csr_wdata_int = ~csr_wdata_i & csr_rdata_o;
CSR_OP_READ: begin
csr_wdata_int = csr_wdata_i;
csr_wreq = 1'b0;
end
default: begin
csr_wdata_int = 'X;
csr_wreq = 1'bX;
end
endcase
end
// only write CSRs during one clock cycle
assign csr_we_int = csr_wreq & ~illegal_csr_insn_o & instr_new_id_i;
assign csr_rdata_o = csr_rdata_int;
// directly output some registers
assign csr_msip_o = mip.irq_software;
assign csr_mtip_o = mip.irq_timer;
assign csr_meip_o = mip.irq_external;
assign csr_mfip_o = mip.irq_fast;
assign csr_mepc_o = mepc_q;
assign csr_depc_o = depc_q;
assign csr_mtvec_o = mtvec_q;
assign csr_mstatus_mie_o = mstatus_q.mie;
assign csr_mstatus_tw_o = mstatus_q.tw;
assign debug_single_step_o = dcsr_q.step;
assign debug_ebreakm_o = dcsr_q.ebreakm;
assign debug_ebreaku_o = dcsr_q.ebreaku;
assign irq_pending_o = csr_msip_o | csr_mtip_o | csr_meip_o | (|csr_mfip_o);
// actual registers
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
priv_lvl_q <= PRIV_LVL_M;
mstatus_q <= '{
mie: 1'b0,
mpie: 1'b1,
mpp: PRIV_LVL_U,
mprv: 1'b0,
tw: 1'b0
};
mie_q <= '0;
mscratch_q <= '0;
mepc_q <= '0;
mcause_q <= '0;
mtval_q <= '0;
mtvec_q <= 32'b01;
dcsr_q <= '{
xdebugver: XDEBUGVER_STD,
cause: DBG_CAUSE_NONE, // 3'h0
prv: PRIV_LVL_M,
default: '0
};
depc_q <= '0;
dscratch0_q <= '0;
dscratch1_q <= '0;
mstack_q <= '{
mpie: 1'b1,
mpp: PRIV_LVL_U
};
mstack_epc_q <= '0;
mstack_cause_q <= '0;
end else begin
// update CSRs
priv_lvl_q <= priv_lvl_d;
mstatus_q <= mstatus_d;
mie_q <= mie_d;
mscratch_q <= mscratch_d;
mepc_q <= mepc_d;
mcause_q <= mcause_d;
mtval_q <= mtval_d;
mtvec_q <= mtvec_d;
dcsr_q <= dcsr_d;
depc_q <= depc_d;
dscratch0_q <= dscratch0_d;
dscratch1_q <= dscratch1_d;
mstack_q <= mstack_d;
mstack_epc_q <= mstack_epc_d;
mstack_cause_q <= mstack_cause_d;
end
end
// Send current priv level to the decoder
assign priv_mode_id_o = priv_lvl_q;
// New instruction fetches need to account for updates to priv_lvl_q this cycle
assign priv_mode_if_o = priv_lvl_d;
// Load/store instructions must factor in MPRV for PMP checking
assign priv_mode_lsu_o = mstatus_q.mprv ? mstatus_q.mpp : priv_lvl_q;
// -----------------
// PMP registers
// -----------------
if (PMPEnable) begin : g_pmp_registers
pmp_cfg_t pmp_cfg [PMPNumRegions];
pmp_cfg_t pmp_cfg_wdata [PMPNumRegions];
logic [31:0] pmp_addr [PMPNumRegions];
logic [PMPNumRegions-1:0] pmp_cfg_we;
logic [PMPNumRegions-1:0] pmp_addr_we;
// Expanded / qualified register read data
for (genvar i = 0; i < PMP_MAX_REGIONS; i++) begin : g_exp_rd_data
if (i < PMPNumRegions) begin : g_implemented_regions
// Add in zero padding for reserved fields
assign pmp_cfg_rdata[i] = {pmp_cfg[i].lock, 2'b00, pmp_cfg[i].mode,
pmp_cfg[i].exec, pmp_cfg[i].write, pmp_cfg[i].read};
// Address field read data depends on the current programmed mode and the granularity
// See RISC-V Privileged Specification, version 1.11, Section 3.6.1
if (PMPGranularity == 0) begin : g_pmp_g0
// If G == 0, read data is unmodified
assign pmp_addr_rdata[i] = pmp_addr[i];
end else if (PMPGranularity == 1) begin : g_pmp_g1
// If G == 1, bit [G-1] reads as zero in TOR or OFF mode
always_comb begin
pmp_addr_rdata[i] = pmp_addr[i];
if ((pmp_cfg[i].mode == PMP_MODE_OFF) || (pmp_cfg[i].mode == PMP_MODE_TOR)) begin
pmp_addr_rdata[i][PMPGranularity-1:0] = '0;
end
end
end else begin : g_pmp_g2
// For G >= 2, bits are masked to one or zero depending on the mode
always_comb begin
pmp_addr_rdata[i] = pmp_addr[i];
if ((pmp_cfg[i].mode == PMP_MODE_OFF) || (pmp_cfg[i].mode == PMP_MODE_TOR)) begin
// In TOR or OFF mode, bits [G-1:0] must read as zero
pmp_addr_rdata[i][PMPGranularity-1:0] = '0;
end else if (pmp_cfg[i].mode == PMP_MODE_NAPOT) begin
// In NAPOT mode, bits [G-2:0] must read as one
pmp_addr_rdata[i][PMPGranularity-2:0] = '1;
end
end
end
end else begin : g_other_regions
// Non-implemented regions read as zero
assign pmp_cfg_rdata[i] = '0;
assign pmp_addr_rdata[i] = '0;
end
end
// Write data calculation
for (genvar i = 0; i < PMPNumRegions; i++) begin : g_pmp_csrs
// -------------------------
// Instantiate cfg registers
// -------------------------
assign pmp_cfg_we[i] = csr_we_int & ~pmp_cfg[i].lock &
(csr_addr == (CSR_OFF_PMP_CFG + (i[11:0] >> 2)));
// Select the correct WDATA (each CSR contains 4 CFG fields, each with 2 RES bits)
assign pmp_cfg_wdata[i].lock = csr_wdata_int[(i%4)*PMP_CFG_W+7];
// NA4 mode is not selectable when G > 0, mode is treated as OFF
always_comb begin
unique case (csr_wdata_int[(i%4)*PMP_CFG_W+3+:2])
2'b00 : pmp_cfg_wdata[i].mode = PMP_MODE_OFF;
2'b01 : pmp_cfg_wdata[i].mode = PMP_MODE_TOR;
2'b10 : pmp_cfg_wdata[i].mode = (PMPGranularity == 0) ? PMP_MODE_NA4:
PMP_MODE_OFF;
2'b11 : pmp_cfg_wdata[i].mode = PMP_MODE_NAPOT;
default : pmp_cfg_wdata[i].mode = pmp_cfg_mode_e'('X);
endcase
end
assign pmp_cfg_wdata[i].exec = csr_wdata_int[(i%4)*PMP_CFG_W+2];
// W = 1, R = 0 is a reserved combination. For now, we force W to 0 if R == 0
assign pmp_cfg_wdata[i].write = &csr_wdata_int[(i%4)*PMP_CFG_W+:2];
assign pmp_cfg_wdata[i].read = csr_wdata_int[(i%4)*PMP_CFG_W];
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
pmp_cfg[i] <= pmp_cfg_t'('b0);
end else if (pmp_cfg_we[i]) begin
pmp_cfg[i] <= pmp_cfg_wdata[i];
end
end
// --------------------------
// Instantiate addr registers
// --------------------------
if (i < PMPNumRegions - 1) begin : g_lower
assign pmp_addr_we[i] = csr_we_int & ~pmp_cfg[i].lock &
(pmp_cfg[i+1].mode != PMP_MODE_TOR) &
(csr_addr == (CSR_OFF_PMP_ADDR + i[11:0]));
end else begin : g_upper
assign pmp_addr_we[i] = csr_we_int & ~pmp_cfg[i].lock &
(csr_addr == (CSR_OFF_PMP_ADDR + i[11:0]));
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
pmp_addr[i] <= 'b0;
end else if (pmp_addr_we[i]) begin
pmp_addr[i] <= csr_wdata_int;
end
end
assign csr_pmp_cfg_o[i] = pmp_cfg[i];
assign csr_pmp_addr_o[i] = {pmp_addr[i],2'b00};
end
end else begin : g_no_pmp_tieoffs
// Generate tieoffs when PMP is not configured
for (genvar i = 0; i < PMP_MAX_REGIONS; i++) begin : g_rdata
assign pmp_addr_rdata[i] = '0;
assign pmp_cfg_rdata[i] = '0;
end
for (genvar i = 0; i < PMPNumRegions; i++) begin : g_outputs
assign csr_pmp_cfg_o[i] = pmp_cfg_t'(1'b0);
assign csr_pmp_addr_o[i] = '0;
end
end
//////////////////////////
// Performance monitor //
//////////////////////////
// update enable signals
always_comb begin : mcountinhibit_update
if (mcountinhibit_we == 1'b1) begin
mcountinhibit_d = {csr_wdata_int[31:2], 1'b0, csr_wdata_int[0]}; // bit 1 must always be 0
end else begin
mcountinhibit_d = mcountinhibit_q;
end
end
assign mcountinhibit_force = {{29-MHPMCounterNum{1'b1}}, {MHPMCounterNum{1'b0}}, 3'b000};
assign mcountinhibit = mcountinhibit_q | mcountinhibit_force;
// event selection (hardwired) & control
always_comb begin : gen_mhpmcounter_incr
// When adding or altering performance counter meanings and default
// mappings please update dv/verilator/pcount/cpp/ibex_pcounts.cc
// appropriately.
//
// active counters
mhpmcounter_incr[0] = 1'b1; // mcycle
mhpmcounter_incr[1] = 1'b0; // reserved
mhpmcounter_incr[2] = instr_ret_i; // minstret
mhpmcounter_incr[3] = lsu_busy_i; // cycles waiting for data memory
mhpmcounter_incr[4] = imiss_i & ~pc_set_i; // cycles waiting for instr fetches
// excl. jump and branch set cycles
mhpmcounter_incr[5] = mem_load_i; // num of loads
mhpmcounter_incr[6] = mem_store_i; // num of stores
mhpmcounter_incr[7] = jump_i; // num of jumps (unconditional)
mhpmcounter_incr[8] = branch_i; // num of branches (conditional)
mhpmcounter_incr[9] = branch_taken_i; // num of taken branches (conditional)
mhpmcounter_incr[10] = instr_ret_compressed_i; // num of compressed instr
// inactive counters
for (int unsigned i=3+MHPMCounterNum; i<32; i++) begin : gen_mhpmcounter_incr_inactive
mhpmcounter_incr[i] = 1'b0;
end
end
// event selector (hardwired, 0 means no event)
always_comb begin : gen_mhpmevent
// activate all
for (int i=0; i<32; i++) begin : gen_mhpmevent_active
mhpmevent[i] = '0;
mhpmevent[i][i] = 1'b1;
end
// deactivate
mhpmevent[1] = '0; // not existing, reserved
for (int unsigned i=3+MHPMCounterNum; i<32; i++) begin : gen_mhpmevent_inactive
mhpmevent[i] = '0;
end
end
// mask, controls effective counter width
always_comb begin : gen_mask
for (int i=0; i<3; i++) begin : gen_mask_fixed
// mcycle, mtime, minstret are always 64 bit wide
mhpmcounter_mask[i] = {64{1'b1}};
end
for (int unsigned i=3; i<3+MHPMCounterNum; i++) begin : gen_mask_configurable
// mhpmcounters have a configurable width
mhpmcounter_mask[i] = {{64-MHPMCounterWidth{1'b0}}, {MHPMCounterWidth{1'b1}}};
end
for (int unsigned i=3+MHPMCounterNum; i<32; i++) begin : gen_mask_inactive
// mask inactive mhpmcounters
mhpmcounter_mask[i] = '0;
end
end
// update
always_comb begin : mhpmcounter_update
mhpmcounter_d = mhpmcounter_q;
for (int i=0; i<32; i++) begin : gen_mhpmcounter_update
// increment
if (mhpmcounter_incr[i] & ~mcountinhibit[i]) begin
mhpmcounter_d[i] = mhpmcounter_mask[i] & (mhpmcounter_q[i] + 64'h1);
end
// write
if (mhpmcounter_we[i]) begin
mhpmcounter_d[i][31: 0] = mhpmcounter_mask[i][31: 0] & csr_wdata_int;
end else if (mhpmcounterh_we[i]) begin
mhpmcounter_d[i][63:32] = mhpmcounter_mask[i][63:32] & csr_wdata_int;
end
end
end
// performance monitor registers
always_ff @(posedge clk_i or negedge rst_ni) begin : perf_counter_registers
if (!rst_ni) begin
mcountinhibit_q <= '0;
for (int i=0; i<32; i++) begin
mhpmcounter_q[i] <= '0;
end
end else begin
mhpmcounter_q <= mhpmcounter_d;
mcountinhibit_q <= mcountinhibit_d;
end
end
endmodule