mirror of
https://github.com/Domipheus/RPU.git
synced 2025-04-18 19:15:13 -04:00
Updated ISA support to RV32IMZcsr - Passes riscv-compliance. Integer divide/rem in 34 cycles. Integer multiply in 2 cycles (when using xilinx dsp blocks!) Saved multiple cycles from fetch/memory load stages by short-cutting the start of memory requests. Compliant misaligned exceptions for jumps,loads and stores. Addrs starting 0xFxxxxxxx ignore alignment requests (assumes mmio space). Added CSRs for riscv-compliance requirements. Source ran through a formatter for ease of use.
300 lines
No EOL
12 KiB
VHDL
300 lines
No EOL
12 KiB
VHDL
----------------------------------------------------------------------------------
|
|
-- Project Name: RPU
|
|
-- Description: control unit
|
|
--
|
|
----------------------------------------------------------------------------------
|
|
-- Copyright 2016,2018,2019,2020 Colin Riley
|
|
--
|
|
-- Licensed under the Apache License, Version 2.0 (the "License");
|
|
-- you may not use this file except in compliance with the License.
|
|
-- You may obtain a copy of the License at
|
|
--
|
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
|
--
|
|
-- Unless required by applicable law or agreed to in writing, software
|
|
-- distributed under the License is distributed on an "AS IS" BASIS,
|
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
-- See the License for the specific language governing permissions and
|
|
-- limitations under the License.
|
|
----------------------------------------------------------------------------------
|
|
library IEEE;
|
|
use IEEE.STD_LOGIC_1164.all;
|
|
|
|
library work;
|
|
use work.constants.all;
|
|
|
|
entity control_unit is
|
|
port (
|
|
I_clk : in STD_LOGIC;
|
|
I_reset : in STD_LOGIC;
|
|
I_halt : in STD_LOGIC;
|
|
I_aluop : in STD_LOGIC_VECTOR (6 downto 0);
|
|
|
|
-- interrupts
|
|
I_int_enabled : in std_logic;
|
|
I_int : in STD_LOGIC;
|
|
O_int_ack : out STD_LOGIC;
|
|
I_int_mem_data : in STD_LOGIC_VECTOR(XLENM1 downto 0);
|
|
O_idata : out STD_LOGIC_VECTOR(XLENM1 downto 0);
|
|
O_set_idata : out STD_LOGIC;
|
|
O_set_ipc : out STD_LOGIC;
|
|
O_set_irpc : out STD_LOGIC;
|
|
O_instTick : out STD_LOGIC;
|
|
-- mem controller state and control
|
|
I_misalignment : in STD_LOGIC;
|
|
I_ready : in STD_LOGIC;
|
|
O_execute : out STD_LOGIC;
|
|
I_dataReady : in STD_LOGIC;
|
|
|
|
-- alu stall input
|
|
I_aluWait : in STD_LOGIC;
|
|
I_aluMultiCy : in STD_LOGIC;
|
|
O_state : out STD_LOGIC_VECTOR (6 downto 0)
|
|
);
|
|
end control_unit;
|
|
|
|
architecture Behavioral of control_unit is
|
|
signal s_state : STD_LOGIC_VECTOR(6 downto 0) := "0000001";
|
|
|
|
signal mem_ready : std_logic;
|
|
signal mem_execute : std_logic := '0';
|
|
signal mem_dataReady : std_logic;
|
|
|
|
signal mem_cycles : integer := 0;
|
|
|
|
signal next_s_state : STD_LOGIC_VECTOR(6 downto 0) := "0000001";
|
|
|
|
signal interrupt_state : STD_LOGIC_VECTOR(2 downto 0) := "000";
|
|
signal interrupt_ack : STD_LOGIC := '0';
|
|
signal interrupt_was_inactive : STD_LOGIC := '1';
|
|
signal set_idata : STD_LOGIC := '0';
|
|
signal set_ipc : STD_LOGIC := '0';
|
|
signal instTick : STD_LOGIC := '0';
|
|
signal s_hasWaited : STD_LOGIC := '0';
|
|
|
|
signal s_check_alignint : integer := 0;
|
|
begin
|
|
|
|
O_execute <= mem_execute;
|
|
mem_ready <= I_ready;
|
|
mem_dataReady <= I_dataReady;
|
|
O_int_ack <= interrupt_ack;
|
|
O_set_idata <= set_idata;
|
|
O_set_irpc <= set_idata;
|
|
O_set_ipc <= set_ipc;
|
|
O_instTick <= instTick;
|
|
|
|
process (I_clk)
|
|
begin
|
|
if rising_edge(I_clk) and I_halt = '0' then
|
|
|
|
if I_reset = '1' then
|
|
s_state <= "0000001";
|
|
next_s_state <= "0000001";
|
|
mem_cycles <= 0;
|
|
mem_execute <= '0';
|
|
interrupt_was_inactive <= '1';
|
|
interrupt_ack <= '0';
|
|
interrupt_state <= "000";
|
|
set_ipc <= '0';
|
|
O_idata <= X"00000000";
|
|
set_idata <= '0';
|
|
instTick <= '0';
|
|
else
|
|
case s_state is
|
|
---------------------------
|
|
-- FETCH
|
|
when "0000001" => -- fetch
|
|
|
|
if s_check_alignint /= 0 then
|
|
-- If we've seen an alignment hint we need to stall here for s_check_alignint
|
|
-- cycles, checking for an interrupt each time. When it's 0 we give up.
|
|
if I_int_enabled = '1' and interrupt_was_inactive = '1' and I_int = '1' then
|
|
interrupt_ack <= '1';
|
|
interrupt_was_inactive <= '0';
|
|
interrupt_state <= "001";
|
|
next_s_state <= "0000001"; --F
|
|
s_state <= "1000000"; --S
|
|
s_check_alignint <= 0;
|
|
else
|
|
s_check_alignint <= s_check_alignint - 1;
|
|
end if;
|
|
else
|
|
|
|
if I_int = '0' then
|
|
interrupt_was_inactive <= '1';
|
|
end if;
|
|
instTick <= '0';
|
|
if mem_cycles = 0 and mem_ready = '1' then
|
|
mem_execute <= '1';
|
|
mem_cycles <= 1;
|
|
|
|
elsif mem_cycles = 1 then
|
|
mem_execute <= '0';
|
|
mem_cycles <= 2;
|
|
|
|
elsif mem_cycles = 2 then
|
|
mem_execute <= '0';
|
|
if mem_dataReady = '1' then
|
|
mem_cycles <= 0;
|
|
s_state <= "0000010";
|
|
end if;
|
|
end if;
|
|
|
|
end if;
|
|
|
|
---------------------------
|
|
-- DECODE
|
|
when "0000010" => --- decode
|
|
if I_int = '0' then
|
|
interrupt_was_inactive <= '1';
|
|
end if;
|
|
s_hasWaited <= '0';
|
|
s_state <= "0001000"; --E "0000100"; --R
|
|
|
|
|
|
---------------------------
|
|
-- EXECUTE
|
|
when "0001000" => -- execute
|
|
if I_int = '0' then
|
|
interrupt_was_inactive <= '1';
|
|
end if;
|
|
--MEM/WB
|
|
-- if it's not a memory alu op, goto writeback
|
|
if (I_aluop(6 downto 2) = OPCODE_LOAD or
|
|
I_aluop(6 downto 2) = OPCODE_STORE) then
|
|
s_state <= "0010000"; -- MEM
|
|
|
|
-- -- mem load short cut
|
|
-- ISSUE - this fails to take into account the type of request, sizing, address correctly
|
|
-- and therefore needs removed for compliance to pass.
|
|
-- if I_misalignment = '0' and mem_cycles = 0 and mem_ready = '1' then
|
|
-- mem_execute <= '1';
|
|
-- mem_cycles <= 1;
|
|
-- end if;
|
|
|
|
else
|
|
if I_aluWait = '0' then
|
|
if I_aluMultiCy = '1' then
|
|
if s_hasWaited = '1' then
|
|
s_state <= "0100000"; -- WB
|
|
end if;
|
|
else
|
|
s_state <= "0100000"; -- WB
|
|
end if;
|
|
s_hasWaited <= '1';
|
|
end if;
|
|
end if;
|
|
|
|
---------------------------
|
|
-- MEMORY
|
|
when "0010000" => -- mem
|
|
if I_int = '0' then
|
|
interrupt_was_inactive <= '1';
|
|
end if;
|
|
|
|
-- alignment traps here are tricky.
|
|
-- if we see the misalignment hint, wait 6 cycles and then test interrupt stall.
|
|
-- if no interrupt we need to re-run the stage.
|
|
|
|
if I_misalignment = '1' and s_check_alignint = 0 then
|
|
s_check_alignint <= 6;
|
|
elsif s_check_alignint > 0 then
|
|
if I_int_enabled = '1' and interrupt_was_inactive = '1' and I_int = '1' then
|
|
interrupt_ack <= '1';
|
|
interrupt_was_inactive <= '0';
|
|
interrupt_state <= "001";
|
|
next_s_state <= "0000001"; --F
|
|
s_state <= "1000000"; --F
|
|
s_check_alignint <= 0;
|
|
else
|
|
s_check_alignint <= s_check_alignint - 1;
|
|
end if;
|
|
|
|
else
|
|
if mem_cycles = 0 and mem_ready = '1' then
|
|
|
|
mem_execute <= '1';
|
|
mem_cycles <= 1;
|
|
|
|
elsif mem_cycles = 1 then
|
|
mem_execute <= '0';
|
|
-- if it's a write, go through
|
|
if I_aluop(6 downto 2) = OPCODE_STORE then
|
|
mem_cycles <= 0;
|
|
s_state <= "0100000"; -- WB
|
|
elsif mem_dataReady = '1' then
|
|
-- if read, wait for data
|
|
mem_cycles <= 0;
|
|
s_state <= "0100000"; -- WB
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
|
|
---------------------------
|
|
-- WRITEBACK
|
|
when "0100000" => -- writeback
|
|
-- check interrupt?
|
|
if I_int_enabled = '1' and interrupt_was_inactive = '1' and I_int = '1' then
|
|
interrupt_ack <= '1';
|
|
interrupt_was_inactive <= '0';
|
|
interrupt_state <= "001";
|
|
next_s_state <= "0000001"; --F
|
|
s_state <= "1000000"; --F
|
|
else
|
|
if I_int = '0' then
|
|
interrupt_was_inactive <= '1';
|
|
end if;
|
|
if I_misalignment = '1' and s_check_alignint = 0 then
|
|
s_check_alignint <= 3;
|
|
end if;
|
|
|
|
-- misalign interrupts take a while to propagate
|
|
-- this signal short cuts to ensure we can catch any misalignments before fetch.
|
|
|
|
s_state <= "0000001"; --F
|
|
|
|
-- if the mem system is ready, shortcut the fetch
|
|
-- at this point, the next PC/Branch should be set.
|
|
-- need to ensure the sizing is correct.
|
|
if I_misalignment = '0' and mem_cycles = 0 and mem_ready = '1' then -- shortcut
|
|
mem_execute <= '1'; -- shortcut
|
|
mem_cycles <= 2; -- shortcut
|
|
end if; -- shortcut
|
|
|
|
end if;
|
|
instTick <= '1';
|
|
when "1000000" => -- stalls
|
|
if I_int = '0' then
|
|
interrupt_was_inactive <= '1';
|
|
end if;
|
|
instTick <= '0';
|
|
-- interrupt stall
|
|
if interrupt_state = "001" then
|
|
-- give a cycle of latency
|
|
-- set PC to interrupt vector.
|
|
|
|
set_ipc <= '1';
|
|
interrupt_state <= "101";
|
|
|
|
elsif interrupt_state = "101" then
|
|
set_ipc <= '0';
|
|
interrupt_ack <= '0';
|
|
interrupt_state <= "111";
|
|
elsif interrupt_state = "111" then
|
|
interrupt_state <= "000";
|
|
s_state <= "0000001"; --F
|
|
end if;
|
|
when "1001000" =>
|
|
-- alu 1 cycle stall
|
|
s_state <= "0100000"; -- WB
|
|
when others =>
|
|
s_state <= "0000001";
|
|
end case;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
O_state <= s_state;
|
|
end Behavioral; |