| |
| /** |
| * Control and Status Registers |
| * |
| * Control and Status Registers (CSRs) following the RISC-V Privileged |
| * Specification, draft version 1.11 |
| */ |
| |
| module brq_cs_registers #( |
| parameter bit DbgTriggerEn = 0, |
| parameter int unsigned DbgHwBreakNum = 1, |
| parameter bit DataIndTiming = 1'b0, |
| parameter bit DummyInstructions = 1'b0, |
| parameter bit ShadowCSR = 1'b0, |
| parameter bit ICache = 1'b0, |
| parameter int unsigned MHPMCounterNum = 10, |
| parameter int unsigned MHPMCounterWidth = 40, |
| parameter bit PMPEnable = 0, |
| parameter int unsigned PMPGranularity = 0, |
| parameter int unsigned PMPNumRegions = 4, |
| parameter bit RV32E = 0, |
| parameter brq_pkg::rv32m_e RV32M = brq_pkg::RV32MFast, |
| parameter brq_pkg::rvfloat_e RVF = brq_pkg::RV64FDouble // for floating point |
| ) ( |
| // Clock and Reset |
| input logic clk_i, |
| input logic rst_ni, |
| |
| // Hart ID |
| input logic [31:0] hart_id_i, |
| |
| // Privilege mode |
| output brq_pkg::priv_lvl_e priv_mode_id_o, |
| output brq_pkg::priv_lvl_e priv_mode_if_o, |
| output brq_pkg::priv_lvl_e priv_mode_lsu_o, |
| output logic csr_mstatus_tw_o, |
| |
| // mtvec |
| output logic [31:0] csr_mtvec_o, |
| input logic csr_mtvec_init_i, |
| input logic [31:0] boot_addr_i, |
| |
| // Interface to registers (SRAM like) |
| input logic csr_access_i, |
| input brq_pkg::csr_num_e csr_addr_i, |
| input logic [31:0] csr_wdata_i, |
| input brq_pkg::csr_op_e csr_op_i, |
| input csr_op_en_i, |
| output logic [31:0] csr_rdata_o, |
| |
| // interrupts |
| input logic irq_software_i, |
| input logic irq_timer_i, |
| input logic irq_external_i, |
| input logic [14:0] irq_fast_i, |
| input logic nmi_mode_i, |
| output logic irq_pending_o, // interrupt request pending |
| output brq_pkg::irqs_t irqs_o, // interrupt requests qualified with mie |
| output logic csr_mstatus_mie_o, |
| output logic [31:0] csr_mepc_o, |
| |
| // PMP |
| output brq_pkg::pmp_cfg_t csr_pmp_cfg_o [PMPNumRegions], |
| output logic [33:0] csr_pmp_addr_o [PMPNumRegions], |
| |
| // debug |
| input logic debug_mode_i, |
| input brq_pkg::dbg_cause_e debug_cause_i, |
| input logic debug_csr_save_i, |
| output logic [31:0] csr_depc_o, |
| output logic debug_single_step_o, |
| output logic debug_ebreakm_o, |
| output logic debug_ebreaku_o, |
| output logic trigger_match_o, |
| |
| input logic [31:0] pc_if_i, |
| input logic [31:0] pc_id_i, |
| input logic [31:0] pc_wb_i, |
| |
| // CPU control bits |
| output logic data_ind_timing_o, |
| output logic csr_shadow_err_o, |
| |
| // Exception save/restore |
| input logic csr_save_if_i, |
| input logic csr_save_id_i, |
| input logic csr_save_wb_i, |
| input logic csr_restore_mret_i, |
| input logic csr_restore_dret_i, |
| input logic csr_save_cause_i, |
| input brq_pkg::exc_cause_e csr_mcause_i, |
| input logic [31:0] csr_mtval_i, |
| output logic illegal_csr_insn_o, // access to non-existent CSR, |
| // with wrong priviledge level, or |
| // missing write permissions |
| // Performance Counters |
| input logic instr_ret_i, // instr retired in ID/EX stage |
| input logic instr_ret_compressed_i, // compressed instr retired |
| input logic iside_wait_i, // core waiting for the iside |
| input logic jump_i, // jump instr seen (j, jr, jal, jalr) |
| input logic branch_i, // branch instr seen (bf, bnf) |
| input logic branch_taken_i, // branch was taken |
| input logic mem_load_i, // load from memory in this cycle |
| input logic mem_store_i, // store to memory in this cycle |
| input logic dside_wait_i, // core waiting for the dside |
| input logic mul_wait_i, // core waiting for multiply |
| input logic div_wait_i, // core waiting for divide |
| |
| // floating point |
| input logic fp_rm_dynamic_i, |
| output fpnew_pkg::roundmode_e fp_frm_o, |
| input fpnew_pkg::status_t fp_status_i, |
| input logic is_fp_instr_i |
| ); |
| import brq_pkg::*; |
| import fpnew_pkg::roundmode_e; |
| |
| logic dummy_instr_en_o; |
| logic [2:0] dummy_instr_mask_o; |
| logic dummy_instr_seed_en_o; |
| logic [31:0] dummy_instr_seed_o; |
| logic icache_enable_o; |
| |
| localparam int unsigned RV32MEnabled = (RV32M == RV32MNone) ? 0 : 1; |
| localparam int unsigned PMPAddrWidth = (PMPGranularity > 0) ? 33 - PMPGranularity : 32; |
| |
| localparam int unsigned SinglePrecision = (RVF == RV32FSingle) ? 1 : 0; |
| localparam int unsigned DoublePrecision = (RVF == RV64FDouble) ? 1 : 0; |
| |
| // misa |
| localparam logic [31:0] MISA_VALUE = |
| (0 << 0) // A - Atomic Instructions extension |
| | (1 << 2) // C - Compressed extension |
| | (DoublePrecision << 3) // D - Double precision floating-point extension |
| | (32'(RV32E) << 4) // E - RV32E base ISA |
| | (SinglePrecision << 5) // F - Single precision floating-point extension |
| | (32'(!RV32E) << 8) // I - RV32I/64I/128I base ISA |
| | (RV32MEnabled << 12) // M - Integer Multiply/Divide extension |
| | (0 << 13) // N - User level interrupts supported |
| | (0 << 18) // S - Supervisor mode implemented |
| | (1 << 20) // U - User mode implemented |
| | (0 << 23) // X - Non-standard extensions present |
| | (32'(CSR_MISA_MXL) << 30); // M-XLEN |
| |
| typedef struct packed { |
| logic mie; |
| logic mpie; |
| priv_lvl_e mpp; |
| logic mprv; |
| logic tw; |
| } status_t; |
| |
| typedef struct packed { |
| logic mpie; |
| priv_lvl_e mpp; |
| } status_stk_t; |
| |
| typedef struct packed { |
| x_debug_ver_e xdebugver; |
| logic [11:0] zero2; |
| logic ebreakm; |
| logic zero1; |
| logic ebreaks; |
| logic ebreaku; |
| logic stepie; |
| logic stopcount; |
| logic stoptime; |
| dbg_cause_e cause; |
| logic zero0; |
| logic mprven; |
| logic nmip; |
| logic step; |
| priv_lvl_e prv; |
| } dcsr_t; |
| |
| // CPU control register fields |
| typedef struct packed { |
| logic [2:0] dummy_instr_mask; |
| logic dummy_instr_en; |
| logic data_ind_timing; |
| logic icache_enable; |
| } cpu_ctrl_t; |
| |
| // Interrupt and exception control signals |
| logic [31:0] exception_pc; |
| |
| // CSRs |
| fpnew_pkg::status_t fflags_q, fflags_d, fflag_wdata; |
| |
| logic fflags_en; |
| logic frm_en; |
| roundmode_e frm_q, frm_d; |
| priv_lvl_e priv_lvl_q, priv_lvl_d; |
| status_t mstatus_q, mstatus_d; |
| logic mstatus_err; |
| logic mstatus_en; |
| irqs_t mie_q, mie_d; |
| logic mie_en; |
| logic [31:0] mscratch_q; |
| logic mscratch_en; |
| logic [31:0] mepc_q, mepc_d; |
| logic mepc_en; |
| logic [5:0] mcause_q, mcause_d; |
| logic mcause_en; |
| logic [31:0] mtval_q, mtval_d; |
| logic mtval_en; |
| logic [31:0] mtvec_q, mtvec_d; |
| logic mtvec_err; |
| logic mtvec_en; |
| irqs_t mip; |
| dcsr_t dcsr_q, dcsr_d; |
| logic dcsr_en; |
| logic [31:0] depc_q, depc_d; |
| logic depc_en; |
| logic [31:0] dscratch0_q; |
| logic [31:0] dscratch1_q; |
| logic dscratch0_en, dscratch1_en; |
| |
| // CSRs for recoverable NMIs |
| // NOTE: these CSRS are nonstandard, see https://github.com/riscv/riscv-isa-manual/issues/261 |
| status_stk_t mstack_q, mstack_d; |
| logic mstack_en; |
| logic [31:0] mstack_epc_q, mstack_epc_d; |
| logic [5:0] mstack_cause_q, mstack_cause_d; |
| |
| // PMP Signals |
| logic [31:0] pmp_addr_rdata [PMP_MAX_REGIONS]; |
| logic [PMP_CFG_W-1:0] pmp_cfg_rdata [PMP_MAX_REGIONS]; |
| logic pmp_csr_err; |
| |
| // Hardware performance monitor signals |
| logic [31:0] mcountinhibit; |
| // Only have mcountinhibit flops for counters that actually exist |
| logic [MHPMCounterNum+3-1:0] mcountinhibit_d, mcountinhibit_q; |
| logic mcountinhibit_we; |
| |
| // mhpmcounter flops are elaborated below providing only the precise number that is required based |
| // on MHPMCounterNum/MHPMCounterWidth. This signal connects to the Q output of these flops |
| // where they exist and is otherwise 0. |
| logic [63:0] mhpmcounter [32]; |
| logic [31:0] mhpmcounter_we; |
| logic [31:0] mhpmcounterh_we; |
| logic [31:0] mhpmcounter_incr; |
| logic [31:0] mhpmevent [32]; |
| logic [4:0] mhpmcounter_idx; |
| logic unused_mhpmcounter_we_1; |
| logic unused_mhpmcounterh_we_1; |
| logic unused_mhpmcounter_incr_1; |
| |
| // Debug / trigger registers |
| logic [31:0] tselect_rdata; |
| logic [31:0] tmatch_control_rdata; |
| logic [31:0] tmatch_value_rdata; |
| |
| // CPU control bits |
| cpu_ctrl_t cpuctrl_q, cpuctrl_d, cpuctrl_wdata; |
| logic cpuctrl_we; |
| logic cpuctrl_err; |
| |
| // CSR update logic |
| logic [31:0] csr_wdata_int; |
| logic [31:0] csr_rdata_int; |
| logic csr_we_int; |
| logic csr_wreq; |
| |
| // Access violation signals |
| logic illegal_csr; |
| logic illegal_csr_priv; |
| logic illegal_csr_write; |
| |
| logic [7:0] unused_boot_addr; |
| logic [2:0] unused_csr_addr; |
| |
| assign unused_boot_addr = boot_addr_i[7:0]; |
| |
| ///////////// |
| // CSR reg // |
| ///////////// |
| |
| logic illegal_dyn_mod; |
| logic illegal_csr_dyn_mod; |
| logic [$bits(csr_num_e)-1:0] csr_addr; |
| assign csr_addr = {csr_addr_i}; |
| assign unused_csr_addr = csr_addr[7:5]; |
| assign mhpmcounter_idx = csr_addr[4:0]; |
| |
| assign illegal_csr_dyn_mod = illegal_dyn_mod & fp_rm_dynamic_i; |
| |
| // See RISC-V Privileged Specification, version 1.11, Section 2.1 |
| assign illegal_csr_priv = (csr_addr[9:8] > {priv_lvl_q}); |
| assign illegal_csr_write = (csr_addr[11:10] == 2'b11) && csr_wreq; |
| assign illegal_csr_insn_o = (csr_access_i & (illegal_csr | illegal_csr_write | illegal_csr_priv)) | illegal_csr_dyn_mod; |
| |
| // mip CSR is purely combinational - must be able to re-enable the clock upon WFI |
| assign mip.irq_software = irq_software_i; |
| assign mip.irq_timer = irq_timer_i; |
| assign mip.irq_external = irq_external_i; |
| assign mip.irq_fast = irq_fast_i; |
| |
| // Floating point |
| always_comb begin |
| unique case (frm_q) |
| 3'b000, |
| 3'b001, |
| 3'b010, |
| 3'b011, |
| 3'b100: illegal_dyn_mod = 1'b0; |
| 3'b101, |
| 3'b110, |
| 3'b111: illegal_dyn_mod = 1'b1; |
| endcase |
| fp_frm_o = frm_q; |
| end |
| |
| // read logic |
| always_comb begin |
| csr_rdata_int = '0; |
| illegal_csr = 1'b0; |
| |
| unique case (csr_addr_i) |
| // fcsr: floating-point control and status register (frm+fflags) |
| CSR_FCSR: csr_rdata_int = {24'b0 , frm_q, fflags_q}; |
| |
| // fflags: floating-point accrued exception |
| CSR_FFLAG: csr_rdata_int = {27'b0 , fflags_q}; |
| |
| // frm: floating-point dynamic rounding mode |
| CSR_FRM: begin |
| csr_rdata_int = {29'b0 , frm_q}; |
| end |
| |
| // mhartid: unique hardware thread id |
| CSR_MHARTID: csr_rdata_int = hart_id_i; |
| |
| // mstatus: always M-mode, contains IE bit |
| CSR_MSTATUS: begin |
| csr_rdata_int = '0; |
| csr_rdata_int[CSR_MSTATUS_MIE_BIT] = mstatus_q.mie; |
| csr_rdata_int[CSR_MSTATUS_MPIE_BIT] = mstatus_q.mpie; |
| csr_rdata_int[CSR_MSTATUS_MPP_BIT_HIGH:CSR_MSTATUS_MPP_BIT_LOW] = mstatus_q.mpp; |
| csr_rdata_int[CSR_MSTATUS_MPRV_BIT] = mstatus_q.mprv; |
| csr_rdata_int[CSR_MSTATUS_TW_BIT] = mstatus_q.tw; |
| end |
| |
| // misa |
| CSR_MISA: csr_rdata_int = MISA_VALUE; |
| |
| // interrupt enable |
| CSR_MIE: begin |
| csr_rdata_int = '0; |
| csr_rdata_int[CSR_MSIX_BIT] = mie_q.irq_software; |
| csr_rdata_int[CSR_MTIX_BIT] = mie_q.irq_timer; |
| csr_rdata_int[CSR_MEIX_BIT] = mie_q.irq_external; |
| csr_rdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW] = mie_q.irq_fast; |
| end |
| |
| CSR_MSCRATCH: csr_rdata_int = mscratch_q; |
| |
| // mtvec: trap-vector base address |
| CSR_MTVEC: csr_rdata_int = mtvec_q; |
| |
| // mepc: exception program counter |
| CSR_MEPC: csr_rdata_int = mepc_q; |
| |
| // mcause: exception cause |
| CSR_MCAUSE: csr_rdata_int = {mcause_q[5], 26'b0, mcause_q[4:0]}; |
| |
| // mtval: trap value |
| CSR_MTVAL: csr_rdata_int = mtval_q; |
| |
| // mip: interrupt pending |
| CSR_MIP: begin |
| csr_rdata_int = '0; |
| csr_rdata_int[CSR_MSIX_BIT] = mip.irq_software; |
| csr_rdata_int[CSR_MTIX_BIT] = mip.irq_timer; |
| csr_rdata_int[CSR_MEIX_BIT] = mip.irq_external; |
| csr_rdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW] = mip.irq_fast; |
| end |
| |
| // PMP registers |
| CSR_PMPCFG0: csr_rdata_int = {pmp_cfg_rdata[3], pmp_cfg_rdata[2], |
| pmp_cfg_rdata[1], pmp_cfg_rdata[0]}; |
| CSR_PMPCFG1: csr_rdata_int = {pmp_cfg_rdata[7], pmp_cfg_rdata[6], |
| pmp_cfg_rdata[5], pmp_cfg_rdata[4]}; |
| CSR_PMPCFG2: csr_rdata_int = {pmp_cfg_rdata[11], pmp_cfg_rdata[10], |
| pmp_cfg_rdata[9], pmp_cfg_rdata[8]}; |
| CSR_PMPCFG3: csr_rdata_int = {pmp_cfg_rdata[15], pmp_cfg_rdata[14], |
| pmp_cfg_rdata[13], pmp_cfg_rdata[12]}; |
| CSR_PMPADDR0: csr_rdata_int = pmp_addr_rdata[0]; |
| CSR_PMPADDR1: csr_rdata_int = pmp_addr_rdata[1]; |
| CSR_PMPADDR2: csr_rdata_int = pmp_addr_rdata[2]; |
| CSR_PMPADDR3: csr_rdata_int = pmp_addr_rdata[3]; |
| CSR_PMPADDR4: csr_rdata_int = pmp_addr_rdata[4]; |
| CSR_PMPADDR5: csr_rdata_int = pmp_addr_rdata[5]; |
| CSR_PMPADDR6: csr_rdata_int = pmp_addr_rdata[6]; |
| CSR_PMPADDR7: csr_rdata_int = pmp_addr_rdata[7]; |
| CSR_PMPADDR8: csr_rdata_int = pmp_addr_rdata[8]; |
| CSR_PMPADDR9: csr_rdata_int = pmp_addr_rdata[9]; |
| CSR_PMPADDR10: csr_rdata_int = pmp_addr_rdata[10]; |
| CSR_PMPADDR11: csr_rdata_int = pmp_addr_rdata[11]; |
| CSR_PMPADDR12: csr_rdata_int = pmp_addr_rdata[12]; |
| CSR_PMPADDR13: csr_rdata_int = pmp_addr_rdata[13]; |
| CSR_PMPADDR14: csr_rdata_int = pmp_addr_rdata[14]; |
| CSR_PMPADDR15: csr_rdata_int = pmp_addr_rdata[15]; |
| |
| CSR_DCSR: begin |
| csr_rdata_int = dcsr_q; |
| illegal_csr = ~debug_mode_i; |
| end |
| CSR_DPC: begin |
| csr_rdata_int = depc_q; |
| illegal_csr = ~debug_mode_i; |
| end |
| CSR_DSCRATCH0: begin |
| csr_rdata_int = dscratch0_q; |
| illegal_csr = ~debug_mode_i; |
| end |
| CSR_DSCRATCH1: begin |
| csr_rdata_int = dscratch1_q; |
| illegal_csr = ~debug_mode_i; |
| end |
| |
| // machine counter/timers |
| CSR_MCOUNTINHIBIT: csr_rdata_int = mcountinhibit; |
| CSR_MHPMEVENT3, |
| CSR_MHPMEVENT4, CSR_MHPMEVENT5, CSR_MHPMEVENT6, CSR_MHPMEVENT7, |
| CSR_MHPMEVENT8, CSR_MHPMEVENT9, CSR_MHPMEVENT10, CSR_MHPMEVENT11, |
| CSR_MHPMEVENT12, CSR_MHPMEVENT13, CSR_MHPMEVENT14, CSR_MHPMEVENT15, |
| CSR_MHPMEVENT16, CSR_MHPMEVENT17, CSR_MHPMEVENT18, CSR_MHPMEVENT19, |
| CSR_MHPMEVENT20, CSR_MHPMEVENT21, CSR_MHPMEVENT22, CSR_MHPMEVENT23, |
| CSR_MHPMEVENT24, CSR_MHPMEVENT25, CSR_MHPMEVENT26, CSR_MHPMEVENT27, |
| CSR_MHPMEVENT28, CSR_MHPMEVENT29, CSR_MHPMEVENT30, CSR_MHPMEVENT31: begin |
| csr_rdata_int = mhpmevent[mhpmcounter_idx]; |
| end |
| |
| CSR_MCYCLE, |
| CSR_MINSTRET, |
| CSR_MHPMCOUNTER3, |
| CSR_MHPMCOUNTER4, CSR_MHPMCOUNTER5, CSR_MHPMCOUNTER6, CSR_MHPMCOUNTER7, |
| CSR_MHPMCOUNTER8, CSR_MHPMCOUNTER9, CSR_MHPMCOUNTER10, CSR_MHPMCOUNTER11, |
| CSR_MHPMCOUNTER12, CSR_MHPMCOUNTER13, CSR_MHPMCOUNTER14, CSR_MHPMCOUNTER15, |
| CSR_MHPMCOUNTER16, CSR_MHPMCOUNTER17, CSR_MHPMCOUNTER18, CSR_MHPMCOUNTER19, |
| CSR_MHPMCOUNTER20, CSR_MHPMCOUNTER21, CSR_MHPMCOUNTER22, CSR_MHPMCOUNTER23, |
| CSR_MHPMCOUNTER24, CSR_MHPMCOUNTER25, CSR_MHPMCOUNTER26, CSR_MHPMCOUNTER27, |
| CSR_MHPMCOUNTER28, CSR_MHPMCOUNTER29, CSR_MHPMCOUNTER30, CSR_MHPMCOUNTER31: begin |
| csr_rdata_int = mhpmcounter[mhpmcounter_idx][31:0]; |
| end |
| |
| CSR_MCYCLEH, |
| CSR_MINSTRETH, |
| CSR_MHPMCOUNTER3H, |
| CSR_MHPMCOUNTER4H, CSR_MHPMCOUNTER5H, CSR_MHPMCOUNTER6H, CSR_MHPMCOUNTER7H, |
| CSR_MHPMCOUNTER8H, CSR_MHPMCOUNTER9H, CSR_MHPMCOUNTER10H, CSR_MHPMCOUNTER11H, |
| CSR_MHPMCOUNTER12H, CSR_MHPMCOUNTER13H, CSR_MHPMCOUNTER14H, CSR_MHPMCOUNTER15H, |
| CSR_MHPMCOUNTER16H, CSR_MHPMCOUNTER17H, CSR_MHPMCOUNTER18H, CSR_MHPMCOUNTER19H, |
| CSR_MHPMCOUNTER20H, CSR_MHPMCOUNTER21H, CSR_MHPMCOUNTER22H, CSR_MHPMCOUNTER23H, |
| CSR_MHPMCOUNTER24H, CSR_MHPMCOUNTER25H, CSR_MHPMCOUNTER26H, CSR_MHPMCOUNTER27H, |
| CSR_MHPMCOUNTER28H, CSR_MHPMCOUNTER29H, CSR_MHPMCOUNTER30H, CSR_MHPMCOUNTER31H: begin |
| csr_rdata_int = mhpmcounter[mhpmcounter_idx][63:32]; |
| end |
| |
| // Debug triggers |
| CSR_TSELECT: begin |
| csr_rdata_int = tselect_rdata; |
| illegal_csr = ~DbgTriggerEn; |
| end |
| CSR_TDATA1: begin |
| csr_rdata_int = tmatch_control_rdata; |
| illegal_csr = ~DbgTriggerEn; |
| end |
| CSR_TDATA2: begin |
| csr_rdata_int = tmatch_value_rdata; |
| illegal_csr = ~DbgTriggerEn; |
| end |
| CSR_TDATA3: begin |
| csr_rdata_int = '0; |
| illegal_csr = ~DbgTriggerEn; |
| end |
| CSR_MCONTEXT: begin |
| csr_rdata_int = '0; |
| illegal_csr = ~DbgTriggerEn; |
| end |
| CSR_SCONTEXT: begin |
| csr_rdata_int = '0; |
| illegal_csr = ~DbgTriggerEn; |
| end |
| |
| // Custom CSR for controlling CPU features |
| CSR_CPUCTRL: begin |
| csr_rdata_int = {{32-$bits(cpu_ctrl_t){1'b0}},cpuctrl_q}; |
| end |
| |
| // Custom CSR for LFSR re-seeding (cannot be read) |
| CSR_SECURESEED: begin |
| csr_rdata_int = '0; |
| end |
| |
| default: begin |
| illegal_csr = 1'b1; |
| end |
| endcase |
| end |
| |
| // write logic |
| always_comb begin |
| exception_pc = pc_id_i; |
| |
| // Floating point |
| fflags_d = fflags_q; |
| fflags_en = 1'b0; |
| |
| frm_d = frm_q; |
| frm_en = 1'b0; |
| |
| priv_lvl_d = priv_lvl_q; |
| mstatus_en = 1'b0; |
| mstatus_d = mstatus_q; |
| mie_en = 1'b0; |
| mscratch_en = 1'b0; |
| mepc_en = 1'b0; |
| mepc_d = {csr_wdata_int[31:1], 1'b0}; |
| mcause_en = 1'b0; |
| mcause_d = {csr_wdata_int[31], csr_wdata_int[4:0]}; |
| mtval_en = 1'b0; |
| mtval_d = csr_wdata_int; |
| mtvec_en = csr_mtvec_init_i; |
| // mtvec.MODE set to vectored |
| // mtvec.BASE must be 256-byte aligned |
| mtvec_d = csr_mtvec_init_i ? {boot_addr_i[31:2], 2'b00} : |
| {csr_wdata_int[31:2], 2'b00}; |
| dcsr_en = 1'b0; |
| dcsr_d = dcsr_q; |
| depc_d = {csr_wdata_int[31:1], 1'b0}; |
| depc_en = 1'b0; |
| dscratch0_en = 1'b0; |
| dscratch1_en = 1'b0; |
| |
| mstack_en = 1'b0; |
| mstack_d.mpie = mstatus_q.mpie; |
| mstack_d.mpp = mstatus_q.mpp; |
| mstack_epc_d = mepc_q; |
| mstack_cause_d = mcause_q; |
| |
| mcountinhibit_we = 1'b0; |
| mhpmcounter_we = '0; |
| mhpmcounterh_we = '0; |
| |
| cpuctrl_we = 1'b0; |
| |
| if (csr_we_int) begin |
| unique case (csr_addr_i) |
| // mstatus: IE bit |
| |
| CSR_FCSR: begin |
| fflags_en = 1'b1; |
| frm_en = 1'b1; |
| fflags_d = csr_wdata_int[4:0]; |
| frm_d = roundmode_e'(csr_wdata_int[7:5]); |
| end |
| |
| |
| CSR_FFLAG : begin |
| fflags_en = 1'b1; |
| fflags_d = fpnew_pkg::status_t'(csr_wdata_int[4:0]); |
| end |
| |
| CSR_FRM: begin |
| frm_en = 1'b1; |
| frm_d = roundmode_e'(csr_wdata_int[2:0]); |
| end |
| |
| CSR_MSTATUS: begin |
| mstatus_en = 1'b1; |
| mstatus_d = '{ |
| mie: csr_wdata_int[CSR_MSTATUS_MIE_BIT], |
| mpie: csr_wdata_int[CSR_MSTATUS_MPIE_BIT], |
| mpp: priv_lvl_e'(csr_wdata_int[CSR_MSTATUS_MPP_BIT_HIGH:CSR_MSTATUS_MPP_BIT_LOW]), |
| mprv: csr_wdata_int[CSR_MSTATUS_MPRV_BIT], |
| tw: csr_wdata_int[CSR_MSTATUS_TW_BIT] |
| }; |
| // Convert illegal values to M-mode |
| if ((mstatus_d.mpp != PRIV_LVL_M) && (mstatus_d.mpp != PRIV_LVL_U)) begin |
| mstatus_d.mpp = PRIV_LVL_M; |
| end |
| end |
| |
| // interrupt enable |
| CSR_MIE: mie_en = 1'b1; |
| |
| CSR_MSCRATCH: mscratch_en = 1'b1; |
| |
| // mepc: exception program counter |
| CSR_MEPC: mepc_en = 1'b1; |
| |
| // mcause |
| CSR_MCAUSE: mcause_en = 1'b1; |
| |
| // mtval: trap value |
| CSR_MTVAL: mtval_en = 1'b1; |
| |
| // mtvec |
| CSR_MTVEC: mtvec_en = 1'b1; |
| |
| CSR_DCSR: begin |
| dcsr_d = csr_wdata_int; |
| dcsr_d.xdebugver = XDEBUGVER_STD; |
| // Change to PRIV_LVL_M if software writes an unsupported value |
| if ((dcsr_d.prv != PRIV_LVL_M) && (dcsr_d.prv != PRIV_LVL_U)) begin |
| dcsr_d.prv = PRIV_LVL_M; |
| end |
| |
| // Read-only for SW |
| dcsr_d.cause = dcsr_q.cause; |
| |
| // currently not supported: |
| dcsr_d.nmip = 1'b0; |
| dcsr_d.mprven = 1'b0; |
| dcsr_d.stopcount = 1'b0; |
| dcsr_d.stoptime = 1'b0; |
| |
| // forced to be zero |
| dcsr_d.zero0 = 1'b0; |
| dcsr_d.zero1 = 1'b0; |
| dcsr_d.zero2 = 12'h0; |
| dcsr_en = 1'b1; |
| end |
| |
| // dpc: debug program counter |
| CSR_DPC: depc_en = 1'b1; |
| |
| CSR_DSCRATCH0: dscratch0_en = 1'b1; |
| CSR_DSCRATCH1: dscratch1_en = 1'b1; |
| |
| // machine counter/timers |
| CSR_MCOUNTINHIBIT: mcountinhibit_we = 1'b1; |
| |
| CSR_MCYCLE, |
| CSR_MINSTRET, |
| CSR_MHPMCOUNTER3, |
| CSR_MHPMCOUNTER4, CSR_MHPMCOUNTER5, CSR_MHPMCOUNTER6, CSR_MHPMCOUNTER7, |
| CSR_MHPMCOUNTER8, CSR_MHPMCOUNTER9, CSR_MHPMCOUNTER10, CSR_MHPMCOUNTER11, |
| CSR_MHPMCOUNTER12, CSR_MHPMCOUNTER13, CSR_MHPMCOUNTER14, CSR_MHPMCOUNTER15, |
| CSR_MHPMCOUNTER16, CSR_MHPMCOUNTER17, CSR_MHPMCOUNTER18, CSR_MHPMCOUNTER19, |
| CSR_MHPMCOUNTER20, CSR_MHPMCOUNTER21, CSR_MHPMCOUNTER22, CSR_MHPMCOUNTER23, |
| CSR_MHPMCOUNTER24, CSR_MHPMCOUNTER25, CSR_MHPMCOUNTER26, CSR_MHPMCOUNTER27, |
| CSR_MHPMCOUNTER28, CSR_MHPMCOUNTER29, CSR_MHPMCOUNTER30, CSR_MHPMCOUNTER31: begin |
| mhpmcounter_we[mhpmcounter_idx] = 1'b1; |
| end |
| |
| CSR_MCYCLEH, |
| CSR_MINSTRETH, |
| CSR_MHPMCOUNTER3H, |
| CSR_MHPMCOUNTER4H, CSR_MHPMCOUNTER5H, CSR_MHPMCOUNTER6H, CSR_MHPMCOUNTER7H, |
| CSR_MHPMCOUNTER8H, CSR_MHPMCOUNTER9H, CSR_MHPMCOUNTER10H, CSR_MHPMCOUNTER11H, |
| CSR_MHPMCOUNTER12H, CSR_MHPMCOUNTER13H, CSR_MHPMCOUNTER14H, CSR_MHPMCOUNTER15H, |
| CSR_MHPMCOUNTER16H, CSR_MHPMCOUNTER17H, CSR_MHPMCOUNTER18H, CSR_MHPMCOUNTER19H, |
| CSR_MHPMCOUNTER20H, CSR_MHPMCOUNTER21H, CSR_MHPMCOUNTER22H, CSR_MHPMCOUNTER23H, |
| CSR_MHPMCOUNTER24H, CSR_MHPMCOUNTER25H, CSR_MHPMCOUNTER26H, CSR_MHPMCOUNTER27H, |
| CSR_MHPMCOUNTER28H, CSR_MHPMCOUNTER29H, CSR_MHPMCOUNTER30H, CSR_MHPMCOUNTER31H: begin |
| mhpmcounterh_we[mhpmcounter_idx] = 1'b1; |
| end |
| |
| CSR_CPUCTRL: cpuctrl_we = 1'b1; |
| |
| default:; |
| endcase |
| end |
| |
| // exception controller gets priority over other writes |
| unique case (1'b1) |
| |
| csr_save_cause_i: begin |
| unique case (1'b1) |
| csr_save_if_i: begin |
| exception_pc = pc_if_i; |
| end |
| csr_save_id_i: begin |
| exception_pc = pc_id_i; |
| end |
| csr_save_wb_i: begin |
| exception_pc = pc_wb_i; |
| end |
| default:; |
| endcase |
| |
| // Any exception, including debug mode, causes a switch to M-mode |
| priv_lvl_d = PRIV_LVL_M; |
| |
| if (debug_csr_save_i) begin |
| // all interrupts are masked |
| // do not update cause, epc, tval, epc and status |
| dcsr_d.prv = priv_lvl_q; |
| dcsr_d.cause = debug_cause_i; |
| dcsr_en = 1'b1; |
| depc_d = exception_pc; |
| depc_en = 1'b1; |
| end else if (!debug_mode_i) begin |
| // In debug mode, "exceptions do not update any registers. That |
| // includes cause, epc, tval, dpc and mstatus." [Debug Spec v0.13.2, p.39] |
| mtval_en = 1'b1; |
| mtval_d = csr_mtval_i; |
| mstatus_en = 1'b1; |
| mstatus_d.mie = 1'b0; // disable interrupts |
| // save current status |
| mstatus_d.mpie = mstatus_q.mie; |
| mstatus_d.mpp = priv_lvl_q; |
| mepc_en = 1'b1; |
| mepc_d = exception_pc; |
| mcause_en = 1'b1; |
| mcause_d = {csr_mcause_i}; |
| // save previous status for recoverable NMI |
| mstack_en = 1'b1; |
| end |
| end // csr_save_cause_i |
| |
| csr_restore_dret_i: begin // DRET |
| priv_lvl_d = dcsr_q.prv; |
| end // csr_restore_dret_i |
| |
| csr_restore_mret_i: begin // MRET |
| priv_lvl_d = mstatus_q.mpp; |
| mstatus_en = 1'b1; |
| mstatus_d.mie = mstatus_q.mpie; // re-enable interrupts |
| |
| if (nmi_mode_i) begin |
| // when returning from an NMI restore state from mstack CSR |
| mstatus_d.mpie = mstack_q.mpie; |
| mstatus_d.mpp = mstack_q.mpp; |
| mepc_en = 1'b1; |
| mepc_d = mstack_epc_q; |
| mcause_en = 1'b1; |
| mcause_d = mstack_cause_q; |
| end else begin |
| // otherwise just set mstatus.MPIE/MPP |
| // See RISC-V Privileged Specification, version 1.11, Section 3.1.6.1 |
| mstatus_d.mpie = 1'b1; |
| mstatus_d.mpp = PRIV_LVL_U; |
| end |
| end // csr_restore_mret_i |
| |
| default:; |
| endcase |
| end |
| |
| // Update current priv level |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| priv_lvl_q <= PRIV_LVL_M; |
| end else begin |
| priv_lvl_q <= priv_lvl_d; |
| end |
| end |
| |
| // Send current priv level to the decoder |
| assign priv_mode_id_o = priv_lvl_q; |
| // New instruction fetches need to account for updates to priv_lvl_q this cycle |
| assign priv_mode_if_o = priv_lvl_d; |
| // Load/store instructions must factor in MPRV for PMP checking |
| assign priv_mode_lsu_o = mstatus_q.mprv ? mstatus_q.mpp : priv_lvl_q; |
| |
| // CSR operation logic |
| always_comb begin |
| unique case (csr_op_i) |
| CSR_OP_WRITE: csr_wdata_int = csr_wdata_i; |
| CSR_OP_SET: csr_wdata_int = csr_wdata_i | csr_rdata_o; |
| CSR_OP_CLEAR: csr_wdata_int = ~csr_wdata_i & csr_rdata_o; |
| CSR_OP_READ: csr_wdata_int = csr_wdata_i; |
| // default: csr_wdata_int = csr_wdata_i; |
| endcase |
| end |
| |
| assign csr_wreq = csr_op_en_i & |
| (csr_op_i inside {CSR_OP_WRITE, |
| CSR_OP_SET, |
| CSR_OP_CLEAR}); |
| |
| // only write CSRs during one clock cycle |
| assign csr_we_int = csr_wreq & ~illegal_csr_insn_o; |
| |
| assign csr_rdata_o = csr_rdata_int; |
| |
| // directly output some registers |
| assign csr_mepc_o = mepc_q; |
| assign csr_depc_o = depc_q; |
| assign csr_mtvec_o = mtvec_q; |
| |
| assign csr_mstatus_mie_o = mstatus_q.mie; |
| assign csr_mstatus_tw_o = mstatus_q.tw; |
| assign debug_single_step_o = dcsr_q.step; |
| assign debug_ebreakm_o = dcsr_q.ebreakm; |
| assign debug_ebreaku_o = dcsr_q.ebreaku; |
| |
| // Qualify incoming interrupt requests in mip CSR with mie CSR for controller and to re-enable |
| // clock upon WFI (must be purely combinational). |
| assign irqs_o = mip & mie_q; |
| assign irq_pending_o = |irqs_o; |
| |
| //////////////////////// |
| // CSR instantiations // |
| //////////////////////// |
| logic unused_error1; |
| logic unused_error2; |
| logic unused_error3; |
| logic unused_error4; |
| logic unused_error5; |
| logic unused_error6; |
| logic unused_error7; |
| logic unused_error8; |
| logic unused_error9; |
| logic unused_error10; |
| logic unused_error11; |
| logic unused_error12; |
| logic unused_error13; |
| logic unused_error14; |
| logic unused_error15; |
| logic unused_error16; |
| logic unused_error17; |
| // MSTATUS |
| localparam status_t MSTATUS_RST_VAL = '{mie: 1'b0, |
| mpie: 1'b1, |
| mpp: PRIV_LVL_U, |
| mprv: 1'b0, |
| tw: 1'b0}; |
| brq_csr #( |
| .Width ($bits(status_t)), |
| .ShadowCopy (ShadowCSR), |
| .ResetValue ({MSTATUS_RST_VAL}) |
| ) u_mstatus_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i ({mstatus_d}), |
| .wr_en_i (mstatus_en), |
| .rd_data_o (mstatus_q), |
| .rd_error_o (mstatus_err) |
| ); |
| |
| assign fflag_wdata = is_fp_instr_i ? fp_status_i : fflags_d; |
| // FFLAGS |
| brq_csr #( |
| .Width (5), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) fflags_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (fflag_wdata), |
| .wr_en_i (fflags_en | is_fp_instr_i), |
| .rd_data_o (fflags_q), |
| .rd_error_o (unused_error1) |
| ); |
| |
| // FRM |
| brq_csr #( |
| .Width (3), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) frm_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (frm_d), |
| .wr_en_i (frm_en), |
| .rd_data_o (frm_q), |
| .rd_error_o (unused_error2) |
| ); |
| |
| // MEPC |
| brq_csr #( |
| .Width (32), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_mepc_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (mepc_d), |
| .wr_en_i (mepc_en), |
| .rd_data_o (mepc_q), |
| .rd_error_o (unused_error3) |
| ); |
| |
| // MIE |
| assign mie_d.irq_software = csr_wdata_int[CSR_MSIX_BIT]; |
| assign mie_d.irq_timer = csr_wdata_int[CSR_MTIX_BIT]; |
| assign mie_d.irq_external = csr_wdata_int[CSR_MEIX_BIT]; |
| assign mie_d.irq_fast = csr_wdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW]; |
| brq_csr #( |
| .Width ($bits(irqs_t)), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_mie_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i ({mie_d}), |
| .wr_en_i (mie_en), |
| .rd_data_o (mie_q), |
| .rd_error_o (unused_error4) |
| ); |
| |
| // MSCRATCH |
| brq_csr #( |
| .Width (32), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_mscratch_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (csr_wdata_int), |
| .wr_en_i (mscratch_en), |
| .rd_data_o (mscratch_q), |
| .rd_error_o (unused_error5) |
| ); |
| |
| // MCAUSE |
| brq_csr #( |
| .Width (6), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_mcause_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (mcause_d), |
| .wr_en_i (mcause_en), |
| .rd_data_o (mcause_q), |
| .rd_error_o (unused_error6) |
| ); |
| |
| // MTVAL |
| brq_csr #( |
| .Width (32), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_mtval_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (mtval_d), |
| .wr_en_i (mtval_en), |
| .rd_data_o (mtval_q), |
| .rd_error_o (unused_error7) |
| ); |
| |
| // MTVEC |
| brq_csr #( |
| .Width (32), |
| .ShadowCopy (ShadowCSR), |
| .ResetValue (32'd1) |
| ) u_mtvec_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (mtvec_d), |
| .wr_en_i (mtvec_en), |
| .rd_data_o (mtvec_q), |
| .rd_error_o (mtvec_err) |
| ); |
| |
| // DCSR |
| localparam dcsr_t DCSR_RESET_VAL = '{ |
| xdebugver: XDEBUGVER_STD, |
| cause: DBG_CAUSE_NONE, // 3'h0 |
| prv: PRIV_LVL_M, |
| default: '0 |
| }; |
| brq_csr #( |
| .Width ($bits(dcsr_t)), |
| .ShadowCopy (1'b0), |
| .ResetValue ({DCSR_RESET_VAL}) |
| ) u_dcsr_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i ({dcsr_d}), |
| .wr_en_i (dcsr_en), |
| .rd_data_o (dcsr_q), |
| .rd_error_o (unused_error8) |
| ); |
| |
| // DEPC |
| brq_csr #( |
| .Width (32), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_depc_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (depc_d), |
| .wr_en_i (depc_en), |
| .rd_data_o (depc_q), |
| .rd_error_o (unused_error9) |
| ); |
| |
| // DSCRATCH0 |
| brq_csr #( |
| .Width (32), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_dscratch0_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (csr_wdata_int), |
| .wr_en_i (dscratch0_en), |
| .rd_data_o (dscratch0_q), |
| .rd_error_o (unused_error10) |
| ); |
| |
| // DSCRATCH1 |
| brq_csr #( |
| .Width (32), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_dscratch1_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (csr_wdata_int), |
| .wr_en_i (dscratch1_en), |
| .rd_data_o (dscratch1_q), |
| .rd_error_o (unused_error11) |
| ); |
| |
| // MSTACK |
| localparam status_stk_t MSTACK_RESET_VAL = '{ |
| mpie: 1'b1, |
| mpp: PRIV_LVL_U |
| }; |
| brq_csr #( |
| .Width ($bits(status_stk_t)), |
| .ShadowCopy (1'b0), |
| .ResetValue ({MSTACK_RESET_VAL}) |
| ) u_mstack_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i ({mstack_d}), |
| .wr_en_i (mstack_en), |
| .rd_data_o (mstack_q), |
| .rd_error_o (unused_error12) |
| ); |
| |
| // MSTACK_EPC |
| brq_csr #( |
| .Width (32), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_mstack_epc_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (mstack_epc_d), |
| .wr_en_i (mstack_en), |
| .rd_data_o (mstack_epc_q), |
| .rd_error_o (unused_error13) |
| ); |
| |
| // MSTACK_CAUSE |
| brq_csr #( |
| .Width (6), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_mstack_cause_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (mstack_cause_d), |
| .wr_en_i (mstack_en), |
| .rd_data_o (mstack_cause_q), |
| .rd_error_o (unused_error14) |
| ); |
| |
| // ----------------- |
| // PMP registers |
| // ----------------- |
| |
| if (PMPEnable) begin : g_pmp_registers |
| pmp_cfg_t pmp_cfg [PMPNumRegions]; |
| pmp_cfg_t pmp_cfg_wdata [PMPNumRegions]; |
| logic [PMPAddrWidth-1:0] pmp_addr [PMPNumRegions]; |
| logic [PMPNumRegions-1:0] pmp_cfg_we; |
| logic [PMPNumRegions-1:0] pmp_cfg_err; |
| logic [PMPNumRegions-1:0] pmp_addr_we; |
| logic [PMPNumRegions-1:0] pmp_addr_err; |
| |
| // Expanded / qualified register read data |
| for (genvar i = 0; i < PMP_MAX_REGIONS; i++) begin : g_exp_rd_data |
| if (i < PMPNumRegions) begin : g_implemented_regions |
| // Add in zero padding for reserved fields |
| assign pmp_cfg_rdata[i] = {pmp_cfg[i].lock, 2'b00, pmp_cfg[i].mode, |
| pmp_cfg[i].exec, pmp_cfg[i].write, pmp_cfg[i].read}; |
| |
| // Address field read data depends on the current programmed mode and the granularity |
| // See RISC-V Privileged Specification, version 1.11, Section 3.6.1 |
| if (PMPGranularity == 0) begin : g_pmp_g0 |
| // If G == 0, read data is unmodified |
| assign pmp_addr_rdata[i] = pmp_addr[i]; |
| |
| end else if (PMPGranularity == 1) begin : g_pmp_g1 |
| // If G == 1, bit [G-1] reads as zero in TOR or OFF mode |
| always_comb begin |
| pmp_addr_rdata[i] = pmp_addr[i]; |
| if ((pmp_cfg[i].mode == PMP_MODE_OFF) || (pmp_cfg[i].mode == PMP_MODE_TOR)) begin |
| pmp_addr_rdata[i][PMPGranularity-1:0] = '0; |
| end |
| end |
| |
| end else begin : g_pmp_g2 |
| // For G >= 2, bits are masked to one or zero depending on the mode |
| always_comb begin |
| // In NAPOT mode, bits [G-2:0] must read as one |
| pmp_addr_rdata[i] = {pmp_addr[i], {PMPGranularity-1{1'b1}}}; |
| |
| if ((pmp_cfg[i].mode == PMP_MODE_OFF) || (pmp_cfg[i].mode == PMP_MODE_TOR)) begin |
| // In TOR or OFF mode, bits [G-1:0] must read as zero |
| pmp_addr_rdata[i][PMPGranularity-1:0] = '0; |
| end |
| end |
| end |
| |
| end else begin : g_other_regions |
| // Non-implemented regions read as zero |
| assign pmp_cfg_rdata[i] = '0; |
| assign pmp_addr_rdata[i] = '0; |
| end |
| end |
| |
| // Write data calculation |
| for (genvar i = 0; i < PMPNumRegions; i++) begin : g_pmp_csrs |
| // ------------------------- |
| // Instantiate cfg registers |
| // ------------------------- |
| assign pmp_cfg_we[i] = csr_we_int & ~pmp_cfg[i].lock & |
| (csr_addr == (CSR_OFF_PMP_CFG + (i[11:0] >> 2))); |
| |
| // Select the correct WDATA (each CSR contains 4 CFG fields, each with 2 RES bits) |
| assign pmp_cfg_wdata[i].lock = csr_wdata_int[(i%4)*PMP_CFG_W+7]; |
| // NA4 mode is not selectable when G > 0, mode is treated as OFF |
| always_comb begin |
| unique case (csr_wdata_int[(i%4)*PMP_CFG_W+3+:2]) |
| 2'b00 : pmp_cfg_wdata[i].mode = PMP_MODE_OFF; |
| 2'b01 : pmp_cfg_wdata[i].mode = PMP_MODE_TOR; |
| 2'b10 : pmp_cfg_wdata[i].mode = (PMPGranularity == 0) ? PMP_MODE_NA4: |
| PMP_MODE_OFF; |
| 2'b11 : pmp_cfg_wdata[i].mode = PMP_MODE_NAPOT; |
| default : pmp_cfg_wdata[i].mode = PMP_MODE_OFF; |
| endcase |
| end |
| assign pmp_cfg_wdata[i].exec = csr_wdata_int[(i%4)*PMP_CFG_W+2]; |
| // W = 1, R = 0 is a reserved combination. For now, we force W to 0 if R == 0 |
| assign pmp_cfg_wdata[i].write = &csr_wdata_int[(i%4)*PMP_CFG_W+:2]; |
| assign pmp_cfg_wdata[i].read = csr_wdata_int[(i%4)*PMP_CFG_W]; |
| |
| brq_csr #( |
| .Width ($bits(pmp_cfg_t)), |
| .ShadowCopy (ShadowCSR), |
| .ResetValue ('0) |
| ) u_pmp_cfg_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i ({pmp_cfg_wdata[i]}), |
| .wr_en_i (pmp_cfg_we[i]), |
| .rd_data_o (pmp_cfg[i]), |
| .rd_error_o (pmp_cfg_err[i]) |
| ); |
| |
| // -------------------------- |
| // Instantiate addr registers |
| // -------------------------- |
| if (i < PMPNumRegions - 1) begin : g_lower |
| assign pmp_addr_we[i] = csr_we_int & ~pmp_cfg[i].lock & |
| (~pmp_cfg[i+1].lock | (pmp_cfg[i+1].mode != PMP_MODE_TOR)) & |
| (csr_addr == (CSR_OFF_PMP_ADDR + i[11:0])); |
| end else begin : g_upper |
| assign pmp_addr_we[i] = csr_we_int & ~pmp_cfg[i].lock & |
| (csr_addr == (CSR_OFF_PMP_ADDR + i[11:0])); |
| end |
| |
| brq_csr #( |
| .Width (PMPAddrWidth), |
| .ShadowCopy (ShadowCSR), |
| .ResetValue ('0) |
| ) u_pmp_addr_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (csr_wdata_int[31-:PMPAddrWidth]), |
| .wr_en_i (pmp_addr_we[i]), |
| .rd_data_o (pmp_addr[i]), |
| .rd_error_o (pmp_addr_err[i]) |
| ); |
| |
| assign csr_pmp_cfg_o[i] = pmp_cfg[i]; |
| assign csr_pmp_addr_o[i] = {pmp_addr_rdata[i], 2'b00}; |
| end |
| |
| assign pmp_csr_err = (|pmp_cfg_err) | (|pmp_addr_err); |
| |
| end else begin : g_no_pmp_tieoffs |
| // Generate tieoffs when PMP is not configured |
| for (genvar i = 0; i < PMP_MAX_REGIONS; i++) begin : g_rdata |
| assign pmp_addr_rdata[i] = '0; |
| assign pmp_cfg_rdata[i] = '0; |
| end |
| for (genvar i = 0; i < PMPNumRegions; i++) begin : g_outputs |
| assign csr_pmp_cfg_o[i] = pmp_cfg_t'(1'b0); |
| assign csr_pmp_addr_o[i] = '0; |
| end |
| assign pmp_csr_err = 1'b0; |
| end |
| |
| ////////////////////////// |
| // Performance monitor // |
| ////////////////////////// |
| |
| // update enable signals |
| always_comb begin : mcountinhibit_update |
| if (mcountinhibit_we == 1'b1) begin |
| // bit 1 must always be 0 |
| mcountinhibit_d = {csr_wdata_int[MHPMCounterNum+2:2], 1'b0, csr_wdata_int[0]}; |
| end else begin |
| mcountinhibit_d = mcountinhibit_q; |
| end |
| end |
| |
| // event selection (hardwired) & control |
| always_comb begin : gen_mhpmcounter_incr |
| |
| // Assign inactive counters (first to prevent latch inference) |
| for (int unsigned i=0; i<32; i++) begin : gen_mhpmcounter_incr_inactive |
| mhpmcounter_incr[i] = 1'b0; |
| end |
| |
| // When adding or altering performance counter meanings and default |
| // mappings please update dv/verilator/pcount/cpp/brq_pcounts.cc |
| // appropriately. |
| // |
| // active counters |
| mhpmcounter_incr[0] = 1'b1; // mcycle |
| mhpmcounter_incr[1] = 1'b0; // reserved |
| mhpmcounter_incr[2] = instr_ret_i; // minstret |
| mhpmcounter_incr[3] = dside_wait_i; // cycles waiting for data memory |
| mhpmcounter_incr[4] = iside_wait_i; // cycles waiting for instr fetches |
| mhpmcounter_incr[5] = mem_load_i; // num of loads |
| mhpmcounter_incr[6] = mem_store_i; // num of stores |
| mhpmcounter_incr[7] = jump_i; // num of jumps (unconditional) |
| mhpmcounter_incr[8] = branch_i; // num of branches (conditional) |
| mhpmcounter_incr[9] = branch_taken_i; // num of taken branches (conditional) |
| mhpmcounter_incr[10] = instr_ret_compressed_i; // num of compressed instr |
| mhpmcounter_incr[11] = mul_wait_i; // cycles waiting for multiply |
| mhpmcounter_incr[12] = div_wait_i; // cycles waiting for divide |
| end |
| |
| // event selector (hardwired, 0 means no event) |
| always_comb begin : gen_mhpmevent |
| |
| // activate all |
| for (int i=0; i<32; i++) begin : gen_mhpmevent_active |
| mhpmevent[i] = '0; |
| mhpmevent[i][i] = 1'b1; |
| end |
| |
| // deactivate |
| mhpmevent[1] = '0; // not existing, reserved |
| for (int unsigned i=3+MHPMCounterNum; i<32; i++) begin : gen_mhpmevent_inactive |
| mhpmevent[i] = '0; |
| end |
| end |
| |
| // mcycle |
| brq_counter #( |
| .CounterWidth(64) |
| ) mcycle_counter_i ( |
| .clk_i(clk_i), |
| .rst_ni(rst_ni), |
| .counter_inc_i(mhpmcounter_incr[0] & ~mcountinhibit[0]), |
| .counterh_we_i(mhpmcounterh_we[0]), |
| .counter_we_i(mhpmcounter_we[0]), |
| .counter_val_i(csr_wdata_int), |
| .counter_val_o(mhpmcounter[0]) |
| ); |
| |
| // minstret |
| brq_counter #( |
| .CounterWidth(64) |
| ) minstret_counter_i ( |
| .clk_i(clk_i), |
| .rst_ni(rst_ni), |
| .counter_inc_i(mhpmcounter_incr[2] & ~mcountinhibit[2]), |
| .counterh_we_i(mhpmcounterh_we[2]), |
| .counter_we_i(mhpmcounter_we[2]), |
| .counter_val_i(csr_wdata_int), |
| .counter_val_o(mhpmcounter[2]) |
| ); |
| |
| // reserved: |
| assign mhpmcounter[1] = '0; |
| assign unused_mhpmcounter_we_1 = mhpmcounter_we[1]; |
| assign unused_mhpmcounterh_we_1 = mhpmcounterh_we[1]; |
| assign unused_mhpmcounter_incr_1 = mhpmcounter_incr[1]; |
| |
| for (genvar cnt=0; cnt < 29; cnt++) begin : gen_cntrs |
| if (cnt < MHPMCounterNum) begin : gen_imp |
| brq_counter #( |
| .CounterWidth(MHPMCounterWidth) |
| ) mcounters_variable_i ( |
| .clk_i(clk_i), |
| .rst_ni(rst_ni), |
| .counter_inc_i(mhpmcounter_incr[cnt+3] & ~mcountinhibit[cnt+3]), |
| .counterh_we_i(mhpmcounterh_we[cnt+3]), |
| .counter_we_i(mhpmcounter_we[cnt+3]), |
| .counter_val_i(csr_wdata_int), |
| .counter_val_o(mhpmcounter[cnt+3]) |
| ); |
| end else begin : gen_unimp |
| assign mhpmcounter[cnt+3] = '0; |
| end |
| end |
| |
| if(MHPMCounterNum < 29) begin : g_mcountinhibit_reduced |
| logic [29-MHPMCounterNum-1:0] unused_mhphcounter_we; |
| logic [29-MHPMCounterNum-1:0] unused_mhphcounterh_we; |
| logic [29-MHPMCounterNum-1:0] unused_mhphcounter_incr; |
| |
| assign mcountinhibit = {{29-MHPMCounterNum{1'b1}}, mcountinhibit_q}; |
| // Lint tieoffs for unused bits |
| assign unused_mhphcounter_we = mhpmcounter_we[31:MHPMCounterNum+3]; |
| assign unused_mhphcounterh_we = mhpmcounterh_we[31:MHPMCounterNum+3]; |
| assign unused_mhphcounter_incr = mhpmcounter_incr[31:MHPMCounterNum+3]; |
| end else begin : g_mcountinhibit_full |
| assign mcountinhibit = mcountinhibit_q; |
| end |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| mcountinhibit_q <= '0; |
| end else begin |
| mcountinhibit_q <= mcountinhibit_d; |
| end |
| end |
| |
| ///////////////////////////// |
| // Debug trigger registers // |
| ///////////////////////////// |
| |
| if (DbgTriggerEn) begin : gen_trigger_regs |
| localparam int unsigned DbgHwNumLen = DbgHwBreakNum > 1 ? $clog2(DbgHwBreakNum) : 1; |
| // Register values |
| logic [DbgHwNumLen-1:0] tselect_d, tselect_q; |
| logic tmatch_control_d; |
| logic [DbgHwBreakNum-1:0] tmatch_control_q; |
| logic [31:0] tmatch_value_d; |
| logic [31:0] tmatch_value_q[DbgHwBreakNum]; |
| // Write enables |
| logic tselect_we; |
| logic [DbgHwBreakNum-1:0] tmatch_control_we; |
| logic [DbgHwBreakNum-1:0] tmatch_value_we; |
| // Trigger comparison result |
| logic [DbgHwBreakNum-1:0] trigger_match; |
| |
| // Write select |
| assign tselect_we = csr_we_int & debug_mode_i & (csr_addr_i == CSR_TSELECT); |
| for (genvar i = 0; i < DbgHwBreakNum; i++) begin : g_dbg_tmatch_we |
| assign tmatch_control_we[i] = (i[DbgHwNumLen-1:0] == tselect_q) & csr_we_int & debug_mode_i & |
| (csr_addr_i == CSR_TDATA1); |
| assign tmatch_value_we[i] = (i[DbgHwNumLen-1:0] == tselect_q) & csr_we_int & debug_mode_i & |
| (csr_addr_i == CSR_TDATA2); |
| end |
| |
| // Debug interface tests the available number of triggers by writing and reading the trigger |
| // select register. Only allow changes to the register if it is within the supported region. |
| assign tselect_d = (csr_wdata_int < DbgHwBreakNum) ? csr_wdata_int[DbgHwNumLen-1:0] : |
| DbgHwBreakNum-1; |
| // tmatch_control is enabled when the execute bit is set |
| assign tmatch_control_d = csr_wdata_int[2]; |
| assign tmatch_value_d = csr_wdata_int[31:0]; |
| |
| // Registers |
| brq_csr #( |
| .Width (DbgHwNumLen), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_tselect_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (tselect_d), |
| .wr_en_i (tselect_we), |
| .rd_data_o (tselect_q), |
| .rd_error_o (unused_error15) |
| ); |
| |
| for (genvar i = 0; i < DbgHwBreakNum; i++) begin : g_dbg_tmatch_reg |
| brq_csr #( |
| .Width (1), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_tmatch_control_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (tmatch_control_d), |
| .wr_en_i (tmatch_control_we[i]), |
| .rd_data_o (tmatch_control_q[i]), |
| .rd_error_o (unused_error16) |
| ); |
| |
| brq_csr #( |
| .Width (32), |
| .ShadowCopy (1'b0), |
| .ResetValue ('0) |
| ) u_tmatch_value_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i (tmatch_value_d), |
| .wr_en_i (tmatch_value_we[i]), |
| .rd_data_o (tmatch_value_q[i]), |
| .rd_error_o (unused_error7) |
| ); |
| end |
| |
| // Assign read data |
| // TSELECT - number of supported triggers defined by parameter DbgHwBreakNum |
| localparam int unsigned TSelectRdataPadlen = DbgHwNumLen >= 32 ? 0 : (32 - DbgHwNumLen); |
| assign tselect_rdata = {{TSelectRdataPadlen{1'b0}}, tselect_q}; |
| |
| // TDATA0 - only support simple address matching |
| assign tmatch_control_rdata = {4'h2, // type : address/data match |
| 1'b1, // dmode : access from D mode only |
| 6'h00, // maskmax : exact match only |
| 1'b0, // hit : not supported |
| 1'b0, // select : address match only |
| 1'b0, // timing : match before execution |
| 2'b00, // sizelo : match any access |
| 4'h1, // action : enter debug mode |
| 1'b0, // chain : not supported |
| 4'h0, // match : simple match |
| 1'b1, // m : match in m-mode |
| 1'b0, // 0 : zero |
| 1'b0, // s : not supported |
| 1'b1, // u : match in u-mode |
| tmatch_control_q[tselect_q], // execute : match instruction address |
| 1'b0, // store : not supported |
| 1'b0}; // load : not supported |
| // TDATA1 - address match value only |
| assign tmatch_value_rdata = tmatch_value_q[tselect_q]; |
| |
| // Breakpoint matching |
| // We match against the next address, as the breakpoint must be taken before execution |
| for (genvar i = 0; i < DbgHwBreakNum; i++) begin : g_dbg_trigger_match |
| assign trigger_match[i] = tmatch_control_q[i] & (pc_if_i[31:0] == tmatch_value_q[i]); |
| end |
| assign trigger_match_o = |trigger_match; |
| |
| end else begin : gen_no_trigger_regs |
| assign tselect_rdata = 'b0; |
| assign tmatch_control_rdata = 'b0; |
| assign tmatch_value_rdata = 'b0; |
| assign trigger_match_o = 'b0; |
| end |
| |
| ////////////////////////// |
| // CPU control register // |
| ////////////////////////// |
| |
| // Cast register write data |
| assign cpuctrl_wdata = cpu_ctrl_t'(csr_wdata_int[$bits(cpu_ctrl_t)-1:0]); |
| |
| // Generate fixed time execution bit |
| if (DataIndTiming) begin : gen_dit |
| assign cpuctrl_d.data_ind_timing = cpuctrl_wdata.data_ind_timing; |
| |
| end else begin : gen_no_dit |
| // tieoff for the unused bit |
| logic unused_dit; |
| assign unused_dit = cpuctrl_wdata.data_ind_timing; |
| |
| // field will always read as zero if not configured |
| assign cpuctrl_d.data_ind_timing = 1'b0; |
| end |
| |
| assign data_ind_timing_o = cpuctrl_q.data_ind_timing; |
| |
| // Generate dummy instruction signals |
| if (DummyInstructions) begin : gen_dummy |
| assign cpuctrl_d.dummy_instr_en = cpuctrl_wdata.dummy_instr_en; |
| assign cpuctrl_d.dummy_instr_mask = cpuctrl_wdata.dummy_instr_mask; |
| |
| // Signal a write to the seed register |
| assign dummy_instr_seed_en_o = csr_we_int && (csr_addr == CSR_SECURESEED); |
| assign dummy_instr_seed_o = csr_wdata_int; |
| |
| end else begin : gen_no_dummy |
| // tieoff for the unused bit |
| logic unused_dummy_en; |
| logic [2:0] unused_dummy_mask; |
| assign unused_dummy_en = cpuctrl_wdata.dummy_instr_en; |
| assign unused_dummy_mask = cpuctrl_wdata.dummy_instr_mask; |
| |
| // field will always read as zero if not configured |
| assign cpuctrl_d.dummy_instr_en = 1'b0; |
| assign cpuctrl_d.dummy_instr_mask = 3'b000; |
| assign dummy_instr_seed_en_o = 1'b0; |
| assign dummy_instr_seed_o = '0; |
| end |
| |
| assign dummy_instr_en_o = cpuctrl_q.dummy_instr_en; |
| assign dummy_instr_mask_o = cpuctrl_q.dummy_instr_mask; |
| |
| // Generate icache enable bit |
| if (ICache) begin : gen_icache_enable |
| assign cpuctrl_d.icache_enable = cpuctrl_wdata.icache_enable; |
| end else begin : gen_no_icache |
| // tieoff for the unused icen bit |
| logic unused_icen; |
| assign unused_icen = cpuctrl_wdata.icache_enable; |
| |
| // icen field will always read as zero if ICache not configured |
| assign cpuctrl_d.icache_enable = 1'b0; |
| end |
| |
| assign icache_enable_o = cpuctrl_q.icache_enable; |
| |
| brq_csr #( |
| .Width ($bits(cpu_ctrl_t)), |
| .ShadowCopy (ShadowCSR), |
| .ResetValue ('0) |
| ) u_cpuctrl_csr ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .wr_data_i ({cpuctrl_d}), |
| .wr_en_i (cpuctrl_we), |
| .rd_data_o (cpuctrl_q), |
| .rd_error_o (cpuctrl_err) |
| ); |
| |
| assign csr_shadow_err_o = mstatus_err | mtvec_err | pmp_csr_err | cpuctrl_err; |
| |
| |
| |
| endmodule |