2457 lines
134 KiB
VHDL
2457 lines
134 KiB
VHDL
|
-- #################################################################################################
|
||
|
-- # << NEORV32 CPU - Central Operation Control Unit >> #
|
||
|
-- # ********************************************************************************************* #
|
||
|
-- # CPU operations are controlled by several "engines" (modules). These engines operate in #
|
||
|
-- # parallel to implement a tiny 2-stage pipeline: #
|
||
|
-- # + Fetch engine: Fetches 32-bit chunks of instruction words (1st pipeline stage) #
|
||
|
-- # + Issue engine: Decodes compressed instructions, aligns and queues instruction words #
|
||
|
-- # + Execute engine: Multi-cycle execution of instructions (2nd pipeline stage) #
|
||
|
-- # + Trap controller: Handles interrupts and exceptions #
|
||
|
-- # + CSR module: Read/write access to control and status registers #
|
||
|
-- # + CPU counters: Base and HPM counters #
|
||
|
-- # + Debug module: CPU debug mode handling (on-chip debugger) #
|
||
|
-- # + Trigger module: Hardware-assisted breakpoints (on-chip debugger) #
|
||
|
-- # ********************************************************************************************* #
|
||
|
-- # BSD 3-Clause License #
|
||
|
-- # #
|
||
|
-- # The NEORV32 RISC-V Processor, https://github.com/stnolting/neorv32 #
|
||
|
-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. #
|
||
|
-- # #
|
||
|
-- # Redistribution and use in source and binary forms, with or without modification, are #
|
||
|
-- # permitted provided that the following conditions are met: #
|
||
|
-- # #
|
||
|
-- # 1. Redistributions of source code must retain the above copyright notice, this list of #
|
||
|
-- # conditions and the following disclaimer. #
|
||
|
-- # #
|
||
|
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of #
|
||
|
-- # conditions and the following disclaimer in the documentation and/or other materials #
|
||
|
-- # provided with the distribution. #
|
||
|
-- # #
|
||
|
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to #
|
||
|
-- # endorse or promote products derived from this software without specific prior written #
|
||
|
-- # permission. #
|
||
|
-- # #
|
||
|
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
|
||
|
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF #
|
||
|
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
|
||
|
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
|
||
|
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
|
||
|
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED #
|
||
|
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
|
||
|
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
|
||
|
-- # OF THE POSSIBILITY OF SUCH DAMAGE. #
|
||
|
-- #################################################################################################
|
||
|
|
||
|
library ieee;
|
||
|
use ieee.std_logic_1164.all;
|
||
|
use ieee.numeric_std.all;
|
||
|
|
||
|
library neorv32;
|
||
|
use neorv32.neorv32_package.all;
|
||
|
|
||
|
entity neorv32_cpu_control is
|
||
|
generic (
|
||
|
-- General --
|
||
|
HART_ID : std_ulogic_vector(31 downto 0); -- hardware thread ID
|
||
|
VENDOR_ID : std_ulogic_vector(31 downto 0); -- vendor's JEDEC ID
|
||
|
CPU_BOOT_ADDR : std_ulogic_vector(31 downto 0); -- cpu boot address
|
||
|
CPU_DEBUG_PARK_ADDR : std_ulogic_vector(31 downto 0); -- cpu debug mode parking loop entry address, 4-byte aligned
|
||
|
CPU_DEBUG_EXC_ADDR : std_ulogic_vector(31 downto 0); -- cpu debug mode exception entry address, 4-byte aligned
|
||
|
-- RISC-V CPU Extensions --
|
||
|
CPU_EXTENSION_RISCV_A : boolean; -- implement atomic memory operations extension?
|
||
|
CPU_EXTENSION_RISCV_B : boolean; -- implement bit-manipulation extension?
|
||
|
CPU_EXTENSION_RISCV_C : boolean; -- implement compressed extension?
|
||
|
CPU_EXTENSION_RISCV_E : boolean; -- implement embedded RF extension?
|
||
|
CPU_EXTENSION_RISCV_M : boolean; -- implement mul/div extension?
|
||
|
CPU_EXTENSION_RISCV_U : boolean; -- implement user mode extension?
|
||
|
CPU_EXTENSION_RISCV_Zfinx : boolean; -- implement 32-bit floating-point extension (using INT regs)
|
||
|
CPU_EXTENSION_RISCV_Zicntr : boolean; -- implement base counters?
|
||
|
CPU_EXTENSION_RISCV_Zicond : boolean; -- implement integer conditional operations?
|
||
|
CPU_EXTENSION_RISCV_Zihpm : boolean; -- implement hardware performance monitors?
|
||
|
CPU_EXTENSION_RISCV_Zmmul : boolean; -- implement multiply-only M sub-extension?
|
||
|
CPU_EXTENSION_RISCV_Zxcfu : boolean; -- implement custom (instr.) functions unit?
|
||
|
CPU_EXTENSION_RISCV_Sdext : boolean; -- implement external debug mode extension?
|
||
|
CPU_EXTENSION_RISCV_Sdtrig : boolean; -- implement trigger module extension?
|
||
|
CPU_EXTENSION_RISCV_Smpmp : boolean; -- implement physical memory protection?
|
||
|
-- Tuning Options --
|
||
|
FAST_MUL_EN : boolean; -- use DSPs for M extension's multiplier
|
||
|
FAST_SHIFT_EN : boolean; -- use barrel shifter for shift operations
|
||
|
REGFILE_HW_RST : boolean; -- implement full hardware reset for register file
|
||
|
-- Hardware Performance Monitors (HPM) --
|
||
|
HPM_NUM_CNTS : natural range 0 to 13; -- number of implemented HPM counters (0..13)
|
||
|
HPM_CNT_WIDTH : natural range 0 to 64 -- total size of HPM counters (0..64)
|
||
|
);
|
||
|
port (
|
||
|
-- global control --
|
||
|
clk_i : in std_ulogic; -- global clock, rising edge
|
||
|
clk_aux_i : in std_ulogic; -- always-on clock, rising edge
|
||
|
rstn_i : in std_ulogic; -- global reset, low-active, async
|
||
|
ctrl_o : out ctrl_bus_t; -- main control bus
|
||
|
-- instruction fetch interface --
|
||
|
i_pmp_fault_i : in std_ulogic; -- instruction fetch pmp fault
|
||
|
bus_req_o : out bus_req_t; -- request
|
||
|
bus_rsp_i : in bus_rsp_t; -- response
|
||
|
-- data path interface --
|
||
|
alu_cp_done_i : in std_ulogic; -- ALU iterative operation done
|
||
|
cmp_i : in std_ulogic_vector(1 downto 0); -- comparator status
|
||
|
alu_add_i : in std_ulogic_vector(XLEN-1 downto 0); -- ALU address result
|
||
|
rs1_i : in std_ulogic_vector(XLEN-1 downto 0); -- rf source 1
|
||
|
imm_o : out std_ulogic_vector(XLEN-1 downto 0); -- immediate
|
||
|
fetch_pc_o : out std_ulogic_vector(XLEN-1 downto 0); -- instruction fetch address
|
||
|
curr_pc_o : out std_ulogic_vector(XLEN-1 downto 0); -- current PC (corresponding to current instruction)
|
||
|
link_pc_o : out std_ulogic_vector(XLEN-1 downto 0); -- link PC (return address)
|
||
|
csr_rdata_o : out std_ulogic_vector(XLEN-1 downto 0); -- CSR read data
|
||
|
-- external CSR interface --
|
||
|
xcsr_we_o : out std_ulogic; -- global write enable
|
||
|
xcsr_addr_o : out std_ulogic_vector(11 downto 0); -- address
|
||
|
xcsr_wdata_o : out std_ulogic_vector(XLEN-1 downto 0); -- write data
|
||
|
xcsr_rdata_i : in std_ulogic_vector(XLEN-1 downto 0); -- read data
|
||
|
-- interrupts --
|
||
|
db_halt_req_i : in std_ulogic; -- debug mode (halt) request
|
||
|
msi_i : in std_ulogic; -- machine software interrupt
|
||
|
mei_i : in std_ulogic; -- machine external interrupt
|
||
|
mti_i : in std_ulogic; -- machine timer interrupt
|
||
|
firq_i : in std_ulogic_vector(15 downto 0); -- fast interrupts
|
||
|
-- data access interface --
|
||
|
lsu_wait_i : in std_ulogic; -- wait for data bus
|
||
|
mar_i : in std_ulogic_vector(XLEN-1 downto 0); -- memory address register
|
||
|
ma_load_i : in std_ulogic; -- misaligned load data address
|
||
|
ma_store_i : in std_ulogic; -- misaligned store data address
|
||
|
be_load_i : in std_ulogic; -- bus error on load data access
|
||
|
be_store_i : in std_ulogic -- bus error on store data access
|
||
|
);
|
||
|
end neorv32_cpu_control;
|
||
|
|
||
|
architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
|
||
|
|
||
|
-- HPM counter auto-configuration --
|
||
|
constant hpm_num_c : natural := cond_sel_natural_f(CPU_EXTENSION_RISCV_Zihpm, HPM_NUM_CNTS, 0);
|
||
|
constant hpm_cnt_lo_width_c : natural := cond_sel_natural_f(boolean(HPM_CNT_WIDTH < 32), HPM_CNT_WIDTH, 32); -- width low word
|
||
|
constant hpm_cnt_hi_width_c : natural := natural(cond_sel_int_f(boolean(HPM_CNT_WIDTH > 32), HPM_CNT_WIDTH-32, 0)); -- width high word
|
||
|
|
||
|
-- instruction fetch engine --
|
||
|
type fetch_engine_state_t is (IF_RESTART, IF_REQUEST, IF_PENDING);
|
||
|
type fetch_engine_t is record
|
||
|
state : fetch_engine_state_t;
|
||
|
restart : std_ulogic; -- buffered restart request (after branch)
|
||
|
pc : std_ulogic_vector(XLEN-1 downto 0);
|
||
|
reset : std_ulogic; -- restart request (after branch)
|
||
|
resp : std_ulogic; -- bus response
|
||
|
priv : std_ulogic; -- fetch privilege level
|
||
|
end record;
|
||
|
signal fetch_engine : fetch_engine_t;
|
||
|
|
||
|
-- instruction prefetch buffer (FIFO) interface --
|
||
|
type ipb_data_t is array (0 to 1) of std_ulogic_vector(16 downto 0); -- bus_error & 16-bit instruction
|
||
|
type ipb_t is record
|
||
|
wdata, rdata : ipb_data_t;
|
||
|
we, re : std_ulogic_vector(1 downto 0);
|
||
|
free, avail : std_ulogic_vector(1 downto 0);
|
||
|
end record;
|
||
|
signal ipb : ipb_t;
|
||
|
|
||
|
-- instruction issue engine --
|
||
|
type issue_engine_t is record
|
||
|
align : std_ulogic;
|
||
|
align_set : std_ulogic;
|
||
|
align_clr : std_ulogic;
|
||
|
ci_i16 : std_ulogic_vector(15 downto 0);
|
||
|
ci_i32 : std_ulogic_vector(31 downto 0);
|
||
|
data : std_ulogic_vector((2+32)-1 downto 0); -- is_compressed & bus_error & 32-bit instruction
|
||
|
valid : std_ulogic_vector(1 downto 0); -- data word is valid
|
||
|
ack : std_ulogic;
|
||
|
end record;
|
||
|
signal issue_engine : issue_engine_t;
|
||
|
|
||
|
-- instruction decoding helper logic --
|
||
|
type decode_aux_t is record
|
||
|
opcode : std_ulogic_vector(6 downto 0);
|
||
|
is_a_lr : std_ulogic;
|
||
|
is_a_sc : std_ulogic;
|
||
|
is_f_op : std_ulogic;
|
||
|
is_m_mul : std_ulogic;
|
||
|
is_m_div : std_ulogic;
|
||
|
is_b_imm : std_ulogic;
|
||
|
is_b_reg : std_ulogic;
|
||
|
is_zicond : std_ulogic;
|
||
|
rs1_zero : std_ulogic;
|
||
|
rd_zero : std_ulogic;
|
||
|
end record;
|
||
|
signal decode_aux : decode_aux_t;
|
||
|
|
||
|
-- instruction execution engine --
|
||
|
-- make sure reset state is the first item in the list (discussion #415)
|
||
|
type execute_engine_state_t is (DISPATCH, TRAP_ENTER, TRAP_EXIT, RESTART, FENCE, SLEEP,
|
||
|
EXECUTE, ALU_WAIT, BRANCH, BRANCHED, SYSTEM, MEM_REQ, MEM_WAIT);
|
||
|
type execute_engine_t is record
|
||
|
state : execute_engine_state_t;
|
||
|
state_nxt : execute_engine_state_t;
|
||
|
ir : std_ulogic_vector(31 downto 0);
|
||
|
ir_nxt : std_ulogic_vector(31 downto 0);
|
||
|
is_ci : std_ulogic; -- current instruction is de-compressed instruction
|
||
|
is_ci_nxt : std_ulogic;
|
||
|
branch_taken : std_ulogic; -- branch condition fulfilled
|
||
|
pc : std_ulogic_vector(XLEN-1 downto 0); -- actual PC, corresponding to current executed instruction
|
||
|
pc_we : std_ulogic; -- PC update enabled
|
||
|
next_pc : std_ulogic_vector(XLEN-1 downto 0); -- next PC, corresponding to next instruction to be executed
|
||
|
next_pc_inc : std_ulogic_vector(XLEN-1 downto 0); -- increment to get next PC
|
||
|
link_pc : std_ulogic_vector(XLEN-1 downto 0); -- next PC for linking (return address)
|
||
|
end record;
|
||
|
signal execute_engine : execute_engine_t;
|
||
|
|
||
|
-- execution monitor --
|
||
|
type monitor_t is record
|
||
|
cnt : std_ulogic_vector(monitor_mc_tmo_c downto 0);
|
||
|
cnt_add : std_ulogic_vector(monitor_mc_tmo_c downto 0);
|
||
|
exc : std_ulogic;
|
||
|
end record;
|
||
|
signal monitor : monitor_t;
|
||
|
|
||
|
-- CPU sleep-mode --
|
||
|
signal sleep_mode : std_ulogic;
|
||
|
|
||
|
-- trap controller --
|
||
|
type trap_ctrl_t is record
|
||
|
exc_buf : std_ulogic_vector(exc_width_c-1 downto 0); -- synchronous exception buffer (one bit per exception)
|
||
|
exc_fire : std_ulogic; -- set if there is a valid source in the exception buffer
|
||
|
irq_pnd : std_ulogic_vector(irq_width_c-1 downto 0); -- pending interrupt
|
||
|
irq_buf : std_ulogic_vector(irq_width_c-1 downto 0); -- asynchronous exception/interrupt buffer (one bit per interrupt source)
|
||
|
irq_fire : std_ulogic; -- set if an interrupt is actually kicking in
|
||
|
cause : std_ulogic_vector(6 downto 0); -- trap ID for mcause CSR & debug-mode entry identifier
|
||
|
epc : std_ulogic_vector(XLEN-1 downto 0); -- exception program counter
|
||
|
--
|
||
|
env_pending : std_ulogic; -- start of trap environment if pending
|
||
|
env_enter : std_ulogic; -- enter trap environment
|
||
|
env_exit : std_ulogic; -- leave trap environment
|
||
|
wakeup : std_ulogic; -- wakeup from sleep due to an enabled pending IRQ
|
||
|
--
|
||
|
instr_be : std_ulogic; -- instruction fetch bus error
|
||
|
instr_ma : std_ulogic; -- instruction fetch misaligned address
|
||
|
instr_il : std_ulogic; -- illegal instruction
|
||
|
ecall : std_ulogic; -- ecall instruction
|
||
|
ebreak : std_ulogic; -- ebreak instruction
|
||
|
hwtrig : std_ulogic; -- hardware trigger module
|
||
|
end record;
|
||
|
signal trap_ctrl : trap_ctrl_t;
|
||
|
|
||
|
-- CPU main control bus --
|
||
|
signal ctrl, ctrl_nxt : ctrl_bus_t;
|
||
|
|
||
|
-- control and status registers (CSRs) --
|
||
|
type csr_t is record
|
||
|
addr : std_ulogic_vector(11 downto 0); -- csr address
|
||
|
raddr : std_ulogic_vector(11 downto 0); -- simplified csr read address
|
||
|
we, we_nxt : std_ulogic; -- csr write enable
|
||
|
re, re_nxt : std_ulogic; -- csr read enable
|
||
|
wdata, rdata : std_ulogic_vector(XLEN-1 downto 0); -- csr write/read data
|
||
|
--
|
||
|
mstatus_mie : std_ulogic; -- machine-mode IRQ enable
|
||
|
mstatus_mpie : std_ulogic; -- previous machine-mode IRQ enable
|
||
|
mstatus_mpp : std_ulogic; -- machine previous privilege mode
|
||
|
mstatus_mprv : std_ulogic; -- effective privilege level for load/stores
|
||
|
mstatus_tw : std_ulogic; -- do not allow user mode to execute WFI instruction when set
|
||
|
--
|
||
|
mie_msi : std_ulogic; -- machine software interrupt enable
|
||
|
mie_mei : std_ulogic; -- machine external interrupt enable
|
||
|
mie_mti : std_ulogic; -- machine timer interrupt enable
|
||
|
mie_firq : std_ulogic_vector(15 downto 0); -- fast interrupt enable
|
||
|
mip_firq_wdata : std_ulogic_vector(15 downto 0); -- fast interrupt pending write data
|
||
|
mip_firq_we : std_ulogic; -- fast interrupt pending write enable
|
||
|
--
|
||
|
privilege : std_ulogic; -- current privilege mode
|
||
|
privilege_eff : std_ulogic; -- current *effective* privilege mode
|
||
|
--
|
||
|
mepc : std_ulogic_vector(XLEN-1 downto 0); -- machine exception PC
|
||
|
mcause : std_ulogic_vector(5 downto 0); -- machine trap cause
|
||
|
mtvec : std_ulogic_vector(XLEN-1 downto 0); -- machine trap-handler base address
|
||
|
mtval : std_ulogic_vector(XLEN-1 downto 0); -- machine bad address or instruction
|
||
|
mtinst : std_ulogic_vector(XLEN-1 downto 0); -- machine trap instruction
|
||
|
mscratch : std_ulogic_vector(XLEN-1 downto 0); -- machine scratch register
|
||
|
mcounteren : std_ulogic; -- machine counter access enable (from user-mode) for ALL counters
|
||
|
mcountinhibit : std_ulogic_vector(15 downto 0); -- inhibit counter auto-increment
|
||
|
--
|
||
|
dcsr_ebreakm : std_ulogic; -- behavior of ebreak instruction in m-mode
|
||
|
dcsr_ebreaku : std_ulogic; -- behavior of ebreak instruction in u-mode
|
||
|
dcsr_step : std_ulogic; -- single-step mode
|
||
|
dcsr_prv : std_ulogic; -- current privilege level when entering debug mode
|
||
|
dcsr_cause : std_ulogic_vector(2 downto 0); -- why was debug mode entered
|
||
|
dcsr_rd : std_ulogic_vector(XLEN-1 downto 0); -- debug mode control and status register
|
||
|
dpc : std_ulogic_vector(XLEN-1 downto 0); -- mode program counter
|
||
|
dscratch0 : std_ulogic_vector(XLEN-1 downto 0); -- debug mode scratch register 0
|
||
|
--
|
||
|
tdata1_hit_clr : std_ulogic; -- set to manually clear mcontrol6.hit0
|
||
|
tdata1_execute : std_ulogic; -- enable instruction address match trigger
|
||
|
tdata1_action : std_ulogic; -- enter debug mode / ebreak exception when trigger fires
|
||
|
tdata1_dmode : std_ulogic; -- set to ignore tdata* CSR access from machine-mode
|
||
|
tdata1_rd : std_ulogic_vector(XLEN-1 downto 0); -- trigger register read-back
|
||
|
tdata2 : std_ulogic_vector(XLEN-1 downto 0); -- address-match register
|
||
|
end record;
|
||
|
signal csr : csr_t;
|
||
|
|
||
|
-- hpm event configuration CSRs --
|
||
|
type hpmevent_cfg_t is array (3 to 15) of std_ulogic_vector(hpmcnt_event_size_c-1 downto 0);
|
||
|
type hpmevent_rd_t is array (3 to 15) of std_ulogic_vector(XLEN-1 downto 0);
|
||
|
signal hpmevent_cfg : hpmevent_cfg_t;
|
||
|
signal hpmevent_rd : hpmevent_rd_t;
|
||
|
signal hpmevent_we : std_ulogic_vector(15 downto 0);
|
||
|
|
||
|
-- counter CSRs --
|
||
|
type cnt_dat_t is array (0 to 2+hpm_num_c) of std_ulogic_vector(XLEN-1 downto 0);
|
||
|
type cnt_nxt_t is array (0 to 2+hpm_num_c) of std_ulogic_vector(XLEN downto 0);
|
||
|
type cnt_ovf_t is array (0 to 2+hpm_num_c) of std_ulogic_vector(0 downto 0);
|
||
|
type cnt_t is record
|
||
|
we_lo : std_ulogic_vector(15 downto 0);
|
||
|
we_hi : std_ulogic_vector(15 downto 0);
|
||
|
inc : std_ulogic_vector(15 downto 0);
|
||
|
lo : cnt_dat_t; -- counter word low
|
||
|
hi : cnt_dat_t; -- counter word high
|
||
|
nxt : cnt_nxt_t; -- increment, including carry bit
|
||
|
ovf : cnt_ovf_t; -- counter low-to-high-word overflow
|
||
|
end record;
|
||
|
signal cnt : cnt_t;
|
||
|
signal cnt_lo_rd : cnt_dat_t;
|
||
|
signal cnt_hi_rd : cnt_dat_t;
|
||
|
|
||
|
-- counter events --
|
||
|
signal cnt_event : std_ulogic_vector(hpmcnt_event_size_c-1 downto 0);
|
||
|
|
||
|
-- debug mode controller --
|
||
|
type debug_ctrl_t is record
|
||
|
running : std_ulogic; -- CPU is in debug mode
|
||
|
trig_hw : std_ulogic; -- hardware trigger
|
||
|
trig_break : std_ulogic; -- ebreak instruction trigger
|
||
|
trig_halt : std_ulogic; -- external request trigger
|
||
|
trig_step : std_ulogic; -- single-stepping mode trigger
|
||
|
end record;
|
||
|
signal debug_ctrl : debug_ctrl_t;
|
||
|
|
||
|
-- illegal instruction check --
|
||
|
signal illegal_cmd : std_ulogic;
|
||
|
|
||
|
-- CSR access/privilege/read-write check --
|
||
|
signal csr_reg_valid : std_ulogic; -- CSR implemented at all
|
||
|
signal csr_rw_valid : std_ulogic; -- valid r/w access rights
|
||
|
signal csr_priv_valid : std_ulogic; -- valid access privilege
|
||
|
|
||
|
-- hardware trigger module --
|
||
|
signal hw_trigger_match, hw_trigger_fired : std_ulogic;
|
||
|
|
||
|
-- CSR read-back helpers --
|
||
|
signal csr_rdata, xcsr_rdata : std_ulogic_vector(XLEN-1 downto 0);
|
||
|
|
||
|
begin
|
||
|
|
||
|
-- ****************************************************************************************************************************
|
||
|
-- Instruction Fetch (always fetch 32-bit-aligned 32-bit chunks of data)
|
||
|
-- ****************************************************************************************************************************
|
||
|
|
||
|
-- Fetch Engine FSM -----------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
fetch_engine_fsm: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
fetch_engine.state <= IF_RESTART;
|
||
|
fetch_engine.restart <= '1'; -- set to reset IPB
|
||
|
fetch_engine.pc <= CPU_BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit aligned boot address
|
||
|
fetch_engine.priv <= priv_mode_m_c; -- start in machine mode
|
||
|
elsif rising_edge(clk_i) then
|
||
|
-- restart request --
|
||
|
if (fetch_engine.state = IF_RESTART) then -- restart done
|
||
|
fetch_engine.restart <= '0';
|
||
|
else -- buffer request
|
||
|
fetch_engine.restart <= fetch_engine.restart or fetch_engine.reset;
|
||
|
end if;
|
||
|
|
||
|
-- fsm --
|
||
|
case fetch_engine.state is
|
||
|
|
||
|
when IF_REQUEST => -- request next 32-bit-aligned instruction word
|
||
|
-- ------------------------------------------------------------
|
||
|
if (ipb.free = "11") then -- wait for free IPB space
|
||
|
fetch_engine.state <= IF_PENDING;
|
||
|
elsif (fetch_engine.restart = '1') or (fetch_engine.reset = '1') then -- restart request due to branch
|
||
|
fetch_engine.state <= IF_RESTART;
|
||
|
end if;
|
||
|
|
||
|
when IF_PENDING => -- wait for bus response and write instruction data to prefetch buffer
|
||
|
-- ------------------------------------------------------------
|
||
|
if (fetch_engine.resp = '1') then -- wait for bus response
|
||
|
fetch_engine.pc <= std_ulogic_vector(unsigned(fetch_engine.pc) + 4); -- next word
|
||
|
fetch_engine.pc(1) <= '0'; -- (re-)align to 32-bit
|
||
|
if (fetch_engine.restart = '1') or (fetch_engine.reset = '1') then -- restart request due to branch
|
||
|
fetch_engine.state <= IF_RESTART;
|
||
|
else -- request next linear instruction word
|
||
|
fetch_engine.state <= IF_REQUEST;
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
when others => -- IF_RESTART: set new start address
|
||
|
-- ------------------------------------------------------------
|
||
|
fetch_engine.pc <= execute_engine.next_pc(XLEN-1 downto 1) & '0'; -- initialize from PC incl. 16-bit-alignment bit
|
||
|
fetch_engine.priv <= csr.privilege_eff; -- set new privilege level
|
||
|
fetch_engine.state <= IF_REQUEST;
|
||
|
|
||
|
end case;
|
||
|
end if;
|
||
|
end process fetch_engine_fsm;
|
||
|
|
||
|
-- PC output for instruction fetch --
|
||
|
bus_req_o.addr <= fetch_engine.pc(XLEN-1 downto 2) & "00"; -- word aligned
|
||
|
fetch_pc_o <= fetch_engine.pc(XLEN-1 downto 2) & "00"; -- word aligned
|
||
|
|
||
|
-- instruction fetch (read) request if IPB not full --
|
||
|
bus_req_o.stb <= '1' when (fetch_engine.state = IF_REQUEST) and (ipb.free = "11") else '0';
|
||
|
|
||
|
-- instruction bus response --
|
||
|
fetch_engine.resp <= bus_rsp_i.ack or bus_rsp_i.err;
|
||
|
|
||
|
-- IPB instruction data and status --
|
||
|
ipb.wdata(0) <= (bus_rsp_i.err or i_pmp_fault_i) & bus_rsp_i.data(15 downto 00);
|
||
|
ipb.wdata(1) <= (bus_rsp_i.err or i_pmp_fault_i) & bus_rsp_i.data(31 downto 16);
|
||
|
|
||
|
-- IPB write enable --
|
||
|
ipb.we(0) <= '1' when (fetch_engine.state = IF_PENDING) and (fetch_engine.resp = '1') and
|
||
|
((fetch_engine.pc(1) = '0') or (CPU_EXTENSION_RISCV_C = false)) else '0';
|
||
|
ipb.we(1) <= '1' when (fetch_engine.state = IF_PENDING) and (fetch_engine.resp = '1') else '0';
|
||
|
|
||
|
-- bus access type --
|
||
|
bus_req_o.priv <= fetch_engine.priv; -- current effective privilege level
|
||
|
bus_req_o.data <= (others => '0'); -- read-only
|
||
|
bus_req_o.ben <= (others => '0'); -- read-only
|
||
|
bus_req_o.rw <= '0'; -- read-only
|
||
|
bus_req_o.src <= '1'; -- source = instruction fetch
|
||
|
bus_req_o.rvso <= '0'; -- cannot be a reservation set operation
|
||
|
bus_req_o.fence <= ctrl.lsu_fence; -- fence(.i) operation, valid without STB being set
|
||
|
|
||
|
|
||
|
-- Instruction Prefetch Buffer (FIFO) -----------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
prefetch_buffer:
|
||
|
for i in 0 to 1 generate -- low half-word + high half-word (incl. status bits)
|
||
|
prefetch_buffer_inst: entity neorv32.neorv32_fifo
|
||
|
generic map (
|
||
|
FIFO_DEPTH => 2, -- number of fifo entries; has to be a power of two, min 2
|
||
|
FIFO_WIDTH => ipb.wdata(i)'length, -- size of data elements in fifo
|
||
|
FIFO_RSYNC => false, -- we NEED to read data asynchronously
|
||
|
FIFO_SAFE => false, -- no safe access required (ensured by FIFO-external logic)
|
||
|
FULL_RESET => false -- no HW reset, try to infer BRAM
|
||
|
)
|
||
|
port map (
|
||
|
-- control --
|
||
|
clk_i => clk_i, -- clock, rising edge
|
||
|
rstn_i => rstn_i, -- async reset, low-active
|
||
|
clear_i => fetch_engine.restart, -- sync reset, high-active
|
||
|
half_o => open, -- at least half full
|
||
|
-- write port --
|
||
|
wdata_i => ipb.wdata(i), -- write data
|
||
|
we_i => ipb.we(i), -- write enable
|
||
|
free_o => ipb.free(i), -- at least one entry is free when set
|
||
|
-- read port --
|
||
|
re_i => ipb.re(i), -- read enable
|
||
|
rdata_o => ipb.rdata(i), -- read data
|
||
|
avail_o => ipb.avail(i) -- data available when set
|
||
|
);
|
||
|
end generate;
|
||
|
|
||
|
|
||
|
-- ****************************************************************************************************************************
|
||
|
-- Instruction Issue (decompress 16-bit instructions and assemble a 32-bit instruction word)
|
||
|
-- ****************************************************************************************************************************
|
||
|
|
||
|
-- Compressed Instructions Decoder --------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
neorv32_cpu_decompressor_inst_true:
|
||
|
if CPU_EXTENSION_RISCV_C generate
|
||
|
neorv32_cpu_decompressor_inst: entity neorv32.neorv32_cpu_decompressor
|
||
|
port map (
|
||
|
ci_instr16_i => issue_engine.ci_i16, -- compressed instruction
|
||
|
ci_instr32_o => issue_engine.ci_i32 -- decompressed instruction
|
||
|
);
|
||
|
end generate;
|
||
|
|
||
|
neorv32_cpu_decompressor_inst_false:
|
||
|
if not CPU_EXTENSION_RISCV_C generate
|
||
|
issue_engine.ci_i32 <= (others => '0');
|
||
|
end generate;
|
||
|
|
||
|
-- 16-bit instructions: half-word select --
|
||
|
issue_engine.ci_i16 <= ipb.rdata(0)(15 downto 0) when (issue_engine.align = '0') else ipb.rdata(1)(15 downto 0);
|
||
|
|
||
|
|
||
|
-- Issue Engine FSM (required if C extension is enabled) ----------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
issue_engine_enabled:
|
||
|
if CPU_EXTENSION_RISCV_C generate
|
||
|
|
||
|
issue_engine_fsm_sync: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
issue_engine.align <= '0'; -- start aligned after reset
|
||
|
elsif rising_edge(clk_i) then
|
||
|
if (fetch_engine.restart = '1') then
|
||
|
issue_engine.align <= execute_engine.next_pc(1); -- branch to unaligned address?
|
||
|
elsif (issue_engine.ack = '1') then
|
||
|
issue_engine.align <= (issue_engine.align and (not issue_engine.align_clr)) or issue_engine.align_set; -- "RS" flip-flop
|
||
|
end if;
|
||
|
end if;
|
||
|
end process issue_engine_fsm_sync;
|
||
|
|
||
|
issue_engine_fsm_comb: process(issue_engine, ipb)
|
||
|
begin
|
||
|
-- defaults --
|
||
|
issue_engine.align_set <= '0';
|
||
|
issue_engine.align_clr <= '0';
|
||
|
issue_engine.valid <= "00";
|
||
|
-- start with LOW half-word --
|
||
|
if (issue_engine.align = '0') then
|
||
|
if (ipb.rdata(0)(1 downto 0) /= "11") then -- compressed, use IPB(0) entry
|
||
|
issue_engine.align_set <= ipb.avail(0); -- start of next instruction word is NOT 32-bit-aligned
|
||
|
issue_engine.valid(0) <= ipb.avail(0);
|
||
|
issue_engine.data <= '1' & ipb.rdata(0)(16) & issue_engine.ci_i32;
|
||
|
else -- aligned uncompressed; use IPB(0) status flags only
|
||
|
issue_engine.valid <= (others => (ipb.avail(0) and ipb.avail(1)));
|
||
|
issue_engine.data <= '0' & ipb.rdata(0)(16) & ipb.rdata(1)(15 downto 0) & ipb.rdata(0)(15 downto 0);
|
||
|
end if;
|
||
|
-- start with HIGH half-word --
|
||
|
else
|
||
|
if (ipb.rdata(1)(1 downto 0) /= "11") then -- compressed, use IPB(1) entry
|
||
|
issue_engine.align_clr <= ipb.avail(1); -- start of next instruction word is 32-bit-aligned again
|
||
|
issue_engine.valid(1) <= ipb.avail(1);
|
||
|
issue_engine.data <= '1' & ipb.rdata(1)(16) & issue_engine.ci_i32;
|
||
|
else -- unaligned uncompressed; use IPB(0) status flags only
|
||
|
issue_engine.valid <= (others => (ipb.avail(0) and ipb.avail(1)));
|
||
|
issue_engine.data <= '0' & ipb.rdata(0)(16) & ipb.rdata(0)(15 downto 0) & ipb.rdata(1)(15 downto 0);
|
||
|
end if;
|
||
|
end if;
|
||
|
end process issue_engine_fsm_comb;
|
||
|
|
||
|
end generate; -- /issue_engine_enabled
|
||
|
|
||
|
issue_engine_disabled: -- use IPB(0) status flags only
|
||
|
if not CPU_EXTENSION_RISCV_C generate
|
||
|
issue_engine.valid <= (others => ipb.avail(0));
|
||
|
issue_engine.data <= '0' & ipb.rdata(0)(16) & (ipb.rdata(1)(15 downto 0) & ipb.rdata(0)(15 downto 0));
|
||
|
end generate; -- /issue_engine_disabled
|
||
|
|
||
|
-- update IPB FIFOs --
|
||
|
ipb.re(0) <= issue_engine.valid(0) and issue_engine.ack;
|
||
|
ipb.re(1) <= issue_engine.valid(1) and issue_engine.ack;
|
||
|
|
||
|
|
||
|
-- ****************************************************************************************************************************
|
||
|
-- Instruction Execution
|
||
|
-- ****************************************************************************************************************************
|
||
|
|
||
|
-- Immediate Generator --------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
imm_gen: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
imm_o <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
-- default I-immediate: ALU-immediate, load, jump-and-link with register --
|
||
|
imm_o(XLEN-1 downto 11) <= (others => execute_engine.ir(31)); -- sign extension
|
||
|
imm_o(10 downto 01) <= execute_engine.ir(30 downto 21);
|
||
|
imm_o(00) <= execute_engine.ir(20);
|
||
|
--
|
||
|
case decode_aux.opcode is
|
||
|
when opcode_store_c => -- S-immediate: store
|
||
|
imm_o(XLEN-1 downto 11) <= (others => execute_engine.ir(31)); -- sign extension
|
||
|
imm_o(10 downto 05) <= execute_engine.ir(30 downto 25);
|
||
|
imm_o(04 downto 00) <= execute_engine.ir(11 downto 07);
|
||
|
when opcode_branch_c => -- B-immediate: conditional branch
|
||
|
imm_o(XLEN-1 downto 12) <= (others => execute_engine.ir(31)); -- sign extension
|
||
|
imm_o(11) <= execute_engine.ir(07);
|
||
|
imm_o(10 downto 05) <= execute_engine.ir(30 downto 25);
|
||
|
imm_o(04 downto 01) <= execute_engine.ir(11 downto 08);
|
||
|
imm_o(00) <= '0';
|
||
|
when opcode_lui_c | opcode_auipc_c => -- U-immediate: lui, auipc
|
||
|
imm_o(XLEN-1 downto 12) <= execute_engine.ir(31 downto 12);
|
||
|
imm_o(11 downto 00) <= (others => '0');
|
||
|
when opcode_jal_c => -- J-immediate: unconditional jump
|
||
|
imm_o(XLEN-1 downto 20) <= (others => execute_engine.ir(31)); -- sign extension
|
||
|
imm_o(19 downto 12) <= execute_engine.ir(19 downto 12);
|
||
|
imm_o(11) <= execute_engine.ir(20);
|
||
|
imm_o(10 downto 01) <= execute_engine.ir(30 downto 21);
|
||
|
imm_o(00) <= '0';
|
||
|
when opcode_amo_c => -- atomic memory access
|
||
|
if (CPU_EXTENSION_RISCV_A = true) then
|
||
|
imm_o <= (others => '0');
|
||
|
else
|
||
|
NULL;
|
||
|
end if;
|
||
|
when others =>
|
||
|
NULL;
|
||
|
end case;
|
||
|
end if;
|
||
|
end process imm_gen;
|
||
|
|
||
|
|
||
|
-- Branch Condition Check -----------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
branch_check: process(execute_engine.ir, cmp_i)
|
||
|
begin
|
||
|
if (execute_engine.ir(instr_opcode_lsb_c+2) = '0') then -- conditional branch
|
||
|
if (execute_engine.ir(instr_funct3_msb_c) = '0') then -- beq / bne
|
||
|
execute_engine.branch_taken <= cmp_i(cmp_equal_c) xor execute_engine.ir(instr_funct3_lsb_c);
|
||
|
else -- blt(u) / bge(u)
|
||
|
execute_engine.branch_taken <= cmp_i(cmp_less_c) xor execute_engine.ir(instr_funct3_lsb_c);
|
||
|
end if;
|
||
|
else -- unconditional branch
|
||
|
execute_engine.branch_taken <= '1';
|
||
|
end if;
|
||
|
end process branch_check;
|
||
|
|
||
|
|
||
|
-- Execute Engine FSM Sync ----------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
execute_engine_fsm_sync: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
ctrl <= ctrl_bus_zero_c;
|
||
|
execute_engine.state <= RESTART;
|
||
|
execute_engine.ir <= (others => '0');
|
||
|
execute_engine.is_ci <= '0';
|
||
|
execute_engine.pc <= CPU_BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit aligned boot address
|
||
|
execute_engine.next_pc <= CPU_BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit aligned boot address
|
||
|
execute_engine.link_pc <= CPU_BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit aligned boot address
|
||
|
elsif rising_edge(clk_i) then
|
||
|
-- control bus --
|
||
|
ctrl <= ctrl_nxt;
|
||
|
|
||
|
-- execute engine arbiter --
|
||
|
execute_engine.state <= execute_engine.state_nxt;
|
||
|
execute_engine.ir <= execute_engine.ir_nxt;
|
||
|
execute_engine.is_ci <= execute_engine.is_ci_nxt;
|
||
|
|
||
|
-- current PC: address of instruction being executed --
|
||
|
if (execute_engine.pc_we = '1') then
|
||
|
execute_engine.pc <= execute_engine.next_pc(XLEN-1 downto 1) & '0';
|
||
|
end if;
|
||
|
|
||
|
-- link PC: return address --
|
||
|
if (execute_engine.state = BRANCH) then
|
||
|
execute_engine.link_pc <= execute_engine.next_pc(XLEN-1 downto 1) & '0';
|
||
|
end if;
|
||
|
|
||
|
-- next PC: address of next instruction --
|
||
|
case execute_engine.state is
|
||
|
|
||
|
when TRAP_ENTER => -- starting trap environment
|
||
|
if (trap_ctrl.cause(5) = '1') and (CPU_EXTENSION_RISCV_Sdext = true) then -- debug mode (re-)entry
|
||
|
execute_engine.next_pc <= CPU_DEBUG_PARK_ADDR(XLEN-1 downto 2) & "00"; -- debug mode enter; start at "parking loop" <normal_entry>
|
||
|
elsif (debug_ctrl.running = '1') and (CPU_EXTENSION_RISCV_Sdext = true) then -- any other trap INSIDE debug mode
|
||
|
execute_engine.next_pc <= CPU_DEBUG_EXC_ADDR(XLEN-1 downto 2) & "00"; -- debug mode enter: start at "parking loop" <exception_entry>
|
||
|
else -- normal start of trap
|
||
|
if (csr.mtvec(1 downto 0) = "01") and (trap_ctrl.cause(6) = '1') then -- vectored mode + interrupt
|
||
|
execute_engine.next_pc <= csr.mtvec(XLEN-1 downto 7) & trap_ctrl.cause(4 downto 0) & "00"; -- pc = mtvec + 4 * mcause
|
||
|
else
|
||
|
execute_engine.next_pc <= csr.mtvec(XLEN-1 downto 2) & "00"; -- pc = mtvec
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
when TRAP_EXIT => -- leaving trap environment
|
||
|
if (debug_ctrl.running = '1') and (CPU_EXTENSION_RISCV_Sdext = true) then -- debug mode exit
|
||
|
execute_engine.next_pc <= csr.dpc(XLEN-1 downto 1) & '0';
|
||
|
else -- normal end of trap
|
||
|
execute_engine.next_pc <= csr.mepc(XLEN-1 downto 1) & '0';
|
||
|
end if;
|
||
|
|
||
|
when BRANCH => -- control flow transfer
|
||
|
if (trap_ctrl.exc_buf(exc_illegal_c) = '0') and (execute_engine.branch_taken = '1') then -- valid taken branch
|
||
|
execute_engine.next_pc <= alu_add_i(XLEN-1 downto 1) & '0';
|
||
|
end if;
|
||
|
|
||
|
when EXECUTE => -- linear increment
|
||
|
execute_engine.next_pc <= std_ulogic_vector(unsigned(execute_engine.pc) + unsigned(execute_engine.next_pc_inc));
|
||
|
|
||
|
when others => -- no update
|
||
|
NULL;
|
||
|
|
||
|
end case;
|
||
|
end if;
|
||
|
end process execute_engine_fsm_sync;
|
||
|
|
||
|
-- check if branch destination is misaligned --
|
||
|
trap_ctrl.instr_ma <= '1' when (execute_engine.state = BRANCH) and (trap_ctrl.exc_buf(exc_illegal_c) = '0') and -- valid branch instruction
|
||
|
(execute_engine.branch_taken = '1') and -- branch is taken
|
||
|
(alu_add_i(1) = '1') and (CPU_EXTENSION_RISCV_C = false) else '0'; -- misaligned destination
|
||
|
|
||
|
-- PC increment for next LINEAR instruction (+2 for compressed instr., +4 otherwise) --
|
||
|
execute_engine.next_pc_inc(XLEN-1 downto 4) <= (others => '0');
|
||
|
execute_engine.next_pc_inc(3 downto 0) <= x"4" when ((execute_engine.is_ci = '0') or (CPU_EXTENSION_RISCV_C = false)) else x"2";
|
||
|
|
||
|
-- PC output --
|
||
|
curr_pc_o <= execute_engine.pc(XLEN-1 downto 1) & '0'; -- current PC
|
||
|
link_pc_o <= execute_engine.link_pc(XLEN-1 downto 1) & '0'; -- return address
|
||
|
|
||
|
|
||
|
-- Decoding Helper Logic ------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
decode_helper: process(execute_engine)
|
||
|
begin
|
||
|
-- defaults --
|
||
|
decode_aux.is_f_op <= '0';
|
||
|
decode_aux.is_a_lr <= '0';
|
||
|
decode_aux.is_a_sc <= '0';
|
||
|
decode_aux.is_m_mul <= '0';
|
||
|
decode_aux.is_m_div <= '0';
|
||
|
decode_aux.is_b_imm <= '0';
|
||
|
decode_aux.is_b_reg <= '0';
|
||
|
decode_aux.is_zicond <= '0';
|
||
|
|
||
|
-- ATOMIC instructions --
|
||
|
if (CPU_EXTENSION_RISCV_A = true) and -- implemented at all?
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "010") and
|
||
|
(execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c+3) = "0001") then
|
||
|
decode_aux.is_a_lr <= not execute_engine.ir(instr_funct7_lsb_c+2); -- LR.W
|
||
|
decode_aux.is_a_sc <= execute_engine.ir(instr_funct7_lsb_c+2); -- SC.W
|
||
|
end if;
|
||
|
|
||
|
-- BITMANIP instruction --
|
||
|
if (CPU_EXTENSION_RISCV_B = true) then -- implemented at all?
|
||
|
-- register-immediate operation --
|
||
|
if ((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0110000") and (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "001") and (
|
||
|
(execute_engine.ir(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c) = "00000") or -- CLZ
|
||
|
(execute_engine.ir(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c) = "00001") or -- CTZ
|
||
|
(execute_engine.ir(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c) = "00010") or -- CPOP
|
||
|
(execute_engine.ir(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c) = "00100") or -- SEXT.B
|
||
|
(execute_engine.ir(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c) = "00101") -- SEXT.H
|
||
|
)) or
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0110000") and (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "101")) or -- RORI
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0010100") and (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "101") and
|
||
|
(execute_engine.ir(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c) = "00111")) or -- ORCB
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0100100") and (execute_engine.ir(instr_funct3_msb_c-1 downto instr_funct3_lsb_c) = "01")) or -- BCLRI / BEXTI
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0110100") and (execute_engine.ir(instr_funct3_msb_c-1 downto instr_funct3_lsb_c) = "01")) or -- REV8 / BINVI
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0010100") and (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "001")) then -- BSETI
|
||
|
decode_aux.is_b_imm <= '1';
|
||
|
end if;
|
||
|
-- register-register operation --
|
||
|
if ((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0110000") and (execute_engine.ir(instr_funct3_msb_c-1 downto instr_funct3_lsb_c) = "01")) or -- ROR / ROL
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0000101") and (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) /= "000")) or -- MIN[U] / MAX[U] / CMUL[H/R]
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0000100") and (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "100")) or -- ZEXTH
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0100100") and (execute_engine.ir(instr_funct3_msb_c-1 downto instr_funct3_lsb_c) = "01")) or -- BCLR / BEXT
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0110100") and (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "001")) or -- BINV
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0010100") and (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "001")) or -- BSET
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0100000") and (
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "111") or -- ANDN
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "110") or -- ORN
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "100") -- XORN
|
||
|
)) or
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0010000") and (
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "010") or -- SH1ADD
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "100") or -- SH2ADD
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "110") -- SH3ADD
|
||
|
)
|
||
|
) then
|
||
|
decode_aux.is_b_reg <= '1';
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
-- FLOATING-POINT instructions (Zfinx) --
|
||
|
if (CPU_EXTENSION_RISCV_Zfinx = true) then -- FPU implemented at all?
|
||
|
if ((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c+3) = "0000")) or -- FADD.S / FSUB.S
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c+2) = "00010")) or -- FMUL.S
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c+2) = "11100") and (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "001")) or -- FCLASS.S
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c+2) = "00100") and (execute_engine.ir(instr_funct3_msb_c) = '0')) or -- FSGNJ[N/X].S
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c+2) = "00101") and (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_msb_c-1) = "00")) or -- FMIN.S / FMAX.S
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c+2) = "10100") and (execute_engine.ir(instr_funct3_msb_c) = '0')) or -- FEQ.S / FLT.S / FLE.S
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c+2) = "11010") and (execute_engine.ir(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c+1) = "0000")) or -- FCVT.S.W*
|
||
|
((execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c+2) = "11000") and (execute_engine.ir(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c+1) = "0000")) then -- FCVT.W*.S
|
||
|
if (execute_engine.ir(instr_funct7_lsb_c+1 downto instr_funct7_lsb_c) = float_single_c) then -- single-precision operations only
|
||
|
decode_aux.is_f_op <= '1';
|
||
|
end if;
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
-- integer MUL (M/Zmmul) / DIV (M) instruction --
|
||
|
if (execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0000001") then
|
||
|
if ((CPU_EXTENSION_RISCV_M = true) or (CPU_EXTENSION_RISCV_Zmmul = true)) and (execute_engine.ir(instr_funct3_msb_c) = '0') then
|
||
|
decode_aux.is_m_mul <= '1';
|
||
|
end if;
|
||
|
if (CPU_EXTENSION_RISCV_M = true) and (execute_engine.ir(instr_funct3_msb_c) = '1') then
|
||
|
decode_aux.is_m_div <= '1';
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
-- CONDITIONAL instruction (Zicond) --
|
||
|
if (CPU_EXTENSION_RISCV_Zicond = true) and (execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0000111") and
|
||
|
(execute_engine.ir(instr_funct3_msb_c) = '1') and (execute_engine.ir(instr_funct3_lsb_c) = '1') then
|
||
|
decode_aux.is_zicond <= '1';
|
||
|
end if;
|
||
|
end process decode_helper;
|
||
|
|
||
|
-- register/uimm5 checks --
|
||
|
decode_aux.rs1_zero <= '1' when (execute_engine.ir(instr_rs1_msb_c downto instr_rs1_lsb_c) = "00000") else '0';
|
||
|
decode_aux.rd_zero <= '1' when (execute_engine.ir(instr_rd_msb_c downto instr_rd_lsb_c ) = "00000") else '0';
|
||
|
|
||
|
-- simplified opcode --
|
||
|
decode_aux.opcode <= execute_engine.ir(instr_opcode_msb_c downto instr_opcode_lsb_c+2) & "11";
|
||
|
|
||
|
|
||
|
-- Execute Engine FSM Comb ----------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
execute_engine_fsm_comb: process(execute_engine, debug_ctrl, trap_ctrl, hw_trigger_match, decode_aux, issue_engine, csr, alu_cp_done_i, lsu_wait_i)
|
||
|
begin
|
||
|
-- arbiter defaults --
|
||
|
execute_engine.state_nxt <= execute_engine.state;
|
||
|
execute_engine.ir_nxt <= execute_engine.ir;
|
||
|
execute_engine.is_ci_nxt <= execute_engine.is_ci;
|
||
|
execute_engine.pc_we <= '0';
|
||
|
--
|
||
|
issue_engine.ack <= '0';
|
||
|
--
|
||
|
fetch_engine.reset <= '0';
|
||
|
--
|
||
|
trap_ctrl.env_enter <= '0';
|
||
|
trap_ctrl.env_exit <= '0';
|
||
|
trap_ctrl.instr_be <= '0';
|
||
|
trap_ctrl.ecall <= '0';
|
||
|
trap_ctrl.ebreak <= '0';
|
||
|
trap_ctrl.hwtrig <= '0';
|
||
|
--
|
||
|
csr.we_nxt <= '0';
|
||
|
csr.re_nxt <= '0';
|
||
|
--
|
||
|
ctrl_nxt <= ctrl_bus_zero_c; -- all zero/off by default, default ALU operation = ADD, default RF input = ALU
|
||
|
|
||
|
-- ALU sign control --
|
||
|
if (execute_engine.ir(instr_opcode_lsb_c+4) = '1') then -- ALU ops
|
||
|
ctrl_nxt.alu_unsigned <= execute_engine.ir(instr_funct3_lsb_c+0); -- unsigned ALU operation? (SLTIU, SLTU)
|
||
|
else -- branches
|
||
|
ctrl_nxt.alu_unsigned <= execute_engine.ir(instr_funct3_lsb_c+1); -- unsigned branches? (BLTU, BGEU)
|
||
|
end if;
|
||
|
|
||
|
-- ALU operand A: is PC? --
|
||
|
case decode_aux.opcode is
|
||
|
when opcode_auipc_c | opcode_jal_c | opcode_branch_c =>
|
||
|
ctrl_nxt.alu_opa_mux <= '1';
|
||
|
when others =>
|
||
|
ctrl_nxt.alu_opa_mux <= '0';
|
||
|
end case;
|
||
|
|
||
|
-- ALU operand B: is immediate? --
|
||
|
case decode_aux.opcode is
|
||
|
when opcode_alui_c | opcode_lui_c | opcode_auipc_c | opcode_load_c | opcode_store_c | opcode_amo_c | opcode_branch_c | opcode_jal_c | opcode_jalr_c =>
|
||
|
ctrl_nxt.alu_opb_mux <= '1';
|
||
|
when others =>
|
||
|
ctrl_nxt.alu_opb_mux <= '0';
|
||
|
end case;
|
||
|
|
||
|
-- memory read/write access --
|
||
|
if (CPU_EXTENSION_RISCV_A = true) and (decode_aux.opcode(2) = opcode_amo_c(2)) then -- lr/sc
|
||
|
ctrl_nxt.lsu_rw <= execute_engine.ir(instr_funct7_lsb_c+2);
|
||
|
else -- normal load/store
|
||
|
ctrl_nxt.lsu_rw <= execute_engine.ir(5);
|
||
|
end if;
|
||
|
|
||
|
-- state machine --
|
||
|
case execute_engine.state is
|
||
|
|
||
|
when DISPATCH => -- Wait for ISSUE ENGINE to emit valid instruction word
|
||
|
-- ------------------------------------------------------------
|
||
|
if (trap_ctrl.env_pending = '1') or (trap_ctrl.exc_fire = '1') then -- pending trap or pending exception (fast)
|
||
|
execute_engine.state_nxt <= TRAP_ENTER;
|
||
|
elsif (CPU_EXTENSION_RISCV_Sdtrig = true) and (hw_trigger_match = '1') then -- hardware breakpoint
|
||
|
execute_engine.pc_we <= '1'; -- pc <= next_pc
|
||
|
trap_ctrl.hwtrig <= '1';
|
||
|
execute_engine.state_nxt <= TRAP_ENTER;
|
||
|
elsif (issue_engine.valid(0) = '1') or (issue_engine.valid(1) = '1') then -- new instruction word available
|
||
|
issue_engine.ack <= '1';
|
||
|
trap_ctrl.instr_be <= issue_engine.data(32); -- access fault during instruction fetch
|
||
|
execute_engine.is_ci_nxt <= issue_engine.data(33); -- this is a de-compressed instruction
|
||
|
execute_engine.ir_nxt <= issue_engine.data(31 downto 0); -- instruction word
|
||
|
execute_engine.pc_we <= '1'; -- pc <= next_pc
|
||
|
execute_engine.state_nxt <= EXECUTE;
|
||
|
end if;
|
||
|
|
||
|
when TRAP_ENTER => -- Enter trap environment and jump to trap vector
|
||
|
-- ------------------------------------------------------------
|
||
|
if (trap_ctrl.env_pending = '1') then -- wait for sync. exceptions to become pending
|
||
|
trap_ctrl.env_enter <= '1';
|
||
|
execute_engine.state_nxt <= RESTART;
|
||
|
end if;
|
||
|
|
||
|
when TRAP_EXIT => -- Return from trap environment and jump to xEPC
|
||
|
-- ------------------------------------------------------------
|
||
|
trap_ctrl.env_exit <= '1';
|
||
|
execute_engine.state_nxt <= RESTART;
|
||
|
|
||
|
when RESTART => -- reset and restart instruction fetch at <next_pc>
|
||
|
-- ------------------------------------------------------------
|
||
|
fetch_engine.reset <= '1';
|
||
|
execute_engine.state_nxt <= BRANCHED;
|
||
|
|
||
|
when EXECUTE => -- Decode and execute instruction (control will be here for exactly 1 cycle in any case)
|
||
|
-- [NOTE] register file is read in this stage; due to the sync read, data will be available in the _next_ state
|
||
|
-- ------------------------------------------------------------
|
||
|
case decode_aux.opcode is
|
||
|
|
||
|
-- register/immediate ALU operation --
|
||
|
when opcode_alu_c | opcode_alui_c =>
|
||
|
-- ALU core operation --
|
||
|
case execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) is -- actual ALU operation (re-coding)
|
||
|
when funct3_subadd_c => -- ADD(I), SUB
|
||
|
if ((execute_engine.ir(instr_opcode_msb_c-1) = '1') and (execute_engine.ir(instr_funct7_msb_c-1) = '1')) then
|
||
|
ctrl_nxt.alu_op <= alu_op_sub_c; -- SUB if not an immediate op and funct7.6 set
|
||
|
else
|
||
|
ctrl_nxt.alu_op <= alu_op_add_c;
|
||
|
end if;
|
||
|
when funct3_slt_c | funct3_sltu_c => -- SLT(I), SLTU(I)
|
||
|
ctrl_nxt.alu_op <= alu_op_slt_c;
|
||
|
when funct3_xor_c => -- XOR(I)
|
||
|
ctrl_nxt.alu_op <= alu_op_xor_c;
|
||
|
when funct3_or_c => -- OR(I)
|
||
|
ctrl_nxt.alu_op <= alu_op_or_c;
|
||
|
when others => -- AND(I) or multi-cycle / co-processor operation
|
||
|
ctrl_nxt.alu_op <= alu_op_and_c;
|
||
|
end case;
|
||
|
|
||
|
-- EXT: co-processor MULDIV operation (multi-cycle) --
|
||
|
if ((CPU_EXTENSION_RISCV_M = true) and (execute_engine.ir(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and
|
||
|
((decode_aux.is_m_mul = '1') or (decode_aux.is_m_div = '1'))) or -- MUL/DIV
|
||
|
((CPU_EXTENSION_RISCV_Zmmul = true) and (execute_engine.ir(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and
|
||
|
(decode_aux.is_m_mul = '1')) then -- MUL
|
||
|
ctrl_nxt.alu_cp_trig(cp_sel_muldiv_c) <= '1'; -- trigger MULDIV CP
|
||
|
execute_engine.state_nxt <= ALU_WAIT;
|
||
|
-- EXT: co-processor BIT-MANIPULATION operation (multi-cycle) --
|
||
|
elsif (CPU_EXTENSION_RISCV_B = true) and
|
||
|
(((execute_engine.ir(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and (decode_aux.is_b_reg = '1')) or -- register operation
|
||
|
((execute_engine.ir(instr_opcode_lsb_c+5) = opcode_alui_c(5)) and (decode_aux.is_b_imm = '1'))) then -- immediate operation
|
||
|
ctrl_nxt.alu_cp_trig(cp_sel_bitmanip_c) <= '1'; -- trigger BITMANIP CP
|
||
|
execute_engine.state_nxt <= ALU_WAIT;
|
||
|
-- EXT: co-processor CONDITIONAL operations (multi-cycle) --
|
||
|
elsif (CPU_EXTENSION_RISCV_Zicond = true) and (decode_aux.is_zicond = '1') and
|
||
|
(execute_engine.ir(instr_opcode_lsb_c+5) = opcode_alu_c(5)) then
|
||
|
ctrl_nxt.alu_cp_trig(cp_sel_cond_c) <= '1'; -- trigger COND CP
|
||
|
execute_engine.state_nxt <= ALU_WAIT;
|
||
|
-- BASE: co-processor SHIFT operation (multi-cycle) --
|
||
|
elsif (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sll_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sr_c) then
|
||
|
ctrl_nxt.alu_cp_trig(cp_sel_shifter_c) <= '1'; -- trigger SHIFTER CP
|
||
|
execute_engine.state_nxt <= ALU_WAIT;
|
||
|
-- BASE: ALU CORE operation (single-cycle) --
|
||
|
else
|
||
|
ctrl_nxt.rf_wb_en <= '1'; -- valid RF write-back
|
||
|
execute_engine.state_nxt <= DISPATCH;
|
||
|
end if;
|
||
|
|
||
|
-- load upper immediate / add upper immediate to PC --
|
||
|
when opcode_lui_c | opcode_auipc_c =>
|
||
|
if (execute_engine.ir(instr_opcode_lsb_c+5) = opcode_lui_c(5)) then -- LUI
|
||
|
ctrl_nxt.alu_op <= alu_op_movb_c; -- pass immediate
|
||
|
else -- AUIPC
|
||
|
ctrl_nxt.alu_op <= alu_op_add_c; -- add PC and immediate
|
||
|
end if;
|
||
|
ctrl_nxt.rf_wb_en <= '1'; -- valid RF write-back
|
||
|
execute_engine.state_nxt <= DISPATCH;
|
||
|
|
||
|
-- memory access --
|
||
|
when opcode_load_c | opcode_store_c | opcode_amo_c =>
|
||
|
execute_engine.state_nxt <= MEM_REQ;
|
||
|
|
||
|
-- branch / jump and link / with register --
|
||
|
when opcode_branch_c | opcode_jal_c | opcode_jalr_c =>
|
||
|
execute_engine.state_nxt <= BRANCH;
|
||
|
|
||
|
-- memory fence operations --
|
||
|
when opcode_fence_c =>
|
||
|
execute_engine.state_nxt <= FENCE;
|
||
|
|
||
|
-- FPU: floating-point operations --
|
||
|
when opcode_fop_c =>
|
||
|
ctrl_nxt.alu_cp_trig(cp_sel_fpu_c) <= '1'; -- trigger FPU co-processor
|
||
|
execute_engine.state_nxt <= ALU_WAIT; -- will be aborted via monitor exception if FPU not implemented
|
||
|
|
||
|
-- CFU: custom RISC-V instructions --
|
||
|
when opcode_cust0_c | opcode_cust1_c | opcode_cust2_c | opcode_cust3_c =>
|
||
|
ctrl_nxt.alu_cp_trig(cp_sel_cfu_c) <= '1'; -- trigger CFU co-processor
|
||
|
execute_engine.state_nxt <= ALU_WAIT; -- will be aborted via monitor exception if CFU not implemented
|
||
|
|
||
|
-- environment/CSR operation or ILLEGAL opcode --
|
||
|
when others =>
|
||
|
csr.re_nxt <= '1';
|
||
|
execute_engine.state_nxt <= SYSTEM;
|
||
|
|
||
|
end case; -- /EXECUTE
|
||
|
|
||
|
when ALU_WAIT => -- wait for multi-cycle ALU co-processor operation to finish/trap
|
||
|
-- ------------------------------------------------------------
|
||
|
ctrl_nxt.alu_op <= alu_op_cp_c;
|
||
|
if (alu_cp_done_i = '1') or (trap_ctrl.exc_buf(exc_illegal_c) = '1') then
|
||
|
ctrl_nxt.rf_wb_en <= '1'; -- valid RF write-back (won't happen in case of an illegal instruction)
|
||
|
execute_engine.state_nxt <= DISPATCH;
|
||
|
end if;
|
||
|
|
||
|
when FENCE => -- memory fence
|
||
|
-- ------------------------------------------------------------
|
||
|
if (trap_ctrl.exc_buf(exc_illegal_c) = '1') then -- abort if illegal instruction
|
||
|
execute_engine.state_nxt <= DISPATCH;
|
||
|
else
|
||
|
ctrl_nxt.lsu_fence <= '1'; -- NOTE: fence == fence.i
|
||
|
execute_engine.state_nxt <= RESTART; -- reset instruction fetch + IPB (actually only required for fence.i)
|
||
|
end if;
|
||
|
|
||
|
when BRANCH => -- update next_PC on taken branches and jumps
|
||
|
-- ------------------------------------------------------------
|
||
|
ctrl_nxt.rf_mux <= rf_mux_ret_c; -- return address = link PC
|
||
|
ctrl_nxt.rf_wb_en <= execute_engine.ir(instr_opcode_lsb_c+2); -- save return address if link operation (will not happen if misaligned)
|
||
|
if (trap_ctrl.exc_buf(exc_illegal_c) = '0') and (execute_engine.branch_taken = '1') then -- valid taken branch
|
||
|
fetch_engine.reset <= '1'; -- reset instruction fetch to restart at modified PC
|
||
|
execute_engine.state_nxt <= BRANCHED; -- shortcut (faster than going to RESTART)
|
||
|
else
|
||
|
execute_engine.state_nxt <= DISPATCH;
|
||
|
end if;
|
||
|
|
||
|
when BRANCHED => -- delay cycle to wait for reset of pipeline front-end (instruction fetch)
|
||
|
-- ------------------------------------------------------------
|
||
|
execute_engine.state_nxt <= DISPATCH;
|
||
|
-- house keeping: use this state also to (re-)initialize the register file's x0/zero register --
|
||
|
if (REGFILE_HW_RST = false) then -- x0 does not provide a dedicated hardware reset
|
||
|
ctrl_nxt.rf_mux <= rf_mux_csr_c; -- this will return 0 since csr.re_nxt is zero
|
||
|
ctrl_nxt.rf_zero_we <= '1'; -- force write access to x0
|
||
|
end if;
|
||
|
|
||
|
when MEM_REQ => -- trigger memory request
|
||
|
-- ------------------------------------------------------------
|
||
|
if (trap_ctrl.exc_buf(exc_illegal_c) = '1') then -- abort if illegal instruction
|
||
|
execute_engine.state_nxt <= DISPATCH;
|
||
|
else
|
||
|
ctrl_nxt.lsu_req <= '1'; -- memory access request
|
||
|
execute_engine.state_nxt <= MEM_WAIT;
|
||
|
end if;
|
||
|
|
||
|
when MEM_WAIT => -- wait for bus transaction to finish
|
||
|
-- ------------------------------------------------------------
|
||
|
ctrl_nxt.rf_mux <= rf_mux_mem_c; -- RF input = memory read data
|
||
|
if (lsu_wait_i = '0') or -- bus system has completed the transaction
|
||
|
(trap_ctrl.exc_buf(exc_saccess_c) = '1') or (trap_ctrl.exc_buf(exc_laccess_c) = '1') or -- access exception
|
||
|
(trap_ctrl.exc_buf(exc_salign_c) = '1') or (trap_ctrl.exc_buf(exc_lalign_c) = '1') then -- alignment exception
|
||
|
if ((CPU_EXTENSION_RISCV_A = true) and (decode_aux.opcode(2) = opcode_amo_c(2))) or -- atomic operation
|
||
|
(execute_engine.ir(instr_opcode_msb_c-1) = '0') then -- normal load
|
||
|
ctrl_nxt.rf_wb_en <= '1'; -- allow write-back to register file (won't happen in case of exception)
|
||
|
end if;
|
||
|
execute_engine.state_nxt <= DISPATCH;
|
||
|
end if;
|
||
|
|
||
|
when SLEEP => -- sleep mode
|
||
|
-- ------------------------------------------------------------
|
||
|
if (trap_ctrl.wakeup = '1') then
|
||
|
execute_engine.state_nxt <= DISPATCH;
|
||
|
end if;
|
||
|
|
||
|
when others => -- SYSTEM - system environment operation; no effect if illegal instruction
|
||
|
-- ------------------------------------------------------------
|
||
|
execute_engine.state_nxt <= DISPATCH; -- default
|
||
|
ctrl_nxt.rf_mux <= rf_mux_csr_c; -- CSR read data
|
||
|
if (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_env_c) and -- ENVIRONMENT
|
||
|
(trap_ctrl.exc_buf(exc_illegal_c) = '0') then -- not an illegal instruction
|
||
|
case execute_engine.ir(instr_funct12_msb_c downto instr_funct12_lsb_c) is
|
||
|
when funct12_ecall_c => trap_ctrl.ecall <= '1'; -- ecall
|
||
|
when funct12_ebreak_c => trap_ctrl.ebreak <= '1'; -- ebreak
|
||
|
when funct12_mret_c | funct12_dret_c => execute_engine.state_nxt <= TRAP_EXIT; -- mret/dret
|
||
|
when others => execute_engine.state_nxt <= SLEEP; -- "funct12_wfi_c" - wfi/sleep
|
||
|
end case;
|
||
|
else -- CSR ACCESS - no state change if illegal instruction
|
||
|
if (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrw_c) or -- CSRRW: always write CSR
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrwi_c) or -- CSRRWI: always write CSR
|
||
|
(decode_aux.rs1_zero = '0') then -- CSRR(S/C)(I): write CSR if rs1/imm5 is NOT zero
|
||
|
csr.we_nxt <= '1';
|
||
|
end if;
|
||
|
ctrl_nxt.rf_wb_en <= '1'; -- valid RF write-back
|
||
|
end if;
|
||
|
|
||
|
end case;
|
||
|
end process execute_engine_fsm_comb;
|
||
|
|
||
|
|
||
|
-- CPU Sleep Mode Control -----------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
sleep_control: process(rstn_i, clk_aux_i) -- always-on clock domain
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
sleep_mode <= '0';
|
||
|
elsif rising_edge(clk_aux_i) then
|
||
|
if (execute_engine.state = SLEEP) and -- instruction execution has halted
|
||
|
(ipb.free /= "11") and -- instruction fetch has halted
|
||
|
(trap_ctrl.wakeup = '0') then -- no wake-up request
|
||
|
sleep_mode <= '1';
|
||
|
else
|
||
|
sleep_mode <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
end process sleep_control;
|
||
|
|
||
|
|
||
|
-- CPU Control Bus Output -----------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
|
||
|
-- register file --
|
||
|
ctrl_o.rf_wb_en <= ctrl.rf_wb_en and -- inhibit write-back only for rd-updating exceptions that must not commit
|
||
|
(not trap_ctrl.exc_buf(exc_illegal_c)) and
|
||
|
(not trap_ctrl.exc_buf(exc_ialign_c)) and (not trap_ctrl.exc_buf(exc_salign_c)) and (not trap_ctrl.exc_buf(exc_lalign_c)) and
|
||
|
(not trap_ctrl.exc_buf(exc_iaccess_c)) and (not trap_ctrl.exc_buf(exc_saccess_c)) and (not trap_ctrl.exc_buf(exc_laccess_c));
|
||
|
ctrl_o.rf_rs1 <= execute_engine.ir(instr_rs1_msb_c downto instr_rs1_lsb_c);
|
||
|
ctrl_o.rf_rs2 <= execute_engine.ir(instr_rs2_msb_c downto instr_rs2_lsb_c);
|
||
|
ctrl_o.rf_rs3 <= execute_engine.ir(instr_rs3_msb_c downto instr_rs3_lsb_c);
|
||
|
ctrl_o.rf_rd <= execute_engine.ir(instr_rd_msb_c downto instr_rd_lsb_c);
|
||
|
ctrl_o.rf_mux <= ctrl.rf_mux;
|
||
|
ctrl_o.rf_zero_we <= ctrl.rf_zero_we;
|
||
|
|
||
|
-- alu --
|
||
|
ctrl_o.alu_op <= ctrl.alu_op;
|
||
|
ctrl_o.alu_opa_mux <= ctrl.alu_opa_mux;
|
||
|
ctrl_o.alu_opb_mux <= ctrl.alu_opb_mux;
|
||
|
ctrl_o.alu_unsigned <= ctrl.alu_unsigned;
|
||
|
ctrl_o.alu_cp_trig <= ctrl.alu_cp_trig;
|
||
|
|
||
|
-- data bus interface --
|
||
|
ctrl_o.lsu_req <= ctrl.lsu_req;
|
||
|
ctrl_o.lsu_rw <= ctrl.lsu_rw;
|
||
|
ctrl_o.lsu_mo_we <= '1' when (execute_engine.state = MEM_REQ) else '0'; -- write memory output registers (data & address)
|
||
|
ctrl_o.lsu_fence <= ctrl.lsu_fence; -- fence(.i)
|
||
|
ctrl_o.lsu_priv <= csr.mstatus_mpp when (csr.mstatus_mprv = '1') else csr.privilege_eff; -- effective privilege level for loads/stores in M-mode
|
||
|
|
||
|
-- instruction word bit fields --
|
||
|
ctrl_o.ir_funct3 <= execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c);
|
||
|
ctrl_o.ir_funct12 <= execute_engine.ir(instr_funct12_msb_c downto instr_funct12_lsb_c);
|
||
|
ctrl_o.ir_opcode <= execute_engine.ir(instr_opcode_msb_c downto instr_opcode_lsb_c);
|
||
|
|
||
|
-- cpu status --
|
||
|
ctrl_o.cpu_priv <= csr.privilege_eff;
|
||
|
ctrl_o.cpu_sleep <= sleep_mode;
|
||
|
ctrl_o.cpu_trap <= trap_ctrl.env_enter;
|
||
|
ctrl_o.cpu_debug <= debug_ctrl.running;
|
||
|
|
||
|
|
||
|
-- ****************************************************************************************************************************
|
||
|
-- Illegal Instruction Detection
|
||
|
-- ****************************************************************************************************************************
|
||
|
|
||
|
-- Instruction Execution Monitor (trap if multi-cycle instruction does not complete) ------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
multi_cycle_monitor: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
monitor.cnt <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
monitor.cnt <= std_ulogic_vector(unsigned(monitor.cnt_add) + 1);
|
||
|
end if;
|
||
|
end process multi_cycle_monitor;
|
||
|
|
||
|
-- timeout counter (allow mapping of entire logic into the LUTs in front of the carry-chain) --
|
||
|
monitor.cnt_add <= monitor.cnt when (execute_engine.state = ALU_WAIT) else (others => '0');
|
||
|
|
||
|
-- raise illegal instruction exception if a multi-cycle instruction takes longer than a bound amount of time --
|
||
|
monitor.exc <= monitor.cnt(monitor.cnt'left);
|
||
|
|
||
|
|
||
|
-- CSR Access Check: Available at All -----------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
csr_avail_check: process(csr.addr)
|
||
|
begin
|
||
|
case csr.addr is
|
||
|
|
||
|
-- user-defined U-mode CFU CSRs --
|
||
|
when csr_cfureg0_c | csr_cfureg1_c | csr_cfureg2_c | csr_cfureg3_c =>
|
||
|
csr_reg_valid <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zxcfu); -- available if CFU implemented
|
||
|
|
||
|
-- floating-point CSRs --
|
||
|
when csr_fflags_c | csr_frm_c | csr_fcsr_c =>
|
||
|
csr_reg_valid <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zfinx); -- available if FPU implemented
|
||
|
|
||
|
-- machine trap setup/handling, environment/information registers, etc. --
|
||
|
when csr_mstatus_c | csr_mstatush_c | csr_misa_c | csr_mie_c | csr_mtvec_c |
|
||
|
csr_mscratch_c | csr_mepc_c | csr_mcause_c | csr_mip_c | csr_mtval_c |
|
||
|
csr_mtinst_c | csr_mcountinhibit_c | csr_mvendorid_c | csr_marchid_c | csr_mimpid_c |
|
||
|
csr_mhartid_c | csr_mconfigptr_c | csr_mxisa_c =>
|
||
|
csr_reg_valid <= '1'; -- always implemented
|
||
|
|
||
|
-- machine-controlled user-mode CSRs --
|
||
|
when csr_mcounteren_c | csr_menvcfg_c | csr_menvcfgh_c =>
|
||
|
csr_reg_valid <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_U); -- available if U-mode implemented
|
||
|
|
||
|
-- physical memory protection (PMP) --
|
||
|
when csr_pmpcfg0_c | csr_pmpcfg1_c | csr_pmpcfg2_c | csr_pmpcfg3_c | -- configuration
|
||
|
csr_pmpaddr0_c | csr_pmpaddr1_c | csr_pmpaddr2_c | csr_pmpaddr3_c |
|
||
|
csr_pmpaddr4_c | csr_pmpaddr5_c | csr_pmpaddr6_c | csr_pmpaddr7_c | -- address
|
||
|
csr_pmpaddr8_c | csr_pmpaddr9_c | csr_pmpaddr10_c | csr_pmpaddr11_c |
|
||
|
csr_pmpaddr12_c | csr_pmpaddr13_c | csr_pmpaddr14_c | csr_pmpaddr15_c =>
|
||
|
csr_reg_valid <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Smpmp); -- available if PMP implemented
|
||
|
|
||
|
-- hardware performance monitors (HPM) --
|
||
|
when csr_hpmcounter3_c | csr_hpmcounter4_c | csr_hpmcounter5_c | csr_hpmcounter6_c | csr_hpmcounter7_c |
|
||
|
csr_hpmcounter8_c | csr_hpmcounter9_c | csr_hpmcounter10_c | csr_hpmcounter11_c | csr_hpmcounter12_c |
|
||
|
csr_hpmcounter13_c | csr_hpmcounter14_c | csr_hpmcounter15_c | -- user counters LOW
|
||
|
csr_hpmcounter3h_c | csr_hpmcounter4h_c | csr_hpmcounter5h_c | csr_hpmcounter6h_c | csr_hpmcounter7h_c |
|
||
|
csr_hpmcounter8h_c | csr_hpmcounter9h_c | csr_hpmcounter10h_c | csr_hpmcounter11h_c | csr_hpmcounter12h_c |
|
||
|
csr_hpmcounter13h_c | csr_hpmcounter14h_c | csr_hpmcounter15h_c | -- user counters HIGH
|
||
|
csr_mhpmcounter3_c | csr_mhpmcounter4_c | csr_mhpmcounter5_c | csr_mhpmcounter6_c | csr_mhpmcounter7_c |
|
||
|
csr_mhpmcounter8_c | csr_mhpmcounter9_c | csr_mhpmcounter10_c | csr_mhpmcounter11_c | csr_mhpmcounter12_c |
|
||
|
csr_mhpmcounter13_c | csr_mhpmcounter14_c | csr_mhpmcounter15_c | -- machine counters LOW
|
||
|
csr_mhpmcounter3h_c | csr_mhpmcounter4h_c | csr_mhpmcounter5h_c | csr_mhpmcounter6h_c | csr_mhpmcounter7h_c |
|
||
|
csr_mhpmcounter8h_c | csr_mhpmcounter9h_c | csr_mhpmcounter10h_c | csr_mhpmcounter11h_c | csr_mhpmcounter12h_c |
|
||
|
csr_mhpmcounter13h_c | csr_mhpmcounter14h_c | csr_mhpmcounter15h_c | -- machine counters HIGH
|
||
|
csr_mhpmevent3_c | csr_mhpmevent4_c | csr_mhpmevent5_c | csr_mhpmevent6_c | csr_mhpmevent7_c |
|
||
|
csr_mhpmevent8_c | csr_mhpmevent9_c | csr_mhpmevent10_c | csr_mhpmevent11_c | csr_mhpmevent12_c |
|
||
|
csr_mhpmevent13_c | csr_mhpmevent14_c | csr_mhpmevent15_c => -- machine event configuration
|
||
|
csr_reg_valid <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zihpm); -- available if Zihpm implemented
|
||
|
|
||
|
-- counter and timer CSRs --
|
||
|
when csr_cycle_c | csr_mcycle_c | csr_instret_c | csr_minstret_c |
|
||
|
csr_cycleh_c | csr_mcycleh_c | csr_instreth_c | csr_minstreth_c =>
|
||
|
csr_reg_valid <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zicntr); -- available if Zicntr implemented
|
||
|
|
||
|
-- debug-mode CSRs --
|
||
|
when csr_dcsr_c | csr_dpc_c | csr_dscratch0_c =>
|
||
|
csr_reg_valid <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Sdext); -- available if debug-mode implemented
|
||
|
|
||
|
-- trigger module CSRs --
|
||
|
when csr_tselect_c | csr_tdata1_c | csr_tdata2_c | csr_tinfo_c =>
|
||
|
csr_reg_valid <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Sdtrig); -- available if trigger module implemented
|
||
|
|
||
|
-- undefined / not implemented --
|
||
|
when others =>
|
||
|
csr_reg_valid <= '0'; -- invalid access
|
||
|
|
||
|
end case;
|
||
|
end process csr_avail_check;
|
||
|
|
||
|
|
||
|
-- CSR Access Check: R/W Capabilities -----------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
csr_rw_check: process(csr.addr, execute_engine.ir, decode_aux.rs1_zero)
|
||
|
begin
|
||
|
if (csr.addr(11 downto 10) = "11") and -- CSR is read-only
|
||
|
((execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrw_c) or -- will always write to CSR
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrwi_c) or -- will always write to CSR
|
||
|
(decode_aux.rs1_zero = '0')) then -- clear/set instructions: write to CSR only if rs1/imm5 is NOT zero
|
||
|
csr_rw_valid <= '0'; -- invalid access
|
||
|
else
|
||
|
csr_rw_valid <= '1'; -- access granted
|
||
|
end if;
|
||
|
end process csr_rw_check;
|
||
|
|
||
|
|
||
|
-- CSR Access Check: Privilege Level ------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
csr_priv_check: process(csr, debug_ctrl)
|
||
|
begin
|
||
|
if ((csr.addr = csr_dcsr_c) or (csr.addr = csr_dpc_c) or (csr.addr = csr_dscratch0_c)) and -- debug-mode-only CSR?
|
||
|
(CPU_EXTENSION_RISCV_Sdext = true) and (debug_ctrl.running = '0') then -- debug-mode implemented and not running?
|
||
|
csr_priv_valid <= '0'; -- invalid access
|
||
|
elsif (csr.addr(11 downto 8) = csr_cycle_c(11 downto 8)) and -- user-mode counter access
|
||
|
((CPU_EXTENSION_RISCV_Zicntr = true) or (CPU_EXTENSION_RISCV_Zihpm = true)) and -- any counters available?
|
||
|
(CPU_EXTENSION_RISCV_U = true) and (csr.privilege_eff = '0') and (csr.mcounteren = '0') then -- user mode enabled, active and access not allowed?
|
||
|
csr_priv_valid <= '0'; -- invalid access
|
||
|
elsif (csr.addr(9 downto 8) /= "00") and (csr.privilege_eff = '0') then -- invalid privilege level
|
||
|
csr_priv_valid <= '0'; -- invalid access
|
||
|
else
|
||
|
csr_priv_valid <= '1'; -- access granted
|
||
|
end if;
|
||
|
end process csr_priv_check;
|
||
|
|
||
|
|
||
|
-- Illegal Instruction Check --------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
illegal_check: process(execute_engine, decode_aux, csr, csr_reg_valid, csr_rw_valid, csr_priv_valid, debug_ctrl)
|
||
|
begin
|
||
|
illegal_cmd <= '0'; -- default
|
||
|
case decode_aux.opcode is
|
||
|
|
||
|
when opcode_lui_c | opcode_auipc_c | opcode_jal_c =>
|
||
|
illegal_cmd <= '0'; -- all encodings are valid
|
||
|
|
||
|
when opcode_jalr_c =>
|
||
|
case execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) is
|
||
|
when "000" => illegal_cmd <= '0';
|
||
|
when others => illegal_cmd <= '1';
|
||
|
end case;
|
||
|
|
||
|
when opcode_branch_c =>
|
||
|
case execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) is
|
||
|
when funct3_beq_c | funct3_bne_c | funct3_blt_c | funct3_bge_c | funct3_bltu_c | funct3_bgeu_c => illegal_cmd <= '0';
|
||
|
when others => illegal_cmd <= '1';
|
||
|
end case;
|
||
|
|
||
|
when opcode_load_c =>
|
||
|
case execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) is
|
||
|
when funct3_lb_c | funct3_lh_c | funct3_lw_c | funct3_lbu_c | funct3_lhu_c => illegal_cmd <= '0';
|
||
|
when others => illegal_cmd <= '1';
|
||
|
end case;
|
||
|
|
||
|
when opcode_store_c =>
|
||
|
case execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) is
|
||
|
when funct3_sb_c | funct3_sh_c | funct3_sw_c => illegal_cmd <= '0';
|
||
|
when others => illegal_cmd <= '1';
|
||
|
end case;
|
||
|
|
||
|
when opcode_amo_c =>
|
||
|
if (CPU_EXTENSION_RISCV_A = true) and ((decode_aux.is_a_lr = '1') or (decode_aux.is_a_sc = '1')) then -- LR.W/SC.W
|
||
|
illegal_cmd <= '0';
|
||
|
else
|
||
|
illegal_cmd <= '1';
|
||
|
end if;
|
||
|
|
||
|
when opcode_alu_c =>
|
||
|
if ((((execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_subadd_c) or (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sr_c)) and
|
||
|
(execute_engine.ir(instr_funct7_msb_c-2 downto instr_funct7_lsb_c) = "00000") and (execute_engine.ir(instr_funct7_msb_c) = '0')) or
|
||
|
(((execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sll_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_slt_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sltu_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_xor_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_or_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_and_c)) and
|
||
|
(execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0000000"))) or -- valid base ALU instruction?
|
||
|
(((CPU_EXTENSION_RISCV_M = true) or (CPU_EXTENSION_RISCV_Zmmul = true)) and (decode_aux.is_m_mul = '1')) or -- valid MUL instruction?
|
||
|
((CPU_EXTENSION_RISCV_M = true) and (decode_aux.is_m_div = '1')) or -- valid DIV instruction?
|
||
|
((CPU_EXTENSION_RISCV_B = true) and (decode_aux.is_b_reg = '1')) or -- valid BITMANIP register instruction?
|
||
|
((CPU_EXTENSION_RISCV_Zicond = true) and (decode_aux.is_zicond = '1')) then -- valid CONDITIONAL instruction?
|
||
|
illegal_cmd <= '0';
|
||
|
else
|
||
|
illegal_cmd <= '1';
|
||
|
end if;
|
||
|
|
||
|
when opcode_alui_c =>
|
||
|
if ((execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_subadd_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_slt_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sltu_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_xor_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_or_c) or
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_and_c) or
|
||
|
((execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sll_c) and
|
||
|
(execute_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0000000")) or
|
||
|
((execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sr_c) and
|
||
|
((execute_engine.ir(instr_funct7_msb_c-2 downto instr_funct7_lsb_c) = "00000") and (execute_engine.ir(instr_funct7_msb_c) = '0')))) or -- valid base ALUI instruction?
|
||
|
((CPU_EXTENSION_RISCV_B = true) and (decode_aux.is_b_imm = '1')) then -- valid BITMANIP immediate instruction?
|
||
|
illegal_cmd <= '0';
|
||
|
else
|
||
|
illegal_cmd <= '1';
|
||
|
end if;
|
||
|
|
||
|
when opcode_fence_c =>
|
||
|
case execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) is
|
||
|
when funct3_fence_c | funct3_fencei_c => illegal_cmd <= '0'; -- fence[.i]
|
||
|
when others => illegal_cmd <= '1';
|
||
|
end case;
|
||
|
|
||
|
when opcode_system_c =>
|
||
|
if (execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_env_c) then -- system environment
|
||
|
if (decode_aux.rs1_zero = '1') and (decode_aux.rd_zero = '1') then
|
||
|
case execute_engine.ir(instr_funct12_msb_c downto instr_funct12_lsb_c) is
|
||
|
when funct12_ecall_c | funct12_ebreak_c => illegal_cmd <= '0'; -- ecall, ebreak
|
||
|
when funct12_mret_c => illegal_cmd <= (not csr.privilege) or debug_ctrl.running; -- mret allowed in (real/non-debug) M-mode only
|
||
|
when funct12_dret_c => illegal_cmd <= not debug_ctrl.running; -- dret allowed in debug mode only
|
||
|
when funct12_wfi_c => illegal_cmd <= (not csr.privilege) and csr.mstatus_tw; -- wfi allowed in M-mode or if TW is zero
|
||
|
when others => illegal_cmd <= '1';
|
||
|
end case;
|
||
|
else
|
||
|
illegal_cmd <= '1';
|
||
|
end if;
|
||
|
elsif (csr_reg_valid = '0') or (csr_rw_valid = '0') or (csr_priv_valid = '0') or -- invalid CSR access?
|
||
|
(execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csril_c) then -- invalid CSR access instruction?
|
||
|
illegal_cmd <= '1';
|
||
|
else
|
||
|
illegal_cmd <= '0';
|
||
|
end if;
|
||
|
|
||
|
when opcode_fop_c =>
|
||
|
illegal_cmd <= (not bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zfinx)) or (not decode_aux.is_f_op);
|
||
|
|
||
|
when opcode_cust0_c | opcode_cust1_c | opcode_cust2_c | opcode_cust3_c =>
|
||
|
illegal_cmd <= not bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zxcfu); -- all encodings valid if CFU enable
|
||
|
|
||
|
when others =>
|
||
|
illegal_cmd <= '1'; -- undefined/illegal opcode
|
||
|
|
||
|
end case;
|
||
|
end process illegal_check;
|
||
|
|
||
|
|
||
|
-- Illegal Operation Check ----------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
trap_ctrl.instr_il <= '1' when ((execute_engine.state = EXECUTE) or (execute_engine.state = ALU_WAIT)) and -- check in execution states only
|
||
|
((monitor.exc = '1') or -- execution monitor exception (multi-cycle instruction timeout)
|
||
|
(illegal_cmd = '1') or -- illegal instruction?
|
||
|
(execute_engine.ir(instr_opcode_lsb_c+1 downto instr_opcode_lsb_c) /= "11")) else '0'; -- illegal opcode LSBs?
|
||
|
|
||
|
|
||
|
-- ****************************************************************************************************************************
|
||
|
-- Trap Controller
|
||
|
-- ****************************************************************************************************************************
|
||
|
|
||
|
-- Exception Buffer -----------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
exception_buffer: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
trap_ctrl.exc_buf <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
|
||
|
-- Exception Buffer -----------------------------------------------------
|
||
|
-- If several exception sources trigger at once, all the requests will
|
||
|
-- stay active until the trap environment is started. Only the exception
|
||
|
-- with highest priority will be used to update the MCAUSE CSR. All
|
||
|
-- remaining ones will be discarded.
|
||
|
-- ----------------------------------------------------------------------
|
||
|
|
||
|
-- misaligned load/store/instruction address --
|
||
|
trap_ctrl.exc_buf(exc_lalign_c) <= (trap_ctrl.exc_buf(exc_lalign_c) or ma_load_i) and (not trap_ctrl.env_enter);
|
||
|
trap_ctrl.exc_buf(exc_salign_c) <= (trap_ctrl.exc_buf(exc_salign_c) or ma_store_i) and (not trap_ctrl.env_enter);
|
||
|
trap_ctrl.exc_buf(exc_ialign_c) <= (trap_ctrl.exc_buf(exc_ialign_c) or trap_ctrl.instr_ma) and (not trap_ctrl.env_enter);
|
||
|
|
||
|
-- load/store/instruction access fault --
|
||
|
trap_ctrl.exc_buf(exc_laccess_c) <= (trap_ctrl.exc_buf(exc_laccess_c) or be_load_i) and (not trap_ctrl.env_enter);
|
||
|
trap_ctrl.exc_buf(exc_saccess_c) <= (trap_ctrl.exc_buf(exc_saccess_c) or be_store_i) and (not trap_ctrl.env_enter);
|
||
|
trap_ctrl.exc_buf(exc_iaccess_c) <= (trap_ctrl.exc_buf(exc_iaccess_c) or trap_ctrl.instr_be) and (not trap_ctrl.env_enter);
|
||
|
|
||
|
-- illegal instruction & environment call --
|
||
|
trap_ctrl.exc_buf(exc_ecall_c) <= (trap_ctrl.exc_buf(exc_ecall_c) or trap_ctrl.ecall) and (not trap_ctrl.env_enter);
|
||
|
trap_ctrl.exc_buf(exc_illegal_c) <= (trap_ctrl.exc_buf(exc_illegal_c) or trap_ctrl.instr_il) and (not trap_ctrl.env_enter);
|
||
|
|
||
|
-- break point --
|
||
|
if (CPU_EXTENSION_RISCV_Sdext = true) then
|
||
|
trap_ctrl.exc_buf(exc_ebreak_c) <= (not trap_ctrl.env_enter) and (trap_ctrl.exc_buf(exc_ebreak_c) or
|
||
|
(trap_ctrl.hwtrig and (not csr.tdata1_action)) or -- trigger module fires and enter-debug is disabled
|
||
|
(trap_ctrl.ebreak and ( csr.privilege) and (not csr.dcsr_ebreakm) and (not debug_ctrl.running)) or -- enter M-mode handler on ebreak in M-mode
|
||
|
(trap_ctrl.ebreak and (not csr.privilege) and (not csr.dcsr_ebreaku) and (not debug_ctrl.running))); -- enter M-mode handler on ebreak in U-mode
|
||
|
else
|
||
|
trap_ctrl.exc_buf(exc_ebreak_c) <= (trap_ctrl.exc_buf(exc_ebreak_c) or trap_ctrl.ebreak or (trap_ctrl.hwtrig and (not csr.tdata1_action))) and (not trap_ctrl.env_enter);
|
||
|
end if;
|
||
|
|
||
|
-- debug-mode entry --
|
||
|
if (CPU_EXTENSION_RISCV_Sdext = true) then
|
||
|
trap_ctrl.exc_buf(exc_db_break_c) <= (trap_ctrl.exc_buf(exc_db_break_c) or debug_ctrl.trig_break) and (not trap_ctrl.env_enter);
|
||
|
trap_ctrl.exc_buf(exc_db_hw_c) <= (trap_ctrl.exc_buf(exc_db_hw_c) or debug_ctrl.trig_hw) and (not trap_ctrl.env_enter);
|
||
|
else
|
||
|
trap_ctrl.exc_buf(exc_db_break_c) <= '0';
|
||
|
trap_ctrl.exc_buf(exc_db_hw_c) <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
end process exception_buffer;
|
||
|
|
||
|
|
||
|
-- Interrupt Buffer -----------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
interrupt_buffer: process(rstn_i, clk_aux_i) -- always-on clock domain
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
trap_ctrl.irq_pnd <= (others => '0');
|
||
|
trap_ctrl.irq_buf <= (others => '0');
|
||
|
elsif rising_edge(clk_aux_i) then
|
||
|
|
||
|
-- Interrupt-Pending Buffer ---------------------------------------------
|
||
|
-- Once triggered the fast interrupt requests stay active until
|
||
|
-- explicitly cleared via the MIP CSR. The RISC-V standard interrupts
|
||
|
-- have to stay high until cleared by a platform-specific mechanism.
|
||
|
-- ----------------------------------------------------------------------
|
||
|
|
||
|
-- RISC-V machine interrupts --
|
||
|
trap_ctrl.irq_pnd(irq_msi_irq_c) <= msi_i;
|
||
|
trap_ctrl.irq_pnd(irq_mei_irq_c) <= mei_i;
|
||
|
trap_ctrl.irq_pnd(irq_mti_irq_c) <= mti_i;
|
||
|
|
||
|
-- NEORV32-specific fast interrupts --
|
||
|
for i in 0 to 15 loop
|
||
|
if (csr.mip_firq_we = '1') then -- write access to MIP(.FIRQ) CSR
|
||
|
trap_ctrl.irq_pnd(irq_firq_0_c+i) <= firq_i(i) or csr.mip_firq_wdata(i); -- keep buffering incoming FIRQs
|
||
|
else
|
||
|
trap_ctrl.irq_pnd(irq_firq_0_c+i) <= firq_i(i) or trap_ctrl.irq_pnd(irq_firq_0_c+i); -- keep pending FIRQs alive
|
||
|
end if;
|
||
|
end loop;
|
||
|
|
||
|
-- debug-mode entry --
|
||
|
trap_ctrl.irq_pnd(irq_db_halt_c) <= '0'; -- unused
|
||
|
trap_ctrl.irq_pnd(irq_db_step_c) <= '0'; -- unused
|
||
|
|
||
|
-- Interrupt (Masking) Buffer -------------------------------------------
|
||
|
-- Masking of interrupt request lines. Furthermore, this buffer ensures
|
||
|
-- that an *active* interrupt request line *stays* active (even if
|
||
|
-- disabled via MIE) if the trap environment is *currently* starting.
|
||
|
-- ----------------------------------------------------------------------
|
||
|
|
||
|
-- RISC-V machine interrupts --
|
||
|
trap_ctrl.irq_buf(irq_msi_irq_c) <= (trap_ctrl.irq_pnd(irq_msi_irq_c) and csr.mie_msi) or (trap_ctrl.env_pending and trap_ctrl.irq_buf(irq_msi_irq_c));
|
||
|
trap_ctrl.irq_buf(irq_mei_irq_c) <= (trap_ctrl.irq_pnd(irq_mei_irq_c) and csr.mie_mei) or (trap_ctrl.env_pending and trap_ctrl.irq_buf(irq_mei_irq_c));
|
||
|
trap_ctrl.irq_buf(irq_mti_irq_c) <= (trap_ctrl.irq_pnd(irq_mti_irq_c) and csr.mie_mti) or (trap_ctrl.env_pending and trap_ctrl.irq_buf(irq_mti_irq_c));
|
||
|
|
||
|
-- NEORV32-specific fast interrupts --
|
||
|
for i in 0 to 15 loop
|
||
|
trap_ctrl.irq_buf(irq_firq_0_c+i) <= (trap_ctrl.irq_pnd(irq_firq_0_c+i) and csr.mie_firq(i)) or (trap_ctrl.env_pending and trap_ctrl.irq_buf(irq_firq_0_c+i));
|
||
|
end loop;
|
||
|
|
||
|
-- debug-mode entry --
|
||
|
if (CPU_EXTENSION_RISCV_Sdext = true) then
|
||
|
trap_ctrl.irq_buf(irq_db_halt_c) <= debug_ctrl.trig_halt or (trap_ctrl.env_pending and trap_ctrl.irq_buf(irq_db_halt_c));
|
||
|
trap_ctrl.irq_buf(irq_db_step_c) <= debug_ctrl.trig_step or (trap_ctrl.env_pending and trap_ctrl.irq_buf(irq_db_step_c));
|
||
|
else
|
||
|
trap_ctrl.irq_buf(irq_db_halt_c) <= '0';
|
||
|
trap_ctrl.irq_buf(irq_db_step_c) <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
end process interrupt_buffer;
|
||
|
|
||
|
|
||
|
-- Trap Priority Logic --------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
trap_priority: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
trap_ctrl.cause <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
-- standard RISC-V exceptions --
|
||
|
if (trap_ctrl.exc_buf(exc_iaccess_c) = '1') then trap_ctrl.cause <= trap_iaf_c; -- instruction access fault
|
||
|
elsif (trap_ctrl.exc_buf(exc_illegal_c) = '1') then trap_ctrl.cause <= trap_iil_c; -- illegal instruction
|
||
|
elsif (trap_ctrl.exc_buf(exc_ialign_c) = '1') then trap_ctrl.cause <= trap_ima_c; -- instruction address misaligned
|
||
|
elsif (trap_ctrl.exc_buf(exc_ecall_c) = '1') then trap_ctrl.cause <= trap_env_c(6 downto 2) & csr.privilege & csr.privilege; -- environment call (U/M)
|
||
|
elsif (trap_ctrl.exc_buf(exc_ebreak_c) = '1') then trap_ctrl.cause <= trap_brk_c; -- environment breakpoint
|
||
|
elsif (trap_ctrl.exc_buf(exc_salign_c) = '1') then trap_ctrl.cause <= trap_sma_c; -- store address misaligned
|
||
|
elsif (trap_ctrl.exc_buf(exc_lalign_c) = '1') then trap_ctrl.cause <= trap_lma_c; -- load address misaligned
|
||
|
elsif (trap_ctrl.exc_buf(exc_saccess_c) = '1') then trap_ctrl.cause <= trap_saf_c; -- store access fault
|
||
|
elsif (trap_ctrl.exc_buf(exc_laccess_c) = '1') then trap_ctrl.cause <= trap_laf_c; -- load access fault
|
||
|
-- standard RISC-V debug mode exceptions and interrupts --
|
||
|
elsif (trap_ctrl.irq_buf(irq_db_halt_c) = '1') then trap_ctrl.cause <= trap_db_halt_c; -- external halt request (async)
|
||
|
elsif (trap_ctrl.exc_buf(exc_db_hw_c) = '1') then trap_ctrl.cause <= trap_db_trig_c; -- hardware trigger (sync)
|
||
|
elsif (trap_ctrl.exc_buf(exc_db_break_c) = '1') then trap_ctrl.cause <= trap_db_break_c; -- breakpoint (sync)
|
||
|
elsif (trap_ctrl.irq_buf(irq_db_step_c) = '1') then trap_ctrl.cause <= trap_db_step_c; -- single stepping (async)
|
||
|
-- NEORV32-specific fast interrupts --
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_0_c) = '1') then trap_ctrl.cause <= trap_firq0_c; -- fast interrupt channel 0
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_1_c) = '1') then trap_ctrl.cause <= trap_firq1_c; -- fast interrupt channel 1
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_2_c) = '1') then trap_ctrl.cause <= trap_firq2_c; -- fast interrupt channel 2
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_3_c) = '1') then trap_ctrl.cause <= trap_firq3_c; -- fast interrupt channel 3
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_4_c) = '1') then trap_ctrl.cause <= trap_firq4_c; -- fast interrupt channel 4
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_5_c) = '1') then trap_ctrl.cause <= trap_firq5_c; -- fast interrupt channel 5
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_6_c) = '1') then trap_ctrl.cause <= trap_firq6_c; -- fast interrupt channel 6
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_7_c) = '1') then trap_ctrl.cause <= trap_firq7_c; -- fast interrupt channel 7
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_8_c) = '1') then trap_ctrl.cause <= trap_firq8_c; -- fast interrupt channel 8
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_9_c) = '1') then trap_ctrl.cause <= trap_firq9_c; -- fast interrupt channel 9
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_10_c) = '1') then trap_ctrl.cause <= trap_firq10_c; -- fast interrupt channel 10
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_11_c) = '1') then trap_ctrl.cause <= trap_firq11_c; -- fast interrupt channel 11
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_12_c) = '1') then trap_ctrl.cause <= trap_firq12_c; -- fast interrupt channel 12
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_13_c) = '1') then trap_ctrl.cause <= trap_firq13_c; -- fast interrupt channel 13
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_14_c) = '1') then trap_ctrl.cause <= trap_firq14_c; -- fast interrupt channel 14
|
||
|
elsif (trap_ctrl.irq_buf(irq_firq_15_c) = '1') then trap_ctrl.cause <= trap_firq15_c; -- fast interrupt channel 15
|
||
|
-- standard RISC-V interrupts --
|
||
|
elsif (trap_ctrl.irq_buf(irq_mei_irq_c) = '1') then trap_ctrl.cause <= trap_mei_c; -- machine external interrupt (MEI)
|
||
|
elsif (trap_ctrl.irq_buf(irq_msi_irq_c) = '1') then trap_ctrl.cause <= trap_msi_c; -- machine software interrupt (MSI)
|
||
|
elsif (trap_ctrl.irq_buf(irq_mti_irq_c) = '1') then trap_ctrl.cause <= trap_mti_c; -- machine timer interrupt (MTI)
|
||
|
--
|
||
|
else trap_ctrl.cause <= trap_mti_c; end if; -- don't care
|
||
|
end if;
|
||
|
end process trap_priority;
|
||
|
|
||
|
|
||
|
-- Trap Controller ------------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
trap_controller: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
trap_ctrl.env_pending <= '0';
|
||
|
elsif rising_edge(clk_i) then
|
||
|
if (trap_ctrl.env_pending = '0') then -- no pending trap environment yet
|
||
|
-- trigger IRQ only in EXECUTE states to *continue execution* even if there are permanent interrupt requests
|
||
|
if (trap_ctrl.exc_fire = '1') or ((trap_ctrl.irq_fire = '1') and (execute_engine.state = EXECUTE)) then
|
||
|
trap_ctrl.env_pending <= '1'; -- now execute engine can start trap handling
|
||
|
end if;
|
||
|
elsif (trap_ctrl.env_enter = '1') then -- start of trap environment acknowledged by execute engine
|
||
|
trap_ctrl.env_pending <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
end process trap_controller;
|
||
|
|
||
|
-- wake-up from / do not enter sleep mode: during debugging or on pending IRQ --
|
||
|
trap_ctrl.wakeup <= or_reduce_f(trap_ctrl.irq_buf) or debug_ctrl.running or csr.dcsr_step;
|
||
|
|
||
|
-- any exception? --
|
||
|
trap_ctrl.exc_fire <= '1' when (or_reduce_f(trap_ctrl.exc_buf) = '1') else '0'; -- sync. exceptions CANNOT be masked
|
||
|
|
||
|
-- any interrupt? --
|
||
|
trap_ctrl.irq_fire <= '1' when
|
||
|
(
|
||
|
(or_reduce_f(trap_ctrl.irq_buf(irq_firq_15_c downto irq_msi_irq_c)) = '1') and -- pending IRQ
|
||
|
((csr.mstatus_mie = '1') or (csr.privilege = priv_mode_u_c)) and -- take IRQ when in M-mode and MIE=1 OR when in U-mode
|
||
|
(debug_ctrl.running = '0') and (csr.dcsr_step = '0') -- no IRQs when in debug-mode or during debug single-stepping
|
||
|
) or
|
||
|
(trap_ctrl.irq_buf(irq_db_step_c) = '1') or -- debug-mode single-step IRQ
|
||
|
(trap_ctrl.irq_buf(irq_db_halt_c) = '1') else '0'; -- debug-mode halt IRQ
|
||
|
|
||
|
-- exception program counter (for updating xPC CSRs) --
|
||
|
trap_ctrl.epc <= execute_engine.next_pc when (trap_ctrl.cause(trap_ctrl.cause'left) = '1') else execute_engine.pc;
|
||
|
|
||
|
|
||
|
-- ****************************************************************************************************************************
|
||
|
-- Control and Status Registers (CSRs)
|
||
|
-- ****************************************************************************************************************************
|
||
|
|
||
|
-- CSR Access Address ---------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
csr.addr <= execute_engine.ir(instr_imm12_msb_c downto instr_imm12_lsb_c);
|
||
|
-- simplified CSR read address - [WARNING] M-mode (9:8 = 11) and U-mode (9:8 = 00) CSRs only! --
|
||
|
csr.raddr <= csr.addr(11 downto 10) & csr.addr(8) & csr.addr(8) & csr.addr(7 downto 0);
|
||
|
|
||
|
|
||
|
-- CSR Write Data -------------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
csr_write_data: process(execute_engine.ir, csr.rdata, rs1_i)
|
||
|
variable tmp_v : std_ulogic_vector(XLEN-1 downto 0);
|
||
|
begin
|
||
|
-- immediate/register operand --
|
||
|
if (execute_engine.ir(instr_funct3_msb_c) = '1') then
|
||
|
tmp_v := (others => '0');
|
||
|
tmp_v(4 downto 0) := execute_engine.ir(19 downto 15); -- uimm5
|
||
|
else
|
||
|
tmp_v := rs1_i;
|
||
|
end if;
|
||
|
-- tiny ALU to compute CSR write data --
|
||
|
case execute_engine.ir(instr_funct3_msb_c-1 downto instr_funct3_lsb_c) is
|
||
|
when "10" => csr.wdata <= csr.rdata or tmp_v; -- set
|
||
|
when "11" => csr.wdata <= csr.rdata and (not tmp_v); -- clear
|
||
|
when others => csr.wdata <= tmp_v; -- write
|
||
|
end case;
|
||
|
end process csr_write_data;
|
||
|
|
||
|
|
||
|
-- External CSR Interface -----------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
xcsr_we_o <= csr.we;
|
||
|
xcsr_addr_o <= csr.addr;
|
||
|
xcsr_wdata_o <= csr.wdata;
|
||
|
xcsr_rdata <= xcsr_rdata_i;
|
||
|
|
||
|
|
||
|
-- CSR Write Access -----------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
csr_write_access: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
csr.we <= '0';
|
||
|
csr.privilege <= priv_mode_m_c;
|
||
|
csr.mstatus_mie <= '0';
|
||
|
csr.mstatus_mpie <= '0';
|
||
|
csr.mstatus_mpp <= priv_mode_m_c;
|
||
|
csr.mstatus_mprv <= '0';
|
||
|
csr.mstatus_tw <= '0';
|
||
|
csr.mie_msi <= '0';
|
||
|
csr.mie_mei <= '0';
|
||
|
csr.mie_mti <= '0';
|
||
|
csr.mie_firq <= (others => '0');
|
||
|
csr.mtvec <= CPU_BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit aligned boot address
|
||
|
csr.mscratch <= x"19880704";
|
||
|
csr.mepc <= CPU_BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit aligned boot address
|
||
|
csr.mcause <= (others => '0');
|
||
|
csr.mtval <= (others => '0');
|
||
|
csr.mtinst <= (others => '0');
|
||
|
csr.mcounteren <= '0';
|
||
|
csr.mcountinhibit <= (others => '0');
|
||
|
csr.mip_firq_wdata <= (others => '0');
|
||
|
csr.mip_firq_we <= '0';
|
||
|
csr.dcsr_ebreakm <= '0';
|
||
|
csr.dcsr_ebreaku <= '0';
|
||
|
csr.dcsr_step <= '0';
|
||
|
csr.dcsr_prv <= priv_mode_m_c;
|
||
|
csr.dcsr_cause <= (others => '0');
|
||
|
csr.dpc <= CPU_BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit aligned boot address
|
||
|
csr.dscratch0 <= (others => '0');
|
||
|
csr.tdata1_hit_clr <= '0';
|
||
|
csr.tdata1_execute <= '0';
|
||
|
csr.tdata1_action <= '0';
|
||
|
csr.tdata1_dmode <= '0';
|
||
|
csr.tdata2 <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
|
||
|
-- defaults --
|
||
|
csr.we <= csr.we_nxt and (not trap_ctrl.exc_buf(exc_illegal_c)); -- write if not an illegal instruction
|
||
|
csr.mip_firq_we <= '0'; -- no write to MIP.FIRQ by default
|
||
|
csr.tdata1_hit_clr <= '0';
|
||
|
|
||
|
-- ********************************************************************************
|
||
|
-- CSR access by application software
|
||
|
-- ********************************************************************************
|
||
|
if (csr.we = '1') then
|
||
|
case csr.addr is
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- machine trap setup --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when csr_mstatus_c => -- machine status register
|
||
|
csr.mstatus_mie <= csr.wdata(03);
|
||
|
csr.mstatus_mpie <= csr.wdata(07);
|
||
|
if (CPU_EXTENSION_RISCV_U = true) then
|
||
|
csr.mstatus_mpp <= csr.wdata(11) or csr.wdata(12); -- everything /= U will fall back to M
|
||
|
csr.mstatus_mprv <= csr.wdata(17);
|
||
|
csr.mstatus_tw <= csr.wdata(21);
|
||
|
end if;
|
||
|
|
||
|
when csr_mie_c => -- machine interrupt enable register
|
||
|
csr.mie_msi <= csr.wdata(03);
|
||
|
csr.mie_mti <= csr.wdata(07);
|
||
|
csr.mie_mei <= csr.wdata(11);
|
||
|
csr.mie_firq <= csr.wdata(31 downto 16);
|
||
|
|
||
|
when csr_mtvec_c => -- machine trap-handler base address
|
||
|
if (csr.wdata(1 downto 0) = "01") then
|
||
|
csr.mtvec <= csr.wdata(XLEN-1 downto 7) & "00000" & "01"; -- mtvec.MODE=1 (vectored)
|
||
|
else
|
||
|
csr.mtvec <= csr.wdata(XLEN-1 downto 2) & "00"; -- mtvec.MODE=0 (direct)
|
||
|
end if;
|
||
|
|
||
|
when csr_mcounteren_c => -- machine counter access enable
|
||
|
if (CPU_EXTENSION_RISCV_U = true) then
|
||
|
if (CPU_EXTENSION_RISCV_Zicntr = true) and (CPU_EXTENSION_RISCV_Zihpm = true) then
|
||
|
csr.mcounteren <= or_reduce_f(csr.wdata(15 downto 3)) or csr.wdata(2) or csr.wdata(0); -- hpms, instret, cycle
|
||
|
elsif (CPU_EXTENSION_RISCV_Zicntr = true) then
|
||
|
csr.mcounteren <= csr.wdata(2) or csr.wdata(0); -- instret, cycle
|
||
|
elsif (CPU_EXTENSION_RISCV_Zihpm = true) then
|
||
|
csr.mcounteren <= or_reduce_f(csr.wdata(15 downto 3)); -- hpms
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- machine trap handling --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when csr_mscratch_c => -- machine scratch register
|
||
|
csr.mscratch <= csr.wdata;
|
||
|
|
||
|
when csr_mepc_c => -- machine exception program counter
|
||
|
csr.mepc <= csr.wdata(XLEN-1 downto 1) & '0';
|
||
|
if (CPU_EXTENSION_RISCV_C = false) then -- RISC-V priv. spec.: MEPC[1] is masked when IALIGN = 32
|
||
|
csr.mepc(1) <= '0';
|
||
|
end if;
|
||
|
|
||
|
when csr_mcause_c => -- machine trap cause
|
||
|
csr.mcause <= csr.wdata(31) & csr.wdata(4 downto 0); -- type (exception/interrupt) & identifier
|
||
|
|
||
|
when csr_mip_c => -- machine interrupt pending
|
||
|
csr.mip_firq_wdata <= csr.wdata(31 downto 16);
|
||
|
csr.mip_firq_we <= '1'; -- trigger MIP.FIRQ write
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- machine counter setup --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when csr_mcountinhibit_c => -- machine counter-inhibit register
|
||
|
if (CPU_EXTENSION_RISCV_Zicntr = true) then
|
||
|
csr.mcountinhibit(0) <= csr.wdata(0);
|
||
|
csr.mcountinhibit(2) <= csr.wdata(2);
|
||
|
end if;
|
||
|
if (CPU_EXTENSION_RISCV_Zihpm = true) then
|
||
|
csr.mcountinhibit(15 downto 3) <= csr.wdata(15 downto 3);
|
||
|
end if;
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- debug mode CSRs --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when csr_dcsr_c => -- debug mode control and status register
|
||
|
if (CPU_EXTENSION_RISCV_Sdext = true) then
|
||
|
csr.dcsr_ebreakm <= csr.wdata(15);
|
||
|
csr.dcsr_step <= csr.wdata(2);
|
||
|
if (CPU_EXTENSION_RISCV_U = true) then
|
||
|
csr.dcsr_ebreaku <= csr.wdata(12);
|
||
|
csr.dcsr_prv <= csr.wdata(1) or csr.wdata(0); -- everything /= U will fall back to M
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
when csr_dpc_c => -- debug mode program counter
|
||
|
if (CPU_EXTENSION_RISCV_Sdext = true) then
|
||
|
csr.dpc <= csr.wdata(XLEN-1 downto 1) & '0';
|
||
|
if (CPU_EXTENSION_RISCV_C = false) then -- RISC-V priv. spec.: DPC[1] is masked when IALIGN = 32
|
||
|
csr.dpc(1) <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
when csr_dscratch0_c => -- debug mode scratch register 0
|
||
|
if (CPU_EXTENSION_RISCV_Sdext = true) then
|
||
|
csr.dscratch0 <= csr.wdata;
|
||
|
end if;
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- trigger module CSRs --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when csr_tdata1_c => -- match control
|
||
|
if (CPU_EXTENSION_RISCV_Sdtrig = true) then
|
||
|
if (csr.tdata1_dmode = '0') or (debug_ctrl.running = '1') then -- write access from debug-mode only?
|
||
|
csr.tdata1_execute <= csr.wdata(2);
|
||
|
csr.tdata1_action <= csr.wdata(12);
|
||
|
csr.tdata1_hit_clr <= not csr.wdata(22);
|
||
|
end if;
|
||
|
if (debug_ctrl.running = '1') then -- writable from debug-mode only
|
||
|
csr.tdata1_dmode <= csr.wdata(27);
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
when csr_tdata2_c => -- address compare
|
||
|
if (CPU_EXTENSION_RISCV_Sdtrig = true) then
|
||
|
if (csr.tdata1_dmode = '0') or (debug_ctrl.running = '1') then -- write access from debug-mode only?
|
||
|
csr.tdata2 <= csr.wdata(XLEN-1 downto 1) & '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- not implemented (or implemented externally) --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when others => NULL;
|
||
|
|
||
|
end case;
|
||
|
|
||
|
-- ********************************************************************************
|
||
|
-- Hardware CSR access: TRAP ENTER
|
||
|
-- ********************************************************************************
|
||
|
elsif (trap_ctrl.env_enter = '1') then
|
||
|
|
||
|
-- NORMAL trap entry - no CSR update when in debug-mode! --
|
||
|
if (CPU_EXTENSION_RISCV_Sdext = false) or ((trap_ctrl.cause(5) = '0') and (debug_ctrl.running = '0')) then
|
||
|
csr.mcause <= trap_ctrl.cause(trap_ctrl.cause'left) & trap_ctrl.cause(4 downto 0); -- trap type & identifier
|
||
|
csr.mepc <= trap_ctrl.epc(XLEN-1 downto 1) & '0'; -- trap PC
|
||
|
-- trap value --
|
||
|
if (trap_ctrl.cause(6) = '0') and (trap_ctrl.cause(2) = '1') then -- load/store misaligned/access faults [hacky!]
|
||
|
csr.mtval <= mar_i; -- faulting data access address
|
||
|
else -- everything else including all interrupts
|
||
|
csr.mtval <= (others => '0');
|
||
|
end if;
|
||
|
-- trap instruction --
|
||
|
if (trap_ctrl.cause(6) = '0') then -- exception
|
||
|
csr.mtinst <= execute_engine.ir;
|
||
|
if (execute_engine.is_ci = '1') and (CPU_EXTENSION_RISCV_C = true) then
|
||
|
csr.mtinst(1) <= '0'; -- RISC-V priv. spec: clear bit 1 if compressed instruction
|
||
|
end if;
|
||
|
else -- interrupt
|
||
|
csr.mtinst <= (others => '0');
|
||
|
end if;
|
||
|
-- update privilege level and interrupt-enable stack --
|
||
|
csr.privilege <= priv_mode_m_c; -- execute trap in machine mode
|
||
|
csr.mstatus_mie <= '0'; -- disable interrupts
|
||
|
csr.mstatus_mpie <= csr.mstatus_mie; -- backup previous mie state
|
||
|
csr.mstatus_mpp <= csr.privilege; -- backup previous privilege mode
|
||
|
end if;
|
||
|
|
||
|
-- DEBUG MODE entry - no CSR update when already in debug-mode! --
|
||
|
if (CPU_EXTENSION_RISCV_Sdext = true) and (trap_ctrl.cause(5) = '1') and (debug_ctrl.running = '0') then
|
||
|
-- trap cause --
|
||
|
csr.dcsr_cause <= trap_ctrl.cause(2 downto 0); -- why did we enter debug mode?
|
||
|
-- current privilege mode when debug mode was entered --
|
||
|
csr.dcsr_prv <= csr.privilege;
|
||
|
-- trap PC --
|
||
|
csr.dpc <= trap_ctrl.epc(XLEN-1 downto 1) & '0';
|
||
|
end if;
|
||
|
|
||
|
-- ********************************************************************************
|
||
|
-- Hardware CSR access: TRAP EXIT
|
||
|
-- ********************************************************************************
|
||
|
elsif (trap_ctrl.env_exit = '1') then
|
||
|
|
||
|
-- return from debug mode --
|
||
|
if (CPU_EXTENSION_RISCV_Sdext = true) and (debug_ctrl.running = '1') then
|
||
|
if (CPU_EXTENSION_RISCV_U = true) then
|
||
|
csr.privilege <= csr.dcsr_prv;
|
||
|
if (csr.dcsr_prv /= priv_mode_m_c) then
|
||
|
csr.mstatus_mprv <= '0'; -- clear if return to priv. mode less than M
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
-- return from normal trap --
|
||
|
else
|
||
|
if (CPU_EXTENSION_RISCV_U = true) then
|
||
|
csr.privilege <= csr.mstatus_mpp; -- restore previous privilege mode
|
||
|
csr.mstatus_mpp <= priv_mode_u_c; -- set to least-privileged mode that is supported
|
||
|
if (csr.mstatus_mpp /= priv_mode_m_c) then
|
||
|
csr.mstatus_mprv <= '0'; -- clear if return to priv. mode less than M
|
||
|
end if;
|
||
|
end if;
|
||
|
csr.mstatus_mie <= csr.mstatus_mpie; -- restore machine-mode IRQ enable flag
|
||
|
csr.mstatus_mpie <= '1';
|
||
|
end if;
|
||
|
|
||
|
end if;
|
||
|
|
||
|
-- ********************************************************************************
|
||
|
-- Override - hardwire/terminate unimplemented registers/bits
|
||
|
-- ********************************************************************************
|
||
|
|
||
|
-- hardwired bits --
|
||
|
csr.mcountinhibit(1) <= '0'; -- time[h] not implemented
|
||
|
|
||
|
-- no base counters --
|
||
|
if (CPU_EXTENSION_RISCV_Zicntr = false) then
|
||
|
csr.mcountinhibit(2 downto 0) <= (others => '0');
|
||
|
end if;
|
||
|
|
||
|
-- no hardware performance monitors --
|
||
|
if (CPU_EXTENSION_RISCV_Zihpm = false) then
|
||
|
csr.mcountinhibit(15 downto 3) <= (others => '0');
|
||
|
end if;
|
||
|
|
||
|
-- no counters at all --
|
||
|
if (CPU_EXTENSION_RISCV_Zicntr = false) and (CPU_EXTENSION_RISCV_Zihpm = false) then
|
||
|
csr.mcounteren <= '0';
|
||
|
end if;
|
||
|
|
||
|
-- no user mode --
|
||
|
if (CPU_EXTENSION_RISCV_U = false) then
|
||
|
csr.privilege <= priv_mode_m_c;
|
||
|
csr.mstatus_mpp <= priv_mode_m_c;
|
||
|
csr.mstatus_mprv <= '0';
|
||
|
csr.mstatus_tw <= '0';
|
||
|
csr.dcsr_ebreaku <= '0';
|
||
|
csr.dcsr_prv <= '0';
|
||
|
csr.mcounteren <= '0';
|
||
|
end if;
|
||
|
|
||
|
-- no debug mode --
|
||
|
if (CPU_EXTENSION_RISCV_Sdext = false) then
|
||
|
csr.dcsr_ebreakm <= '0';
|
||
|
csr.dcsr_step <= '0';
|
||
|
csr.dcsr_ebreaku <= '0';
|
||
|
csr.dcsr_prv <= priv_mode_m_c;
|
||
|
csr.dcsr_cause <= (others => '0');
|
||
|
csr.dpc <= (others => '0');
|
||
|
csr.dscratch0 <= (others => '0');
|
||
|
end if;
|
||
|
|
||
|
-- no trigger module --
|
||
|
if (CPU_EXTENSION_RISCV_Sdtrig = false) then
|
||
|
csr.tdata1_execute <= '0';
|
||
|
csr.tdata1_action <= '0';
|
||
|
csr.tdata1_dmode <= '0';
|
||
|
csr.tdata2 <= (others => '0');
|
||
|
end if;
|
||
|
|
||
|
end if;
|
||
|
end process csr_write_access;
|
||
|
|
||
|
-- effective privilege mode is MACHINE when in debug mode --
|
||
|
csr.privilege_eff <= priv_mode_m_c when (debug_ctrl.running = '1') else csr.privilege;
|
||
|
|
||
|
|
||
|
-- CSR Read Access ------------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
csr_read_access: process(csr, trap_ctrl.irq_pnd, hpmevent_rd, cnt_lo_rd, cnt_hi_rd)
|
||
|
begin
|
||
|
csr_rdata <= (others => '0'); -- default
|
||
|
case csr.raddr is
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- machine trap setup --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when csr_mstatus_c => -- machine status register - low word
|
||
|
csr_rdata(03) <= csr.mstatus_mie;
|
||
|
csr_rdata(07) <= csr.mstatus_mpie;
|
||
|
csr_rdata(12 downto 11) <= (others => csr.mstatus_mpp);
|
||
|
csr_rdata(17) <= csr.mstatus_mprv;
|
||
|
csr_rdata(21) <= csr.mstatus_tw and bool_to_ulogic_f(CPU_EXTENSION_RISCV_U);
|
||
|
|
||
|
-- when csr_mstatush_c => csr_rdata <= (others => '0'); -- machine status register - high word - hardwired to zero
|
||
|
|
||
|
when csr_misa_c => -- ISA and extensions
|
||
|
csr_rdata(00) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_A); -- A CPU extension
|
||
|
csr_rdata(01) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_B); -- B CPU extension
|
||
|
csr_rdata(02) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_C); -- C CPU extension
|
||
|
csr_rdata(04) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E); -- E CPU extension
|
||
|
csr_rdata(08) <= bool_to_ulogic_f(not CPU_EXTENSION_RISCV_E); -- I CPU extension (if not E)
|
||
|
csr_rdata(12) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_M); -- M CPU extension
|
||
|
csr_rdata(20) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_U); -- U CPU extension
|
||
|
csr_rdata(23) <= '1'; -- X CPU extension (non-standard extensions / NEORV32-specific)
|
||
|
csr_rdata(31 downto 30) <= "01"; -- MXL = 32
|
||
|
|
||
|
when csr_mie_c => -- machine interrupt-enable register
|
||
|
csr_rdata(03) <= csr.mie_msi;
|
||
|
csr_rdata(07) <= csr.mie_mti;
|
||
|
csr_rdata(11) <= csr.mie_mei;
|
||
|
csr_rdata(31 downto 16) <= csr.mie_firq;
|
||
|
|
||
|
when csr_mtvec_c => -- machine trap-handler base address
|
||
|
csr_rdata <= csr.mtvec;
|
||
|
|
||
|
when csr_mcounteren_c => -- machine counter enable register
|
||
|
if (CPU_EXTENSION_RISCV_U = true) then
|
||
|
if (CPU_EXTENSION_RISCV_Zicntr = true) then
|
||
|
csr_rdata(0) <= csr.mcounteren; -- cycle
|
||
|
csr_rdata(2) <= csr.mcounteren; -- instret
|
||
|
end if;
|
||
|
if (CPU_EXTENSION_RISCV_Zihpm = true) then
|
||
|
csr_rdata(15 downto 3) <= (others => csr.mcounteren); -- hpmcounter
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- machine configuration --
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- when csr_menvcfg_c => csr_rdata <= (others => '0'); -- hardwired to zero
|
||
|
-- when csr_menvcfgh_c => csr_rdata <= (others => '0'); -- hardwired to zero
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- machine trap handling --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when csr_mscratch_c => -- machine scratch register
|
||
|
csr_rdata <= csr.mscratch;
|
||
|
|
||
|
when csr_mepc_c => -- machine exception program counter
|
||
|
csr_rdata <= csr.mepc(XLEN-1 downto 1) & '0';
|
||
|
|
||
|
when csr_mcause_c => -- machine trap cause
|
||
|
csr_rdata(31) <= csr.mcause(5);
|
||
|
csr_rdata(4 downto 0) <= csr.mcause(4 downto 0);
|
||
|
|
||
|
when csr_mtval_c => -- machine trap value
|
||
|
csr_rdata <= csr.mtval;
|
||
|
|
||
|
when csr_mip_c => -- machine interrupt pending
|
||
|
csr_rdata(03) <= trap_ctrl.irq_pnd(irq_msi_irq_c);
|
||
|
csr_rdata(07) <= trap_ctrl.irq_pnd(irq_mti_irq_c);
|
||
|
csr_rdata(11) <= trap_ctrl.irq_pnd(irq_mei_irq_c);
|
||
|
csr_rdata(31 downto 16) <= trap_ctrl.irq_pnd(irq_firq_15_c downto irq_firq_0_c);
|
||
|
|
||
|
when csr_mtinst_c => -- machine trap instruction
|
||
|
csr_rdata <= csr.mtinst;
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- machine counter setup --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when csr_mcountinhibit_c => -- machine counter-inhibit register
|
||
|
if (CPU_EXTENSION_RISCV_Zicntr = true) then
|
||
|
csr_rdata(0) <= csr.mcountinhibit(0); -- [m]cycle[h]
|
||
|
csr_rdata(2) <= csr.mcountinhibit(2); -- [m]instret[h]
|
||
|
end if;
|
||
|
if (CPU_EXTENSION_RISCV_Zihpm = true) and (hpm_num_c > 0) then
|
||
|
for i in 3 to (hpm_num_c+3)-1 loop
|
||
|
csr_rdata(i) <= csr.mcountinhibit(i); -- [m]hpmcounter*[h]
|
||
|
end loop;
|
||
|
end if;
|
||
|
|
||
|
-- HPM event configuration --
|
||
|
when csr_mhpmevent3_c => if (hpm_num_c > 00) then csr_rdata <= hpmevent_rd(03); end if;
|
||
|
when csr_mhpmevent4_c => if (hpm_num_c > 01) then csr_rdata <= hpmevent_rd(04); end if;
|
||
|
when csr_mhpmevent5_c => if (hpm_num_c > 02) then csr_rdata <= hpmevent_rd(05); end if;
|
||
|
when csr_mhpmevent6_c => if (hpm_num_c > 03) then csr_rdata <= hpmevent_rd(06); end if;
|
||
|
when csr_mhpmevent7_c => if (hpm_num_c > 04) then csr_rdata <= hpmevent_rd(07); end if;
|
||
|
when csr_mhpmevent8_c => if (hpm_num_c > 05) then csr_rdata <= hpmevent_rd(08); end if;
|
||
|
when csr_mhpmevent9_c => if (hpm_num_c > 06) then csr_rdata <= hpmevent_rd(09); end if;
|
||
|
when csr_mhpmevent10_c => if (hpm_num_c > 07) then csr_rdata <= hpmevent_rd(10); end if;
|
||
|
when csr_mhpmevent11_c => if (hpm_num_c > 08) then csr_rdata <= hpmevent_rd(11); end if;
|
||
|
when csr_mhpmevent12_c => if (hpm_num_c > 09) then csr_rdata <= hpmevent_rd(12); end if;
|
||
|
when csr_mhpmevent13_c => if (hpm_num_c > 10) then csr_rdata <= hpmevent_rd(13); end if;
|
||
|
when csr_mhpmevent14_c => if (hpm_num_c > 11) then csr_rdata <= hpmevent_rd(14); end if;
|
||
|
when csr_mhpmevent15_c => if (hpm_num_c > 12) then csr_rdata <= hpmevent_rd(15); end if;
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- counters and timers --
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- low word --
|
||
|
when csr_mcycle_c | csr_cycle_c => if (CPU_EXTENSION_RISCV_Zicntr) then csr_rdata <= cnt_lo_rd(00); end if;
|
||
|
when csr_minstret_c | csr_instret_c => if (CPU_EXTENSION_RISCV_Zicntr) then csr_rdata <= cnt_lo_rd(02); end if;
|
||
|
when csr_mhpmcounter3_c | csr_hpmcounter3_c => if (hpm_num_c > 00) then csr_rdata <= cnt_lo_rd(03); end if;
|
||
|
when csr_mhpmcounter4_c | csr_hpmcounter4_c => if (hpm_num_c > 01) then csr_rdata <= cnt_lo_rd(04); end if;
|
||
|
when csr_mhpmcounter5_c | csr_hpmcounter5_c => if (hpm_num_c > 02) then csr_rdata <= cnt_lo_rd(05); end if;
|
||
|
when csr_mhpmcounter6_c | csr_hpmcounter6_c => if (hpm_num_c > 03) then csr_rdata <= cnt_lo_rd(06); end if;
|
||
|
when csr_mhpmcounter7_c | csr_hpmcounter7_c => if (hpm_num_c > 04) then csr_rdata <= cnt_lo_rd(07); end if;
|
||
|
when csr_mhpmcounter8_c | csr_hpmcounter8_c => if (hpm_num_c > 05) then csr_rdata <= cnt_lo_rd(08); end if;
|
||
|
when csr_mhpmcounter9_c | csr_hpmcounter9_c => if (hpm_num_c > 06) then csr_rdata <= cnt_lo_rd(09); end if;
|
||
|
when csr_mhpmcounter10_c | csr_hpmcounter10_c => if (hpm_num_c > 07) then csr_rdata <= cnt_lo_rd(10); end if;
|
||
|
when csr_mhpmcounter11_c | csr_hpmcounter11_c => if (hpm_num_c > 08) then csr_rdata <= cnt_lo_rd(11); end if;
|
||
|
when csr_mhpmcounter12_c | csr_hpmcounter12_c => if (hpm_num_c > 09) then csr_rdata <= cnt_lo_rd(12); end if;
|
||
|
when csr_mhpmcounter13_c | csr_hpmcounter13_c => if (hpm_num_c > 10) then csr_rdata <= cnt_lo_rd(13); end if;
|
||
|
when csr_mhpmcounter14_c | csr_hpmcounter14_c => if (hpm_num_c > 11) then csr_rdata <= cnt_lo_rd(14); end if;
|
||
|
when csr_mhpmcounter15_c | csr_hpmcounter15_c => if (hpm_num_c > 12) then csr_rdata <= cnt_lo_rd(15); end if;
|
||
|
|
||
|
-- high word --
|
||
|
when csr_mcycleh_c | csr_cycleh_c => if (CPU_EXTENSION_RISCV_Zicntr) then csr_rdata <= cnt_hi_rd(00); end if;
|
||
|
when csr_minstreth_c | csr_instreth_c => if (CPU_EXTENSION_RISCV_Zicntr) then csr_rdata <= cnt_hi_rd(02); end if;
|
||
|
when csr_mhpmcounter3h_c | csr_hpmcounter3h_c => if (hpm_num_c > 00) then csr_rdata <= cnt_hi_rd(03); end if;
|
||
|
when csr_mhpmcounter4h_c | csr_hpmcounter4h_c => if (hpm_num_c > 01) then csr_rdata <= cnt_hi_rd(04); end if;
|
||
|
when csr_mhpmcounter5h_c | csr_hpmcounter5h_c => if (hpm_num_c > 02) then csr_rdata <= cnt_hi_rd(05); end if;
|
||
|
when csr_mhpmcounter6h_c | csr_hpmcounter6h_c => if (hpm_num_c > 03) then csr_rdata <= cnt_hi_rd(06); end if;
|
||
|
when csr_mhpmcounter7h_c | csr_hpmcounter7h_c => if (hpm_num_c > 04) then csr_rdata <= cnt_hi_rd(07); end if;
|
||
|
when csr_mhpmcounter8h_c | csr_hpmcounter8h_c => if (hpm_num_c > 05) then csr_rdata <= cnt_hi_rd(08); end if;
|
||
|
when csr_mhpmcounter9h_c | csr_hpmcounter9h_c => if (hpm_num_c > 06) then csr_rdata <= cnt_hi_rd(09); end if;
|
||
|
when csr_mhpmcounter10h_c | csr_hpmcounter10h_c => if (hpm_num_c > 07) then csr_rdata <= cnt_hi_rd(10); end if;
|
||
|
when csr_mhpmcounter11h_c | csr_hpmcounter11h_c => if (hpm_num_c > 08) then csr_rdata <= cnt_hi_rd(11); end if;
|
||
|
when csr_mhpmcounter12h_c | csr_hpmcounter12h_c => if (hpm_num_c > 09) then csr_rdata <= cnt_hi_rd(12); end if;
|
||
|
when csr_mhpmcounter13h_c | csr_hpmcounter13h_c => if (hpm_num_c > 10) then csr_rdata <= cnt_hi_rd(13); end if;
|
||
|
when csr_mhpmcounter14h_c | csr_hpmcounter14h_c => if (hpm_num_c > 11) then csr_rdata <= cnt_hi_rd(14); end if;
|
||
|
when csr_mhpmcounter15h_c | csr_hpmcounter15h_c => if (hpm_num_c > 12) then csr_rdata <= cnt_hi_rd(15); end if;
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- machine information registers --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when csr_mvendorid_c => csr_rdata <= VENDOR_ID; -- vendor's JEDEC ID
|
||
|
when csr_marchid_c => csr_rdata(4 downto 0) <= "10011"; -- architecture ID - official RISC-V open-source arch ID
|
||
|
when csr_mimpid_c => csr_rdata <= hw_version_c; -- implementation ID -- NEORV32 hardware version
|
||
|
when csr_mhartid_c => csr_rdata <= HART_ID; -- hardware thread ID
|
||
|
-- when csr_mconfigptr_c => csr_rdata <= (others => '0'); -- machine configuration pointer register - hardwired to zero
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- debug mode CSRs --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when csr_dcsr_c => if (CPU_EXTENSION_RISCV_Sdext) then csr_rdata <= csr.dcsr_rd; end if; -- debug mode control and status
|
||
|
when csr_dpc_c => if (CPU_EXTENSION_RISCV_Sdext) then csr_rdata <= csr.dpc; end if; -- debug mode program counter
|
||
|
when csr_dscratch0_c => if (CPU_EXTENSION_RISCV_Sdext) then csr_rdata <= csr.dscratch0; end if; -- debug mode scratch register 0
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- trigger module CSRs --
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- when csr_tselect_c => if (CPU_EXTENSION_RISCV_Sdtrig) then csr_rdata <= (others => '0'); end if; -- hardwired to zero = only 1 trigger available
|
||
|
when csr_tdata1_c => if (CPU_EXTENSION_RISCV_Sdtrig) then csr_rdata <= csr.tdata1_rd; end if; -- match control
|
||
|
when csr_tdata2_c => if (CPU_EXTENSION_RISCV_Sdtrig) then csr_rdata <= csr.tdata2; end if; -- address-compare
|
||
|
when csr_tinfo_c => -- trigger information
|
||
|
if (CPU_EXTENSION_RISCV_Sdtrig) then
|
||
|
csr_rdata(31 downto 24) <= x"01"; -- Sdtrig ISA spec. version 1.0
|
||
|
csr_rdata(15 downto 00) <= x"0006"; -- mcontrol6 type trigger only
|
||
|
end if;
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- NEORV32-specific (RISC-V "custom") read-only CSRs --
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- machine extended ISA extensions information --
|
||
|
when csr_mxisa_c =>
|
||
|
-- extended ISA (sub-)extensions --
|
||
|
csr_rdata(00) <= '1'; -- Zicsr: CSR access (always enabled)
|
||
|
csr_rdata(01) <= '1'; -- Zifencei: instruction stream sync. (always enabled)
|
||
|
csr_rdata(02) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zmmul); -- Zmmul: mul/div
|
||
|
csr_rdata(03) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zxcfu); -- Zxcfu: custom RISC-V instructions
|
||
|
csr_rdata(04) <= '0'; -- reserved
|
||
|
csr_rdata(05) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zfinx); -- Zfinx: FPU using x registers
|
||
|
csr_rdata(06) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zicond); -- Zicond: integer conditional operations
|
||
|
csr_rdata(07) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zicntr); -- Zicntr: base counters
|
||
|
csr_rdata(08) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Smpmp); -- Smpmp: physical memory protection
|
||
|
csr_rdata(09) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zihpm); -- Zihpm: hardware performance monitors
|
||
|
csr_rdata(10) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Sdext); -- Sdext: RISC-V (external) debug mode
|
||
|
csr_rdata(11) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Sdtrig); -- Sdtrig: trigger module
|
||
|
-- misc --
|
||
|
csr_rdata(20) <= bool_to_ulogic_f(is_simulation_c); -- is this a simulation?
|
||
|
-- tuning options --
|
||
|
csr_rdata(29) <= bool_to_ulogic_f(REGFILE_HW_RST); -- full hardware reset of register file
|
||
|
csr_rdata(30) <= bool_to_ulogic_f(FAST_MUL_EN); -- DSP-based multiplication (M extensions only)
|
||
|
csr_rdata(31) <= bool_to_ulogic_f(FAST_SHIFT_EN); -- parallel logic for shifts (barrel shifters)
|
||
|
|
||
|
-- --------------------------------------------------------------------
|
||
|
-- undefined/unavailable (or implemented externally) --
|
||
|
-- --------------------------------------------------------------------
|
||
|
when others => NULL; -- read as zero
|
||
|
|
||
|
end case;
|
||
|
end process csr_read_access;
|
||
|
|
||
|
|
||
|
-- CSR read-data gate --
|
||
|
csr_read_reg: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
csr.re <= '0';
|
||
|
csr.rdata <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
csr.re <= csr.re_nxt;
|
||
|
if (csr.re = '1') then
|
||
|
csr.rdata <= csr_rdata or xcsr_rdata;
|
||
|
else
|
||
|
csr.rdata <= (others => '0'); -- output zero if no valid CSR read access operation
|
||
|
end if;
|
||
|
end if;
|
||
|
end process csr_read_reg;
|
||
|
|
||
|
-- CSR read data output --
|
||
|
csr_rdata_o <= csr.rdata;
|
||
|
|
||
|
|
||
|
-- ****************************************************************************************************************************
|
||
|
-- CPU Counters (Standard Counters and Hardware Performance Monitors)
|
||
|
-- ****************************************************************************************************************************
|
||
|
|
||
|
-- Counter CSRs ---------------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
-- write enable decoder --
|
||
|
cnt_we: process(csr)
|
||
|
begin
|
||
|
cnt.we_lo <= (others => '0');
|
||
|
cnt.we_hi <= (others => '0');
|
||
|
-- [NOTE] no need to check bits 6:4 of the address as they're always zero (checked by illegal CSR logic)
|
||
|
if (csr.we = '1') and (csr.addr(11 downto 8) = csr_mcycle_c(11 downto 8)) then
|
||
|
if (csr.addr(7) = '0') then -- low word
|
||
|
cnt.we_lo(to_integer(unsigned(csr.addr(3 downto 0)))) <= '1';
|
||
|
else -- high word
|
||
|
cnt.we_hi(to_integer(unsigned(csr.addr(3 downto 0)))) <= '1';
|
||
|
end if;
|
||
|
end if;
|
||
|
end process cnt_we;
|
||
|
|
||
|
|
||
|
-- hardware counters --
|
||
|
cpu_counter_gen:
|
||
|
for i in 0 to 2+hpm_num_c generate
|
||
|
|
||
|
-- counter CSRs --
|
||
|
cnt_reg: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
cnt.lo(i) <= (others => '0');
|
||
|
cnt.ovf(i) <= (others => '0');
|
||
|
cnt.hi(i) <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
-- low word --
|
||
|
if (cnt.we_lo(i) = '1') then
|
||
|
cnt.lo(i) <= csr.wdata;
|
||
|
else
|
||
|
cnt.lo(i) <= cnt.nxt(i)(XLEN-1 downto 0);
|
||
|
end if;
|
||
|
cnt.ovf(i)(0) <= cnt.nxt(i)(XLEN);
|
||
|
-- high word --
|
||
|
if (cnt.we_hi(i) = '1') then
|
||
|
cnt.hi(i) <= csr.wdata;
|
||
|
else
|
||
|
cnt.hi(i) <= std_ulogic_vector(unsigned(cnt.hi(i)) + unsigned(cnt.ovf(i)));
|
||
|
end if;
|
||
|
end if;
|
||
|
end process cnt_reg;
|
||
|
|
||
|
-- low-word increment --
|
||
|
cnt.nxt(i) <= std_ulogic_vector(unsigned('0' & cnt.lo(i)) + 1) when (cnt.inc(i) = '1') else
|
||
|
std_ulogic_vector(unsigned('0' & cnt.lo(i)) + 0);
|
||
|
|
||
|
end generate;
|
||
|
|
||
|
|
||
|
-- read-back --
|
||
|
cnt_connect: process(cnt)
|
||
|
begin
|
||
|
cnt_lo_rd <= (others => (others => '0'));
|
||
|
cnt_hi_rd <= (others => (others => '0'));
|
||
|
-- base counters --
|
||
|
if (CPU_EXTENSION_RISCV_Zicntr = true) then
|
||
|
cnt_lo_rd(0) <= cnt.lo(0); -- cycle
|
||
|
cnt_hi_rd(0) <= cnt.hi(0); -- cycleh
|
||
|
cnt_lo_rd(2) <= cnt.lo(2); -- instret
|
||
|
cnt_hi_rd(2) <= cnt.hi(2); -- instreth
|
||
|
end if;
|
||
|
-- hpm counters --
|
||
|
if (CPU_EXTENSION_RISCV_Zihpm = true) and (hpm_num_c > 0) then
|
||
|
for i in 3 to (hpm_num_c+3)-1 loop
|
||
|
if (hpm_cnt_lo_width_c > 0) then -- constrain low word size
|
||
|
cnt_lo_rd(i)(hpm_cnt_lo_width_c-1 downto 0) <= cnt.lo(i)(hpm_cnt_lo_width_c-1 downto 0);
|
||
|
end if;
|
||
|
if (hpm_cnt_hi_width_c > 0) then -- constrain high word size
|
||
|
cnt_hi_rd(i)(hpm_cnt_hi_width_c-1 downto 0) <= cnt.hi(i)(hpm_cnt_hi_width_c-1 downto 0);
|
||
|
end if;
|
||
|
end loop;
|
||
|
end if;
|
||
|
end process cnt_connect;
|
||
|
|
||
|
|
||
|
-- Hardware Performance Monitors (HPM) - Counter Event Configuration CSRs -----------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
hpmevent_gen_enable:
|
||
|
if CPU_EXTENSION_RISCV_Zihpm and (hpm_num_c > 0) generate
|
||
|
|
||
|
-- write enable decoder --
|
||
|
hpmevent_write: process(csr)
|
||
|
begin
|
||
|
hpmevent_we <= (others => '0');
|
||
|
-- [NOTE] no need to check bit 4 of the address as it's always zero (checked by illegal CSR logic)
|
||
|
if (csr.addr(11 downto 5) = csr_mcountinhibit_c(11 downto 5)) and (csr.we = '1') then
|
||
|
hpmevent_we(to_integer(unsigned(csr.addr(3 downto 0)))) <= '1';
|
||
|
end if;
|
||
|
end process hpmevent_write;
|
||
|
|
||
|
-- event registers --
|
||
|
hpmevent_reg_gen:
|
||
|
for i in 3 to (hpm_num_c+3)-1 generate
|
||
|
hpmevent_reg: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
hpmevent_cfg(i) <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
if (hpmevent_we(i) = '1') then
|
||
|
hpmevent_cfg(i) <= csr.wdata(hpmcnt_event_size_c-1 downto 0);
|
||
|
end if;
|
||
|
hpmevent_cfg(i)(hpmcnt_event_tm_c) <= '0'; -- time, unused/reserved
|
||
|
end if;
|
||
|
end process hpmevent_reg;
|
||
|
-- read-back --
|
||
|
hpmevent_rd(i)(XLEN-1 downto hpmcnt_event_size_c) <= (others => '0');
|
||
|
hpmevent_rd(i)(hpmcnt_event_size_c-1 downto 0) <= hpmevent_cfg(i);
|
||
|
end generate;
|
||
|
|
||
|
-- terminate unused entries --
|
||
|
hpmevent_terminate_gen:
|
||
|
for i in hpm_num_c+3 to 15 generate
|
||
|
hpmevent_cfg(i) <= (others => '0');
|
||
|
hpmevent_rd(i) <= (others => '0');
|
||
|
end generate;
|
||
|
|
||
|
end generate;
|
||
|
|
||
|
|
||
|
-- no HPMs implemented --
|
||
|
hpmevent_gen_disable:
|
||
|
if (not CPU_EXTENSION_RISCV_Zihpm) or (hpm_num_c = 0) generate
|
||
|
hpmevent_cfg <= (others => (others => '0'));
|
||
|
hpmevent_rd <= (others => (others => '0'));
|
||
|
end generate;
|
||
|
|
||
|
|
||
|
-- Counter Increment Control (Trigger Events) ---------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
counter_event: process(rstn_i, clk_i)
|
||
|
begin -- increment if an enabled event fires; do not increment if CPU is in debug mode or if counter is inhibited
|
||
|
if (rstn_i = '0') then
|
||
|
cnt.inc <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
cnt.inc <= (others => '0'); -- default
|
||
|
-- base counters --
|
||
|
if (CPU_EXTENSION_RISCV_Zicntr = true) then
|
||
|
cnt.inc(0) <= cnt_event(hpmcnt_event_cy_c) and (not csr.mcountinhibit(0)) and (not debug_ctrl.running);
|
||
|
cnt.inc(2) <= cnt_event(hpmcnt_event_ir_c) and (not csr.mcountinhibit(2)) and (not debug_ctrl.running);
|
||
|
end if;
|
||
|
-- hpm counters --
|
||
|
if (CPU_EXTENSION_RISCV_Zihpm = true) and (hpm_num_c > 0) then
|
||
|
for i in 3 to (hpm_num_c+3)-1 loop
|
||
|
cnt.inc(i) <= or_reduce_f(cnt_event and hpmevent_cfg(i)) and (not csr.mcountinhibit(i)) and (not debug_ctrl.running);
|
||
|
end loop;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process counter_event;
|
||
|
|
||
|
-- RISC-V-specific base counter events (for HPM and base counters) --
|
||
|
cnt_event(hpmcnt_event_cy_c) <= '1' when (sleep_mode = '0') else '0'; -- cycle: active cycle
|
||
|
cnt_event(hpmcnt_event_tm_c) <= '0'; -- time: unused/reserved
|
||
|
cnt_event(hpmcnt_event_ir_c) <= '1' when (execute_engine.state = EXECUTE) else '0'; -- instret: retired (==executed) instruction
|
||
|
|
||
|
-- NEORV32-specific counter events (for HPM counters only) --
|
||
|
cnt_event(hpmcnt_event_compr_c) <= '1' when (execute_engine.state = EXECUTE) and (execute_engine.is_ci = '1') else '0'; -- executed compressed instruction
|
||
|
cnt_event(hpmcnt_event_wait_dis_c) <= '1' when (execute_engine.state = DISPATCH) and (issue_engine.valid = "00") else '0'; -- instruction dispatch wait cycle
|
||
|
cnt_event(hpmcnt_event_wait_alu_c) <= '1' when (execute_engine.state = ALU_WAIT) else '0'; -- multi-cycle ALU co-processor wait cycle
|
||
|
|
||
|
cnt_event(hpmcnt_event_branch_c) <= '1' when (execute_engine.state = BRANCH) else '0'; -- executed branch instruction
|
||
|
cnt_event(hpmcnt_event_branched_c) <= '1' when (execute_engine.state = BRANCHED) else '0'; -- control flow transfer
|
||
|
|
||
|
cnt_event(hpmcnt_event_load_c) <= '1' when (ctrl.lsu_req = '1') and (ctrl.lsu_rw = '0') else '0'; -- executed load operation
|
||
|
cnt_event(hpmcnt_event_store_c) <= '1' when (ctrl.lsu_req = '1') and (ctrl.lsu_rw = '1') else '0'; -- executed store operation
|
||
|
cnt_event(hpmcnt_event_wait_lsu_c) <= '1' when (ctrl.lsu_req = '0') and (execute_engine.state = MEM_WAIT) else '0'; -- load/store unit memory wait cycle
|
||
|
|
||
|
cnt_event(hpmcnt_event_trap_c) <= '1' when (trap_ctrl.env_enter = '1') else '0'; -- entered trap
|
||
|
|
||
|
|
||
|
-- ****************************************************************************************************************************
|
||
|
-- CPU Debug Mode (Part of the On-Chip Debugger)
|
||
|
-- ****************************************************************************************************************************
|
||
|
|
||
|
-- Debug Control --------------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
debug_mode_enable:
|
||
|
if CPU_EXTENSION_RISCV_Sdext generate
|
||
|
|
||
|
debug_control: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
debug_ctrl.running <= '0';
|
||
|
elsif rising_edge(clk_i) then
|
||
|
if (debug_ctrl.running = '0') then -- debug mode OFFLINE
|
||
|
if (trap_ctrl.env_enter = '1') and (trap_ctrl.cause(5) = '1') then -- waiting for entry event
|
||
|
debug_ctrl.running <= '1';
|
||
|
end if;
|
||
|
else -- debug mode ONLINE
|
||
|
if (trap_ctrl.env_exit = '1') then -- waiting for exit event
|
||
|
debug_ctrl.running <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process debug_control;
|
||
|
|
||
|
-- debug mode entry triggers --
|
||
|
debug_ctrl.trig_hw <= trap_ctrl.hwtrig and (not debug_ctrl.running) and csr.tdata1_action and csr.tdata1_dmode; -- enter debug mode by HW trigger module request (only valid if dmode = 1)
|
||
|
debug_ctrl.trig_break <= trap_ctrl.ebreak and (debug_ctrl.running or -- re-enter debug mode
|
||
|
(( csr.privilege) and csr.dcsr_ebreakm) or -- enabled goto-debug-mode in machine mode on "ebreak"
|
||
|
((not csr.privilege) and csr.dcsr_ebreaku)); -- enabled goto-debug-mode in user mode on "ebreak"
|
||
|
debug_ctrl.trig_halt <= db_halt_req_i and (not debug_ctrl.running); -- external halt request (if not halted already)
|
||
|
debug_ctrl.trig_step <= csr.dcsr_step and (not debug_ctrl.running); -- single-step mode (trigger when NOT CURRENTLY in debug mode)
|
||
|
|
||
|
end generate;
|
||
|
|
||
|
-- Sdext ISA extension not enabled --
|
||
|
debug_mode_disable:
|
||
|
if not CPU_EXTENSION_RISCV_Sdext generate
|
||
|
debug_ctrl.running <= '0';
|
||
|
debug_ctrl.trig_hw <= '0';
|
||
|
debug_ctrl.trig_break <= '0';
|
||
|
debug_ctrl.trig_halt <= '0';
|
||
|
debug_ctrl.trig_step <= '0';
|
||
|
end generate;
|
||
|
|
||
|
|
||
|
-- Debug Control and Status Register (dcsr) - Read-Back -----------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
csr.dcsr_rd(31 downto 28) <= "0100"; -- xdebugver: external debug support compatible to spec. version 1.0
|
||
|
csr.dcsr_rd(27 downto 16) <= (others => '0'); -- reserved
|
||
|
csr.dcsr_rd(15) <= csr.dcsr_ebreakm; -- ebreakm: what happens on ebreak in m-mode? (normal trap OR debug-enter)
|
||
|
csr.dcsr_rd(14) <= '0'; -- reserved
|
||
|
csr.dcsr_rd(13) <= '0'; -- ebreaks: supervisor mode not implemented
|
||
|
csr.dcsr_rd(12) <= csr.dcsr_ebreaku when (CPU_EXTENSION_RISCV_U = true) else '0'; -- ebreaku: what happens on ebreak in u-mode? (normal trap OR debug-enter)
|
||
|
csr.dcsr_rd(11) <= '0'; -- stepie: interrupts are disabled during single-stepping
|
||
|
csr.dcsr_rd(10) <= '1'; -- stopcount: standard counters and HPMs are stopped when in debug mode
|
||
|
csr.dcsr_rd(09) <= '0'; -- stoptime: timers increment as usual
|
||
|
csr.dcsr_rd(08 downto 06) <= csr.dcsr_cause; -- debug mode entry cause
|
||
|
csr.dcsr_rd(05) <= '0'; -- reserved
|
||
|
csr.dcsr_rd(04) <= '1'; -- mprven: mstatus.mprv is also evaluated in debug mode
|
||
|
csr.dcsr_rd(03) <= '0'; -- nmip: no pending non-maskable interrupt
|
||
|
csr.dcsr_rd(02) <= csr.dcsr_step; -- step: single-step mode
|
||
|
csr.dcsr_rd(01 downto 00) <= (others => csr.dcsr_prv); -- prv: privilege mode when debug mode was entered
|
||
|
|
||
|
|
||
|
-- ****************************************************************************************************************************
|
||
|
-- Hardware Trigger Module (Part of the On-Chip Debugger)
|
||
|
-- ****************************************************************************************************************************
|
||
|
trigger_module_enable:
|
||
|
if CPU_EXTENSION_RISCV_Sdtrig generate
|
||
|
|
||
|
-- trigger on instruction address match (trigger right BEFORE execution) --
|
||
|
hw_trigger_match <= '1' when (csr.tdata1_execute = '1') and -- trigger enabled to match on instruction address
|
||
|
(hw_trigger_fired = '0') and -- trigger has not fired yet
|
||
|
(csr.tdata2(XLEN-1 downto 1) = execute_engine.next_pc(XLEN-1 downto 1)) -- address match
|
||
|
else '0';
|
||
|
|
||
|
-- status flag - set when trigger has fired --
|
||
|
hw_trigger_exception: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
hw_trigger_fired <= '0';
|
||
|
elsif rising_edge(clk_i) then
|
||
|
if (hw_trigger_match = '1') and (trap_ctrl.exc_buf(exc_ebreak_c) = '1') then -- trigger has fired and breakpoint exception is pending
|
||
|
hw_trigger_fired <= '1';
|
||
|
elsif (csr.tdata1_hit_clr = '1') then
|
||
|
hw_trigger_fired <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
end process hw_trigger_exception;
|
||
|
|
||
|
end generate;
|
||
|
|
||
|
-- Sdtrig ISA extension not enabled --
|
||
|
trigger_module_disable:
|
||
|
if not CPU_EXTENSION_RISCV_Sdtrig generate
|
||
|
hw_trigger_match <= '0';
|
||
|
hw_trigger_fired <= '0';
|
||
|
end generate;
|
||
|
|
||
|
|
||
|
-- Match Control CSR (mcontrol6 @ tdata1) - Read-Back -------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
csr.tdata1_rd(31 downto 28) <= x"6"; -- type: address match trigger (mcontrol6)
|
||
|
csr.tdata1_rd(27) <= csr.tdata1_dmode; -- dmode: set to ignore machine-mode write access to tdata* CSRs
|
||
|
csr.tdata1_rd(26) <= '0'; -- uncertain: trigger satisfies the configured conditions
|
||
|
csr.tdata1_rd(25) <= '0'; -- hit1: hardwired to zero
|
||
|
csr.tdata1_rd(24) <= '0'; -- vs: VS-mode not implemented
|
||
|
csr.tdata1_rd(23) <= '0'; -- vu: VU-mode not implemented
|
||
|
csr.tdata1_rd(22) <= hw_trigger_fired; -- hit0: set when trigger has fired
|
||
|
csr.tdata1_rd(21) <= '0'; -- select: only address matching is supported
|
||
|
csr.tdata1_rd(20 downto 19) <= "00"; -- reserved
|
||
|
csr.tdata1_rd(18 downto 16) <= "000"; -- size: match accesses of any size
|
||
|
csr.tdata1_rd(15 downto 12) <= "000" & csr.tdata1_action; -- action = 1: enter debug mode on trigger, action = 0: ebreak exception on trigger
|
||
|
csr.tdata1_rd(11) <= '0'; -- chain: chaining not supported - there is only one trigger
|
||
|
csr.tdata1_rd(10 downto 07) <= "0000"; -- match: equal-match only
|
||
|
csr.tdata1_rd(6) <= '1'; -- m: trigger always enabled when in machine-mode
|
||
|
csr.tdata1_rd(5) <= '0'; -- uncertainen: hardwired to zero
|
||
|
csr.tdata1_rd(4) <= '0'; -- s: supervisor-mode not supported
|
||
|
csr.tdata1_rd(3) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_U); -- u: trigger always enabled when in user-mode (if implemented)
|
||
|
csr.tdata1_rd(2) <= csr.tdata1_execute; -- execute: enable trigger on instruction match
|
||
|
csr.tdata1_rd(1) <= '0'; -- store: store address or data matching not supported
|
||
|
csr.tdata1_rd(0) <= '0'; -- load: load address or data matching not supported
|
||
|
|
||
|
|
||
|
end neorv32_cpu_control_rtl;
|