neorv32/rtl/core/neorv32_cpu_control.vhd

2457 lines
134 KiB
VHDL
Raw Permalink Normal View History

2024-02-24 08:25:27 +00:00
-- #################################################################################################
-- # << 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;