Line 95... |
Line 95... |
end neorv32_cpu_control;
|
end neorv32_cpu_control;
|
|
|
architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
|
architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
|
|
|
-- instruction fetch enginge --
|
-- instruction fetch enginge --
|
type fetch_engine_state_t is (IFETCH_RESET, IFETCH_0, IFETCH_1, IFETCH_2, IFETCH_3);
|
type fetch_engine_state_t is (IFETCH_RESET, IFETCH_0, IFETCH_1, IFETCH_2);
|
type fetch_engine_t is record
|
type fetch_engine_t is record
|
state : fetch_engine_state_t;
|
state : fetch_engine_state_t;
|
state_nxt : fetch_engine_state_t;
|
state_nxt : fetch_engine_state_t;
|
i_buf : std_ulogic_vector(33 downto 0);
|
i_buf : std_ulogic_vector(33 downto 0);
|
i_buf_nxt : std_ulogic_vector(33 downto 0);
|
i_buf_nxt : std_ulogic_vector(33 downto 0);
|
i_buf2 : std_ulogic_vector(33 downto 0);
|
i_buf2 : std_ulogic_vector(33 downto 0);
|
i_buf2_nxt : std_ulogic_vector(33 downto 0);
|
i_buf2_nxt : std_ulogic_vector(33 downto 0);
|
ci_reg : std_ulogic_vector(17 downto 0);
|
ci_input : std_ulogic_vector(15 downto 0); -- input to compressed instr. decoder
|
ci_reg_nxt : std_ulogic_vector(17 downto 0);
|
|
i_buf_state : std_ulogic_vector(01 downto 0);
|
i_buf_state : std_ulogic_vector(01 downto 0);
|
i_buf_state_nxt : std_ulogic_vector(01 downto 0);
|
i_buf_state_nxt : std_ulogic_vector(01 downto 0);
|
pc_real : std_ulogic_vector(data_width_c-1 downto 0);
|
pc_real : std_ulogic_vector(data_width_c-1 downto 0);
|
pc_real_add : std_ulogic_vector(data_width_c-1 downto 0);
|
pc_real_add : std_ulogic_vector(data_width_c-1 downto 0);
|
pc_fetch : std_ulogic_vector(data_width_c-1 downto 0);
|
pc_fetch : std_ulogic_vector(data_width_c-1 downto 0);
|
pc_fetch_add : std_ulogic_vector(data_width_c-1 downto 0);
|
pc_fetch_add : std_ulogic_vector(data_width_c-1 downto 0);
|
ci_return : std_ulogic;
|
|
ci_return_nxt : std_ulogic;
|
|
reset : std_ulogic;
|
reset : std_ulogic;
|
bus_err_ack : std_ulogic;
|
bus_err_ack : std_ulogic;
|
end record;
|
end record;
|
signal fetch_engine : fetch_engine_t;
|
signal fetch_engine : fetch_engine_t;
|
|
|
Line 124... |
Line 121... |
signal ci_instr32 : std_ulogic_vector(31 downto 0);
|
signal ci_instr32 : std_ulogic_vector(31 downto 0);
|
signal ci_illegal : std_ulogic;
|
signal ci_illegal : std_ulogic;
|
|
|
-- instrucion prefetch buffer (IPB) --
|
-- instrucion prefetch buffer (IPB) --
|
type ipb_t is record
|
type ipb_t is record
|
wdata : std_ulogic_vector(34 downto 0);
|
wdata : std_ulogic_vector(35 downto 0);
|
rdata : std_ulogic_vector(34 downto 0);
|
rdata : std_ulogic_vector(35 downto 0);
|
waddr : std_ulogic_vector(31 downto 0);
|
waddr : std_ulogic_vector(31 downto 0);
|
raddr : std_ulogic_vector(31 downto 0);
|
raddr : std_ulogic_vector(31 downto 0);
|
status : std_ulogic;
|
status : std_ulogic;
|
free : std_ulogic;
|
free : std_ulogic;
|
avail : std_ulogic;
|
avail : std_ulogic;
|
Line 235... |
Line 232... |
neorv32_cpu_decompressor_inst_true:
|
neorv32_cpu_decompressor_inst_true:
|
if (CPU_EXTENSION_RISCV_C = true) generate
|
if (CPU_EXTENSION_RISCV_C = true) generate
|
neorv32_cpu_decompressor_inst: neorv32_cpu_decompressor
|
neorv32_cpu_decompressor_inst: neorv32_cpu_decompressor
|
port map (
|
port map (
|
-- instruction input --
|
-- instruction input --
|
ci_instr16_i => fetch_engine.ci_reg(15 downto 0), -- compressed instruction input
|
ci_instr16_i => fetch_engine.ci_input, -- compressed instruction input
|
-- instruction output --
|
-- instruction output --
|
ci_illegal_o => ci_illegal, -- is an illegal compressed instruction
|
ci_illegal_o => ci_illegal, -- is an illegal compressed instruction
|
ci_instr32_o => ci_instr32 -- 32-bit decompressed instruction
|
ci_instr32_o => ci_instr32 -- 32-bit decompressed instruction
|
);
|
);
|
end generate;
|
end generate;
|
Line 281... |
Line 278... |
end if;
|
end if;
|
--
|
--
|
fetch_engine.i_buf <= fetch_engine.i_buf_nxt;
|
fetch_engine.i_buf <= fetch_engine.i_buf_nxt;
|
fetch_engine.i_buf2 <= fetch_engine.i_buf2_nxt;
|
fetch_engine.i_buf2 <= fetch_engine.i_buf2_nxt;
|
fetch_engine.i_buf_state <= fetch_engine.i_buf_state_nxt;
|
fetch_engine.i_buf_state <= fetch_engine.i_buf_state_nxt;
|
--
|
|
fetch_engine.ci_reg <= fetch_engine.ci_reg_nxt;
|
|
fetch_engine.ci_return <= fetch_engine.ci_return_nxt;
|
|
end if;
|
end if;
|
end process fetch_engine_fsm_sync;
|
end process fetch_engine_fsm_sync;
|
|
|
-- PC output --
|
-- PC output --
|
fetch_pc_o <= fetch_engine.pc_fetch(data_width_c-1 downto 1) & '0';
|
fetch_pc_o <= fetch_engine.pc_fetch(data_width_c-1 downto 1) & '0';
|
|
|
|
|
-- Fetch Engine FSM Comb ------------------------------------------------------------------
|
-- Fetch Engine FSM Comb ------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
fetch_engine_fsm_comb: process(fetch_engine, csr, ipb, instr_i, bus_i_wait_i, ci_instr32, be_instr_i, ma_instr_i)
|
fetch_engine_fsm_comb: process(fetch_engine, csr, ipb, instr_i, bus_i_wait_i, ci_instr32, ci_illegal, be_instr_i, ma_instr_i)
|
begin
|
begin
|
-- arbiter defaults --
|
-- arbiter defaults --
|
|
bus_fast_ir <= '0';
|
fetch_engine.state_nxt <= fetch_engine.state;
|
fetch_engine.state_nxt <= fetch_engine.state;
|
fetch_engine.pc_fetch_add <= (others => '0');
|
fetch_engine.pc_fetch_add <= (others => '0');
|
fetch_engine.pc_real_add <= (others => '0');
|
fetch_engine.pc_real_add <= (others => '0');
|
bus_fast_ir <= '0';
|
|
fetch_engine.i_buf_nxt <= fetch_engine.i_buf;
|
fetch_engine.i_buf_nxt <= fetch_engine.i_buf;
|
fetch_engine.i_buf2_nxt <= fetch_engine.i_buf2;
|
fetch_engine.i_buf2_nxt <= fetch_engine.i_buf2;
|
fetch_engine.i_buf_state_nxt <= fetch_engine.i_buf_state;
|
fetch_engine.i_buf_state_nxt <= fetch_engine.i_buf_state;
|
fetch_engine.ci_reg_nxt <= fetch_engine.ci_reg;
|
fetch_engine.ci_input <= fetch_engine.i_buf2(15 downto 00);
|
fetch_engine.ci_return_nxt <= fetch_engine.ci_return;
|
|
fetch_engine.bus_err_ack <= '0';
|
fetch_engine.bus_err_ack <= '0';
|
|
|
-- instruction prefetch buffer interface --
|
-- instruction prefetch buffer interface --
|
ipb.we <= '0';
|
ipb.we <= '0';
|
ipb.clear <= '0';
|
ipb.clear <= '0';
|
ipb.wdata <= fetch_engine.i_buf2(33 downto 32) & '0' & fetch_engine.i_buf2(31 downto 0);
|
ipb.wdata <= '0' & fetch_engine.i_buf2(33 downto 32) & '0' & fetch_engine.i_buf2(31 downto 0);
|
ipb.waddr <= fetch_engine.pc_real(data_width_c-1 downto 1) & '0';
|
ipb.waddr <= fetch_engine.pc_real(data_width_c-1 downto 1) & '0';
|
|
|
-- state machine --
|
-- state machine --
|
case fetch_engine.state is
|
case fetch_engine.state is
|
|
|
when IFETCH_RESET => -- reset engine, prefetch buffer, get appilcation PC
|
when IFETCH_RESET => -- reset engine, prefetch buffer, get appilcation PC
|
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
fetch_engine.i_buf_state_nxt <= (others => '0');
|
fetch_engine.i_buf_state_nxt <= (others => '0');
|
fetch_engine.ci_return_nxt <= '0';
|
|
ipb.clear <= '1'; -- clear instruction prefetch buffer
|
ipb.clear <= '1'; -- clear instruction prefetch buffer
|
fetch_engine.state_nxt <= IFETCH_0;
|
fetch_engine.state_nxt <= IFETCH_0;
|
|
|
when IFETCH_0 => -- output current PC to bus system, request 32-bit word
|
when IFETCH_0 => -- output current PC to bus system, request 32-bit word
|
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
Line 346... |
Line 338... |
end if;
|
end if;
|
|
|
when IFETCH_2 => -- construct instruction word and issue
|
when IFETCH_2 => -- construct instruction word and issue
|
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
if (fetch_engine.pc_fetch(1) = '0') or (CPU_EXTENSION_RISCV_C = false) then -- 32-bit aligned
|
if (fetch_engine.pc_fetch(1) = '0') or (CPU_EXTENSION_RISCV_C = false) then -- 32-bit aligned
|
fetch_engine.ci_reg_nxt <= fetch_engine.i_buf2(33 downto 32) & fetch_engine.i_buf2(15 downto 00);
|
fetch_engine.ci_input <= fetch_engine.i_buf2(15 downto 00);
|
ipb.wdata <= fetch_engine.i_buf2(33 downto 32) & '0' & fetch_engine.i_buf2(31 downto 0);
|
|
|
|
if (fetch_engine.i_buf2(01 downto 00) = "11") or (CPU_EXTENSION_RISCV_C = false) then -- uncompressed
|
|
if (ipb.free = '1') then -- free entry in buffer?
|
if (ipb.free = '1') then -- free entry in buffer?
|
ipb.we <= '1';
|
ipb.we <= '1';
|
|
if (fetch_engine.i_buf2(01 downto 00) = "11") or (CPU_EXTENSION_RISCV_C = false) then -- uncompressed
|
|
ipb.wdata <= '0' & fetch_engine.i_buf2(33 downto 32) & '0' & fetch_engine.i_buf2(31 downto 0);
|
fetch_engine.pc_real_add <= std_ulogic_vector(to_unsigned(4, data_width_c));
|
fetch_engine.pc_real_add <= std_ulogic_vector(to_unsigned(4, data_width_c));
|
fetch_engine.pc_fetch_add <= std_ulogic_vector(to_unsigned(4, data_width_c));
|
fetch_engine.pc_fetch_add <= std_ulogic_vector(to_unsigned(4, data_width_c));
|
fetch_engine.state_nxt <= IFETCH_0;
|
fetch_engine.state_nxt <= IFETCH_0;
|
end if;
|
|
else -- compressed
|
else -- compressed
|
fetch_engine.ci_return_nxt <= '1'; -- come back here after issueing
|
ipb.wdata <= ci_illegal & fetch_engine.i_buf2(33 downto 32) & '1' & ci_instr32;
|
fetch_engine.state_nxt <= IFETCH_3;
|
fetch_engine.pc_fetch_add <= std_ulogic_vector(to_unsigned(2, data_width_c));
|
|
fetch_engine.pc_real_add <= std_ulogic_vector(to_unsigned(2, data_width_c));
|
|
fetch_engine.state_nxt <= IFETCH_2; -- try to get another 16-bit instruction word in next round
|
|
end if;
|
end if;
|
end if;
|
|
|
else -- 16-bit aligned
|
else -- 16-bit aligned
|
fetch_engine.ci_reg_nxt <= fetch_engine.i_buf2(33 downto 32) & fetch_engine.i_buf2(31 downto 16);
|
fetch_engine.ci_input <= fetch_engine.i_buf2(31 downto 16);
|
ipb.wdata <= fetch_engine.i_buf(33 downto 32) & '0' & fetch_engine.i_buf(15 downto 00) & fetch_engine.i_buf2(31 downto 16);
|
|
|
|
if (fetch_engine.i_buf2(17 downto 16) = "11") then -- uncompressed
|
|
if (ipb.free = '1') then -- free entry in buffer?
|
if (ipb.free = '1') then -- free entry in buffer?
|
ipb.we <= '1';
|
ipb.we <= '1';
|
|
if (fetch_engine.i_buf2(17 downto 16) = "11") then -- uncompressed
|
|
ipb.wdata <= '0' & fetch_engine.i_buf(33 downto 32) & '0' & fetch_engine.i_buf(15 downto 00) & fetch_engine.i_buf2(31 downto 16);
|
fetch_engine.pc_real_add <= std_ulogic_vector(to_unsigned(4, data_width_c));
|
fetch_engine.pc_real_add <= std_ulogic_vector(to_unsigned(4, data_width_c));
|
fetch_engine.pc_fetch_add <= std_ulogic_vector(to_unsigned(4, data_width_c));
|
fetch_engine.pc_fetch_add <= std_ulogic_vector(to_unsigned(4, data_width_c));
|
fetch_engine.state_nxt <= IFETCH_0;
|
fetch_engine.state_nxt <= IFETCH_0;
|
end if;
|
else -- uncompressed
|
else -- compressed
|
ipb.wdata <= ci_illegal & fetch_engine.i_buf(33 downto 32) & '1' & ci_instr32;
|
fetch_engine.ci_return_nxt <= '0'; -- start next fetch after issueing
|
|
fetch_engine.state_nxt <= IFETCH_3;
|
|
end if;
|
|
end if;
|
|
|
|
when IFETCH_3 => -- additional cycle for issueing decompressed instructions
|
|
-- ------------------------------------------------------------
|
|
if (ipb.free = '1') then -- free entry in buffer?
|
|
ipb.we <= '1';
|
|
ipb.wdata <= fetch_engine.ci_reg(17 downto 16) & '1' & ci_instr32;
|
|
fetch_engine.pc_fetch_add <= std_ulogic_vector(to_unsigned(2, data_width_c));
|
fetch_engine.pc_fetch_add <= std_ulogic_vector(to_unsigned(2, data_width_c));
|
fetch_engine.pc_real_add <= std_ulogic_vector(to_unsigned(2, data_width_c));
|
fetch_engine.pc_real_add <= std_ulogic_vector(to_unsigned(2, data_width_c));
|
if (fetch_engine.ci_return = '0') then
|
|
fetch_engine.state_nxt <= IFETCH_0;
|
fetch_engine.state_nxt <= IFETCH_0;
|
else
|
end if;
|
fetch_engine.state_nxt <= IFETCH_2;
|
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
when others => -- undefined
|
when others => -- undefined
|
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
Line 407... |
Line 389... |
-- ****************************************************************************************************************************
|
-- ****************************************************************************************************************************
|
|
|
|
|
-- Instruction Prefetch Buffer Stage ------------------------------------------------------
|
-- Instruction Prefetch Buffer Stage ------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
instr_prefetch_buffer: process(rstn_i, clk_i)
|
instr_prefetch_buffer: process(rstn_i, clk_i) -- once upon a time, this was a fifo with 8 entries
|
begin
|
begin
|
if (rstn_i = '0') then
|
if (rstn_i = '0') then
|
ipb.status <= '0';
|
ipb.status <= '0';
|
ipb.rdata <= (others => '0');
|
ipb.rdata <= (others => '0');
|
ipb.raddr <= (others => '0');
|
ipb.raddr <= (others => '0');
|
Line 507... |
Line 489... |
begin
|
begin
|
if (rstn_i = '0') then
|
if (rstn_i = '0') then
|
execute_engine.pc <= CPU_BOOT_ADDR(data_width_c-1 downto 1) & '0';
|
execute_engine.pc <= CPU_BOOT_ADDR(data_width_c-1 downto 1) & '0';
|
execute_engine.last_pc <= CPU_BOOT_ADDR(data_width_c-1 downto 1) & '0';
|
execute_engine.last_pc <= CPU_BOOT_ADDR(data_width_c-1 downto 1) & '0';
|
execute_engine.state <= SYS_WAIT;
|
execute_engine.state <= SYS_WAIT;
|
--
|
|
execute_engine.sleep <= '0';
|
execute_engine.sleep <= '0';
|
elsif rising_edge(clk_i) then
|
elsif rising_edge(clk_i) then
|
execute_engine.pc <= execute_engine.pc_nxt(data_width_c-1 downto 1) & '0';
|
execute_engine.pc <= execute_engine.pc_nxt(data_width_c-1 downto 1) & '0';
|
if (execute_engine.state = EXECUTE) then
|
if (execute_engine.state = EXECUTE) then
|
execute_engine.last_pc <= execute_engine.pc(data_width_c-1 downto 1) & '0';
|
execute_engine.last_pc <= execute_engine.pc(data_width_c-1 downto 1) & '0';
|
end if;
|
end if;
|
execute_engine.state <= execute_engine.state_nxt;
|
execute_engine.state <= execute_engine.state_nxt;
|
--
|
|
execute_engine.sleep <= execute_engine.sleep_nxt;
|
execute_engine.sleep <= execute_engine.sleep_nxt;
|
end if;
|
end if;
|
end process execute_engine_fsm_sync_rst;
|
end process execute_engine_fsm_sync_rst;
|
|
|
|
|
Line 570... |
Line 550... |
-- arbiter defaults --
|
-- arbiter defaults --
|
execute_engine.state_nxt <= execute_engine.state;
|
execute_engine.state_nxt <= execute_engine.state;
|
execute_engine.i_reg_nxt <= execute_engine.i_reg;
|
execute_engine.i_reg_nxt <= execute_engine.i_reg;
|
execute_engine.is_jump_nxt <= '0';
|
execute_engine.is_jump_nxt <= '0';
|
execute_engine.is_ci_nxt <= execute_engine.is_ci;
|
execute_engine.is_ci_nxt <= execute_engine.is_ci;
|
execute_engine.pc_nxt <= execute_engine.pc(data_width_c-1 downto 1) & '0';
|
execute_engine.pc_nxt <= execute_engine.pc;
|
execute_engine.sleep_nxt <= execute_engine.sleep;
|
execute_engine.sleep_nxt <= execute_engine.sleep;
|
|
|
-- instruction dispatch --
|
-- instruction dispatch --
|
fetch_engine.reset <= '0';
|
fetch_engine.reset <= '0';
|
ipb.re <= '0';
|
ipb.re <= '0';
|
Line 586... |
Line 566... |
-- exception trigger --
|
-- exception trigger --
|
trap_ctrl.instr_be <= '0';
|
trap_ctrl.instr_be <= '0';
|
trap_ctrl.instr_ma <= '0';
|
trap_ctrl.instr_ma <= '0';
|
trap_ctrl.env_call <= '0';
|
trap_ctrl.env_call <= '0';
|
trap_ctrl.break_point <= '0';
|
trap_ctrl.break_point <= '0';
|
|
illegal_compressed <= '0';
|
|
|
-- CSR access --
|
-- CSR access --
|
csr.we_nxt <= '0';
|
csr.we_nxt <= '0';
|
csr.re_nxt <= '0';
|
csr.re_nxt <= '0';
|
|
|
Line 599... |
Line 580... |
ctrl_nxt(ctrl_alu_unsigned_c) <= execute_engine.i_reg(instr_funct3_lsb_c+0); -- unsigned ALU operation (SLTIU, SLTU)
|
ctrl_nxt(ctrl_alu_unsigned_c) <= execute_engine.i_reg(instr_funct3_lsb_c+0); -- unsigned ALU operation (SLTIU, SLTU)
|
else -- branches
|
else -- branches
|
ctrl_nxt(ctrl_alu_unsigned_c) <= execute_engine.i_reg(instr_funct3_lsb_c+1); -- unsigned branches (BLTU, BGEU)
|
ctrl_nxt(ctrl_alu_unsigned_c) <= execute_engine.i_reg(instr_funct3_lsb_c+1); -- unsigned branches (BLTU, BGEU)
|
end if;
|
end if;
|
ctrl_nxt(ctrl_bus_unsigned_c) <= execute_engine.i_reg(instr_funct3_msb_c); -- unsigned LOAD (LBU, LHU)
|
ctrl_nxt(ctrl_bus_unsigned_c) <= execute_engine.i_reg(instr_funct3_msb_c); -- unsigned LOAD (LBU, LHU)
|
ctrl_nxt(ctrl_alu_shift_dir_c) <= execute_engine.i_reg(instr_funct3_msb_c); -- shift direction
|
ctrl_nxt(ctrl_alu_shift_dir_c) <= execute_engine.i_reg(instr_funct3_msb_c); -- shift direction (left/right)
|
ctrl_nxt(ctrl_alu_shift_ar_c) <= execute_engine.i_reg(30); -- arithmetic shift
|
ctrl_nxt(ctrl_alu_shift_ar_c) <= execute_engine.i_reg(30); -- is arithmetic shift
|
ctrl_nxt(ctrl_bus_size_lsb_c) <= execute_engine.i_reg(instr_funct3_lsb_c+0); -- transfer size lsb (00=byte, 01=half-word)
|
ctrl_nxt(ctrl_bus_size_lsb_c) <= execute_engine.i_reg(instr_funct3_lsb_c+0); -- transfer size lsb (00=byte, 01=half-word)
|
ctrl_nxt(ctrl_bus_size_msb_c) <= execute_engine.i_reg(instr_funct3_lsb_c+1); -- transfer size msb (10=word, 11=?)
|
ctrl_nxt(ctrl_bus_size_msb_c) <= execute_engine.i_reg(instr_funct3_lsb_c+1); -- transfer size msb (10=word, 11=?)
|
ctrl_nxt(ctrl_cp_cmd2_c downto ctrl_cp_cmd0_c) <= execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c); -- CP operation
|
ctrl_nxt(ctrl_cp_cmd2_c downto ctrl_cp_cmd0_c) <= execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c); -- CP operation
|
ctrl_nxt(ctrl_cp_id_msb_c downto ctrl_cp_id_lsb_c) <= cp_sel_muldiv_c; -- only CP0 (MULDIV) implemented yet
|
ctrl_nxt(ctrl_cp_id_msb_c downto ctrl_cp_id_lsb_c) <= cp_sel_muldiv_c; -- only CP0 (MULDIV) implemented yet
|
|
|
Line 647... |
Line 628... |
|
|
when DISPATCH => -- Get new command from instruction prefetch buffer (IPB)
|
when DISPATCH => -- Get new command from instruction prefetch buffer (IPB)
|
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
if (ipb.avail = '1') then -- instruction available?
|
if (ipb.avail = '1') then -- instruction available?
|
ipb.re <= '1';
|
ipb.re <= '1';
|
trap_ctrl.instr_ma <= ipb.rdata(33);
|
trap_ctrl.instr_ma <= ipb.rdata(33); -- misaligned instruction fetch address
|
trap_ctrl.instr_be <= ipb.rdata(34);
|
trap_ctrl.instr_be <= ipb.rdata(34); -- bus access fault druing instrucion fetch
|
if (trap_ctrl.env_start = '1') or (ipb.rdata(33) = '1') or (ipb.rdata(34) = '1') then -- exception/interrupt?
|
illegal_compressed <= ipb.rdata(35); -- invalid decompressed instruction
|
|
if (trap_ctrl.env_start = '1') or ((ipb.rdata(33) or ipb.rdata(34) or ipb.rdata(35)) = '1') then -- exception/interrupt?
|
execute_engine.state_nxt <= TRAP;
|
execute_engine.state_nxt <= TRAP;
|
else
|
else
|
execute_engine.is_ci_nxt <= ipb.rdata(32); -- flag to indicate this is a compressed instruction beeing executed
|
execute_engine.is_ci_nxt <= ipb.rdata(32); -- flag to indicate this is a compressed instruction beeing executed
|
execute_engine.i_reg_nxt <= ipb.rdata(31 downto 0);
|
execute_engine.i_reg_nxt <= ipb.rdata(31 downto 0);
|
execute_engine.pc_nxt <= ipb.raddr(data_width_c-1 downto 1) & '0'; -- the PC according to the current instruction
|
execute_engine.pc_nxt <= ipb.raddr; -- the PC according to the current instruction
|
if (execute_engine.sleep = '1') then
|
if (execute_engine.sleep = '1') then
|
execute_engine.state_nxt <= TRAP;
|
execute_engine.state_nxt <= TRAP;
|
else
|
else
|
execute_engine.state_nxt <= EXECUTE;
|
execute_engine.state_nxt <= EXECUTE;
|
end if;
|
end if;
|
Line 666... |
Line 648... |
end if;
|
end if;
|
|
|
when TRAP => -- Start trap environment (also used as cpu sleep state)
|
when TRAP => -- Start trap environment (also used as cpu sleep state)
|
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
fetch_engine.reset <= '1';
|
fetch_engine.reset <= '1';
|
if (trap_ctrl.env_start = '1') then
|
if (trap_ctrl.env_start = '1') then -- check here again if we came directly from DISPATCH
|
trap_ctrl.env_start_ack <= '1';
|
trap_ctrl.env_start_ack <= '1';
|
|
execute_engine.pc_nxt <= csr.mtvec;
|
execute_engine.sleep_nxt <= '0'; -- waky waky
|
execute_engine.sleep_nxt <= '0'; -- waky waky
|
execute_engine.pc_nxt <= csr.mtvec(data_width_c-1 downto 2) & "00"; -- has to be here for wfi to work
|
|
execute_engine.state_nxt <= SYS_WAIT;
|
execute_engine.state_nxt <= SYS_WAIT;
|
end if;
|
end if;
|
|
|
when EXECUTE => -- Decode and execute instruction
|
when EXECUTE => -- Decode and execute instruction
|
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
Line 740... |
Line 722... |
else -- JALR
|
else -- JALR
|
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '0'; -- use RS1 as ALU.OPA
|
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '0'; -- use RS1 as ALU.OPA
|
end if;
|
end if;
|
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '1'; -- use IMM as ALU.OPB
|
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '1'; -- use IMM as ALU.OPB
|
-- save return address --
|
-- save return address --
|
ctrl_nxt(ctrl_rf_in_mux_msb_c downto ctrl_rf_in_mux_lsb_c) <= "10"; -- RF input = current PC
|
ctrl_nxt(ctrl_rf_in_mux_msb_c downto ctrl_rf_in_mux_lsb_c) <= "10"; -- RF input = next PC (save return address)
|
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back
|
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back
|
--
|
--
|
execute_engine.is_jump_nxt <= '1'; -- this is a jump operation
|
execute_engine.is_jump_nxt <= '1'; -- this is a jump operation
|
execute_engine.state_nxt <= BRANCH;
|
execute_engine.state_nxt <= BRANCH;
|
|
|
Line 760... |
Line 742... |
end if;
|
end if;
|
execute_engine.state_nxt <= SYS_WAIT;
|
execute_engine.state_nxt <= SYS_WAIT;
|
|
|
when opcode_syscsr_c => -- system/csr access
|
when opcode_syscsr_c => -- system/csr access
|
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
csr.re_nxt <= '1'; -- always read CSR
|
csr.re_nxt <= '1'; -- always read CSR - regardless of actual operation
|
--
|
|
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_env_c) then -- system
|
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_env_c) then -- system
|
case execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) is
|
case execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) is
|
when funct12_ecall_c => -- ECALL
|
when funct12_ecall_c => -- ECALL
|
trap_ctrl.env_call <= '1';
|
trap_ctrl.env_call <= '1';
|
when funct12_ebreak_c => -- EBREAK
|
when funct12_ebreak_c => -- EBREAK
|
trap_ctrl.break_point <= '1';
|
trap_ctrl.break_point <= '1';
|
when funct12_mret_c => -- MRET
|
when funct12_mret_c => -- MRET
|
trap_ctrl.env_end <= '1';
|
trap_ctrl.env_end <= '1';
|
execute_engine.pc_nxt <= csr.mepc(data_width_c-1 downto 1) & '0';
|
execute_engine.pc_nxt <= csr.mepc;
|
fetch_engine.reset <= '1';
|
fetch_engine.reset <= '1';
|
when funct12_wfi_c => -- WFI = "CPU sleep"
|
when funct12_wfi_c => -- WFI = "CPU sleep"
|
execute_engine.sleep_nxt <= '1'; -- good night
|
execute_engine.sleep_nxt <= '1'; -- good night
|
when others => -- undefined
|
when others => -- undefined
|
NULL;
|
NULL;
|
end case;
|
end case;
|
execute_engine.state_nxt <= SYS_WAIT;
|
execute_engine.state_nxt <= SYS_WAIT;
|
else
|
else -- CSR access
|
if (CPU_EXTENSION_RISCV_Zicsr = true) then -- CSR access
|
|
execute_engine.state_nxt <= CSR_ACCESS;
|
execute_engine.state_nxt <= CSR_ACCESS;
|
else -- undefined
|
|
execute_engine.state_nxt <= DISPATCH;
|
|
end if;
|
|
end if;
|
end if;
|
|
|
when others => -- undefined
|
when others => -- undefined
|
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
execute_engine.state_nxt <= DISPATCH;
|
execute_engine.state_nxt <= DISPATCH;
|
Line 853... |
Line 830... |
execute_engine.state_nxt <= DISPATCH;
|
execute_engine.state_nxt <= DISPATCH;
|
end if;
|
end if;
|
|
|
when BRANCH => -- update PC for taken branches and jumps
|
when BRANCH => -- update PC for taken branches and jumps
|
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
|
execute_engine.pc_nxt <= alu_add_i; -- branch/jump destination
|
if (execute_engine.is_jump = '1') or (execute_engine.branch_taken = '1') then
|
if (execute_engine.is_jump = '1') or (execute_engine.branch_taken = '1') then
|
execute_engine.pc_nxt <= alu_add_i(data_width_c-1 downto 1) & '0'; -- branch/jump destination
|
fetch_engine.reset <= '1'; -- trigger new instruction fetch from modified PC
|
fetch_engine.reset <= '1';
|
|
execute_engine.state_nxt <= SYS_WAIT;
|
execute_engine.state_nxt <= SYS_WAIT;
|
else
|
else
|
execute_engine.state_nxt <= DISPATCH;
|
execute_engine.state_nxt <= DISPATCH;
|
end if;
|
end if;
|
|
|
Line 898... |
Line 875... |
end process execute_engine_fsm_comb;
|
end process execute_engine_fsm_comb;
|
|
|
|
|
-- Illegal Instruction Check --------------------------------------------------------------
|
-- Illegal Instruction Check --------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
illegal_instruction_check: process(execute_engine, csr, ctrl_nxt, ci_illegal)
|
illegal_instruction_check: process(execute_engine, csr, ctrl_nxt)
|
begin
|
begin
|
-- illegal instructions are checked in the EXECUTE stage
|
-- illegal instructions are checked in the EXECUTE stage
|
-- the execute engine will only commit valid instructions
|
-- the execute engine will only commit valid instructions
|
if (execute_engine.state = EXECUTE) then
|
if (execute_engine.state = EXECUTE) then
|
-- defaults --
|
-- defaults --
|
illegal_instruction <= '0';
|
illegal_instruction <= '0';
|
illegal_register <= '0';
|
illegal_register <= '0';
|
illegal_compressed <= '0';
|
|
|
|
-- check if using reg >= 16 for E-CPUs --
|
-- check if using reg >= 16 for E-CPUs --
|
--if (CPU_EXTENSION_RISCV_E = true) then
|
--if (CPU_EXTENSION_RISCV_E = true) then
|
-- illegal_register <= ctrl_nxt(ctrl_rf_rd_adr4_c) or ctrl_nxt(ctrl_rf_rs2_adr4_c) or ctrl_nxt(ctrl_rf_rs1_adr4_c);
|
-- illegal_register <= ????? FIXME
|
--else
|
--else
|
-- illegal_register <= '0';
|
-- illegal_register <= '0';
|
--end if;
|
--end if;
|
|
|
-- check instructions --
|
-- check instructions --
|
Line 1053... |
Line 1029... |
end if;
|
end if;
|
|
|
when others => -- compressed instruction or undefined instruction
|
when others => -- compressed instruction or undefined instruction
|
if (execute_engine.i_reg(1 downto 0) = "11") then -- undefined/unimplemented opcode
|
if (execute_engine.i_reg(1 downto 0) = "11") then -- undefined/unimplemented opcode
|
illegal_instruction <= '1';
|
illegal_instruction <= '1';
|
else -- compressed instruction: illegal or not implemented
|
|
illegal_compressed <= ci_illegal;
|
|
end if;
|
end if;
|
|
|
end case;
|
end case;
|
else
|
else
|
illegal_instruction <= '0';
|
illegal_instruction <= '0';
|
illegal_register <= '0';
|
illegal_register <= '0';
|
illegal_compressed <= '0';
|
|
end if;
|
end if;
|
end process illegal_instruction_check;
|
end process illegal_instruction_check;
|
|
|
-- any illegal condition? --
|
-- any illegal condition? --
|
trap_ctrl.instr_il <= illegal_instruction or illegal_register or illegal_compressed;
|
trap_ctrl.instr_il <= illegal_instruction or illegal_register or illegal_compressed;
|
Line 1107... |
Line 1080... |
trap_ctrl.irq_buf(interrupt_mtime_irq_c) <= csr.mie_mtie and (trap_ctrl.irq_buf(interrupt_mtime_irq_c) or mtime_irq_i) and (not trap_ctrl.irq_ack(interrupt_mtime_irq_c));
|
trap_ctrl.irq_buf(interrupt_mtime_irq_c) <= csr.mie_mtie and (trap_ctrl.irq_buf(interrupt_mtime_irq_c) or mtime_irq_i) and (not trap_ctrl.irq_ack(interrupt_mtime_irq_c));
|
|
|
-- trap control --
|
-- trap control --
|
if (trap_ctrl.env_start = '0') then -- no started trap handler
|
if (trap_ctrl.env_start = '0') then -- no started trap handler
|
if (trap_ctrl.exc_fire = '1') or ((trap_ctrl.irq_fire = '1') and -- exception/IRQ detected!
|
if (trap_ctrl.exc_fire = '1') or ((trap_ctrl.irq_fire = '1') and -- exception/IRQ detected!
|
((execute_engine.state = EXECUTE) or (execute_engine.state = TRAP))) then -- sample IRQs in EXECUTE or TRAP state only
|
((execute_engine.state = EXECUTE) or (execute_engine.state = TRAP))) then -- sample IRQs in EXECUTE or TRAP state only -> continue execution even if permanent IRQ
|
trap_ctrl.cause <= trap_ctrl.cause_nxt; -- capture source ID for program
|
trap_ctrl.cause <= trap_ctrl.cause_nxt; -- capture source ID for program (for mcause csr)
|
trap_ctrl.exc_ack <= '1'; -- clear execption
|
trap_ctrl.exc_ack <= '1'; -- clear execption
|
trap_ctrl.irq_ack <= trap_ctrl.irq_ack_nxt; -- capture and clear with interrupt ACK mask
|
trap_ctrl.irq_ack <= trap_ctrl.irq_ack_nxt; -- capture and clear with interrupt ACK mask
|
trap_ctrl.env_start <= '1'; -- now we want to start the trap handler
|
trap_ctrl.env_start <= '1'; -- now execute engine can start trap handler
|
end if;
|
end if;
|
else -- trap waiting to get started
|
else -- trap waiting to get started
|
if (trap_ctrl.env_start_ack = '1') then -- start of trap handler acknowledged by execution engine
|
if (trap_ctrl.env_start_ack = '1') then -- start of trap handler acknowledged by execution engine
|
trap_ctrl.exc_ack <= '0';
|
trap_ctrl.exc_ack <= '0';
|
trap_ctrl.irq_ack <= (others => '0');
|
trap_ctrl.irq_ack <= (others => '0');
|
Line 1125... |
Line 1098... |
end if;
|
end if;
|
end if;
|
end if;
|
end process trap_controller;
|
end process trap_controller;
|
|
|
-- any exception/interrupt? --
|
-- any exception/interrupt? --
|
trap_ctrl.exc_fire <= or_all_f(trap_ctrl.exc_buf); -- classic exceptions (faults/traps) cannot be masked
|
trap_ctrl.exc_fire <= or_all_f(trap_ctrl.exc_buf); -- exceptions/faults cannot be masked
|
trap_ctrl.irq_fire <= or_all_f(trap_ctrl.irq_buf) and csr.mstatus_mie; -- classic interrupts can be enabled/disabled
|
trap_ctrl.irq_fire <= or_all_f(trap_ctrl.irq_buf) and csr.mstatus_mie; -- interrupts can be masked
|
|
|
|
|
-- Trap Priority Detector -----------------------------------------------------------------
|
-- Trap Priority Detector -----------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
trap_priority: process(trap_ctrl)
|
trap_priority: process(trap_ctrl)
|
Line 1278... |
Line 1251... |
else
|
else
|
|
|
-- machine exception PC & trap value register --
|
-- machine exception PC & trap value register --
|
if (trap_ctrl.env_start_ack = '1') then -- trap handler starting?
|
if (trap_ctrl.env_start_ack = '1') then -- trap handler starting?
|
csr.mcause <= trap_ctrl.cause(4) & "000" & x"000000" & trap_ctrl.cause(3 downto 0);
|
csr.mcause <= trap_ctrl.cause(4) & "000" & x"000000" & trap_ctrl.cause(3 downto 0);
|
if (trap_ctrl.cause(4) = '1') then -- for INTERRUPTS only
|
if (trap_ctrl.cause(4) = '1') then -- for INTERRUPTS only (is mcause(31))
|
csr.mepc <= execute_engine.pc(data_width_c-1 downto 1) & '0'; -- this is the CURRENT pc = interrupted instruction
|
csr.mepc <= execute_engine.pc(data_width_c-1 downto 1) & '0'; -- this is the CURRENT pc = interrupted instruction
|
csr.mtval <= (others => '0'); -- mtval is zero for interrupts
|
csr.mtval <= (others => '0'); -- mtval is zero for interrupts
|
else -- for EXCEPTIONS (according to their priority)
|
else -- for EXCEPTIONS (according to their priority)
|
csr.mepc <= execute_engine.last_pc(data_width_c-1 downto 1) & '0'; -- this is the LAST pc = last executed instruction
|
csr.mepc <= execute_engine.last_pc(data_width_c-1 downto 1) & '0'; -- this is the LAST pc = last executed instruction
|
if (trap_ctrl.cause(3 downto 0) = trap_iba_c(3 downto 0)) or -- instr access error OR
|
if (trap_ctrl.cause(3 downto 0) = trap_iba_c(3 downto 0)) or -- instr access error OR
|