mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-19 03:44:46 -04:00
Follow-up to the discussion on extending Linux support to the Ara vector processor. * Main changes: Add: Add external MMU interface to share the MMU with the external accelerator. Add avoid_neg() function used to clip negative numbers to zero. Useful for parametric array sizes and vector multipliers. Modifications: 2 commit ports by default in cv64a6_imafdcv_config_pkg. Change exception_t from localparam to param in cva6.sv. Add parameters accelerator_req_t, accelerator_resp_t, acc_mmu_req_t, and acc_mmu_resp_t to cva6.sv. Replace the fall-through register with a spill register in acc_dispatcher to decouple timing with the accelerator. Decrease cache sizes in cv64a6_imafdcv_sv39_config_pkg. Modify Bender.yml package name from ariane to cva6. Add harmless code to prevent synthesizer tool from crashing when compiling csr_regfile. * Collateral changes: Fixes: Guard some X-IF code lines with correct parameter in cva6.sv. Parametrize the tracer interface with NrCommitPorts. Add missing local dependencies to Bender.yml. --------- Co-authored-by: JeanRochCoulon <jean-roch.coulon@thalesgroup.com>
2786 lines
114 KiB
Systemverilog
2786 lines
114 KiB
Systemverilog
// Copyright 2018 ETH Zurich and University of Bologna.
|
|
// Copyright and related rights are licensed under the Solderpad Hardware
|
|
// License, Version 0.51 (the "License"); you may not use this file except in
|
|
// compliance with the License. You may obtain a copy of the License at
|
|
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
|
// or agreed to in writing, software, hardware and materials distributed under
|
|
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
//
|
|
// Author: Florian Zaruba, ETH Zurich
|
|
// Date: 05.05.2017
|
|
// Description: CSR Register File as specified by RISC-V
|
|
|
|
|
|
module csr_regfile
|
|
import ariane_pkg::*;
|
|
#(
|
|
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
|
|
parameter type exception_t = logic,
|
|
parameter type jvt_t = logic,
|
|
parameter type irq_ctrl_t = logic,
|
|
parameter type scoreboard_entry_t = logic,
|
|
parameter type rvfi_probes_csr_t = logic,
|
|
parameter int VmidWidth = 1,
|
|
parameter int unsigned MHPMCounterNum = 6
|
|
) (
|
|
// Subsystem Clock - SUBSYSTEM
|
|
input logic clk_i,
|
|
// Asynchronous reset active low - SUBSYSTEM
|
|
input logic rst_ni,
|
|
// Timer threw a interrupt - SUBSYSTEM
|
|
input logic time_irq_i,
|
|
// send a flush request out when a CSR with a side effect changes - CONTROLLER
|
|
output logic flush_o,
|
|
// halt requested - CONTROLLER
|
|
output logic halt_csr_o,
|
|
// Instruction to be committed - ID_STAGE
|
|
input scoreboard_entry_t commit_instr_i,
|
|
// Commit acknowledged a instruction -> increase instret CSR - COMMIT_STAGE
|
|
input logic [CVA6Cfg.NrCommitPorts-1:0] commit_ack_i,
|
|
// Address from which to start booting, mtvec is set to the same address - SUBSYSTEM
|
|
input logic [CVA6Cfg.VLEN-1:0] boot_addr_i,
|
|
// Hart id in a multicore environment (reflected in a CSR) - SUBSYSTEM
|
|
input logic [CVA6Cfg.XLEN-1:0] hart_id_i,
|
|
// We've got an exception from the commit stage, take it - COMMIT_STAGE
|
|
input exception_t ex_i,
|
|
// Operation to perform on the CSR file - COMMIT_STAGE
|
|
input fu_op csr_op_i,
|
|
// Address of the register to read/write - EX_STAGE
|
|
input logic [11:0] csr_addr_i,
|
|
// Write data in - COMMIT_STAGE
|
|
input logic [CVA6Cfg.XLEN-1:0] csr_wdata_i,
|
|
// Read data out - COMMIT_STAGE
|
|
output logic [CVA6Cfg.XLEN-1:0] csr_rdata_o,
|
|
// Mark the FP sate as dirty - COMMIT_STAGE
|
|
input logic dirty_fp_state_i,
|
|
// Write fflags register e.g.: we are retiring a floating point instruction - COMMIT_STAGE
|
|
input logic csr_write_fflags_i,
|
|
// Mark the V state as dirty - ACC_DISPATCHER
|
|
input logic dirty_v_state_i,
|
|
// PC of instruction accessing the CSR - COMMIT_STAGE
|
|
input logic [CVA6Cfg.VLEN-1:0] pc_i,
|
|
// attempts to access a CSR without appropriate privilege - COMMIT_STAGE
|
|
output exception_t csr_exception_o,
|
|
// Output the exception PC to PC Gen, the correct CSR (mepc, sepc) is set accordingly - FRONTEND
|
|
output logic [CVA6Cfg.VLEN-1:0] epc_o,
|
|
// Return from exception, set the PC of epc_o - FRONTEND
|
|
output logic eret_o,
|
|
// Output base of exception vector, correct CSR is output (mtvec, stvec) - FRONTEND
|
|
output logic [CVA6Cfg.VLEN-1:0] trap_vector_base_o,
|
|
// Current privilege level the CPU is in - EX_STAGE
|
|
output riscv::priv_lvl_t priv_lvl_o,
|
|
// Current virtualization mode state the CPU is in - EX_STAGE
|
|
output logic v_o,
|
|
// Imprecise FP exception from the accelerator (fcsr.fflags format) - ACC_DISPATCHER
|
|
input logic [4:0] acc_fflags_ex_i,
|
|
// An FP exception from the accelerator occurred - ACC_DISPATCHER
|
|
input logic acc_fflags_ex_valid_i,
|
|
// Floating point extension status - ID_STAGE
|
|
output riscv::xs_t fs_o,
|
|
// Floating point extension virtual status - ID_STAGE
|
|
output riscv::xs_t vfs_o,
|
|
// Floating-Point Accured Exceptions - COMMIT_STAGE
|
|
output logic [4:0] fflags_o,
|
|
// Floating-Point Dynamic Rounding Mode - EX_STAGE
|
|
output logic [2:0] frm_o,
|
|
// Floating-Point Precision Control - EX_STAGE
|
|
output logic [6:0] fprec_o,
|
|
// Vector extension status - ID_STAGE
|
|
output riscv::xs_t vs_o,
|
|
// interrupt management to id stage - ID_STAGE
|
|
output irq_ctrl_t irq_ctrl_o,
|
|
// Enable virtual address translation - EX_STAGE
|
|
output logic en_translation_o,
|
|
// Enable G-Stage address translation - EX_STAGE
|
|
output logic en_g_translation_o,
|
|
// Enable virtual address translation for load and stores - EX_STAGE
|
|
output logic en_ld_st_translation_o,
|
|
// Enable G-Stage address translation for load and stores - EX_STAGE
|
|
output logic en_ld_st_g_translation_o,
|
|
// Privilege level at which load and stores should happen - EX_STAGE
|
|
output riscv::priv_lvl_t ld_st_priv_lvl_o,
|
|
// Virtualization mode at which load and stores should happen - EX_STAGE
|
|
output logic ld_st_v_o,
|
|
// Current instruction is a Hypervisor Load/Store Instruction - EX_STAGE
|
|
input logic csr_hs_ld_st_inst_i,
|
|
// Supervisor User Memory - EX_STAGE
|
|
output logic sum_o,
|
|
// Virtual Supervisor User Memory - EX_STAGE
|
|
output logic vs_sum_o,
|
|
// Make Executable Readable - EX_STAGE
|
|
output logic mxr_o,
|
|
// Make Executable Readable for VS-mode - EX_STAGE
|
|
output logic vmxr_o,
|
|
// TO_BE_COMPLETED - EX_STAGE
|
|
output logic [CVA6Cfg.PPNW-1:0] satp_ppn_o,
|
|
// TO_BE_COMPLETED - EX_STAGE
|
|
output logic [CVA6Cfg.ASID_WIDTH-1:0] asid_o,
|
|
// TO_BE_COMPLETED - EX_STAGE
|
|
output logic [CVA6Cfg.PPNW-1:0] vsatp_ppn_o,
|
|
// TO_BE_COMPLETED - EX_STAGE
|
|
output logic [CVA6Cfg.ASID_WIDTH-1:0] vs_asid_o,
|
|
// TO_BE_COMPLETED - EX_STAGE
|
|
output logic [CVA6Cfg.PPNW-1:0] hgatp_ppn_o,
|
|
// TO_BE_COMPLETED - EX_STAGE
|
|
output logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_o,
|
|
// external interrupt in - SUBSYSTEM
|
|
input logic [1:0] irq_i,
|
|
// inter processor interrupt -> connected to machine mode sw - SUBSYSTEM
|
|
input logic ipi_i,
|
|
// debug request in - ID_STAGE
|
|
input logic debug_req_i,
|
|
// TO_BE_COMPLETED - FRONTEND
|
|
output logic set_debug_pc_o,
|
|
// trap virtual memory - ID_STAGE
|
|
output logic tvm_o,
|
|
// timeout wait - ID_STAGE
|
|
output logic tw_o,
|
|
// virtual timeout wait - ID_STAGE
|
|
output logic vtw_o,
|
|
// trap sret - ID_STAGE
|
|
output logic tsr_o,
|
|
// hypervisor user mode - ID_STAGE
|
|
output logic hu_o,
|
|
// we are in debug mode -> that will change some decoding - EX_STAGE
|
|
output logic debug_mode_o,
|
|
// we are in single-step mode - COMMIT_STAGE
|
|
output logic single_step_o,
|
|
// L1 ICache Enable - CACHE
|
|
output logic icache_en_o,
|
|
// L1 DCache Enable - CACHE
|
|
output logic dcache_en_o,
|
|
// Accelerator memory consistent mode - ACC_DISPATCHER
|
|
output logic acc_cons_en_o,
|
|
// read/write address to performance counter module - PERF_COUNTERS
|
|
output logic [11:0] perf_addr_o,
|
|
// write data to performance counter module - PERF_COUNTERS
|
|
output logic [CVA6Cfg.XLEN-1:0] perf_data_o,
|
|
// read data from performance counter module - PERF_COUNTERS
|
|
input logic [CVA6Cfg.XLEN-1:0] perf_data_i,
|
|
// TO_BE_COMPLETED - PERF_COUNTERS
|
|
output logic perf_we_o,
|
|
// PMP configuration containing pmpcfg for max 64 PMPs - ACC_DISPATCHER
|
|
output riscv::pmpcfg_t [avoid_neg(CVA6Cfg.NrPMPEntries-1):0] pmpcfg_o,
|
|
// PMP addresses - ACC_DISPATCHER
|
|
output logic [avoid_neg(CVA6Cfg.NrPMPEntries-1):0][CVA6Cfg.PLEN-3:0] pmpaddr_o,
|
|
// TO_BE_COMPLETED - PERF_COUNTERS
|
|
output logic [31:0] mcountinhibit_o,
|
|
// RVFI
|
|
output rvfi_probes_csr_t rvfi_csr_o,
|
|
//jvt output
|
|
output jvt_t jvt_o
|
|
);
|
|
|
|
localparam logic [63:0] SMODE_STATUS_READ_MASK = ariane_pkg::smode_status_read_mask(CVA6Cfg);
|
|
localparam logic [63:0] HS_DELEG_INTERRUPTS = {
|
|
{32{1'b0}}, ariane_pkg::hs_deleg_interrupts(CVA6Cfg)
|
|
};
|
|
localparam logic [63:0] VS_DELEG_INTERRUPTS = {
|
|
{32{1'b0}}, ariane_pkg::vs_deleg_interrupts(CVA6Cfg)
|
|
};
|
|
localparam int SELECT_COUNTER_WIDTH = CVA6Cfg.IS_XLEN64 ? 6 : 5;
|
|
|
|
typedef struct packed {
|
|
logic [CVA6Cfg.ModeW-1:0] mode;
|
|
logic [CVA6Cfg.ASIDW-1:0] asid;
|
|
logic [CVA6Cfg.PPNW-1:0] ppn;
|
|
} satp_t;
|
|
|
|
typedef struct packed {
|
|
logic [CVA6Cfg.ModeW-1:0] mode;
|
|
logic [1:0] warl0;
|
|
logic [CVA6Cfg.VMIDW-1:0] vmid;
|
|
logic [CVA6Cfg.PPNW-1:0] ppn;
|
|
} hgatp_t;
|
|
|
|
// internal signal to keep track of access exceptions
|
|
logic read_access_exception, update_access_exception, privilege_violation;
|
|
logic virtual_read_access_exception, virtual_update_access_exception, virtual_privilege_violation;
|
|
logic csr_we, csr_read;
|
|
logic [CVA6Cfg.XLEN-1:0] csr_wdata, csr_rdata;
|
|
riscv::priv_lvl_t trap_to_priv_lvl;
|
|
logic trap_to_v;
|
|
// register for enabling load store address translation, this is critical, hence the register
|
|
logic en_ld_st_translation_d, en_ld_st_translation_q;
|
|
logic en_ld_st_g_translation_d, en_ld_st_g_translation_q;
|
|
logic mprv;
|
|
logic mret; // return from M-mode exception
|
|
logic sret; // return from S-mode exception
|
|
logic dret; // return from debug mode
|
|
// CSR write causes us to mark the FPU state as dirty
|
|
logic dirty_fp_state_csr;
|
|
riscv::mstatus_rv_t mstatus_q, mstatus_d;
|
|
riscv::hstatus_rv_t hstatus_q, hstatus_d;
|
|
riscv::mstatus_rv_t vsstatus_q, vsstatus_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mstatus_extended;
|
|
logic [CVA6Cfg.XLEN-1:0] vsstatus_extended;
|
|
satp_t satp_q, satp_d;
|
|
satp_t vsatp_q, vsatp_d;
|
|
hgatp_t hgatp_q, hgatp_d;
|
|
riscv::dcsr_t dcsr_q, dcsr_d;
|
|
riscv::csr_t csr_addr;
|
|
riscv::csr_t conv_csr_addr;
|
|
// privilege level register
|
|
riscv::priv_lvl_t priv_lvl_d, priv_lvl_q;
|
|
logic v_q, v_d; // virtualization mode
|
|
// we are in debug
|
|
logic debug_mode_q, debug_mode_d;
|
|
logic mtvec_rst_load_q; // used to determine whether we came out of reset
|
|
|
|
logic [CVA6Cfg.XLEN-1:0] dpc_q, dpc_d;
|
|
logic [CVA6Cfg.XLEN-1:0] dscratch0_q, dscratch0_d;
|
|
logic [CVA6Cfg.XLEN-1:0] dscratch1_q, dscratch1_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mtvec_q, mtvec_d;
|
|
logic [CVA6Cfg.XLEN-1:0] medeleg_q, medeleg_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mideleg_q, mideleg_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mip_q, mip_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mie_q, mie_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mcounteren_q, mcounteren_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mscratch_q, mscratch_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mepc_q, mepc_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mcause_q, mcause_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mtval_q, mtval_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mtinst_q, mtinst_d;
|
|
logic [CVA6Cfg.XLEN-1:0] mtval2_q, mtval2_d;
|
|
logic fiom_d, fiom_q;
|
|
|
|
logic [CVA6Cfg.XLEN-1:0] stvec_q, stvec_d;
|
|
logic [CVA6Cfg.XLEN-1:0] scounteren_q, scounteren_d;
|
|
logic [CVA6Cfg.XLEN-1:0] sscratch_q, sscratch_d;
|
|
logic [CVA6Cfg.XLEN-1:0] sepc_q, sepc_d;
|
|
logic [CVA6Cfg.XLEN-1:0] scause_q, scause_d;
|
|
logic [CVA6Cfg.XLEN-1:0] stval_q, stval_d;
|
|
|
|
logic [CVA6Cfg.XLEN-1:0] hedeleg_q, hedeleg_d;
|
|
logic [CVA6Cfg.XLEN-1:0] hideleg_q, hideleg_d;
|
|
logic [CVA6Cfg.XLEN-1:0] hcounteren_q, hcounteren_d;
|
|
logic [CVA6Cfg.XLEN-1:0] hgeie_q, hgeie_d;
|
|
logic [CVA6Cfg.XLEN-1:0] htinst_q, htinst_d;
|
|
logic [CVA6Cfg.XLEN-1:0] htval_q, htval_d;
|
|
|
|
logic [CVA6Cfg.XLEN-1:0] vstvec_q, vstvec_d;
|
|
logic [CVA6Cfg.XLEN-1:0] vsscratch_q, vsscratch_d;
|
|
logic [CVA6Cfg.XLEN-1:0] vsepc_q, vsepc_d;
|
|
logic [CVA6Cfg.XLEN-1:0] vscause_q, vscause_d;
|
|
logic [CVA6Cfg.XLEN-1:0] vstval_q, vstval_d;
|
|
|
|
logic [CVA6Cfg.XLEN-1:0] dcache_q, dcache_d;
|
|
logic [CVA6Cfg.XLEN-1:0] icache_q, icache_d;
|
|
logic [CVA6Cfg.XLEN-1:0] acc_cons_q, acc_cons_d;
|
|
|
|
logic wfi_d, wfi_q;
|
|
|
|
logic [63:0] cycle_q, cycle_d;
|
|
logic [63:0] instret_q, instret_d;
|
|
|
|
riscv::pmpcfg_t [63:0] pmpcfg_q, pmpcfg_d, pmpcfg_next;
|
|
logic [63:0][CVA6Cfg.PLEN-3:0] pmpaddr_q, pmpaddr_d, pmpaddr_next;
|
|
logic [MHPMCounterNum+3-1:0] mcountinhibit_d, mcountinhibit_q;
|
|
|
|
localparam logic [CVA6Cfg.XLEN-1:0] IsaCode = (CVA6Cfg.XLEN'(CVA6Cfg.RVA) << 0) // A - Atomic Instructions extension
|
|
| (CVA6Cfg.XLEN'(CVA6Cfg.RVB) << 1) // B - Bitmanip extension
|
|
| (CVA6Cfg.XLEN'(CVA6Cfg.RVC) << 2) // C - Compressed extension
|
|
| (CVA6Cfg.XLEN'(CVA6Cfg.RVD) << 3) // D - Double precision floating-point extension
|
|
| (CVA6Cfg.XLEN'(CVA6Cfg.RVF) << 5) // F - Single precision floating-point extension
|
|
| (CVA6Cfg.XLEN'(CVA6Cfg.RVH) << 7) // H - Hypervisor extension
|
|
| (CVA6Cfg.XLEN'(1) << 8) // I - RV32I/64I/128I base ISA
|
|
| (CVA6Cfg.XLEN'(1) << 12) // M - Integer Multiply/Divide extension
|
|
| (CVA6Cfg.XLEN'(0) << 13) // N - User level interrupts supported
|
|
| (CVA6Cfg.XLEN'(CVA6Cfg.RVS) << 18) // S - Supervisor mode implemented
|
|
| (CVA6Cfg.XLEN'(CVA6Cfg.RVU) << 20) // U - User mode implemented
|
|
| (CVA6Cfg.XLEN'(CVA6Cfg.RVV) << 21) // V - Vector extension
|
|
| (CVA6Cfg.XLEN'(CVA6Cfg.NSX) << 23) // X - Non-standard extensions present
|
|
| ((CVA6Cfg.XLEN == 64 ? 2 : 1) << CVA6Cfg.XLEN - 2); // MXL
|
|
|
|
assign pmpcfg_o = pmpcfg_q[(CVA6Cfg.NrPMPEntries>0?CVA6Cfg.NrPMPEntries-1 : 0):0];
|
|
assign pmpaddr_o = pmpaddr_q[(CVA6Cfg.NrPMPEntries>0?CVA6Cfg.NrPMPEntries-1 : 0):0];
|
|
|
|
riscv::fcsr_t fcsr_q, fcsr_d;
|
|
jvt_t jvt_q, jvt_d;
|
|
// ----------------
|
|
// Assignments
|
|
// ----------------
|
|
assign csr_addr = riscv::csr_t'(csr_addr_i);
|
|
assign conv_csr_addr = (CVA6Cfg.RVH) ? riscv::convert_vs_access_csr(
|
|
(riscv::csr_t'(csr_addr_i)), v_q
|
|
) : csr_addr;
|
|
assign fs_o = mstatus_q.fs;
|
|
assign vfs_o = (CVA6Cfg.RVH) ? vsstatus_q.fs : riscv::Off;
|
|
assign vs_o = mstatus_q.vs;
|
|
// ----------------
|
|
// CSR Read logic
|
|
// ----------------
|
|
assign mstatus_extended = CVA6Cfg.IS_XLEN64 ? mstatus_q[CVA6Cfg.XLEN-1:0] :
|
|
{mstatus_q.sd, mstatus_q.wpri3[7:0], mstatus_q[22:0]};
|
|
if (CVA6Cfg.RVH) begin
|
|
if (CVA6Cfg.IS_XLEN64) begin : gen_vsstatus_64read
|
|
assign vsstatus_extended = vsstatus_q[CVA6Cfg.XLEN-1:0];
|
|
end else begin : gen_vsstatus_32read
|
|
assign vsstatus_extended = {vsstatus_q.sd, vsstatus_q.wpri3[7:0], vsstatus_q[22:0]};
|
|
end
|
|
end else begin
|
|
assign vsstatus_extended = '0;
|
|
end
|
|
|
|
always_comb begin : csr_read_process
|
|
// a read access exception can only occur if we attempt to read a CSR which does not exist
|
|
read_access_exception = 1'b0;
|
|
virtual_read_access_exception = 1'b0;
|
|
csr_rdata = '0;
|
|
perf_addr_o = csr_addr.address[11:0];
|
|
|
|
if (csr_read) begin
|
|
unique case (conv_csr_addr.address)
|
|
riscv::CSR_FFLAGS: begin
|
|
if (CVA6Cfg.FpPresent && !(mstatus_q.fs == riscv::Off || (CVA6Cfg.RVH && v_q && vsstatus_q.fs == riscv::Off))) begin
|
|
csr_rdata = {{CVA6Cfg.XLEN - 5{1'b0}}, fcsr_q.fflags};
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_FRM: begin
|
|
if (CVA6Cfg.FpPresent && !(mstatus_q.fs == riscv::Off || (CVA6Cfg.RVH && v_q && vsstatus_q.fs == riscv::Off))) begin
|
|
csr_rdata = {{CVA6Cfg.XLEN - 3{1'b0}}, fcsr_q.frm};
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_FCSR: begin
|
|
if (CVA6Cfg.FpPresent && !(mstatus_q.fs == riscv::Off || (CVA6Cfg.RVH && v_q && vsstatus_q.fs == riscv::Off))) begin
|
|
csr_rdata = {{CVA6Cfg.XLEN - 8{1'b0}}, fcsr_q.frm, fcsr_q.fflags};
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_JVT: begin
|
|
if (CVA6Cfg.RVZCMT) begin
|
|
csr_rdata = {jvt_q.base, jvt_q.mode};
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
end
|
|
// non-standard extension
|
|
riscv::CSR_FTRAN: begin
|
|
if (CVA6Cfg.FpPresent && !(mstatus_q.fs == riscv::Off || (CVA6Cfg.RVH && v_q && vsstatus_q.fs == riscv::Off))) begin
|
|
csr_rdata = {{CVA6Cfg.XLEN - 7{1'b0}}, fcsr_q.fprec};
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
end
|
|
// debug registers
|
|
riscv::CSR_DCSR:
|
|
if (CVA6Cfg.DebugEn) csr_rdata = {{CVA6Cfg.XLEN - 32{1'b0}}, dcsr_q};
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_DPC:
|
|
if (CVA6Cfg.DebugEn) csr_rdata = dpc_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_DSCRATCH0:
|
|
if (CVA6Cfg.DebugEn) csr_rdata = dscratch0_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_DSCRATCH1:
|
|
if (CVA6Cfg.DebugEn) csr_rdata = dscratch1_q;
|
|
else read_access_exception = 1'b1;
|
|
// trigger module registers
|
|
riscv::CSR_TSELECT: read_access_exception = 1'b1; // not implemented
|
|
riscv::CSR_TDATA1: read_access_exception = 1'b1; // not implemented
|
|
riscv::CSR_TDATA2: read_access_exception = 1'b1; // not implemented
|
|
riscv::CSR_TDATA3: read_access_exception = 1'b1; // not implemented
|
|
riscv::CSR_VSSTATUS:
|
|
if (CVA6Cfg.RVH) csr_rdata = vsstatus_extended;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_VSIE:
|
|
if (CVA6Cfg.RVH)
|
|
csr_rdata = (mie_q & VS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0] & hideleg_q) >> 1;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_VSIP:
|
|
if (CVA6Cfg.RVH)
|
|
csr_rdata = (mip_q & VS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0] & hideleg_q) >> 1;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_VSTVEC:
|
|
if (CVA6Cfg.RVH) csr_rdata = vstvec_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_VSSCRATCH:
|
|
if (CVA6Cfg.RVH) csr_rdata = vsscratch_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_VSEPC:
|
|
if (CVA6Cfg.RVH) csr_rdata = vsepc_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_VSCAUSE:
|
|
if (CVA6Cfg.RVH) csr_rdata = vscause_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_VSTVAL:
|
|
if (CVA6Cfg.RVH) csr_rdata = vstval_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_VSATP:
|
|
// intercept reads to VSATP if in VS-Mode and VTVM is enabled
|
|
if (CVA6Cfg.RVH) begin
|
|
if (priv_lvl_o == riscv::PRIV_LVL_S && hstatus_q.vtvm && v_q)
|
|
virtual_read_access_exception = 1'b1;
|
|
else csr_rdata = vsatp_q;
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
// supervisor registers
|
|
riscv::CSR_SSTATUS: begin
|
|
if (CVA6Cfg.RVS) csr_rdata = mstatus_extended & SMODE_STATUS_READ_MASK[CVA6Cfg.XLEN-1:0];
|
|
else read_access_exception = 1'b1;
|
|
end
|
|
riscv::CSR_SIE:
|
|
if (CVA6Cfg.RVS)
|
|
csr_rdata = (CVA6Cfg.RVH) ? mie_q & mideleg_q & ~HS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0] : mie_q & mideleg_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_SIP:
|
|
if (CVA6Cfg.RVS)
|
|
csr_rdata = (CVA6Cfg.RVH) ? mip_q & mideleg_q & ~HS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0] : mip_q & mideleg_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_STVEC:
|
|
if (CVA6Cfg.RVS) csr_rdata = stvec_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_SCOUNTEREN:
|
|
if (CVA6Cfg.RVS) csr_rdata = scounteren_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_SSCRATCH:
|
|
if (CVA6Cfg.RVS) csr_rdata = sscratch_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_SEPC:
|
|
if (CVA6Cfg.RVS) csr_rdata = sepc_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_SCAUSE:
|
|
if (CVA6Cfg.RVS) csr_rdata = scause_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_STVAL:
|
|
if (CVA6Cfg.RVS) csr_rdata = stval_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_SATP: begin
|
|
if (CVA6Cfg.RVS) begin
|
|
// intercept reads to SATP if in S-Mode and TVM is enabled
|
|
if (priv_lvl_o == riscv::PRIV_LVL_S && mstatus_q.tvm) begin
|
|
read_access_exception = 1'b1;
|
|
end else begin
|
|
csr_rdata = satp_q;
|
|
end
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_SENVCFG:
|
|
if (CVA6Cfg.RVS) csr_rdata = '0 | fiom_q;
|
|
else read_access_exception = 1'b1;
|
|
// hypervisor mode registers
|
|
riscv::CSR_HSTATUS:
|
|
if (CVA6Cfg.RVH) csr_rdata = hstatus_q[CVA6Cfg.XLEN-1:0];
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HEDELEG:
|
|
if (CVA6Cfg.RVH) csr_rdata = hedeleg_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HIDELEG:
|
|
if (CVA6Cfg.RVH) csr_rdata = hideleg_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HIE:
|
|
if (CVA6Cfg.RVH) csr_rdata = mie_q & HS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0];
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HIP:
|
|
if (CVA6Cfg.RVH) csr_rdata = mip_q & HS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0];
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HVIP:
|
|
if (CVA6Cfg.RVH) csr_rdata = mip_q & VS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0];
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HCOUNTEREN:
|
|
if (CVA6Cfg.RVH) csr_rdata = hcounteren_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HTVAL:
|
|
if (CVA6Cfg.RVH) csr_rdata = htval_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HTINST:
|
|
if (CVA6Cfg.RVH) csr_rdata = htinst_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HGEIE:
|
|
if (CVA6Cfg.RVH) csr_rdata = '0;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HGEIP:
|
|
if (CVA6Cfg.RVH) csr_rdata = '0;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HENVCFG:
|
|
if (CVA6Cfg.RVH) csr_rdata = '0 | {{CVA6Cfg.XLEN - 1{1'b0}}, fiom_q};
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_HGATP: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
// intercept reads to HGATP if in HS-Mode and TVM is enabled
|
|
if (priv_lvl_o == riscv::PRIV_LVL_S && !v_q && mstatus_q.tvm) begin
|
|
read_access_exception = 1'b1;
|
|
end else begin
|
|
csr_rdata = hgatp_q;
|
|
end
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
end
|
|
|
|
// machine mode registers
|
|
riscv::CSR_MSTATUS: csr_rdata = mstatus_extended;
|
|
riscv::CSR_MSTATUSH:
|
|
if (CVA6Cfg.XLEN == 32) csr_rdata = '0;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_MISA: csr_rdata = IsaCode;
|
|
riscv::CSR_MEDELEG:
|
|
if (CVA6Cfg.RVS) csr_rdata = medeleg_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_MIDELEG:
|
|
if (CVA6Cfg.RVS) csr_rdata = mideleg_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_MIE: csr_rdata = mie_q;
|
|
riscv::CSR_MTVEC: csr_rdata = mtvec_q;
|
|
riscv::CSR_MCOUNTEREN:
|
|
if (CVA6Cfg.RVU) csr_rdata = mcounteren_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_MSCRATCH: csr_rdata = mscratch_q;
|
|
riscv::CSR_MEPC: csr_rdata = mepc_q;
|
|
riscv::CSR_MCAUSE: csr_rdata = mcause_q;
|
|
riscv::CSR_MTVAL:
|
|
if (CVA6Cfg.TvalEn) csr_rdata = mtval_q;
|
|
else csr_rdata = '0;
|
|
riscv::CSR_MTINST:
|
|
if (CVA6Cfg.RVH) csr_rdata = mtinst_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_MTVAL2:
|
|
if (CVA6Cfg.RVH) csr_rdata = mtval2_q;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_MIP: csr_rdata = mip_q;
|
|
riscv::CSR_MENVCFG: begin
|
|
if (CVA6Cfg.RVU) csr_rdata = '0 | fiom_q;
|
|
else read_access_exception = 1'b1;
|
|
end
|
|
riscv::CSR_MENVCFGH: begin
|
|
if (CVA6Cfg.RVU && CVA6Cfg.XLEN == 32) csr_rdata = '0;
|
|
else read_access_exception = 1'b1;
|
|
end
|
|
riscv::CSR_MVENDORID: csr_rdata = {{CVA6Cfg.XLEN - 32{1'b0}}, OPENHWGROUP_MVENDORID};
|
|
riscv::CSR_MARCHID: csr_rdata = {{CVA6Cfg.XLEN - 32{1'b0}}, ARIANE_MARCHID};
|
|
riscv::CSR_MIMPID: csr_rdata = '0; // not implemented
|
|
riscv::CSR_MHARTID: csr_rdata = hart_id_i;
|
|
riscv::CSR_MCONFIGPTR: csr_rdata = '0; // not implemented
|
|
riscv::CSR_MCOUNTINHIBIT:
|
|
csr_rdata = {{(CVA6Cfg.XLEN - (MHPMCounterNum + 3)) {1'b0}}, mcountinhibit_q};
|
|
// Counters and Timers
|
|
riscv::CSR_MCYCLE: csr_rdata = cycle_q[CVA6Cfg.XLEN-1:0];
|
|
riscv::CSR_MCYCLEH:
|
|
if (CVA6Cfg.XLEN == 32) csr_rdata = cycle_q[63:32];
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_MINSTRET: csr_rdata = instret_q[CVA6Cfg.XLEN-1:0];
|
|
riscv::CSR_MINSTRETH:
|
|
if (CVA6Cfg.XLEN == 32) csr_rdata = instret_q[63:32];
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_CYCLE:
|
|
if (CVA6Cfg.RVZicntr) csr_rdata = cycle_q[CVA6Cfg.XLEN-1:0];
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_CYCLEH:
|
|
if (CVA6Cfg.RVZicntr)
|
|
if (CVA6Cfg.XLEN == 32) csr_rdata = cycle_q[63:32];
|
|
else read_access_exception = 1'b1;
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_INSTRET:
|
|
if (CVA6Cfg.RVZicntr) csr_rdata = instret_q[CVA6Cfg.XLEN-1:0];
|
|
else read_access_exception = 1'b1;
|
|
riscv::CSR_INSTRETH:
|
|
if (CVA6Cfg.RVZicntr)
|
|
if (CVA6Cfg.XLEN == 32) csr_rdata = instret_q[63:32];
|
|
else read_access_exception = 1'b1;
|
|
else read_access_exception = 1'b1;
|
|
//Event Selector
|
|
riscv::CSR_MHPM_EVENT_3,
|
|
riscv::CSR_MHPM_EVENT_4,
|
|
riscv::CSR_MHPM_EVENT_5,
|
|
riscv::CSR_MHPM_EVENT_6,
|
|
riscv::CSR_MHPM_EVENT_7,
|
|
riscv::CSR_MHPM_EVENT_8,
|
|
riscv::CSR_MHPM_EVENT_9,
|
|
riscv::CSR_MHPM_EVENT_10,
|
|
riscv::CSR_MHPM_EVENT_11,
|
|
riscv::CSR_MHPM_EVENT_12,
|
|
riscv::CSR_MHPM_EVENT_13,
|
|
riscv::CSR_MHPM_EVENT_14,
|
|
riscv::CSR_MHPM_EVENT_15,
|
|
riscv::CSR_MHPM_EVENT_16,
|
|
riscv::CSR_MHPM_EVENT_17,
|
|
riscv::CSR_MHPM_EVENT_18,
|
|
riscv::CSR_MHPM_EVENT_19,
|
|
riscv::CSR_MHPM_EVENT_20,
|
|
riscv::CSR_MHPM_EVENT_21,
|
|
riscv::CSR_MHPM_EVENT_22,
|
|
riscv::CSR_MHPM_EVENT_23,
|
|
riscv::CSR_MHPM_EVENT_24,
|
|
riscv::CSR_MHPM_EVENT_25,
|
|
riscv::CSR_MHPM_EVENT_26,
|
|
riscv::CSR_MHPM_EVENT_27,
|
|
riscv::CSR_MHPM_EVENT_28,
|
|
riscv::CSR_MHPM_EVENT_29,
|
|
riscv::CSR_MHPM_EVENT_30,
|
|
riscv::CSR_MHPM_EVENT_31 :
|
|
csr_rdata = perf_data_i;
|
|
|
|
riscv::CSR_MHPM_COUNTER_3,
|
|
riscv::CSR_MHPM_COUNTER_4,
|
|
riscv::CSR_MHPM_COUNTER_5,
|
|
riscv::CSR_MHPM_COUNTER_6,
|
|
riscv::CSR_MHPM_COUNTER_7,
|
|
riscv::CSR_MHPM_COUNTER_8,
|
|
riscv::CSR_MHPM_COUNTER_9,
|
|
riscv::CSR_MHPM_COUNTER_10,
|
|
riscv::CSR_MHPM_COUNTER_11,
|
|
riscv::CSR_MHPM_COUNTER_12,
|
|
riscv::CSR_MHPM_COUNTER_13,
|
|
riscv::CSR_MHPM_COUNTER_14,
|
|
riscv::CSR_MHPM_COUNTER_15,
|
|
riscv::CSR_MHPM_COUNTER_16,
|
|
riscv::CSR_MHPM_COUNTER_17,
|
|
riscv::CSR_MHPM_COUNTER_18,
|
|
riscv::CSR_MHPM_COUNTER_19,
|
|
riscv::CSR_MHPM_COUNTER_20,
|
|
riscv::CSR_MHPM_COUNTER_21,
|
|
riscv::CSR_MHPM_COUNTER_22,
|
|
riscv::CSR_MHPM_COUNTER_23,
|
|
riscv::CSR_MHPM_COUNTER_24,
|
|
riscv::CSR_MHPM_COUNTER_25,
|
|
riscv::CSR_MHPM_COUNTER_26,
|
|
riscv::CSR_MHPM_COUNTER_27,
|
|
riscv::CSR_MHPM_COUNTER_28,
|
|
riscv::CSR_MHPM_COUNTER_29,
|
|
riscv::CSR_MHPM_COUNTER_30,
|
|
riscv::CSR_MHPM_COUNTER_31 :
|
|
csr_rdata = perf_data_i;
|
|
|
|
riscv::CSR_MHPM_COUNTER_3H,
|
|
riscv::CSR_MHPM_COUNTER_4H,
|
|
riscv::CSR_MHPM_COUNTER_5H,
|
|
riscv::CSR_MHPM_COUNTER_6H,
|
|
riscv::CSR_MHPM_COUNTER_7H,
|
|
riscv::CSR_MHPM_COUNTER_8H,
|
|
riscv::CSR_MHPM_COUNTER_9H,
|
|
riscv::CSR_MHPM_COUNTER_10H,
|
|
riscv::CSR_MHPM_COUNTER_11H,
|
|
riscv::CSR_MHPM_COUNTER_12H,
|
|
riscv::CSR_MHPM_COUNTER_13H,
|
|
riscv::CSR_MHPM_COUNTER_14H,
|
|
riscv::CSR_MHPM_COUNTER_15H,
|
|
riscv::CSR_MHPM_COUNTER_16H,
|
|
riscv::CSR_MHPM_COUNTER_17H,
|
|
riscv::CSR_MHPM_COUNTER_18H,
|
|
riscv::CSR_MHPM_COUNTER_19H,
|
|
riscv::CSR_MHPM_COUNTER_20H,
|
|
riscv::CSR_MHPM_COUNTER_21H,
|
|
riscv::CSR_MHPM_COUNTER_22H,
|
|
riscv::CSR_MHPM_COUNTER_23H,
|
|
riscv::CSR_MHPM_COUNTER_24H,
|
|
riscv::CSR_MHPM_COUNTER_25H,
|
|
riscv::CSR_MHPM_COUNTER_26H,
|
|
riscv::CSR_MHPM_COUNTER_27H,
|
|
riscv::CSR_MHPM_COUNTER_28H,
|
|
riscv::CSR_MHPM_COUNTER_29H,
|
|
riscv::CSR_MHPM_COUNTER_30H,
|
|
riscv::CSR_MHPM_COUNTER_31H :
|
|
if (CVA6Cfg.XLEN == 32) csr_rdata = perf_data_i;
|
|
else read_access_exception = 1'b1;
|
|
|
|
// Performance counters (User Mode - R/O Shadows)
|
|
riscv::CSR_HPM_COUNTER_3,
|
|
riscv::CSR_HPM_COUNTER_4,
|
|
riscv::CSR_HPM_COUNTER_5,
|
|
riscv::CSR_HPM_COUNTER_6,
|
|
riscv::CSR_HPM_COUNTER_7,
|
|
riscv::CSR_HPM_COUNTER_8,
|
|
riscv::CSR_HPM_COUNTER_9,
|
|
riscv::CSR_HPM_COUNTER_10,
|
|
riscv::CSR_HPM_COUNTER_11,
|
|
riscv::CSR_HPM_COUNTER_12,
|
|
riscv::CSR_HPM_COUNTER_13,
|
|
riscv::CSR_HPM_COUNTER_14,
|
|
riscv::CSR_HPM_COUNTER_15,
|
|
riscv::CSR_HPM_COUNTER_16,
|
|
riscv::CSR_HPM_COUNTER_17,
|
|
riscv::CSR_HPM_COUNTER_18,
|
|
riscv::CSR_HPM_COUNTER_19,
|
|
riscv::CSR_HPM_COUNTER_20,
|
|
riscv::CSR_HPM_COUNTER_21,
|
|
riscv::CSR_HPM_COUNTER_22,
|
|
riscv::CSR_HPM_COUNTER_23,
|
|
riscv::CSR_HPM_COUNTER_24,
|
|
riscv::CSR_HPM_COUNTER_25,
|
|
riscv::CSR_HPM_COUNTER_26,
|
|
riscv::CSR_HPM_COUNTER_27,
|
|
riscv::CSR_HPM_COUNTER_28,
|
|
riscv::CSR_HPM_COUNTER_29,
|
|
riscv::CSR_HPM_COUNTER_30,
|
|
riscv::CSR_HPM_COUNTER_31 :
|
|
if (CVA6Cfg.RVZihpm) begin
|
|
csr_rdata = perf_data_i;
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
|
|
riscv::CSR_HPM_COUNTER_3H,
|
|
riscv::CSR_HPM_COUNTER_4H,
|
|
riscv::CSR_HPM_COUNTER_5H,
|
|
riscv::CSR_HPM_COUNTER_6H,
|
|
riscv::CSR_HPM_COUNTER_7H,
|
|
riscv::CSR_HPM_COUNTER_8H,
|
|
riscv::CSR_HPM_COUNTER_9H,
|
|
riscv::CSR_HPM_COUNTER_10H,
|
|
riscv::CSR_HPM_COUNTER_11H,
|
|
riscv::CSR_HPM_COUNTER_12H,
|
|
riscv::CSR_HPM_COUNTER_13H,
|
|
riscv::CSR_HPM_COUNTER_14H,
|
|
riscv::CSR_HPM_COUNTER_15H,
|
|
riscv::CSR_HPM_COUNTER_16H,
|
|
riscv::CSR_HPM_COUNTER_17H,
|
|
riscv::CSR_HPM_COUNTER_18H,
|
|
riscv::CSR_HPM_COUNTER_19H,
|
|
riscv::CSR_HPM_COUNTER_20H,
|
|
riscv::CSR_HPM_COUNTER_21H,
|
|
riscv::CSR_HPM_COUNTER_22H,
|
|
riscv::CSR_HPM_COUNTER_23H,
|
|
riscv::CSR_HPM_COUNTER_24H,
|
|
riscv::CSR_HPM_COUNTER_25H,
|
|
riscv::CSR_HPM_COUNTER_26H,
|
|
riscv::CSR_HPM_COUNTER_27H,
|
|
riscv::CSR_HPM_COUNTER_28H,
|
|
riscv::CSR_HPM_COUNTER_29H,
|
|
riscv::CSR_HPM_COUNTER_30H,
|
|
riscv::CSR_HPM_COUNTER_31H :
|
|
if (CVA6Cfg.RVZihpm) begin
|
|
if (CVA6Cfg.XLEN == 32) csr_rdata = perf_data_i;
|
|
else read_access_exception = 1'b1;
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
|
|
// custom (non RISC-V) cache control
|
|
riscv::CSR_DCACHE: csr_rdata = dcache_q;
|
|
riscv::CSR_ICACHE: csr_rdata = icache_q;
|
|
// custom (non RISC-V) accelerator memory consistency mode
|
|
riscv::CSR_ACC_CONS: begin
|
|
if (CVA6Cfg.EnableAccelerator) begin
|
|
csr_rdata = acc_cons_q;
|
|
end else begin
|
|
read_access_exception = 1'b1;
|
|
end
|
|
end
|
|
// PMPs
|
|
riscv::CSR_PMPCFG0,
|
|
riscv::CSR_PMPCFG1,
|
|
riscv::CSR_PMPCFG2,
|
|
riscv::CSR_PMPCFG3,
|
|
riscv::CSR_PMPCFG4,
|
|
riscv::CSR_PMPCFG5,
|
|
riscv::CSR_PMPCFG6,
|
|
riscv::CSR_PMPCFG7,
|
|
riscv::CSR_PMPCFG8,
|
|
riscv::CSR_PMPCFG9,
|
|
riscv::CSR_PMPCFG10,
|
|
riscv::CSR_PMPCFG11,
|
|
riscv::CSR_PMPCFG12,
|
|
riscv::CSR_PMPCFG13,
|
|
riscv::CSR_PMPCFG14,
|
|
riscv::CSR_PMPCFG15: begin
|
|
// index is calculated using PMPCFG0 as the offset
|
|
automatic logic [3:0] index = csr_addr.address[11:0] - riscv::CSR_PMPCFG0;
|
|
|
|
// if index is not even and XLEN==64, raise exception
|
|
if (CVA6Cfg.XLEN == 64 && index[0] == 1'b1) read_access_exception = 1'b1;
|
|
else begin
|
|
// The following line has no effect. It's here just to prevent the synthesizer from crashing
|
|
if (CVA6Cfg.XLEN == 64) index = (index >> 1) << 1;
|
|
csr_rdata = pmpcfg_q[index*4+:CVA6Cfg.XLEN/8];
|
|
end
|
|
end
|
|
// PMPADDR
|
|
riscv::CSR_PMPADDR0,
|
|
riscv::CSR_PMPADDR1,
|
|
riscv::CSR_PMPADDR2,
|
|
riscv::CSR_PMPADDR3,
|
|
riscv::CSR_PMPADDR4,
|
|
riscv::CSR_PMPADDR5,
|
|
riscv::CSR_PMPADDR6,
|
|
riscv::CSR_PMPADDR7,
|
|
riscv::CSR_PMPADDR8,
|
|
riscv::CSR_PMPADDR9,
|
|
riscv::CSR_PMPADDR10,
|
|
riscv::CSR_PMPADDR11,
|
|
riscv::CSR_PMPADDR12,
|
|
riscv::CSR_PMPADDR13,
|
|
riscv::CSR_PMPADDR14,
|
|
riscv::CSR_PMPADDR15,
|
|
riscv::CSR_PMPADDR16,
|
|
riscv::CSR_PMPADDR17,
|
|
riscv::CSR_PMPADDR18,
|
|
riscv::CSR_PMPADDR19,
|
|
riscv::CSR_PMPADDR20,
|
|
riscv::CSR_PMPADDR21,
|
|
riscv::CSR_PMPADDR22,
|
|
riscv::CSR_PMPADDR23,
|
|
riscv::CSR_PMPADDR24,
|
|
riscv::CSR_PMPADDR25,
|
|
riscv::CSR_PMPADDR26,
|
|
riscv::CSR_PMPADDR27,
|
|
riscv::CSR_PMPADDR28,
|
|
riscv::CSR_PMPADDR29,
|
|
riscv::CSR_PMPADDR30,
|
|
riscv::CSR_PMPADDR31,
|
|
riscv::CSR_PMPADDR32,
|
|
riscv::CSR_PMPADDR33,
|
|
riscv::CSR_PMPADDR34,
|
|
riscv::CSR_PMPADDR35,
|
|
riscv::CSR_PMPADDR36,
|
|
riscv::CSR_PMPADDR37,
|
|
riscv::CSR_PMPADDR38,
|
|
riscv::CSR_PMPADDR39,
|
|
riscv::CSR_PMPADDR40,
|
|
riscv::CSR_PMPADDR41,
|
|
riscv::CSR_PMPADDR42,
|
|
riscv::CSR_PMPADDR43,
|
|
riscv::CSR_PMPADDR44,
|
|
riscv::CSR_PMPADDR45,
|
|
riscv::CSR_PMPADDR46,
|
|
riscv::CSR_PMPADDR47,
|
|
riscv::CSR_PMPADDR48,
|
|
riscv::CSR_PMPADDR49,
|
|
riscv::CSR_PMPADDR50,
|
|
riscv::CSR_PMPADDR51,
|
|
riscv::CSR_PMPADDR52,
|
|
riscv::CSR_PMPADDR53,
|
|
riscv::CSR_PMPADDR54,
|
|
riscv::CSR_PMPADDR55,
|
|
riscv::CSR_PMPADDR56,
|
|
riscv::CSR_PMPADDR57,
|
|
riscv::CSR_PMPADDR58,
|
|
riscv::CSR_PMPADDR59,
|
|
riscv::CSR_PMPADDR60,
|
|
riscv::CSR_PMPADDR61,
|
|
riscv::CSR_PMPADDR62,
|
|
riscv::CSR_PMPADDR63: begin
|
|
// index is calculated using PMPADDR0 as the offset
|
|
automatic logic [11:0] index = csr_addr.address[11:0] - riscv::CSR_PMPADDR0;
|
|
// Important: we only support granularity 8 bytes (G=1)
|
|
// -> last bit of pmpaddr must be set 0/1 based on the mode:
|
|
// NA4, NAPOT: 1
|
|
// TOR, OFF: 0
|
|
if (pmpcfg_q[index].addr_mode[1] == 1'b1)
|
|
csr_rdata = {pmpaddr_q[index][CVA6Cfg.PLEN-3:1], 1'b1};
|
|
else csr_rdata = {pmpaddr_q[index][CVA6Cfg.PLEN-3:1], 1'b0};
|
|
end
|
|
default: read_access_exception = 1'b1;
|
|
endcase
|
|
end
|
|
end
|
|
// ---------------------------
|
|
// CSR Write and update logic
|
|
// ---------------------------
|
|
logic [CVA6Cfg.XLEN-1:0] mask;
|
|
always_comb begin : csr_update
|
|
automatic satp_t satp;
|
|
automatic satp_t vsatp;
|
|
automatic hgatp_t hgatp;
|
|
automatic logic [63:0] instret;
|
|
|
|
if (CVA6Cfg.RVS) begin
|
|
satp = satp_q;
|
|
end
|
|
if (CVA6Cfg.RVH) begin
|
|
hgatp = hgatp_q;
|
|
vsatp = vsatp_q;
|
|
end
|
|
instret = instret_q;
|
|
|
|
mcountinhibit_d = mcountinhibit_q;
|
|
|
|
// --------------------
|
|
// Counters
|
|
// --------------------
|
|
cycle_d = cycle_q;
|
|
instret_d = instret_q;
|
|
if (!(CVA6Cfg.DebugEn && debug_mode_q)) begin
|
|
// increase instruction retired counter
|
|
for (int i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin
|
|
if (commit_ack_i[i] && !ex_i.valid && (!CVA6Cfg.PerfCounterEn || (CVA6Cfg.PerfCounterEn && !mcountinhibit_q[2])))
|
|
instret++;
|
|
end
|
|
instret_d = instret;
|
|
// increment the cycle count
|
|
if (!CVA6Cfg.PerfCounterEn || (CVA6Cfg.PerfCounterEn && !mcountinhibit_q[0]))
|
|
cycle_d = cycle_q + 1'b1;
|
|
else cycle_d = cycle_q;
|
|
end
|
|
|
|
eret_o = 1'b0;
|
|
flush_o = 1'b0;
|
|
update_access_exception = 1'b0;
|
|
virtual_update_access_exception = 1'b0;
|
|
|
|
set_debug_pc_o = 1'b0;
|
|
|
|
perf_we_o = 1'b0;
|
|
perf_data_o = 'b0;
|
|
if (CVA6Cfg.RVZCMT) begin
|
|
jvt_d = jvt_q;
|
|
end
|
|
fcsr_d = fcsr_q;
|
|
|
|
priv_lvl_d = priv_lvl_q;
|
|
v_d = v_q;
|
|
debug_mode_d = debug_mode_q;
|
|
|
|
if (CVA6Cfg.DebugEn) begin
|
|
dcsr_d = dcsr_q;
|
|
dpc_d = dpc_q;
|
|
dscratch0_d = dscratch0_q;
|
|
dscratch1_d = dscratch1_q;
|
|
end
|
|
mstatus_d = mstatus_q;
|
|
if (CVA6Cfg.RVH) begin
|
|
hstatus_d = hstatus_q;
|
|
vsstatus_d = vsstatus_q;
|
|
end
|
|
|
|
// check whether we come out of reset
|
|
// this is a workaround. some tools have issues
|
|
// having boot_addr_i in the asynchronous
|
|
// reset assignment to mtvec_d, even though
|
|
// boot_addr_i will be assigned a constant
|
|
// on the top-level.
|
|
if (mtvec_rst_load_q) begin
|
|
mtvec_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, boot_addr_i} + 'h40;
|
|
end else begin
|
|
mtvec_d = mtvec_q;
|
|
end
|
|
|
|
if (CVA6Cfg.RVS) begin
|
|
medeleg_d = medeleg_q;
|
|
mideleg_d = mideleg_q;
|
|
end
|
|
mip_d = mip_q;
|
|
mie_d = mie_q;
|
|
mepc_d = mepc_q;
|
|
mcause_d = mcause_q;
|
|
mcounteren_d = mcounteren_q;
|
|
mscratch_d = mscratch_q;
|
|
if (CVA6Cfg.TvalEn) mtval_d = mtval_q;
|
|
if (CVA6Cfg.RVH) begin
|
|
mtinst_d = mtinst_q;
|
|
mtval2_d = mtval2_q;
|
|
end
|
|
|
|
fiom_d = fiom_q;
|
|
dcache_d = dcache_q;
|
|
icache_d = icache_q;
|
|
acc_cons_d = acc_cons_q;
|
|
|
|
if (CVA6Cfg.RVH) begin
|
|
vstvec_d = vstvec_q;
|
|
vsscratch_d = vsscratch_q;
|
|
vsepc_d = vsepc_q;
|
|
vscause_d = vscause_q;
|
|
vstval_d = vstval_q;
|
|
vsatp_d = vsatp_q;
|
|
hgatp_d = hgatp_q;
|
|
hedeleg_d = hedeleg_q;
|
|
hideleg_d = hideleg_q;
|
|
hgeie_d = hgeie_q;
|
|
hcounteren_d = hcounteren_q;
|
|
htinst_d = htinst_q;
|
|
htval_d = htval_q;
|
|
en_ld_st_g_translation_d = en_ld_st_g_translation_q;
|
|
end
|
|
|
|
if (CVA6Cfg.RVS) begin
|
|
sepc_d = sepc_q;
|
|
scause_d = scause_q;
|
|
stvec_d = stvec_q;
|
|
scounteren_d = scounteren_q;
|
|
sscratch_d = sscratch_q;
|
|
stval_d = stval_q;
|
|
satp_d = satp_q;
|
|
end
|
|
|
|
en_ld_st_translation_d = en_ld_st_translation_q;
|
|
dirty_fp_state_csr = 1'b0;
|
|
|
|
pmpcfg_d = pmpcfg_q;
|
|
pmpaddr_d = pmpaddr_q;
|
|
|
|
// check for correct access rights and that we are writing
|
|
if (csr_we) begin
|
|
unique case (conv_csr_addr.address)
|
|
// Floating-Point
|
|
riscv::CSR_FFLAGS: begin
|
|
if (CVA6Cfg.FpPresent && !(mstatus_q.fs == riscv::Off || (CVA6Cfg.RVH && v_q && vsstatus_q.fs == riscv::Off))) begin
|
|
dirty_fp_state_csr = 1'b1;
|
|
fcsr_d.fflags = csr_wdata[4:0];
|
|
// this instruction has side-effects
|
|
flush_o = 1'b1;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_FRM: begin
|
|
if (CVA6Cfg.FpPresent && !(mstatus_q.fs == riscv::Off || (CVA6Cfg.RVH && v_q && vsstatus_q.fs == riscv::Off))) begin
|
|
dirty_fp_state_csr = 1'b1;
|
|
fcsr_d.frm = csr_wdata[2:0];
|
|
// this instruction has side-effects
|
|
flush_o = 1'b1;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_FCSR: begin
|
|
if (CVA6Cfg.FpPresent && !(mstatus_q.fs == riscv::Off || (CVA6Cfg.RVH && v_q && vsstatus_q.fs == riscv::Off))) begin
|
|
dirty_fp_state_csr = 1'b1;
|
|
fcsr_d[7:0] = csr_wdata[7:0]; // ignore writes to reserved space
|
|
// this instruction has side-effects
|
|
flush_o = 1'b1;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_FTRAN: begin
|
|
if (CVA6Cfg.FpPresent && !(mstatus_q.fs == riscv::Off || (CVA6Cfg.RVH && v_q && vsstatus_q.fs == riscv::Off))) begin
|
|
dirty_fp_state_csr = 1'b1;
|
|
fcsr_d.fprec = csr_wdata[6:0]; // ignore writes to reserved space
|
|
// this instruction has side-effects
|
|
flush_o = 1'b1;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
// debug CSR
|
|
riscv::CSR_DCSR: begin
|
|
if (CVA6Cfg.DebugEn) begin
|
|
dcsr_d = csr_wdata[31:0];
|
|
// debug is implemented
|
|
dcsr_d.xdebugver = 4'h4;
|
|
// currently not supported
|
|
dcsr_d.nmip = 1'b0;
|
|
dcsr_d.stopcount = 1'b0;
|
|
dcsr_d.stoptime = 1'b0;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_DPC:
|
|
if (CVA6Cfg.DebugEn) dpc_d = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_DSCRATCH0:
|
|
if (CVA6Cfg.DebugEn) dscratch0_d = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_DSCRATCH1:
|
|
if (CVA6Cfg.DebugEn) dscratch1_d = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_JVT: begin
|
|
if (CVA6Cfg.RVZCMT) begin
|
|
jvt_d.base = csr_wdata[CVA6Cfg.XLEN-1:6];
|
|
jvt_d.mode = 6'b000000;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
// trigger module CSRs
|
|
riscv::CSR_TSELECT: update_access_exception = 1'b1; // not implemented
|
|
riscv::CSR_TDATA1: update_access_exception = 1'b1; // not implemented
|
|
riscv::CSR_TDATA2: update_access_exception = 1'b1; // not implemented
|
|
riscv::CSR_TDATA3: update_access_exception = 1'b1; // not implemented
|
|
// virtual supervisor registers
|
|
riscv::CSR_VSSTATUS: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
mask = ariane_pkg::SMODE_STATUS_WRITE_MASK[CVA6Cfg.XLEN-1:0];
|
|
vsstatus_d = (vsstatus_q & ~{{64-CVA6Cfg.XLEN{1'b0}}, mask}) | {{64-CVA6Cfg.XLEN{1'b0}}, (csr_wdata & mask)};
|
|
// hardwire to zero if floating point extension is not present
|
|
vsstatus_d.xs = riscv::Off;
|
|
if (!CVA6Cfg.FpPresent) begin
|
|
vsstatus_d.fs = riscv::Off;
|
|
end
|
|
// this instruction has side-effects
|
|
flush_o = 1'b1;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_VSIE:
|
|
if (CVA6Cfg.RVH) mie_d = (mie_q & ~hideleg_q) | ((csr_wdata << 1) & hideleg_q);
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_VSIP: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
// only the virtual supervisor software interrupt is write-able, iff delegated
|
|
mask = CVA6Cfg.XLEN'(riscv::MIP_VSSIP) & hideleg_q;
|
|
mip_d = (mip_q & ~mask) | ((csr_wdata << 1) & mask);
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_VSTVEC: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
vstvec_d = {csr_wdata[CVA6Cfg.XLEN-1:2], 1'b0, csr_wdata[0]};
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_VSSCRATCH:
|
|
if (CVA6Cfg.RVH) vsscratch_d = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_VSEPC:
|
|
if (CVA6Cfg.RVH) vsepc_d = {csr_wdata[CVA6Cfg.XLEN-1:1], 1'b0};
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_VSCAUSE:
|
|
if (CVA6Cfg.RVH) vscause_d = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_VSTVAL:
|
|
if (CVA6Cfg.RVH) vstval_d = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
// virtual supervisor address translation and protection
|
|
riscv::CSR_VSATP: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
if (priv_lvl_o == riscv::PRIV_LVL_S && hstatus_q.vtvm && v_q) begin
|
|
virtual_update_access_exception = 1'b1;
|
|
end else begin
|
|
vsatp = satp_t'(csr_wdata);
|
|
// only make ASID_LEN - 1 bit stick, that way software can figure out how many ASID bits are supported
|
|
vsatp.asid = vsatp.asid & {{(CVA6Cfg.ASIDW - CVA6Cfg.ASID_WIDTH) {1'b0}}, {CVA6Cfg.ASID_WIDTH{1'b1}}};
|
|
// only update if we actually support this mode
|
|
if (config_pkg::vm_mode_t'(vsatp.mode) == config_pkg::ModeOff ||
|
|
config_pkg::vm_mode_t'(vsatp.mode) == CVA6Cfg.MODE_SV)
|
|
vsatp_d = vsatp;
|
|
end
|
|
// changing the mode can have side-effects on address translation (e.g.: other instructions), re-fetch
|
|
// the next instruction by executing a flush
|
|
flush_o = 1'b1;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
// sstatus is a subset of mstatus - mask it accordingly
|
|
riscv::CSR_SSTATUS: begin
|
|
if (CVA6Cfg.RVS) begin
|
|
mask = ariane_pkg::SMODE_STATUS_WRITE_MASK[CVA6Cfg.XLEN-1:0];
|
|
mstatus_d = (mstatus_q & ~{{64-CVA6Cfg.XLEN{1'b0}}, mask}) | {{64-CVA6Cfg.XLEN{1'b0}}, (csr_wdata & mask)};
|
|
// hardwire to zero if floating point extension is not present
|
|
if (!CVA6Cfg.FpPresent) begin
|
|
mstatus_d.fs = riscv::Off;
|
|
end
|
|
// hardwire to zero if vector extension is not present
|
|
if (!CVA6Cfg.RVV) begin
|
|
mstatus_d.vs = riscv::Off;
|
|
end
|
|
// If h-extension is not enabled, priv level HS is reserved
|
|
if (!CVA6Cfg.RVH) begin
|
|
if (mstatus_d.mpp == riscv::PRIV_LVL_HS) begin
|
|
mstatus_d.mpp = mstatus_q.mpp;
|
|
end
|
|
end
|
|
// this instruction has side-effects
|
|
flush_o = 1'b1;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
// even machine mode interrupts can be visible and set-able to supervisor
|
|
// if the corresponding bit in mideleg is set
|
|
riscv::CSR_SIE: begin
|
|
if (CVA6Cfg.RVS) begin
|
|
mask = (CVA6Cfg.RVH) ? mideleg_q & ~HS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0] : mideleg_q;
|
|
// the mideleg makes sure only delegate-able register (and therefore also only implemented registers) are written
|
|
mie_d = (mie_q & ~mask) | (csr_wdata & mask);
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
|
|
riscv::CSR_SIP: begin
|
|
if (CVA6Cfg.RVS) begin
|
|
// only the supervisor software interrupt is write-able, iff delegated
|
|
mask = CVA6Cfg.XLEN'(riscv::MIP_SSIP) & mideleg_q;
|
|
mip_d = (mip_q & ~mask) | (csr_wdata & mask);
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
|
|
riscv::CSR_STVEC:
|
|
if (CVA6Cfg.RVS) stvec_d = {csr_wdata[CVA6Cfg.XLEN-1:2], 1'b0, csr_wdata[0]};
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_SCOUNTEREN:
|
|
if (CVA6Cfg.RVS) scounteren_d = {{CVA6Cfg.XLEN - 32{1'b0}}, csr_wdata[31:0]};
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_SSCRATCH:
|
|
if (CVA6Cfg.RVS) sscratch_d = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_SEPC:
|
|
if (CVA6Cfg.RVS) sepc_d = {csr_wdata[CVA6Cfg.XLEN-1:1], 1'b0};
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_SCAUSE:
|
|
if (CVA6Cfg.RVS) scause_d = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_STVAL:
|
|
if (CVA6Cfg.RVS && CVA6Cfg.TvalEn) stval_d = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
// supervisor address translation and protection
|
|
riscv::CSR_SATP: begin
|
|
if (CVA6Cfg.RVS) begin
|
|
// intercept SATP writes if in S-Mode and TVM is enabled
|
|
if (priv_lvl_o == riscv::PRIV_LVL_S && mstatus_q.tvm) update_access_exception = 1'b1;
|
|
else begin
|
|
satp = satp_t'(csr_wdata);
|
|
// only make ASID_LEN - 1 bit stick, that way software can figure out how many ASID bits are supported
|
|
satp.asid = satp.asid & {{(CVA6Cfg.ASIDW - CVA6Cfg.ASID_WIDTH) {1'b0}}, {CVA6Cfg.ASID_WIDTH{1'b1}}};
|
|
// only update if we actually support this mode
|
|
if (config_pkg::vm_mode_t'(satp.mode) == config_pkg::ModeOff ||
|
|
config_pkg::vm_mode_t'(satp.mode) == CVA6Cfg.MODE_SV)
|
|
satp_d = satp;
|
|
end
|
|
// changing the mode can have side-effects on address translation (e.g.: other instructions), re-fetch
|
|
// the next instruction by executing a flush
|
|
flush_o = 1'b1;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_SENVCFG:
|
|
if (CVA6Cfg.RVU) fiom_d = csr_wdata[0];
|
|
else update_access_exception = 1'b1;
|
|
//hypervisor mode registers
|
|
riscv::CSR_HSTATUS: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
mask = ariane_pkg::HSTATUS_WRITE_MASK[CVA6Cfg.XLEN-1:0];
|
|
hstatus_d = (hstatus_q & ~{{64-CVA6Cfg.XLEN{1'b0}}, mask}) | {{64-CVA6Cfg.XLEN{1'b0}}, (csr_wdata & mask)};
|
|
// this instruction has side-effects
|
|
flush_o = 1'b1;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_HEDELEG: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
mask = (1 << riscv::INSTR_ADDR_MISALIGNED) |
|
|
(1 << riscv::INSTR_ACCESS_FAULT) |
|
|
(1 << riscv::ILLEGAL_INSTR) |
|
|
(1 << riscv::BREAKPOINT) |
|
|
(1 << riscv::LD_ADDR_MISALIGNED) |
|
|
(1 << riscv::LD_ACCESS_FAULT) |
|
|
(1 << riscv::ST_ADDR_MISALIGNED) |
|
|
(1 << riscv::ST_ACCESS_FAULT) |
|
|
(1 << riscv::ENV_CALL_UMODE) |
|
|
(1 << riscv::INSTR_PAGE_FAULT) |
|
|
(1 << riscv::LOAD_PAGE_FAULT) |
|
|
(1 << riscv::STORE_PAGE_FAULT);
|
|
hedeleg_d = (hedeleg_q & ~mask) | (csr_wdata & mask);
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_HIDELEG: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
hideleg_d = (hideleg_q & ~VS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0]) | (csr_wdata & VS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0]);
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_HIE: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
mask = HS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0];
|
|
mie_d = (mie_q & ~mask) | (csr_wdata & mask);
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_HIP: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
mask = CVA6Cfg.XLEN'(riscv::MIP_VSSIP);
|
|
mip_d = (mip_q & ~mask) | (csr_wdata & mask);
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_HVIP: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
mask = VS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0];
|
|
mip_d = (mip_q & ~mask) | (csr_wdata & mask);
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_HCOUNTEREN: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
hcounteren_d = {{CVA6Cfg.XLEN - 32{1'b0}}, csr_wdata[31:0]};
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_HTVAL: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
htval_d = csr_wdata;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_HTINST: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
htinst_d = {{CVA6Cfg.XLEN - 32{1'b0}}, csr_wdata[31:0]};
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
//TODO Hyp: implement hgeie write
|
|
riscv::CSR_HGEIE: begin
|
|
if (!CVA6Cfg.RVH) begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_HGATP: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
// intercept HGATP writes if in HS-Mode and TVM is enabled
|
|
if (priv_lvl_o == riscv::PRIV_LVL_S && !v_q && mstatus_q.tvm)
|
|
update_access_exception = 1'b1;
|
|
else begin
|
|
hgatp = hgatp_t'(csr_wdata);
|
|
//hardwire PPN[1:0] to zero
|
|
hgatp[1:0] = 2'b0;
|
|
// only make VMID_LEN - 1 bit stick, that way software can figure out how many VMID bits are supported
|
|
hgatp.vmid = hgatp.vmid & {{(CVA6Cfg.VMIDW - CVA6Cfg.VMID_WIDTH) {1'b0}}, {CVA6Cfg.VMID_WIDTH{1'b1}}};
|
|
// only update if we actually support this mode
|
|
if (config_pkg::vm_mode_t'(hgatp.mode) == config_pkg::ModeOff ||
|
|
config_pkg::vm_mode_t'(hgatp.mode) == CVA6Cfg.MODE_SV)
|
|
hgatp_d = hgatp;
|
|
end
|
|
// changing the mode can have side-effects on address translation (e.g.: other instructions), re-fetch
|
|
// the next instruction by executing a flush
|
|
flush_o = 1'b1;
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
riscv::CSR_HENVCFG:
|
|
if (CVA6Cfg.RVH) fiom_d = csr_wdata[0];
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_MSTATUS: begin
|
|
mstatus_d = {{64 - CVA6Cfg.XLEN{1'b0}}, csr_wdata};
|
|
mstatus_d.xs = riscv::Off;
|
|
if (!CVA6Cfg.FpPresent) begin
|
|
mstatus_d.fs = riscv::Off;
|
|
end
|
|
if (!CVA6Cfg.RVV) begin
|
|
mstatus_d.vs = riscv::Off;
|
|
end
|
|
if (!CVA6Cfg.RVS) begin
|
|
mstatus_d.sie = riscv::Off;
|
|
mstatus_d.spie = riscv::Off;
|
|
mstatus_d.spp = riscv::Off;
|
|
mstatus_d.sum = riscv::Off;
|
|
mstatus_d.mxr = riscv::Off;
|
|
mstatus_d.tvm = riscv::Off;
|
|
mstatus_d.tsr = riscv::Off;
|
|
end
|
|
if (!CVA6Cfg.RVU) begin
|
|
mstatus_d.tw = riscv::Off;
|
|
mstatus_d.mprv = riscv::Off;
|
|
end
|
|
if ((!CVA6Cfg.RVH & mstatus_d.mpp == riscv::PRIV_LVL_HS) |
|
|
(!CVA6Cfg.RVS & mstatus_d.mpp == riscv::PRIV_LVL_S) |
|
|
(!CVA6Cfg.RVU & mstatus_d.mpp == riscv::PRIV_LVL_U)) begin
|
|
mstatus_d.mpp = mstatus_q.mpp;
|
|
end
|
|
mstatus_d.wpri3 = 9'b0;
|
|
mstatus_d.wpri1 = 1'b0;
|
|
mstatus_d.wpri2 = 1'b0;
|
|
mstatus_d.wpri0 = 1'b0;
|
|
mstatus_d.ube = 1'b0; // CVA6 is little-endian
|
|
// this register has side-effects on other registers, flush the pipeline
|
|
flush_o = 1'b1;
|
|
end
|
|
riscv::CSR_MSTATUSH: if (CVA6Cfg.XLEN != 32) update_access_exception = 1'b1;
|
|
// MISA is WARL (Write Any Value, Reads Legal Value)
|
|
riscv::CSR_MISA: ;
|
|
// machine exception delegation register
|
|
// 0 - 15 exceptions supported
|
|
riscv::CSR_MEDELEG: begin
|
|
if (CVA6Cfg.RVS) begin
|
|
mask = (1 << riscv::INSTR_ADDR_MISALIGNED) |
|
|
(1 << riscv::INSTR_ACCESS_FAULT) |
|
|
(1 << riscv::ILLEGAL_INSTR) |
|
|
(1 << riscv::BREAKPOINT) |
|
|
(1 << riscv::LD_ADDR_MISALIGNED) |
|
|
(1 << riscv::LD_ACCESS_FAULT) |
|
|
(1 << riscv::ST_ADDR_MISALIGNED) |
|
|
(1 << riscv::ST_ACCESS_FAULT) |
|
|
(1 << riscv::ENV_CALL_UMODE) |
|
|
((CVA6Cfg.RVH ? 1 : 0) << riscv::ENV_CALL_VSMODE) |
|
|
(1 << riscv::INSTR_PAGE_FAULT) |
|
|
(1 << riscv::LOAD_PAGE_FAULT) |
|
|
(1 << riscv::STORE_PAGE_FAULT) |
|
|
((CVA6Cfg.RVH ? 1 : 0) << riscv::INSTR_GUEST_PAGE_FAULT) |
|
|
((CVA6Cfg.RVH ? 1 : 0) << riscv::LOAD_GUEST_PAGE_FAULT) |
|
|
((CVA6Cfg.RVH ? 1 : 0) << riscv::VIRTUAL_INSTRUCTION) |
|
|
((CVA6Cfg.RVH ? 1 : 0) << riscv::STORE_GUEST_PAGE_FAULT);
|
|
medeleg_d = (medeleg_q & ~mask) | (csr_wdata & mask);
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
// machine interrupt delegation register
|
|
// we do not support user interrupt delegation
|
|
riscv::CSR_MIDELEG: begin
|
|
if (CVA6Cfg.RVS) begin
|
|
mask = CVA6Cfg.XLEN'(riscv::MIP_SSIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_STIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_SEIP);
|
|
if (CVA6Cfg.RVH) begin
|
|
mideleg_d = (mideleg_q & ~mask) | (csr_wdata & mask) | HS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0];
|
|
end else begin
|
|
mideleg_d = (mideleg_q & ~mask) | (csr_wdata & mask);
|
|
end
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
// mask the register so that unsupported interrupts can never be set
|
|
riscv::CSR_MIE: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
mask = HS_DELEG_INTERRUPTS[CVA6Cfg.XLEN-1:0]
|
|
| CVA6Cfg.XLEN'(riscv::MIP_SSIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_STIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_SEIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_MSIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_MTIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_MEIP);
|
|
end else begin
|
|
if (CVA6Cfg.RVS) begin
|
|
mask = CVA6Cfg.XLEN'(riscv::MIP_SSIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_STIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_SEIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_MSIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_MTIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_MEIP);
|
|
end else begin
|
|
if (CVA6Cfg.SoftwareInterruptEn) begin
|
|
mask = CVA6Cfg.XLEN'(riscv::MIP_MSIP) // same shift as MSIE
|
|
| CVA6Cfg.XLEN'(riscv::MIP_MTIP) // same shift as MTIE
|
|
| CVA6Cfg.XLEN'(riscv::MIP_MEIP); // same shift as MEIE
|
|
end else begin
|
|
mask = CVA6Cfg.XLEN'(riscv::MIP_MTIP) // same shift as MTIE
|
|
| CVA6Cfg.XLEN'(riscv::MIP_MEIP); // same shift as MEIE
|
|
end
|
|
end
|
|
end
|
|
mie_d = (mie_q & ~mask) | (csr_wdata & mask); // we only support supervisor and M-mode interrupts
|
|
end
|
|
|
|
riscv::CSR_MTVEC: begin
|
|
logic DirVecOnly;
|
|
DirVecOnly = CVA6Cfg.DirectVecOnly ? 1'b0 : csr_wdata[0];
|
|
mtvec_d = {csr_wdata[CVA6Cfg.XLEN-1:2], 1'b0, DirVecOnly};
|
|
// we are in vector mode, this implementation requires the additional
|
|
// alignment constraint of 64 * 4 bytes
|
|
if (DirVecOnly) mtvec_d = {csr_wdata[CVA6Cfg.XLEN-1:8], 7'b0, DirVecOnly};
|
|
end
|
|
riscv::CSR_MCOUNTEREN: begin
|
|
if (CVA6Cfg.RVU) mcounteren_d = {{CVA6Cfg.XLEN - 32{1'b0}}, csr_wdata[31:0]};
|
|
else update_access_exception = 1'b1;
|
|
end
|
|
|
|
riscv::CSR_MSCRATCH: mscratch_d = csr_wdata;
|
|
riscv::CSR_MEPC: mepc_d = {csr_wdata[CVA6Cfg.XLEN-1:1], 1'b0};
|
|
riscv::CSR_MCAUSE: mcause_d = csr_wdata;
|
|
riscv::CSR_MTVAL: begin
|
|
if (CVA6Cfg.TvalEn) mtval_d = csr_wdata;
|
|
end
|
|
riscv::CSR_MTINST:
|
|
if (CVA6Cfg.RVH) mtinst_d = {{CVA6Cfg.XLEN - 32{1'b0}}, csr_wdata[31:0]};
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_MTVAL2:
|
|
if (CVA6Cfg.RVH) mtval2_d = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_MIP: begin
|
|
if (CVA6Cfg.RVH) begin
|
|
mask = CVA6Cfg.XLEN'(riscv::MIP_SSIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_STIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_SEIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_VSSIP);
|
|
end else if (CVA6Cfg.RVS) begin
|
|
mask = CVA6Cfg.XLEN'(riscv::MIP_SSIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_STIP)
|
|
| CVA6Cfg.XLEN'(riscv::MIP_SEIP);
|
|
end else begin
|
|
mask = '0;
|
|
end
|
|
mip_d = (mip_q & ~mask) | (csr_wdata & mask);
|
|
end
|
|
riscv::CSR_MENVCFG: if (CVA6Cfg.RVU) fiom_d = csr_wdata[0];
|
|
riscv::CSR_MENVCFGH: begin
|
|
if (!CVA6Cfg.RVU || CVA6Cfg.XLEN != 32) update_access_exception = 1'b1;
|
|
end
|
|
riscv::CSR_MCOUNTINHIBIT:
|
|
if (CVA6Cfg.PerfCounterEn)
|
|
mcountinhibit_d = {csr_wdata[MHPMCounterNum+2:2], 1'b0, csr_wdata[0]};
|
|
else mcountinhibit_d = '0;
|
|
// performance counters
|
|
riscv::CSR_MCYCLE: cycle_d[CVA6Cfg.XLEN-1:0] = csr_wdata;
|
|
riscv::CSR_MCYCLEH:
|
|
if (CVA6Cfg.XLEN == 32) cycle_d[63:32] = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
riscv::CSR_MINSTRET: instret_d[CVA6Cfg.XLEN-1:0] = csr_wdata;
|
|
riscv::CSR_MINSTRETH:
|
|
if (CVA6Cfg.XLEN == 32) instret_d[63:32] = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
//Event Selector
|
|
riscv::CSR_MHPM_EVENT_3,
|
|
riscv::CSR_MHPM_EVENT_4,
|
|
riscv::CSR_MHPM_EVENT_5,
|
|
riscv::CSR_MHPM_EVENT_6,
|
|
riscv::CSR_MHPM_EVENT_7,
|
|
riscv::CSR_MHPM_EVENT_8,
|
|
riscv::CSR_MHPM_EVENT_9,
|
|
riscv::CSR_MHPM_EVENT_10,
|
|
riscv::CSR_MHPM_EVENT_11,
|
|
riscv::CSR_MHPM_EVENT_12,
|
|
riscv::CSR_MHPM_EVENT_13,
|
|
riscv::CSR_MHPM_EVENT_14,
|
|
riscv::CSR_MHPM_EVENT_15,
|
|
riscv::CSR_MHPM_EVENT_16,
|
|
riscv::CSR_MHPM_EVENT_17,
|
|
riscv::CSR_MHPM_EVENT_18,
|
|
riscv::CSR_MHPM_EVENT_19,
|
|
riscv::CSR_MHPM_EVENT_20,
|
|
riscv::CSR_MHPM_EVENT_21,
|
|
riscv::CSR_MHPM_EVENT_22,
|
|
riscv::CSR_MHPM_EVENT_23,
|
|
riscv::CSR_MHPM_EVENT_24,
|
|
riscv::CSR_MHPM_EVENT_25,
|
|
riscv::CSR_MHPM_EVENT_26,
|
|
riscv::CSR_MHPM_EVENT_27,
|
|
riscv::CSR_MHPM_EVENT_28,
|
|
riscv::CSR_MHPM_EVENT_29,
|
|
riscv::CSR_MHPM_EVENT_30,
|
|
riscv::CSR_MHPM_EVENT_31 : begin
|
|
perf_we_o = 1'b1;
|
|
perf_data_o = csr_wdata;
|
|
end
|
|
|
|
riscv::CSR_MHPM_COUNTER_3,
|
|
riscv::CSR_MHPM_COUNTER_4,
|
|
riscv::CSR_MHPM_COUNTER_5,
|
|
riscv::CSR_MHPM_COUNTER_6,
|
|
riscv::CSR_MHPM_COUNTER_7,
|
|
riscv::CSR_MHPM_COUNTER_8,
|
|
riscv::CSR_MHPM_COUNTER_9,
|
|
riscv::CSR_MHPM_COUNTER_10,
|
|
riscv::CSR_MHPM_COUNTER_11,
|
|
riscv::CSR_MHPM_COUNTER_12,
|
|
riscv::CSR_MHPM_COUNTER_13,
|
|
riscv::CSR_MHPM_COUNTER_14,
|
|
riscv::CSR_MHPM_COUNTER_15,
|
|
riscv::CSR_MHPM_COUNTER_16,
|
|
riscv::CSR_MHPM_COUNTER_17,
|
|
riscv::CSR_MHPM_COUNTER_18,
|
|
riscv::CSR_MHPM_COUNTER_19,
|
|
riscv::CSR_MHPM_COUNTER_20,
|
|
riscv::CSR_MHPM_COUNTER_21,
|
|
riscv::CSR_MHPM_COUNTER_22,
|
|
riscv::CSR_MHPM_COUNTER_23,
|
|
riscv::CSR_MHPM_COUNTER_24,
|
|
riscv::CSR_MHPM_COUNTER_25,
|
|
riscv::CSR_MHPM_COUNTER_26,
|
|
riscv::CSR_MHPM_COUNTER_27,
|
|
riscv::CSR_MHPM_COUNTER_28,
|
|
riscv::CSR_MHPM_COUNTER_29,
|
|
riscv::CSR_MHPM_COUNTER_30,
|
|
riscv::CSR_MHPM_COUNTER_31 : begin
|
|
perf_we_o = 1'b1;
|
|
perf_data_o = csr_wdata;
|
|
end
|
|
|
|
riscv::CSR_MHPM_COUNTER_3H,
|
|
riscv::CSR_MHPM_COUNTER_4H,
|
|
riscv::CSR_MHPM_COUNTER_5H,
|
|
riscv::CSR_MHPM_COUNTER_6H,
|
|
riscv::CSR_MHPM_COUNTER_7H,
|
|
riscv::CSR_MHPM_COUNTER_8H,
|
|
riscv::CSR_MHPM_COUNTER_9H,
|
|
riscv::CSR_MHPM_COUNTER_10H,
|
|
riscv::CSR_MHPM_COUNTER_11H,
|
|
riscv::CSR_MHPM_COUNTER_12H,
|
|
riscv::CSR_MHPM_COUNTER_13H,
|
|
riscv::CSR_MHPM_COUNTER_14H,
|
|
riscv::CSR_MHPM_COUNTER_15H,
|
|
riscv::CSR_MHPM_COUNTER_16H,
|
|
riscv::CSR_MHPM_COUNTER_17H,
|
|
riscv::CSR_MHPM_COUNTER_18H,
|
|
riscv::CSR_MHPM_COUNTER_19H,
|
|
riscv::CSR_MHPM_COUNTER_20H,
|
|
riscv::CSR_MHPM_COUNTER_21H,
|
|
riscv::CSR_MHPM_COUNTER_22H,
|
|
riscv::CSR_MHPM_COUNTER_23H,
|
|
riscv::CSR_MHPM_COUNTER_24H,
|
|
riscv::CSR_MHPM_COUNTER_25H,
|
|
riscv::CSR_MHPM_COUNTER_26H,
|
|
riscv::CSR_MHPM_COUNTER_27H,
|
|
riscv::CSR_MHPM_COUNTER_28H,
|
|
riscv::CSR_MHPM_COUNTER_29H,
|
|
riscv::CSR_MHPM_COUNTER_30H,
|
|
riscv::CSR_MHPM_COUNTER_31H : begin
|
|
perf_we_o = 1'b1;
|
|
if (CVA6Cfg.XLEN == 32) perf_data_o = csr_wdata;
|
|
else update_access_exception = 1'b1;
|
|
end
|
|
|
|
riscv::CSR_DCACHE: dcache_d = {{CVA6Cfg.XLEN - 1{1'b0}}, csr_wdata[0]}; // enable bit
|
|
riscv::CSR_ICACHE: icache_d = {{CVA6Cfg.XLEN - 1{1'b0}}, csr_wdata[0]}; // enable bit
|
|
riscv::CSR_ACC_CONS: begin
|
|
if (CVA6Cfg.EnableAccelerator) begin
|
|
acc_cons_d = {{CVA6Cfg.XLEN - 1{1'b0}}, csr_wdata[0]}; // enable bit
|
|
end else begin
|
|
update_access_exception = 1'b1;
|
|
end
|
|
end
|
|
// PMP locked logic
|
|
// 1. refuse to update any locked entry
|
|
// 2. also refuse to update the entry below a locked TOR entry
|
|
// Note that writes to pmpcfg below a locked TOR entry are valid
|
|
riscv::CSR_PMPCFG0,
|
|
riscv::CSR_PMPCFG1,
|
|
riscv::CSR_PMPCFG2,
|
|
riscv::CSR_PMPCFG3,
|
|
riscv::CSR_PMPCFG4,
|
|
riscv::CSR_PMPCFG5,
|
|
riscv::CSR_PMPCFG6,
|
|
riscv::CSR_PMPCFG7,
|
|
riscv::CSR_PMPCFG8,
|
|
riscv::CSR_PMPCFG9,
|
|
riscv::CSR_PMPCFG10,
|
|
riscv::CSR_PMPCFG11,
|
|
riscv::CSR_PMPCFG12,
|
|
riscv::CSR_PMPCFG13,
|
|
riscv::CSR_PMPCFG14,
|
|
riscv::CSR_PMPCFG15: begin
|
|
// index is calculated using PMPCFG0 as the offset
|
|
automatic logic [11:0] index = csr_addr.address[11:0] - riscv::CSR_PMPCFG0;
|
|
|
|
// if index is not even and XLEN==64, raise exception
|
|
if (CVA6Cfg.XLEN == 64 && index[0] == 1'b1) update_access_exception = 1'b1;
|
|
else begin
|
|
for (int i = 0; i < CVA6Cfg.XLEN / 8; i++) begin
|
|
if (!pmpcfg_q[index*4+i].locked) pmpcfg_d[index*4+i] = csr_wdata[i*8+:8];
|
|
end
|
|
end
|
|
end
|
|
riscv::CSR_PMPADDR0,
|
|
riscv::CSR_PMPADDR1,
|
|
riscv::CSR_PMPADDR2,
|
|
riscv::CSR_PMPADDR3,
|
|
riscv::CSR_PMPADDR4,
|
|
riscv::CSR_PMPADDR5,
|
|
riscv::CSR_PMPADDR6,
|
|
riscv::CSR_PMPADDR7,
|
|
riscv::CSR_PMPADDR8,
|
|
riscv::CSR_PMPADDR9,
|
|
riscv::CSR_PMPADDR10,
|
|
riscv::CSR_PMPADDR11,
|
|
riscv::CSR_PMPADDR12,
|
|
riscv::CSR_PMPADDR13,
|
|
riscv::CSR_PMPADDR14,
|
|
riscv::CSR_PMPADDR15,
|
|
riscv::CSR_PMPADDR16,
|
|
riscv::CSR_PMPADDR17,
|
|
riscv::CSR_PMPADDR18,
|
|
riscv::CSR_PMPADDR19,
|
|
riscv::CSR_PMPADDR20,
|
|
riscv::CSR_PMPADDR21,
|
|
riscv::CSR_PMPADDR22,
|
|
riscv::CSR_PMPADDR23,
|
|
riscv::CSR_PMPADDR24,
|
|
riscv::CSR_PMPADDR25,
|
|
riscv::CSR_PMPADDR26,
|
|
riscv::CSR_PMPADDR27,
|
|
riscv::CSR_PMPADDR28,
|
|
riscv::CSR_PMPADDR29,
|
|
riscv::CSR_PMPADDR30,
|
|
riscv::CSR_PMPADDR31,
|
|
riscv::CSR_PMPADDR32,
|
|
riscv::CSR_PMPADDR33,
|
|
riscv::CSR_PMPADDR34,
|
|
riscv::CSR_PMPADDR35,
|
|
riscv::CSR_PMPADDR36,
|
|
riscv::CSR_PMPADDR37,
|
|
riscv::CSR_PMPADDR38,
|
|
riscv::CSR_PMPADDR39,
|
|
riscv::CSR_PMPADDR40,
|
|
riscv::CSR_PMPADDR41,
|
|
riscv::CSR_PMPADDR42,
|
|
riscv::CSR_PMPADDR43,
|
|
riscv::CSR_PMPADDR44,
|
|
riscv::CSR_PMPADDR45,
|
|
riscv::CSR_PMPADDR46,
|
|
riscv::CSR_PMPADDR47,
|
|
riscv::CSR_PMPADDR48,
|
|
riscv::CSR_PMPADDR49,
|
|
riscv::CSR_PMPADDR50,
|
|
riscv::CSR_PMPADDR51,
|
|
riscv::CSR_PMPADDR52,
|
|
riscv::CSR_PMPADDR53,
|
|
riscv::CSR_PMPADDR54,
|
|
riscv::CSR_PMPADDR55,
|
|
riscv::CSR_PMPADDR56,
|
|
riscv::CSR_PMPADDR57,
|
|
riscv::CSR_PMPADDR58,
|
|
riscv::CSR_PMPADDR59,
|
|
riscv::CSR_PMPADDR60,
|
|
riscv::CSR_PMPADDR61,
|
|
riscv::CSR_PMPADDR62,
|
|
riscv::CSR_PMPADDR63: begin
|
|
// index is calculated using PMPADDR0 as the offset
|
|
automatic logic [11:0] index = csr_addr.address[11:0] - riscv::CSR_PMPADDR0;
|
|
// check if the entry or the entry above is locked
|
|
if (!pmpcfg_q[index].locked && !(pmpcfg_q[index+1].locked && pmpcfg_q[index+1].addr_mode == riscv::TOR)) begin
|
|
pmpaddr_d[index] = csr_wdata[CVA6Cfg.PLEN-3:0];
|
|
end
|
|
end
|
|
default: update_access_exception = 1'b1;
|
|
endcase
|
|
end
|
|
if (CVA6Cfg.IS_XLEN64) begin
|
|
mstatus_d.sxl = riscv::XLEN_64;
|
|
mstatus_d.uxl = riscv::XLEN_64;
|
|
end
|
|
if (!CVA6Cfg.RVU) begin
|
|
mstatus_d.mpp = riscv::PRIV_LVL_M;
|
|
end
|
|
|
|
if (CVA6Cfg.RVH) begin
|
|
hstatus_d.vsxl = riscv::XLEN_64;
|
|
vsstatus_d.uxl = riscv::XLEN_64;
|
|
end
|
|
// mark the floating point extension register as dirty
|
|
if (CVA6Cfg.FpPresent && (dirty_fp_state_csr || dirty_fp_state_i)) begin
|
|
mstatus_d.fs = riscv::Dirty;
|
|
if (CVA6Cfg.RVH && v_q) begin
|
|
vsstatus_d.fs = riscv::Dirty;
|
|
end
|
|
end
|
|
// mark the vector extension register as dirty
|
|
if (CVA6Cfg.RVV && dirty_v_state_i) begin
|
|
mstatus_d.vs = riscv::Dirty;
|
|
end
|
|
// hardwired extension registers
|
|
if (CVA6Cfg.RVS || CVA6Cfg.RVF) begin
|
|
mstatus_d.sd = (mstatus_q.xs == riscv::Dirty) | (mstatus_q.fs == riscv::Dirty);
|
|
end else begin
|
|
mstatus_d.sd = riscv::Off;
|
|
end
|
|
if (CVA6Cfg.RVH) begin
|
|
vsstatus_d.sd = (vsstatus_q.xs == riscv::Dirty) | (vsstatus_q.fs == riscv::Dirty);
|
|
end
|
|
|
|
// reserve PMPCFG bits 5 and 6 (hardwire to 0)
|
|
for (int i = 0; i < CVA6Cfg.NrPMPEntries; i++) pmpcfg_d[i].reserved = 2'b0;
|
|
|
|
// write the floating point status register
|
|
if (CVA6Cfg.FpPresent && csr_write_fflags_i) begin
|
|
fcsr_d.fflags = csr_wdata_i[4:0] | fcsr_q.fflags;
|
|
end
|
|
|
|
// ----------------------------
|
|
// Accelerator FP imprecise exceptions
|
|
// ----------------------------
|
|
|
|
// Update fflags as soon as a FP exception occurs in the accelerator
|
|
// The exception is imprecise, and the fcsr.fflags update always happens immediately
|
|
if (CVA6Cfg.EnableAccelerator) begin
|
|
fcsr_d.fflags |= acc_fflags_ex_valid_i ? acc_fflags_ex_i : 5'b0;
|
|
end
|
|
|
|
// ---------------------
|
|
// External Interrupts
|
|
// ---------------------
|
|
// Machine Mode External Interrupt Pending
|
|
mip_d[riscv::IRQ_M_EXT] = irq_i[0];
|
|
// Machine software interrupt
|
|
mip_d[riscv::IRQ_M_SOFT] = CVA6Cfg.SoftwareInterruptEn && ipi_i;
|
|
// Timer interrupt pending, coming from platform timer
|
|
mip_d[riscv::IRQ_M_TIMER] = time_irq_i;
|
|
|
|
// -----------------------
|
|
// Manage Exception Stack
|
|
// -----------------------
|
|
// update exception CSRs
|
|
// we got an exception update cause, pc and stval register
|
|
trap_to_priv_lvl = riscv::PRIV_LVL_M;
|
|
trap_to_v = 1'b0;
|
|
// Exception is taken and we are not in debug mode
|
|
// exceptions in debug mode don't update any fields
|
|
if ((CVA6Cfg.DebugEn && !debug_mode_q && ex_i.cause != riscv::DEBUG_REQUEST && ex_i.valid) || (!CVA6Cfg.DebugEn && ex_i.valid)) begin
|
|
// do not flush, flush is reserved for CSR writes with side effects
|
|
flush_o = 1'b0;
|
|
// figure out where to trap to
|
|
// a m-mode trap might be delegated if we are taking it in S mode
|
|
// first figure out if this was an exception or an interrupt e.g.: look at bit (XLEN-1)
|
|
// the cause register can only be $clog2(CVA6Cfg.XLEN) bits long (as we only support XLEN exceptions)
|
|
if (CVA6Cfg.RVS) begin
|
|
if ((ex_i.cause[CVA6Cfg.XLEN-1] && mideleg_q[ex_i.cause[$clog2(
|
|
CVA6Cfg.XLEN
|
|
)-1:0]]) || (~ex_i.cause[CVA6Cfg.XLEN-1] && medeleg_q[ex_i.cause[$clog2(
|
|
CVA6Cfg.XLEN
|
|
)-1:0]])) begin
|
|
// traps never transition from a more-privileged mode to a less privileged mode
|
|
// so if we are already in M mode, stay there
|
|
trap_to_priv_lvl = (priv_lvl_o == riscv::PRIV_LVL_M) ? riscv::PRIV_LVL_M : riscv::PRIV_LVL_S;
|
|
if (CVA6Cfg.RVH) begin
|
|
if ((ex_i.cause[CVA6Cfg.XLEN-1] && hideleg_q[ex_i.cause[$clog2(
|
|
CVA6Cfg.XLEN
|
|
)-1:0]]) || (~ex_i.cause[CVA6Cfg.XLEN-1] && hedeleg_q[ex_i.cause[$clog2(
|
|
CVA6Cfg.XLEN
|
|
)-1:0]])) begin
|
|
// trap to VS only if it is the currently active mode
|
|
trap_to_v = v_q;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
// trap to supervisor mode
|
|
if (CVA6Cfg.RVS && trap_to_priv_lvl == riscv::PRIV_LVL_S) begin
|
|
if (CVA6Cfg.RVH && trap_to_v) begin
|
|
// update sstatus
|
|
vsstatus_d.sie = 1'b0;
|
|
vsstatus_d.spie = (CVA6Cfg.RVH) ? vsstatus_q.sie : '0;
|
|
// this can either be user or supervisor mode
|
|
vsstatus_d.spp = priv_lvl_q[0];
|
|
// set cause
|
|
vscause_d = ex_i.cause[CVA6Cfg.XLEN-1] ? {ex_i.cause[CVA6Cfg.XLEN-1:2], 2'b01} : ex_i.cause;
|
|
// set epc
|
|
vsepc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i};
|
|
// set vstval
|
|
vstval_d = (ariane_pkg::ZERO_TVAL
|
|
&& (ex_i.cause inside {
|
|
riscv::ILLEGAL_INSTR,
|
|
riscv::BREAKPOINT,
|
|
riscv::ENV_CALL_UMODE
|
|
} || ex_i.cause[CVA6Cfg.XLEN-1])) ? '0 : ex_i.tval;
|
|
end else begin
|
|
// update sstatus
|
|
mstatus_d.sie = 1'b0;
|
|
mstatus_d.spie = mstatus_q.sie;
|
|
// this can either be user or supervisor mode
|
|
mstatus_d.spp = priv_lvl_q[0];
|
|
// set cause
|
|
scause_d = ex_i.cause;
|
|
// set epc
|
|
sepc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i};
|
|
// set mtval or stval
|
|
stval_d = (ariane_pkg::ZERO_TVAL
|
|
&& (ex_i.cause inside {
|
|
riscv::ILLEGAL_INSTR,
|
|
riscv::BREAKPOINT,
|
|
riscv::ENV_CALL_UMODE,
|
|
riscv::ENV_CALL_SMODE,
|
|
riscv::ENV_CALL_MMODE
|
|
} || ex_i.cause[CVA6Cfg.XLEN-1])) ? '0 : ex_i.tval;
|
|
if (CVA6Cfg.RVH) begin
|
|
htinst_d = (ariane_pkg::ZERO_TVAL
|
|
&& (ex_i.cause inside {
|
|
riscv::INSTR_ACCESS_FAULT,
|
|
riscv::ILLEGAL_INSTR,
|
|
riscv::BREAKPOINT,
|
|
riscv::ENV_CALL_UMODE,
|
|
riscv::ENV_CALL_SMODE,
|
|
riscv::ENV_CALL_MMODE,
|
|
riscv::INSTR_PAGE_FAULT,
|
|
riscv::INSTR_GUEST_PAGE_FAULT,
|
|
riscv::VIRTUAL_INSTRUCTION
|
|
} || ex_i.cause[CVA6Cfg.XLEN-1])) ? '0 : {{CVA6Cfg.XLEN - 32 {1'b0}}, ex_i.tinst};
|
|
hstatus_d.spvp = v_q ? priv_lvl_q[0] : hstatus_d.spvp;
|
|
htval_d = {{CVA6Cfg.XLEN - CVA6Cfg.GPLEN + 2{1'b0}}, ex_i.tval2[CVA6Cfg.GPLEN-1:2]};
|
|
hstatus_d.gva = ex_i.gva;
|
|
hstatus_d.spv = v_q;
|
|
end
|
|
end
|
|
// trap to machine mode
|
|
end else begin
|
|
// update mstatus
|
|
mstatus_d.mie = 1'b0;
|
|
mstatus_d.mpie = mstatus_q.mie;
|
|
// save the previous privilege mode
|
|
mstatus_d.mpp = priv_lvl_q;
|
|
mcause_d = ex_i.cause;
|
|
// set epc
|
|
mepc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i};
|
|
// set mtval or stval
|
|
if (CVA6Cfg.TvalEn) begin
|
|
mtval_d = (ariane_pkg::ZERO_TVAL
|
|
&& (ex_i.cause inside {
|
|
riscv::ILLEGAL_INSTR,
|
|
riscv::BREAKPOINT,
|
|
riscv::ENV_CALL_UMODE,
|
|
riscv::ENV_CALL_SMODE,
|
|
riscv::ENV_CALL_MMODE
|
|
} || ex_i.cause[CVA6Cfg.GPLEN-1])) ? '0 : ex_i.tval;
|
|
end else begin
|
|
mtval_d = '0;
|
|
end
|
|
|
|
if (CVA6Cfg.RVH) begin
|
|
// save previous virtualization mode
|
|
mstatus_d.mpv = v_q;
|
|
mtinst_d = (ariane_pkg::ZERO_TVAL
|
|
&& (ex_i.cause inside {
|
|
riscv::INSTR_ADDR_MISALIGNED,
|
|
riscv::INSTR_ACCESS_FAULT,
|
|
riscv::ILLEGAL_INSTR,
|
|
riscv::BREAKPOINT,
|
|
riscv::ENV_CALL_UMODE,
|
|
riscv::ENV_CALL_SMODE,
|
|
riscv::ENV_CALL_MMODE,
|
|
riscv::INSTR_PAGE_FAULT,
|
|
riscv::INSTR_GUEST_PAGE_FAULT,
|
|
riscv::VIRTUAL_INSTRUCTION
|
|
} || ex_i.cause[CVA6Cfg.XLEN-1])) ? '0 : {{CVA6Cfg.XLEN - 32 {1'b0}}, ex_i.tinst};
|
|
mtval2_d = {{CVA6Cfg.XLEN - CVA6Cfg.GPLEN + 2{1'b0}}, ex_i.tval2[CVA6Cfg.GPLEN-1:2]};
|
|
mstatus_d.gva = ex_i.gva;
|
|
end
|
|
end
|
|
|
|
priv_lvl_d = trap_to_priv_lvl;
|
|
if (CVA6Cfg.RVH) begin
|
|
v_d = trap_to_v;
|
|
end
|
|
end
|
|
|
|
// ------------------------------
|
|
// Debug
|
|
// ------------------------------
|
|
// Explains why Debug Mode was entered.
|
|
// When there are multiple reasons to enter Debug Mode in a single cycle, hardware should set cause to the cause with the highest priority.
|
|
// 1: An ebreak instruction was executed. (priority 3)
|
|
// 2: The Trigger Module caused a breakpoint exception. (priority 4)
|
|
// 3: The debugger requested entry to Debug Mode. (priority 2)
|
|
// 4: The hart single stepped because step was set. (priority 1)
|
|
// we are currently not in debug mode and could potentially enter
|
|
if (CVA6Cfg.DebugEn && !debug_mode_q) begin
|
|
dcsr_d.prv = priv_lvl_o;
|
|
// save virtualization mode bit
|
|
dcsr_d.v = (!CVA6Cfg.RVH) ? 1'b0 : v_q;
|
|
// trigger module fired
|
|
|
|
// caused by a breakpoint
|
|
if (ex_i.valid && ex_i.cause == riscv::BREAKPOINT) begin
|
|
dcsr_d.prv = priv_lvl_o;
|
|
// save virtualization mode bit
|
|
dcsr_d.v = (!CVA6Cfg.RVH) ? 1'b0 : v_q;
|
|
// check that we actually want to enter debug depending on the privilege level we are currently in
|
|
unique case (priv_lvl_o)
|
|
riscv::PRIV_LVL_M: begin
|
|
debug_mode_d = dcsr_q.ebreakm;
|
|
set_debug_pc_o = dcsr_q.ebreakm;
|
|
end
|
|
riscv::PRIV_LVL_S: begin
|
|
if (CVA6Cfg.RVS) begin
|
|
debug_mode_d = (CVA6Cfg.RVH && v_q) ? dcsr_q.ebreakvs : dcsr_q.ebreaks;
|
|
set_debug_pc_o = (CVA6Cfg.RVH && v_q) ? dcsr_q.ebreakvs : dcsr_q.ebreaks;
|
|
end
|
|
end
|
|
riscv::PRIV_LVL_U: begin
|
|
if (CVA6Cfg.RVU) begin
|
|
debug_mode_d = (CVA6Cfg.RVH && v_q) ? dcsr_q.ebreakvu : dcsr_q.ebreaku;
|
|
set_debug_pc_o = (CVA6Cfg.RVH && v_q) ? dcsr_q.ebreakvu : dcsr_q.ebreaku;
|
|
end
|
|
end
|
|
default: ;
|
|
endcase
|
|
// save PC of next this instruction e.g.: the next one to be executed
|
|
dpc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i};
|
|
dcsr_d.cause = ariane_pkg::CauseBreakpoint;
|
|
end
|
|
|
|
// we've got a debug request
|
|
if (ex_i.valid && ex_i.cause == riscv::DEBUG_REQUEST) begin
|
|
dcsr_d.prv = priv_lvl_o;
|
|
dcsr_d.v = (!CVA6Cfg.RVH) ? 1'b0 : v_q;
|
|
// save the PC
|
|
dpc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i};
|
|
// enter debug mode
|
|
debug_mode_d = 1'b1;
|
|
// jump to the base address
|
|
set_debug_pc_o = 1'b1;
|
|
// save the cause as external debug request
|
|
dcsr_d.cause = ariane_pkg::CauseRequest;
|
|
end
|
|
|
|
// single step enable and we just retired an instruction
|
|
if (dcsr_q.step && commit_ack_i[0]) begin
|
|
dcsr_d.prv = priv_lvl_o;
|
|
dcsr_d.v = (!CVA6Cfg.RVH) ? 1'b0 : v_q;
|
|
// valid CTRL flow change
|
|
if (commit_instr_i.fu == CTRL_FLOW) begin
|
|
// we saved the correct target address during execute
|
|
dpc_d = {
|
|
{CVA6Cfg.XLEN - CVA6Cfg.VLEN{commit_instr_i.bp.predict_address[CVA6Cfg.VLEN-1]}},
|
|
commit_instr_i.bp.predict_address
|
|
};
|
|
// exception valid
|
|
end else if (ex_i.valid) begin
|
|
dpc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, trap_vector_base_o};
|
|
// return from environment
|
|
end else if (eret_o) begin
|
|
dpc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, epc_o};
|
|
// consecutive PC
|
|
end else begin
|
|
dpc_d = {
|
|
{CVA6Cfg.XLEN - CVA6Cfg.VLEN{commit_instr_i.pc[CVA6Cfg.VLEN-1]}},
|
|
commit_instr_i.pc + (commit_instr_i.is_compressed ? 'h2 : 'h4)
|
|
};
|
|
end
|
|
debug_mode_d = 1'b1;
|
|
set_debug_pc_o = 1'b1;
|
|
dcsr_d.cause = ariane_pkg::CauseSingleStep;
|
|
end
|
|
end
|
|
// go in halt-state again when we encounter an exception
|
|
if (CVA6Cfg.DebugEn && debug_mode_q && ex_i.valid && ex_i.cause == riscv::BREAKPOINT) begin
|
|
set_debug_pc_o = 1'b1;
|
|
end
|
|
|
|
// ------------------------------
|
|
// MPRV - Modify Privilege Level
|
|
// ------------------------------
|
|
// Set the address translation at which the load and stores should occur
|
|
// we can use the previous values since changing the address translation will always involve a pipeline flush
|
|
if (CVA6Cfg.RVH) begin
|
|
if (mprv && (mstatus_q.mpv == 1'b0) && (config_pkg::vm_mode_t'(satp_q.mode) == CVA6Cfg.MODE_SV) && (mstatus_q.mpp != riscv::PRIV_LVL_M)) begin
|
|
en_ld_st_translation_d = 1'b1;
|
|
end else if (mprv && (mstatus_q.mpv == 1'b1)) begin
|
|
if (config_pkg::vm_mode_t'(vsatp_q.mode) == CVA6Cfg.MODE_SV) begin
|
|
en_ld_st_translation_d = 1'b1;
|
|
end else begin
|
|
en_ld_st_translation_d = 1'b0;
|
|
end
|
|
end else begin // otherwise we go with the regular settings
|
|
en_ld_st_translation_d = en_translation_o;
|
|
end
|
|
|
|
if (mprv && (mstatus_q.mpv == 1'b1)) begin
|
|
if (config_pkg::vm_mode_t'(hgatp_q.mode) == CVA6Cfg.MODE_SV) begin
|
|
en_ld_st_g_translation_d = 1'b1;
|
|
end else begin
|
|
en_ld_st_g_translation_d = 1'b0;
|
|
end
|
|
end else begin
|
|
en_ld_st_g_translation_d = en_g_translation_o;
|
|
end
|
|
|
|
if (csr_hs_ld_st_inst_i) ld_st_priv_lvl_o = riscv::priv_lvl_t'(hstatus_q.spvp);
|
|
else ld_st_priv_lvl_o = (mprv) ? mstatus_q.mpp : priv_lvl_o;
|
|
|
|
ld_st_v_o = ((mprv ? mstatus_q.mpv : v_q) || (csr_hs_ld_st_inst_i));
|
|
|
|
en_ld_st_translation_o = (en_ld_st_translation_q && !csr_hs_ld_st_inst_i) || (config_pkg::vm_mode_t'(vsatp_q.mode) == CVA6Cfg.MODE_SV && csr_hs_ld_st_inst_i);
|
|
|
|
en_ld_st_g_translation_o = (en_ld_st_g_translation_q && !csr_hs_ld_st_inst_i) || (csr_hs_ld_st_inst_i && config_pkg::vm_mode_t'(hgatp_q.mode) == CVA6Cfg.MODE_SV && csr_hs_ld_st_inst_i);
|
|
end else begin
|
|
if (CVA6Cfg.MmuPresent && mprv && CVA6Cfg.RVS && config_pkg::vm_mode_t'(satp_q.mode) == CVA6Cfg.MODE_SV && (mstatus_q.mpp != riscv::PRIV_LVL_M))
|
|
en_ld_st_translation_d = 1'b1;
|
|
else // otherwise we go with the regular settings
|
|
en_ld_st_translation_d = en_translation_o;
|
|
|
|
if (CVA6Cfg.RVU) begin
|
|
ld_st_priv_lvl_o = (mprv) ? mstatus_q.mpp : priv_lvl_o;
|
|
end else begin
|
|
ld_st_priv_lvl_o = priv_lvl_o;
|
|
end
|
|
en_ld_st_translation_o = en_ld_st_translation_q;
|
|
ld_st_v_o = 1'b0;
|
|
en_ld_st_g_translation_o = 1'b0;
|
|
end
|
|
// ------------------------------
|
|
// Return from Environment
|
|
// ------------------------------
|
|
// 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 U
|
|
if (mret) begin
|
|
// return from exception, IF doesn't care from where we are returning
|
|
eret_o = 1'b1;
|
|
// return to the previous privilege level and restore all enable flags
|
|
// get the previous machine interrupt enable flag
|
|
mstatus_d.mie = mstatus_q.mpie;
|
|
// restore the previous privilege level
|
|
priv_lvl_d = mstatus_q.mpp;
|
|
mstatus_d.mpp = riscv::PRIV_LVL_M;
|
|
if (CVA6Cfg.RVU) begin
|
|
// set mpp to user mode
|
|
mstatus_d.mpp = riscv::PRIV_LVL_U;
|
|
end
|
|
// set mpie to 1
|
|
mstatus_d.mpie = 1'b1;
|
|
if (CVA6Cfg.RVH) begin
|
|
// set virtualization mode
|
|
v_d = mstatus_q.mpv;
|
|
//set mstatus mpv to false
|
|
mstatus_d.mpv = 1'b0;
|
|
if (mstatus_q.mpp != riscv::PRIV_LVL_M) mstatus_d.mprv = 1'b0;
|
|
end
|
|
end
|
|
|
|
if (CVA6Cfg.RVS && sret && ((CVA6Cfg.RVH && !v_q) || !CVA6Cfg.RVH)) begin
|
|
// return from exception, IF doesn't care from where we are returning
|
|
eret_o = 1'b1;
|
|
// return the previous supervisor interrupt enable flag
|
|
mstatus_d.sie = mstatus_q.spie;
|
|
// restore the previous privilege level
|
|
priv_lvl_d = riscv::priv_lvl_t'({1'b0, mstatus_q.spp});
|
|
// set spp to user mode
|
|
mstatus_d.spp = 1'b0;
|
|
// set spie to 1
|
|
mstatus_d.spie = 1'b1;
|
|
if (CVA6Cfg.RVH) begin
|
|
// set virtualization mode
|
|
v_d = hstatus_q.spv;
|
|
//set hstatus spv to false
|
|
hstatus_d.spv = 1'b0;
|
|
mstatus_d.mprv = 1'b0;
|
|
end
|
|
end
|
|
|
|
if (CVA6Cfg.RVH) begin
|
|
if (sret && v_q) begin
|
|
// return from exception, IF doesn't care from where we are returning
|
|
eret_o = 1'b1;
|
|
// return the previous supervisor interrupt enable flag
|
|
vsstatus_d.sie = vsstatus_q.spie;
|
|
// restore the previous privilege level
|
|
priv_lvl_d = riscv::priv_lvl_t'({1'b0, vsstatus_q.spp});
|
|
// set spp to user mode
|
|
vsstatus_d.spp = 1'b0;
|
|
// set spie to 1
|
|
vsstatus_d.spie = 1'b1;
|
|
end
|
|
end
|
|
|
|
// return from debug mode
|
|
if (CVA6Cfg.DebugEn) begin
|
|
if (dret) begin
|
|
// return from exception, IF doesn't care from where we are returning
|
|
eret_o = 1'b1;
|
|
// restore the previous privilege level
|
|
priv_lvl_d = riscv::priv_lvl_t'(dcsr_q.prv);
|
|
if (CVA6Cfg.RVH) begin
|
|
// restore the previous virtualization mode
|
|
v_d = dcsr_q.v;
|
|
end
|
|
// actually return from debug mode
|
|
debug_mode_d = 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
// ---------------------------
|
|
// CSR OP Select Logic
|
|
// ---------------------------
|
|
always_comb begin : csr_op_logic
|
|
csr_wdata = csr_wdata_i;
|
|
csr_we = 1'b1;
|
|
csr_read = 1'b1;
|
|
mret = 1'b0;
|
|
sret = 1'b0;
|
|
dret = 1'b0;
|
|
|
|
unique case (csr_op_i)
|
|
CSR_WRITE: csr_wdata = csr_wdata_i;
|
|
CSR_SET: csr_wdata = csr_wdata_i | csr_rdata;
|
|
CSR_CLEAR: csr_wdata = (~csr_wdata_i) & csr_rdata;
|
|
CSR_READ: csr_we = 1'b0;
|
|
MRET: begin
|
|
// the return should not have any write or read side-effects
|
|
csr_we = 1'b0;
|
|
csr_read = 1'b0;
|
|
mret = 1'b1; // signal a return from machine mode
|
|
end
|
|
default: begin
|
|
if (CVA6Cfg.RVS && csr_op_i == SRET) begin
|
|
// the return should not have any write or read side-effects
|
|
csr_we = 1'b0;
|
|
csr_read = 1'b0;
|
|
sret = 1'b1; // signal a return from supervisor mode
|
|
end else if (CVA6Cfg.DebugEn && csr_op_i == DRET) begin
|
|
// the return should not have any write or read side-effects
|
|
csr_we = 1'b0;
|
|
csr_read = 1'b0;
|
|
dret = 1'b1; // signal a return from debug mode
|
|
end else begin
|
|
csr_we = 1'b0;
|
|
csr_read = 1'b0;
|
|
end
|
|
end
|
|
endcase
|
|
// 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
|
|
|
|
assign irq_ctrl_o.mie = mie_q;
|
|
assign irq_ctrl_o.mip = mip_q;
|
|
if (CVA6Cfg.RVH) begin
|
|
assign irq_ctrl_o.sie = (v_q) ? vsstatus_q.sie : mstatus_q.sie;
|
|
end else begin
|
|
assign irq_ctrl_o.sie = mstatus_q.sie;
|
|
end
|
|
assign irq_ctrl_o.mideleg = (CVA6Cfg.RVS) ? mideleg_q : '0;
|
|
assign irq_ctrl_o.hideleg = (CVA6Cfg.RVH) ? hideleg_q : '0;
|
|
assign irq_ctrl_o.global_enable = ~(CVA6Cfg.DebugEn & debug_mode_q)
|
|
// interrupts are enabled during single step or we are not stepping
|
|
// No need to check interrupts during single step if we don't support DEBUG mode
|
|
& (~CVA6Cfg.DebugEn | (~dcsr_q.step | dcsr_q.stepie))
|
|
& ((mstatus_q.mie & (priv_lvl_o == riscv::PRIV_LVL_M))
|
|
| (CVA6Cfg.RVU & priv_lvl_o != riscv::PRIV_LVL_M));
|
|
|
|
always_comb begin : privilege_check
|
|
if (CVA6Cfg.RVH) begin
|
|
automatic riscv::priv_lvl_t access_priv;
|
|
automatic riscv::priv_lvl_t curr_priv;
|
|
automatic logic [SELECT_COUNTER_WIDTH-1:0] sel_cnt_en;
|
|
// transforms S mode accesses into HS mode
|
|
access_priv = (priv_lvl_o == riscv::PRIV_LVL_S && !v_q) ? riscv::PRIV_LVL_HS : priv_lvl_o;
|
|
curr_priv = priv_lvl_o;
|
|
sel_cnt_en = {{SELECT_COUNTER_WIDTH - 5{1'b0}}, csr_addr_i[4:0]};
|
|
// -----------------
|
|
// Privilege Check
|
|
// -----------------
|
|
privilege_violation = 1'b0;
|
|
virtual_privilege_violation = 1'b0;
|
|
// if we are reading or writing, check for the correct privilege level this has
|
|
// precedence over interrupts
|
|
if (csr_op_i inside {CSR_WRITE, CSR_SET, CSR_CLEAR, CSR_READ}) begin
|
|
if (access_priv < csr_addr.csr_decode.priv_lvl) begin
|
|
if (v_q && csr_addr.csr_decode.priv_lvl <= riscv::PRIV_LVL_HS)
|
|
virtual_privilege_violation = 1'b1;
|
|
else privilege_violation = 1'b1;
|
|
end
|
|
// check access to debug mode only CSRs
|
|
if ((!CVA6Cfg.DebugEn && csr_addr_i[11:4] == 8'h7b) || (CVA6Cfg.DebugEn && csr_addr_i[11:4] == 8'h7b && !debug_mode_q)) begin
|
|
privilege_violation = 1'b1;
|
|
end
|
|
// check counter-enabled counter CSR accesses
|
|
// counter address range is C00 to C1F
|
|
if (CVA6Cfg.RVZihpm) begin
|
|
if (csr_addr_i inside {[riscv::CSR_HPM_COUNTER_3 : riscv::CSR_HPM_COUNTER_31]} |
|
|
csr_addr_i inside {[riscv::CSR_HPM_COUNTER_3H : riscv::CSR_HPM_COUNTER_31H]}) begin
|
|
if (curr_priv == riscv::PRIV_LVL_S && CVA6Cfg.RVS) begin
|
|
virtual_privilege_violation = v_q & mcounteren_q[sel_cnt_en] & ~hcounteren_q[sel_cnt_en];
|
|
privilege_violation = ~mcounteren_q[sel_cnt_en];
|
|
end else if (priv_lvl_o == riscv::PRIV_LVL_U && CVA6Cfg.RVU) begin
|
|
virtual_privilege_violation = v_q & mcounteren_q[sel_cnt_en] & ~hcounteren_q[sel_cnt_en];
|
|
if (v_q) begin
|
|
privilege_violation = ~mcounteren_q[sel_cnt_en] & ~scounteren_q[sel_cnt_en] & hcounteren_q[sel_cnt_en];
|
|
end else begin
|
|
privilege_violation = ~mcounteren_q[sel_cnt_en] & ~scounteren_q[sel_cnt_en];
|
|
end
|
|
end else if (priv_lvl_o == riscv::PRIV_LVL_M) begin
|
|
privilege_violation = 1'b0;
|
|
end
|
|
end
|
|
end
|
|
if (CVA6Cfg.RVZicntr) begin
|
|
if (csr_addr_i inside {[riscv::CSR_CYCLE : riscv::CSR_INSTRET]} |
|
|
csr_addr_i inside {[riscv::CSR_CYCLEH : riscv::CSR_INSTRETH]}) begin
|
|
if (curr_priv == riscv::PRIV_LVL_S && CVA6Cfg.RVS) begin
|
|
virtual_privilege_violation = v_q & mcounteren_q[sel_cnt_en] & ~hcounteren_q[sel_cnt_en];
|
|
privilege_violation = ~mcounteren_q[sel_cnt_en];
|
|
end else if (priv_lvl_o == riscv::PRIV_LVL_U && CVA6Cfg.RVU) begin
|
|
virtual_privilege_violation = v_q & mcounteren_q[sel_cnt_en] & ~hcounteren_q[sel_cnt_en];
|
|
if (v_q) begin
|
|
privilege_violation = ~mcounteren_q[sel_cnt_en] & ~scounteren_q[sel_cnt_en] & hcounteren_q[sel_cnt_en];
|
|
end else begin
|
|
privilege_violation = ~mcounteren_q[sel_cnt_en] & ~scounteren_q[sel_cnt_en];
|
|
end
|
|
end else if (priv_lvl_o == riscv::PRIV_LVL_M) begin
|
|
privilege_violation = 1'b0;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end else begin
|
|
// -----------------
|
|
// 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_op_i inside {CSR_WRITE, CSR_SET, CSR_CLEAR, CSR_READ}) begin
|
|
if (CVA6Cfg.RVU && (riscv::priv_lvl_t'(priv_lvl_o & csr_addr.csr_decode.priv_lvl) != csr_addr.csr_decode.priv_lvl)) begin
|
|
privilege_violation = 1'b1;
|
|
end
|
|
// check access to debug mode only CSRs
|
|
if ((!CVA6Cfg.DebugEn && csr_addr_i[11:4] == 8'h7b) || (CVA6Cfg.DebugEn && csr_addr_i[11:4] == 8'h7b && !debug_mode_q)) begin
|
|
privilege_violation = 1'b1;
|
|
end
|
|
// check counter-enabled counter CSR accesses
|
|
// counter address range is C00 to C1F
|
|
if (CVA6Cfg.RVZihpm) begin
|
|
if (csr_addr_i inside {[riscv::CSR_HPM_COUNTER_3 : riscv::CSR_HPM_COUNTER_31]} |
|
|
csr_addr_i inside {[riscv::CSR_HPM_COUNTER_3H : riscv::CSR_HPM_COUNTER_31H]}) begin
|
|
if (priv_lvl_o == riscv::PRIV_LVL_S && CVA6Cfg.RVS) begin
|
|
privilege_violation = ~mcounteren_q[csr_addr_i[4:0]];
|
|
end else if (priv_lvl_o == riscv::PRIV_LVL_U && CVA6Cfg.RVU) begin
|
|
privilege_violation = ~mcounteren_q[csr_addr_i[4:0]] | ~scounteren_q[csr_addr_i[4:0]];
|
|
end else if (priv_lvl_o == riscv::PRIV_LVL_M) begin
|
|
privilege_violation = 1'b0;
|
|
end
|
|
end
|
|
end
|
|
if (CVA6Cfg.RVZicntr) begin
|
|
if (csr_addr_i inside {[riscv::CSR_CYCLE : riscv::CSR_INSTRET]} |
|
|
csr_addr_i inside {[riscv::CSR_CYCLEH : riscv::CSR_INSTRETH]}) begin
|
|
if (priv_lvl_o == riscv::PRIV_LVL_S && CVA6Cfg.RVS) begin
|
|
privilege_violation = ~mcounteren_q[csr_addr_i[4:0]];
|
|
end else if (priv_lvl_o == riscv::PRIV_LVL_U && CVA6Cfg.RVU) begin
|
|
privilege_violation = ~mcounteren_q[csr_addr_i[4:0]] | ~scounteren_q[csr_addr_i[4:0]];
|
|
end else if (priv_lvl_o == riscv::PRIV_LVL_M) begin
|
|
privilege_violation = 1'b0;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
// ----------------------
|
|
// CSR Exception Control
|
|
// ----------------------
|
|
always_comb begin : exception_ctrl
|
|
csr_exception_o = {
|
|
{CVA6Cfg.XLEN{1'b0}}, {CVA6Cfg.XLEN{1'b0}}, {CVA6Cfg.GPLEN{1'b0}}, {32{1'b0}}, 1'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
|
|
csr_exception_o.cause = riscv::ILLEGAL_INSTR;
|
|
// we don't set the tval field as this will be set by the commit stage
|
|
// this spares the extra wiring from commit to CSR and back to commit
|
|
csr_exception_o.valid = 1'b1;
|
|
end
|
|
|
|
if (privilege_violation) begin
|
|
csr_exception_o.cause = riscv::ILLEGAL_INSTR;
|
|
csr_exception_o.valid = 1'b1;
|
|
end
|
|
|
|
if (CVA6Cfg.RVH && (virtual_update_access_exception || virtual_read_access_exception || virtual_privilege_violation)) begin
|
|
csr_exception_o.cause = riscv::VIRTUAL_INSTRUCTION;
|
|
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 (enabled) interrupt pending un-stall the core
|
|
// also un-stall if we want to enter debug mode
|
|
if (|(mip_q & mie_q) || (CVA6Cfg.DebugEn && debug_req_i) || irq_i[1]) begin
|
|
wfi_d = 1'b0;
|
|
// or alternatively if there is no exception pending and we are not in debug mode wait here
|
|
// for the interrupt
|
|
end else if (((CVA6Cfg.DebugEn && !debug_mode_q) && csr_op_i == WFI && !ex_i.valid) || (!CVA6Cfg.DebugEn && csr_op_i == WFI && !ex_i.valid)) begin
|
|
wfi_d = 1'b1;
|
|
end
|
|
end
|
|
|
|
// output assignments dependent on privilege mode
|
|
always_comb begin : priv_output
|
|
trap_vector_base_o = {mtvec_q[CVA6Cfg.VLEN-1:2], 2'b0};
|
|
// output user mode stvec
|
|
if (CVA6Cfg.RVS && trap_to_priv_lvl == riscv::PRIV_LVL_S) begin
|
|
trap_vector_base_o = (CVA6Cfg.RVH && trap_to_v) ? {vstvec_q[CVA6Cfg.VLEN-1:2], 2'b0} : {stvec_q[CVA6Cfg.VLEN-1:2], 2'b0};
|
|
end
|
|
|
|
// if we are in debug mode jump to a specific address
|
|
if (CVA6Cfg.DebugEn && debug_mode_q) begin
|
|
trap_vector_base_o = CVA6Cfg.DmBaseAddress[CVA6Cfg.VLEN-1:0] + CVA6Cfg.ExceptionAddress[CVA6Cfg.VLEN-1:0];
|
|
end
|
|
|
|
// 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. Furthermore check to which
|
|
// privilege level we are jumping and whether the vectored mode is
|
|
// activated for _that_ privilege level.
|
|
if (ex_i.cause[CVA6Cfg.XLEN-1] &&
|
|
((((CVA6Cfg.RVS || CVA6Cfg.RVU) && trap_to_priv_lvl == riscv::PRIV_LVL_M && (!CVA6Cfg.DirectVecOnly && mtvec_q[0])) || (!CVA6Cfg.RVS && !CVA6Cfg.RVU && (!CVA6Cfg.DirectVecOnly && mtvec_q[0])))
|
|
|| (CVA6Cfg.RVS && trap_to_priv_lvl == riscv::PRIV_LVL_S && !trap_to_v && stvec_q[0]))) begin
|
|
trap_vector_base_o[7:2] = ex_i.cause[5:0];
|
|
end
|
|
if (ex_i.cause[CVA6Cfg.XLEN-1] &&
|
|
(CVA6Cfg.RVH && trap_to_priv_lvl == riscv::PRIV_LVL_S && trap_to_v && vstvec_q[0])) begin
|
|
trap_vector_base_o[7:2] = {ex_i.cause[5:2], 2'b01};
|
|
end
|
|
|
|
epc_o = mepc_q[CVA6Cfg.VLEN-1:0];
|
|
// we are returning from supervisor or virtual supervisor mode, so take the sepc register
|
|
if (CVA6Cfg.RVS) begin
|
|
if (sret) begin
|
|
epc_o = (CVA6Cfg.RVH && v_q) ? vsepc_q[CVA6Cfg.VLEN-1:0] : sepc_q[CVA6Cfg.VLEN-1:0];
|
|
end
|
|
end
|
|
// we are returning from debug mode, to take the dpc register
|
|
if (CVA6Cfg.DebugEn) begin
|
|
if (dret) begin
|
|
epc_o = dpc_q[CVA6Cfg.VLEN-1:0];
|
|
end
|
|
end
|
|
end
|
|
|
|
// -------------------
|
|
// Output Assignments
|
|
// -------------------
|
|
always_comb begin
|
|
// When the SEIP bit is read with a CSRRW, CSRRS, or CSRRC instruction, the value
|
|
// returned in the rd destination register contains the logical-OR of the software-writable
|
|
// bit and the interrupt signal from the interrupt controller.
|
|
csr_rdata_o = csr_rdata;
|
|
|
|
unique case (conv_csr_addr.address)
|
|
riscv::CSR_MIP:
|
|
csr_rdata_o = csr_rdata | ({{CVA6Cfg.XLEN - 1{1'b0}}, CVA6Cfg.RVS && irq_i[1]} << riscv::IRQ_S_EXT);
|
|
// in supervisor mode we also need to check whether we delegated this bit
|
|
riscv::CSR_SIP: begin
|
|
if (CVA6Cfg.RVS) begin
|
|
csr_rdata_o = csr_rdata
|
|
| ({{CVA6Cfg.XLEN-1{1'b0}}, (irq_i[1] & mideleg_q[riscv::IRQ_S_EXT])} << riscv::IRQ_S_EXT);
|
|
end
|
|
end
|
|
default: ;
|
|
endcase
|
|
end
|
|
|
|
// in debug mode we execute with privilege level M
|
|
assign priv_lvl_o = (CVA6Cfg.DebugEn && debug_mode_q) ? riscv::PRIV_LVL_M : priv_lvl_q;
|
|
assign v_o = CVA6Cfg.RVH ? v_q : 1'b0;
|
|
// FPU outputs
|
|
assign fflags_o = fcsr_q.fflags;
|
|
assign frm_o = fcsr_q.frm;
|
|
assign fprec_o = fcsr_q.fprec;
|
|
//JVT outputs
|
|
if (CVA6Cfg.RVZCMT) begin
|
|
assign jvt_o.base = jvt_q.base;
|
|
assign jvt_o.mode = jvt_q.mode;
|
|
end else begin
|
|
assign jvt_o.base = '0;
|
|
assign jvt_o.mode = '0;
|
|
end
|
|
// MMU outputs
|
|
assign satp_ppn_o = CVA6Cfg.RVS ? satp_q.ppn : '0;
|
|
assign vsatp_ppn_o = CVA6Cfg.RVH ? vsatp_q.ppn : '0;
|
|
assign hgatp_ppn_o = CVA6Cfg.RVH ? hgatp_q.ppn : '0;
|
|
if (CVA6Cfg.RVS) begin
|
|
assign asid_o = satp_q.asid[CVA6Cfg.ASID_WIDTH-1:0];
|
|
end else begin
|
|
assign asid_o = '0;
|
|
end
|
|
assign vs_asid_o = CVA6Cfg.RVH ? vsatp_q.asid[CVA6Cfg.ASID_WIDTH-1:0] : '0;
|
|
assign vmid_o = CVA6Cfg.RVH ? hgatp_q.vmid[CVA6Cfg.VMID_WIDTH-1:0] : '0;
|
|
assign sum_o = mstatus_q.sum;
|
|
assign vs_sum_o = CVA6Cfg.RVH ? vsstatus_q.sum : '0;
|
|
assign hu_o = CVA6Cfg.RVH ? hstatus_q.hu : '0;
|
|
// we support bare memory addressing and SV39
|
|
if (CVA6Cfg.RVH) begin
|
|
assign en_translation_o = (((config_pkg::vm_mode_t'(satp_q.mode) == CVA6Cfg.MODE_SV && !v_q) || (config_pkg::vm_mode_t'(vsatp_q.mode) == CVA6Cfg.MODE_SV && v_q)) &&
|
|
priv_lvl_o != riscv::PRIV_LVL_M)
|
|
? 1'b1
|
|
: 1'b0;
|
|
assign en_g_translation_o = (config_pkg::vm_mode_t'(hgatp_q.mode) == CVA6Cfg.MODE_SV &&
|
|
priv_lvl_o != riscv::PRIV_LVL_M && v_q)
|
|
? 1'b1
|
|
: 1'b0;
|
|
end else begin
|
|
assign en_translation_o = (CVA6Cfg.RVS && config_pkg::vm_mode_t'(satp_q.mode) == CVA6Cfg.MODE_SV &&
|
|
priv_lvl_o != riscv::PRIV_LVL_M)
|
|
? 1'b1
|
|
: 1'b0;
|
|
assign en_g_translation_o = 1'b0;
|
|
end
|
|
assign mxr_o = mstatus_q.mxr;
|
|
assign vmxr_o = CVA6Cfg.RVH ? vsstatus_q.mxr : '0;
|
|
if (CVA6Cfg.RVH) begin
|
|
assign tvm_o = (v_q) ? hstatus_q.vtvm : mstatus_q.tvm;
|
|
end else begin
|
|
assign tvm_o = mstatus_q.tvm;
|
|
end
|
|
assign tw_o = mstatus_q.tw;
|
|
assign vtw_o = CVA6Cfg.RVH ? hstatus_q.vtw : '0;
|
|
if (CVA6Cfg.RVH) begin
|
|
assign tsr_o = (v_q) ? hstatus_q.vtsr : mstatus_q.tsr;
|
|
end else begin
|
|
assign tsr_o = mstatus_q.tsr;
|
|
end
|
|
assign halt_csr_o = wfi_q;
|
|
`ifdef PITON_ARIANE
|
|
assign icache_en_o = icache_q[0];
|
|
`else
|
|
assign icache_en_o = icache_q[0] & ~(CVA6Cfg.DebugEn && debug_mode_q);
|
|
`endif
|
|
assign dcache_en_o = dcache_q[0];
|
|
assign acc_cons_en_o = CVA6Cfg.EnableAccelerator ? acc_cons_q[0] : 1'b0;
|
|
|
|
// determine if mprv needs to be considered if in debug mode
|
|
assign mprv = (CVA6Cfg.DebugEn && debug_mode_q && !dcsr_q.mprven) ? 1'b0 : mstatus_q.mprv;
|
|
assign debug_mode_o = debug_mode_q;
|
|
assign single_step_o = CVA6Cfg.DebugEn ? dcsr_q.step : 1'b0;
|
|
assign mcountinhibit_o = {{29 - MHPMCounterNum{1'b0}}, mcountinhibit_q};
|
|
|
|
// sequential process
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (~rst_ni) begin
|
|
priv_lvl_q <= riscv::PRIV_LVL_M;
|
|
// floating-point registers
|
|
fcsr_q <= '0;
|
|
if (CVA6Cfg.RVZCMT) begin
|
|
jvt_q <= '0;
|
|
end
|
|
// debug signals
|
|
if (CVA6Cfg.DebugEn) begin
|
|
debug_mode_q <= 1'b0;
|
|
dcsr_q <= '{xdebugver: 4'h4, prv: riscv::PRIV_LVL_M, default: '0};
|
|
dpc_q <= '0;
|
|
dscratch0_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
dscratch1_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
end
|
|
// machine mode registers
|
|
mstatus_q <= 64'b0;
|
|
// set to boot address + direct mode + 4 byte offset which is the initial trap
|
|
mtvec_rst_load_q <= 1'b1;
|
|
mtvec_q <= '0;
|
|
mip_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
mie_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
mepc_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
mcause_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
mcounteren_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
mscratch_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
if (CVA6Cfg.TvalEn) mtval_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
fiom_q <= '0;
|
|
dcache_q <= {{CVA6Cfg.XLEN - 1{1'b0}}, 1'b1};
|
|
icache_q <= {{CVA6Cfg.XLEN - 1{1'b0}}, 1'b1};
|
|
mcountinhibit_q <= '0;
|
|
acc_cons_q <= {{CVA6Cfg.XLEN - 1{1'b0}}, CVA6Cfg.EnableAccelerator};
|
|
// supervisor mode registers
|
|
if (CVA6Cfg.RVS) begin
|
|
medeleg_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
mideleg_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
sepc_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
scause_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
stvec_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
scounteren_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
sscratch_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
stval_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
satp_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
end
|
|
|
|
if (CVA6Cfg.RVH) begin
|
|
v_q <= '0;
|
|
mtval2_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
mtinst_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
hstatus_q <= 64'b0;
|
|
hedeleg_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
hideleg_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
hgeie_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
hgatp_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
hcounteren_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
htval_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
htinst_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
// virtual supervisor mode registers
|
|
vsstatus_q <= 64'b0;
|
|
vsepc_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
vscause_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
vstvec_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
vsscratch_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
vstval_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
vsatp_q <= {CVA6Cfg.XLEN{1'b0}};
|
|
en_ld_st_g_translation_q <= 1'b0;
|
|
end
|
|
// timer and counters
|
|
cycle_q <= 64'b0;
|
|
instret_q <= 64'b0;
|
|
// aux registers
|
|
en_ld_st_translation_q <= 1'b0;
|
|
// wait for interrupt
|
|
wfi_q <= 1'b0;
|
|
// pmp
|
|
for (int i = 0; i < 64; i++) begin
|
|
if (i < CVA6Cfg.NrPMPEntries) begin
|
|
pmpcfg_q[i] <= riscv::pmpcfg_t'(CVA6Cfg.PMPCfgRstVal[i]);
|
|
pmpaddr_q[i] <= CVA6Cfg.PMPAddrRstVal[i][CVA6Cfg.PLEN-3:0];
|
|
end else begin
|
|
pmpcfg_q[i] <= '0;
|
|
pmpaddr_q[i] <= '0;
|
|
end
|
|
end
|
|
end else begin
|
|
priv_lvl_q <= priv_lvl_d;
|
|
// floating-point registers
|
|
fcsr_q <= fcsr_d;
|
|
if (CVA6Cfg.RVZCMT) begin
|
|
jvt_q <= jvt_d;
|
|
end
|
|
// debug signals
|
|
if (CVA6Cfg.DebugEn) begin
|
|
debug_mode_q <= debug_mode_d;
|
|
dcsr_q <= dcsr_d;
|
|
dpc_q <= dpc_d;
|
|
dscratch0_q <= dscratch0_d;
|
|
dscratch1_q <= dscratch1_d;
|
|
end
|
|
// machine mode registers
|
|
mstatus_q <= mstatus_d;
|
|
mtvec_rst_load_q <= 1'b0;
|
|
mtvec_q <= mtvec_d;
|
|
mip_q <= mip_d;
|
|
mie_q <= mie_d;
|
|
mepc_q <= mepc_d;
|
|
mcause_q <= mcause_d;
|
|
mcounteren_q <= mcounteren_d;
|
|
mscratch_q <= mscratch_d;
|
|
if (CVA6Cfg.TvalEn) mtval_q <= mtval_d;
|
|
fiom_q <= fiom_d;
|
|
dcache_q <= dcache_d;
|
|
icache_q <= icache_d;
|
|
mcountinhibit_q <= mcountinhibit_d;
|
|
acc_cons_q <= acc_cons_d;
|
|
// supervisor mode registers
|
|
if (CVA6Cfg.RVS) begin
|
|
medeleg_q <= medeleg_d;
|
|
mideleg_q <= mideleg_d;
|
|
sepc_q <= sepc_d;
|
|
scause_q <= scause_d;
|
|
stvec_q <= stvec_d;
|
|
scounteren_q <= scounteren_d;
|
|
sscratch_q <= sscratch_d;
|
|
if (CVA6Cfg.TvalEn) stval_q <= stval_d;
|
|
satp_q <= satp_d;
|
|
end
|
|
if (CVA6Cfg.RVH) begin
|
|
v_q <= v_d;
|
|
mtval2_q <= mtval2_d;
|
|
mtinst_q <= mtinst_d;
|
|
// hypervisor mode registers
|
|
hstatus_q <= hstatus_d;
|
|
hedeleg_q <= hedeleg_d;
|
|
hideleg_q <= hideleg_d;
|
|
hgeie_q <= hgeie_d;
|
|
hgatp_q <= hgatp_d;
|
|
hcounteren_q <= hcounteren_d;
|
|
htval_q <= htval_d;
|
|
htinst_q <= htinst_d;
|
|
// virtual supervisor mode registers
|
|
vsstatus_q <= vsstatus_d;
|
|
vsepc_q <= vsepc_d;
|
|
vscause_q <= vscause_d;
|
|
vstvec_q <= vstvec_d;
|
|
vsscratch_q <= vsscratch_d;
|
|
vstval_q <= vstval_d;
|
|
vsatp_q <= vsatp_d;
|
|
en_ld_st_g_translation_q <= en_ld_st_g_translation_d;
|
|
end
|
|
// timer and counters
|
|
cycle_q <= cycle_d;
|
|
instret_q <= instret_d;
|
|
// aux registers
|
|
en_ld_st_translation_q <= en_ld_st_translation_d;
|
|
// wait for interrupt
|
|
wfi_q <= wfi_d;
|
|
// pmp
|
|
pmpcfg_q <= pmpcfg_next;
|
|
pmpaddr_q <= pmpaddr_next;
|
|
end
|
|
end
|
|
|
|
// write logic pmp
|
|
always_comb begin : write
|
|
for (int i = 0; i < 64; i++) begin
|
|
if (i < CVA6Cfg.NrPMPEntries) begin
|
|
if (!CVA6Cfg.PMPEntryReadOnly[i]) begin
|
|
// PMP locked logic is handled in the CSR write process above
|
|
pmpcfg_next[i] = pmpcfg_d[i];
|
|
// We only support >=8-byte granularity, NA4 is not supported
|
|
if ((!CVA6Cfg.PMPNapotEn && pmpcfg_d[i].addr_mode == riscv::NAPOT) ||pmpcfg_d[i].addr_mode == riscv::NA4) begin
|
|
pmpcfg_next[i].addr_mode = pmpcfg_q[i].addr_mode;
|
|
end
|
|
// Follow collective WARL spec for RWX fields
|
|
if (pmpcfg_d[i].access_type.r == '0 && pmpcfg_d[i].access_type.w == '1) begin
|
|
pmpcfg_next[i].access_type = pmpcfg_q[i].access_type;
|
|
end
|
|
end else begin
|
|
pmpcfg_next[i] = pmpcfg_q[i];
|
|
end
|
|
if (!CVA6Cfg.PMPEntryReadOnly[i]) begin
|
|
pmpaddr_next[i] = pmpaddr_d[i];
|
|
end else begin
|
|
pmpaddr_next[i] = pmpaddr_q[i];
|
|
end
|
|
end else begin
|
|
pmpcfg_next[i] = '0;
|
|
pmpaddr_next[i] = '0;
|
|
end
|
|
end
|
|
end
|
|
|
|
//-------------
|
|
// Assertions
|
|
//-------------
|
|
//pragma translate_off
|
|
// check that eret and ex are never valid together
|
|
assert property (@(posedge clk_i) disable iff (!rst_ni !== '0) !(eret_o && ex_i.valid))
|
|
else begin
|
|
$error("eret and exception should never be valid at the same time");
|
|
$stop();
|
|
end
|
|
//pragma translate_on
|
|
|
|
|
|
//RVFI CSR
|
|
|
|
//-------------
|
|
// RVFI
|
|
//-------------
|
|
assign rvfi_csr_o.fcsr_q = CVA6Cfg.FpPresent ? fcsr_q : '0;
|
|
assign rvfi_csr_o.jvt_q = CVA6Cfg.RVZCMT ? jvt_q : '0;
|
|
assign rvfi_csr_o.dcsr_q = CVA6Cfg.DebugEn ? dcsr_q : '0;
|
|
assign rvfi_csr_o.dpc_q = CVA6Cfg.DebugEn ? dpc_q : '0;
|
|
assign rvfi_csr_o.dscratch0_q = CVA6Cfg.DebugEn ? dscratch0_q : '0;
|
|
assign rvfi_csr_o.dscratch1_q = CVA6Cfg.DebugEn ? dscratch1_q : '0;
|
|
assign rvfi_csr_o.mie_q = mie_q;
|
|
assign rvfi_csr_o.mip_q = mip_q;
|
|
assign rvfi_csr_o.stvec_q = CVA6Cfg.RVS ? stvec_q : '0;
|
|
assign rvfi_csr_o.scounteren_q = CVA6Cfg.RVS ? scounteren_q : '0;
|
|
assign rvfi_csr_o.sscratch_q = CVA6Cfg.RVS ? sscratch_q : '0;
|
|
assign rvfi_csr_o.sepc_q = CVA6Cfg.RVS ? sepc_q : '0;
|
|
assign rvfi_csr_o.scause_q = CVA6Cfg.RVS ? scause_q : '0;
|
|
assign rvfi_csr_o.stval_q = CVA6Cfg.RVS ? stval_q : '0;
|
|
assign rvfi_csr_o.satp_q = CVA6Cfg.RVS ? satp_q : '0;
|
|
assign rvfi_csr_o.mstatus_extended = mstatus_extended;
|
|
assign rvfi_csr_o.medeleg_q = CVA6Cfg.RVS ? medeleg_q : '0;
|
|
assign rvfi_csr_o.mideleg_q = CVA6Cfg.RVS ? mideleg_q : '0;
|
|
assign rvfi_csr_o.mtvec_q = mtvec_q;
|
|
assign rvfi_csr_o.mcounteren_q = mcounteren_q;
|
|
assign rvfi_csr_o.mscratch_q = mscratch_q;
|
|
assign rvfi_csr_o.mepc_q = mepc_q;
|
|
assign rvfi_csr_o.mcause_q = mcause_q;
|
|
assign rvfi_csr_o.mtval_q = CVA6Cfg.TvalEn ? mtval_q : '0;
|
|
assign rvfi_csr_o.fiom_q = fiom_q;
|
|
assign rvfi_csr_o.mcountinhibit_q = mcountinhibit_q;
|
|
assign rvfi_csr_o.cycle_q = cycle_q;
|
|
assign rvfi_csr_o.instret_q = instret_q;
|
|
assign rvfi_csr_o.dcache_q = dcache_q;
|
|
assign rvfi_csr_o.icache_q = icache_q;
|
|
assign rvfi_csr_o.acc_cons_q = CVA6Cfg.EnableAccelerator ? acc_cons_q : '0;
|
|
assign rvfi_csr_o.pmpcfg_q = pmpcfg_q;
|
|
assign rvfi_csr_o.pmpaddr_q = pmpaddr_q;
|
|
|
|
|
|
endmodule
|