diff --git a/README.md b/README.md index 5f8b581..9a9ce35 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,10 @@ # RPU Basic RISC-V CPU implementation in VHDL. + +This is a RV32I ISA CPU implementation, based off of my TPU CPU design. It is very simple, is missing several features, but can run rv32i-compiled GCC toolchain binaries at over 200MHz on a Digilent Arty S7-50 board, built with Xilinx Spartan 7 tools. + +Implementation detail is written about via blogs available at http://labs.domipheus.com/blog/designing-a-cpu-in-vhdl-part-15-introducing-rpu/ + +The tests in the repo are incredibly old and basic, and included only as a baseline to help. They will be expanded upon in time. + +Please let me know if you are using any of the RPU design in your own projects! I am contactable on twitter @domipheus. diff --git a/rpu_core_diagram.png b/rpu_core_diagram.png new file mode 100644 index 0000000..b4f86ab Binary files /dev/null and b/rpu_core_diagram.png differ diff --git a/tests/tb_unit_alu_RV32I_01.vhd b/tests/tb_unit_alu_RV32I_01.vhd new file mode 100644 index 0000000..dbda8ca --- /dev/null +++ b/tests/tb_unit_alu_RV32I_01.vhd @@ -0,0 +1,155 @@ +-------------------------------------------------------------------------------- +-- Company: +-- Engineer: +-- +-- Create Date: 16:43:32 12/10/2016 +-- Design Name: +-- Module Name: C:/Users/colin/Desktop/riscy/ise/tb_unit_alu_RV32I_01.vhd +-- Project Name: riscv32_v1 +-- Target Device: +-- Tool versions: +-- Description: +-- +-- VHDL Test Bench Created by ISE for module: alu_RV32I +-- +-- Dependencies: +-- +-- Revision: +-- Revision 0.01 - File Created +-- Additional Comments: +-- +-- Notes: +-- This testbench has been automatically generated using types std_logic and +-- std_logic_vector for the ports of the unit under test. Xilinx recommends +-- that these types always be used for the top-level I/O of a design in order +-- to guarantee that the testbench will bind correctly to the post-implementation +-- simulation model. +-------------------------------------------------------------------------------- +LIBRARY ieee; +USE ieee.std_logic_1164.ALL; + +use work.constants.all; + +-- Uncomment the following library declaration if using +-- arithmetic functions with Signed or Unsigned values +--USE ieee.numeric_std.ALL; + +ENTITY tb_unit_alu_RV32I_01 IS +END tb_unit_alu_RV32I_01; + +ARCHITECTURE behavior OF tb_unit_alu_RV32I_01 IS + + -- Component Declaration for the Unit Under Test (UUT) + + COMPONENT alu_RV32I + PORT( + I_clk : IN std_logic; + I_en : IN std_logic; + I_dataA : IN std_logic_vector(31 downto 0); + I_dataB : IN std_logic_vector(31 downto 0); + I_dataDwe : IN std_logic; + I_aluop : IN std_logic_vector(4 downto 0); + I_aluFunc : IN std_logic_vector(15 downto 0); + I_PC : IN std_logic_vector(31 downto 0); + I_dataIMM : IN std_logic_vector(31 downto 0); + O_dataResult : OUT std_logic_vector(31 downto 0); + O_branchTarget : OUT std_logic_vector(31 downto 0); + O_dataWriteReg : OUT std_logic; + O_shouldBranch : OUT std_logic + ); + END COMPONENT; + + + --Inputs + signal I_clk : std_logic := '0'; + signal I_en : std_logic := '0'; + signal I_dataA : std_logic_vector(31 downto 0) := (others => '0'); + signal I_dataB : std_logic_vector(31 downto 0) := (others => '0'); + signal I_dataDwe : std_logic := '0'; + signal I_aluop : std_logic_vector(4 downto 0) := (others => '0'); + signal I_aluFunc : std_logic_vector(15 downto 0) := (others => '0'); + signal I_PC : std_logic_vector(31 downto 0) := (others => '0'); + signal I_dataIMM : std_logic_vector(31 downto 0) := (others => '0'); + + --Outputs + signal O_dataResult : std_logic_vector(31 downto 0); + signal O_branchTarget : std_logic_vector(31 downto 0); + signal O_dataWriteReg : std_logic := '0'; + signal O_shouldBranch : std_logic := '0'; + + -- Clock period definitions + constant I_clk_period : time := 10 ns; + +BEGIN + + -- Instantiate the Unit Under Test (UUT) + uut: alu_RV32I PORT MAP ( + I_clk => I_clk, + I_en => I_en, + I_dataA => I_dataA, + I_dataB => I_dataB, + I_dataDwe => I_dataDwe, + I_aluop => I_aluop, + I_aluFunc => I_aluFunc, + I_PC => I_PC, + I_dataIMM => I_dataIMM, + O_dataResult => O_dataResult, + O_branchTarget => O_branchTarget, + O_dataWriteReg => O_dataWriteReg, + O_shouldBranch => O_shouldBranch + ); + + -- Clock process definitions + I_clk_process :process + begin + I_clk <= '0'; + wait for I_clk_period/2; + I_clk <= '1'; + wait for I_clk_period/2; + end process; + + + -- Stimulus process + stim_proc: process + begin + -- hold reset state for 100 ns. + wait for 100 ns; + + wait for I_clk_period*10; + -- insert stimulus here + + I_dataA <= X"00001000"; + I_dataB <= X"01A01001"; + I_aluOp <= OPCODE_OP; + I_aluFunc <= "000000" & F7_OP_ADD & F3_OP_ADD; + I_dataImm <= X"00000000"; + I_PC <= X"A0000000"; + I_dataDwe <= '1'; + I_en <= '1'; + + wait for I_clk_period*2; + + I_dataA <= X"00000001"; + I_dataB <= X"00000006"; + I_aluOp <= OPCODE_OP; + I_aluFunc <= "000000" & F7_OP_ADD & F3_OP_ADD; + I_dataImm <= X"00000000"; + I_PC <= X"A0000004"; + I_dataDwe <= '1'; + I_en <= '1'; + + wait for I_clk_period*2; + + I_dataA <= X"00346A00"; + I_dataB <= X"120000B6"; + I_aluOp <= OPCODE_OP; + I_aluFunc <= "000000" & F7_OP_OR & F3_OP_OR; + I_dataImm <= X"00000000"; + I_PC <= X"A0000008"; + I_dataDwe <= '1'; + I_en <= '1'; + + wait; + end process; + +END; diff --git a/tests/tb_unit_decoder_RV32_01.vhd b/tests/tb_unit_decoder_RV32_01.vhd new file mode 100644 index 0000000..49afe34 --- /dev/null +++ b/tests/tb_unit_decoder_RV32_01.vhd @@ -0,0 +1,136 @@ +-------------------------------------------------------------------------------- +-- Company: +-- Engineer: +-- +-- Create Date: 22:43:26 12/08/2016 +-- Design Name: +-- Module Name: C:/Users/colin/Desktop/riscy/ise/tb_unit_decoder_RV32_01.vhd +-- Project Name: riscv32_v1 +-- Target Device: +-- Tool versions: +-- Description: +-- +-- VHDL Test Bench Created by ISE for module: decoder_RV32 +-- +-- Dependencies: +-- +-- Revision: +-- Revision 0.01 - File Created +-- Additional Comments: +-- +-- Notes: +-- This testbench has been automatically generated using types std_logic and +-- std_logic_vector for the ports of the unit under test. Xilinx recommends +-- that these types always be used for the top-level I/O of a design in order +-- to guarantee that the testbench will bind correctly to the post-implementation +-- simulation model. +-------------------------------------------------------------------------------- +LIBRARY ieee; +USE ieee.std_logic_1164.ALL; + +-- Uncomment the following library declaration if using +-- arithmetic functions with Signed or Unsigned values +--USE ieee.numeric_std.ALL; + +ENTITY tb_unit_decoder_RV32_01 IS +END tb_unit_decoder_RV32_01; + +ARCHITECTURE behavior OF tb_unit_decoder_RV32_01 IS + + -- Component Declaration for the Unit Under Test (UUT) + + COMPONENT decoder_RV32 + PORT( + I_clk : IN std_logic; + I_en : IN std_logic; + I_dataInst : IN std_logic_vector(31 downto 0); + O_selRS1 : OUT std_logic_vector(4 downto 0); + O_selRS2 : OUT std_logic_vector(4 downto 0); + O_selD : OUT std_logic_vector(4 downto 0); + O_dataIMM : OUT std_logic_vector(31 downto 0); + O_regDwe : OUT std_logic; + O_aluOp : OUT std_logic_vector(6 downto 0); + O_aluFunc : OUT std_logic_vector(15 downto 0); -- ALU function + O_memOp : out STD_LOGIC_VECTOR(4 downto 0) + ); + END COMPONENT; + + + --Inputs + signal I_clk : std_logic := '0'; + signal I_en : std_logic := '0'; + signal I_dataInst : std_logic_vector(31 downto 0) := (others => '0'); + + --Outputs + signal O_selRS1 : std_logic_vector(4 downto 0); + signal O_selRS2 : std_logic_vector(4 downto 0); + signal O_selD : std_logic_vector(4 downto 0); + signal O_dataIMM : std_logic_vector(31 downto 0); + signal O_regDwe : std_logic; + signal O_aluOp : std_logic_vector(6 downto 0); + signal O_aluFunc : std_logic_vector(15 downto 0); + signal O_memOp : STD_LOGIC_VECTOR(4 downto 0); + + -- Clock period definitions + constant I_clk_period : time := 10 ns; + +BEGIN + + -- Instantiate the Unit Under Test (UUT) + uut: decoder_RV32 PORT MAP ( + I_clk => I_clk, + I_en => I_en, + I_dataInst => I_dataInst, + O_selRS1 => O_selRS1, + O_selRS2 => O_selRS2, + O_selD => O_selD, + O_dataIMM => O_dataIMM, + O_regDwe => O_regDwe, + O_aluOp => O_aluOp, + O_aluFunc => O_aluFunc, + O_memOp => O_memOp + ); + + -- Clock process definitions + I_clk_process :process + begin + I_clk <= '0'; + wait for I_clk_period/2; + I_clk <= '1'; + wait for I_clk_period/2; + end process; + + + -- Stimulus process + stim_proc: process + begin + -- hold reset state for 100 ns. + wait for 100 ns; + + wait for I_clk_period*10; + + -- insert stimulus here + I_dataInst <= "0000000" & "00001" & "00010" & "000" & "01001" & "0110011"; + I_en <= '1'; + + wait for I_clk_period*2; + + I_dataInst <= "000000000001" & "00010" & "000" & "01001" & "0010011"; + I_en <= '1'; + + wait for I_clk_period*2; + + I_dataInst <= "100000000001" & "00010" & "000" & "01001" & "0010011"; + I_en <= '1'; + + wait for I_clk_period*2; + + I_dataInst <= "100001000001" & "00000" & "010" & "00001" & "0000011"; + I_en <= '1'; + + wait for I_clk_period*2; + + wait; + end process; + +END; diff --git a/vhdl/constants.vhd b/vhdl/constants.vhd new file mode 100644 index 0000000..2361afe --- /dev/null +++ b/vhdl/constants.vhd @@ -0,0 +1,158 @@ +---------------------------------------------------------------------------------- +-- Project Name: RISC-V CPU +-- Description: Constants for instruction forms, opcodes, conditional flags, etc. +-- +-- Revision: 1 +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.all; + +package constants is + + +constant XLEN: integer := 32; +constant XLENM1: integer := XLEN - 1; + +constant XLEN32: integer:= 32; +constant XLEN32M1: integer:= XLEN32 -1; + + +constant BWIDTH: integer:= 32; +constant BWIDTHM1: integer:= BWIDTH -1; + + +constant ADDR_RESET: std_logic_vector(XLEN32M1 downto 0) := X"00000000"; +constant ADDR_INTVEC: std_logic_vector(XLEN32M1 downto 0) := X"00000100"; + + +-- PC unit opcodes +constant PCU_OP_NOP: std_logic_vector(1 downto 0):= "00"; +constant PCU_OP_INC: std_logic_vector(1 downto 0):= "01"; +constant PCU_OP_ASSIGN: std_logic_vector(1 downto 0):= "10"; +constant PCU_OP_RESET: std_logic_vector(1 downto 0):= "11"; + +-- Instruction Form Offsets +constant OPCODE_START: integer := 6; +constant OPCODE_END: integer := 0; +constant OPCODE_END_2: integer := 2; + +constant RD_START: integer := 11; +constant RD_END: integer := 7; + +constant FUNCT3_START: integer := 14; +constant FUNCT3_END: integer := 12; + +constant R1_START: integer := 19; +constant R1_END: integer := 15; + +constant R2_START: integer := 24; +constant R2_END: integer := 20; + +constant FUNCT7_START: integer := 31; +constant FUNCT7_END: integer := 25; + +constant IMM_I_START: integer := 31; +constant IMM_I_END: integer := 20; + +constant IMM_U_START: integer := 31; +constant IMM_U_END: integer := 12; + +constant IMM_S_A_START: integer := 31; +constant IMM_S_A_END: integer := 25; + +constant IMM_S_B_START: integer := 11; +constant IMM_S_B_END: integer := 7; + +-- Opcodes +constant OPCODE_LOAD: std_logic_vector(4 downto 0) := "00000"; +constant OPCODE_STORE: std_logic_vector(4 downto 0) := "01000"; +constant OPCODE_MADD: std_logic_vector(4 downto 0) := "10000"; +constant OPCODE_BRANCH: std_logic_vector(4 downto 0) := "11000"; +constant OPCODE_JALR: std_logic_vector(4 downto 0) := "11001"; +constant OPCODE_JAL: std_logic_vector(4 downto 0) := "11011"; +constant OPCODE_SYSTEM: std_logic_vector(4 downto 0) := "11100"; +constant OPCODE_OP: std_logic_vector(4 downto 0) := "01100"; +constant OPCODE_OPIMM: std_logic_vector(4 downto 0) := "00100"; +constant OPCODE_MISCMEM: std_logic_vector(4 downto 0) := "00011"; +constant OPCODE_AUIPC: std_logic_vector(4 downto 0) := "00101"; +constant OPCODE_LUI: std_logic_vector(4 downto 0) := "01101"; + +-- Flags +constant F3_BRANCH_BEQ: std_logic_vector(2 downto 0) := "000"; +constant F3_BRANCH_BNE: std_logic_vector(2 downto 0) := "001"; +constant F3_BRANCH_BLT: std_logic_vector(2 downto 0) := "100"; +constant F3_BRANCH_BGE: std_logic_vector(2 downto 0) := "101"; +constant F3_BRANCH_BLTU: std_logic_vector(2 downto 0) := "110"; +constant F3_BRANCH_BGEU: std_logic_vector(2 downto 0) := "111"; + +constant F3_JALR: std_logic_vector(2 downto 0) := "000"; + +constant F3_LOAD_LB: std_logic_vector(2 downto 0) := "000"; +constant F3_LOAD_LH: std_logic_vector(2 downto 0) := "001"; +constant F3_LOAD_LW: std_logic_vector(2 downto 0) := "010"; +constant F3_LOAD_LBU: std_logic_vector(2 downto 0) := "100"; +constant F3_LOAD_LHU: std_logic_vector(2 downto 0) := "101"; + +constant F2_MEM_LS_SIZE_B: std_logic_vector(1 downto 0) := "00"; +constant F2_MEM_LS_SIZE_H: std_logic_vector(1 downto 0) := "01"; +constant F2_MEM_LS_SIZE_W: std_logic_vector(1 downto 0) := "10"; + +constant F3_STORE_SB: std_logic_vector(2 downto 0) := "000"; +constant F3_STORE_SH: std_logic_vector(2 downto 0) := "001"; +constant F3_STORE_SW: std_logic_vector(2 downto 0) := "010"; + +constant F3_OPIMM_ADDI: std_logic_vector(2 downto 0) := "000"; +constant F3_OPIMM_SLTI: std_logic_vector(2 downto 0) := "010"; +constant F3_OPIMM_SLTIU: std_logic_vector(2 downto 0) := "011"; +constant F3_OPIMM_XORI: std_logic_vector(2 downto 0) := "100"; +constant F3_OPIMM_ORI: std_logic_vector(2 downto 0) := "110"; +constant F3_OPIMM_ANDI: std_logic_vector(2 downto 0) := "111"; + +constant F3_OPIMM_SLLI: std_logic_vector(2 downto 0) := "001"; +constant F7_OPIMM_SLLI: std_logic_vector(6 downto 0) := "0000000"; +constant F3_OPIMM_SRLI: std_logic_vector(2 downto 0) := "101"; +constant F7_OPIMM_SRLI: std_logic_vector(6 downto 0) := "0000000"; +constant F3_OPIMM_SRAI: std_logic_vector(2 downto 0) := "101"; +constant F7_OPIMM_SRAI: std_logic_vector(6 downto 0) := "0100000"; + +constant F3_OP_ADD: std_logic_vector(2 downto 0) := "000"; +constant F7_OP_ADD: std_logic_vector(6 downto 0) := "0000000"; +constant F3_OP_SUB: std_logic_vector(2 downto 0) := "000"; +constant F7_OP_SUB: std_logic_vector(6 downto 0) := "0100000"; +constant F3_OP_SLL: std_logic_vector(2 downto 0) := "001"; +constant F7_OP_SLL: std_logic_vector(6 downto 0) := "0000000"; +constant F3_OP_SLT: std_logic_vector(2 downto 0) := "010"; +constant F7_OP_SLT: std_logic_vector(6 downto 0) := "0000000"; +constant F3_OP_SLTU: std_logic_vector(2 downto 0) := "011"; +constant F7_OP_SLTU: std_logic_vector(6 downto 0) := "0000000"; +constant F3_OP_XOR: std_logic_vector(2 downto 0) := "100"; +constant F7_OP_XOR: std_logic_vector(6 downto 0) := "0000000"; +constant F3_OP_SRL: std_logic_vector(2 downto 0) := "101"; +constant F7_OP_SRL: std_logic_vector(6 downto 0) := "0000000"; +constant F3_OP_SRA: std_logic_vector(2 downto 0) := "101"; +constant F7_OP_SRA: std_logic_vector(6 downto 0) := "0100000"; +constant F3_OP_OR: std_logic_vector(2 downto 0) := "110"; +constant F7_OP_OR: std_logic_vector(6 downto 0) := "0000000"; +constant F3_OP_AND: std_logic_vector(2 downto 0) := "111"; +constant F7_OP_AND: std_logic_vector(6 downto 0) := "0000000"; + +constant F3_MISCMEM_FENCE: std_logic_vector(2 downto 0) := "000"; +constant F3_MISCMEM_FENCEI: std_logic_vector(2 downto 0) := "001"; + +constant F3_SYSTEM_ECALL: std_logic_vector(2 downto 0) := "000"; +constant IMM_I_SYSTEM_ECALL: std_logic_vector(11 downto 0) := "000000000000"; +constant F3_SYSTEM_EBREAK: std_logic_vector(2 downto 0) := "000"; +constant IMM_I_SYSTEM_EBREAK: std_logic_vector(11 downto 0) := "000000000001"; +constant F3_SYSTEM_CSRRW: std_logic_vector(2 downto 0) := "001"; +constant F3_SYSTEM_CSRRS: std_logic_vector(2 downto 0) := "010"; +constant F3_SYSTEM_CSRRC: std_logic_vector(2 downto 0) := "011"; +constant F3_SYSTEM_CSRRWI: std_logic_vector(2 downto 0) := "101"; +constant F3_SYSTEM_CSRRSI: std_logic_vector(2 downto 0) := "110"; +constant F3_SYSTEM_CSRRCI: std_logic_vector(2 downto 0) := "111"; + +end constants; + +package body constants is + +end constants; diff --git a/vhdl/control_unit.vhd b/vhdl/control_unit.vhd new file mode 100644 index 0000000..e149653 --- /dev/null +++ b/vhdl/control_unit.vhd @@ -0,0 +1,174 @@ +---------------------------------------------------------------------------------- +-- Project Name: RPU +-- Description: control unit +-- +---------------------------------------------------------------------------------- +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_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; + + -- mem controller state and control + I_ready: in STD_LOGIC; + O_execute: out STD_LOGIC; + I_dataReady: 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'; + +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; + + process(I_clk) + begin + if rising_edge(I_clk) 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'; + else + if I_int = '0' then + interrupt_was_inactive <= '1'; + end if; + case s_state is + when "0000001" => -- fetch + 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 + if mem_dataReady = '1' then + mem_cycles <= 0; + s_state <= "0000010"; + end if; + end if; + when "0000010" => --- decode + s_state <= "0001000"; --E "0000100"; --R + when "0000100" => -- read -- DEPRECATED STAGE + s_state <= "0001000"; --E + when "0001000" => -- execute + --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 + else + s_state <= "0100000"; -- WB + end if; + when "0010000" => -- mem + -- sometimes memory can be busy, if so we need to relook here + 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 <= "0100001";-- "0100000"; -- WB + elsif mem_dataReady = '1' then + -- if read, wait for data + mem_cycles <= 0; + s_state <= "0100000"; -- WB + end if; + end if; + 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 + s_state <= "0000001"; --F + end if; + + when "1000000" => -- stalls + -- interrupt stall + if interrupt_state = "001" then + -- give a cycle of latency + interrupt_state <= "010"; + elsif interrupt_state = "010" then + -- sample input data for state? + O_idata <= I_int_mem_data; + set_idata <= '1'; + interrupt_state <= "100"; + elsif interrupt_state = "100" then + set_idata <= '0'; + -- set PC to interrupt vector. + set_ipc <= '1'; + interrupt_state <= "101"; + elsif interrupt_state = "101" then + set_ipc <= '0'; + interrupt_ack <= '0'; + interrupt_state <= "000"; + s_state <= "0000001"; --F + end if; + + when others => + s_state <= "0000001"; + end case; + end if; + end if; + end process; + + O_state <= s_state; +end Behavioral; + + + diff --git a/vhdl/core.vhd b/vhdl/core.vhd new file mode 100644 index 0000000..278c3dd --- /dev/null +++ b/vhdl/core.vhd @@ -0,0 +1,351 @@ +---------------------------------------------------------------------------------- +-- Project Name: RPU +-- Description: RPU core glue entity +-- +-- Brings all core components together with a little logic. +-- This is the CPU interface required. +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +library work; +use work.constants.all; + +entity core is + Port ( + I_clk : in STD_LOGIC; + I_reset : in STD_LOGIC; + I_halt : in STD_LOGIC; + + -- unused interrupt interface, relic from TPU implementation + I_int: in STD_LOGIC; + O_int_ack: out STD_LOGIC; + + -- memory interface + MEM_I_ready : IN std_logic; + MEM_O_cmd : OUT std_logic; + MEM_O_we : OUT std_logic; + -- fixme: this is not a true byteEnable and so is confusing. + -- Will be fixed when memory swizzling is brought core-size + MEM_O_byteEnable : OUT std_logic_vector(1 downto 0); + MEM_O_addr : OUT std_logic_vector(XLEN32M1 downto 0); + MEM_O_data : OUT std_logic_vector(XLEN32M1 downto 0); + MEM_I_data : IN std_logic_vector(XLEN32M1 downto 0); + MEM_I_dataReady : IN std_logic + + ; -- This debug output contains some internal state for debugging + O_DBG:out std_logic_vector(XLEN32M1 downto 0) + ); +end core; + +architecture Behavioral of core is + COMPONENT pc_unit + PORT( + I_clk : IN std_logic; + I_nPC : IN std_logic_vector(XLENM1 downto 0); + I_nPCop : IN std_logic_vector(1 downto 0); + I_intVec: IN std_logic; + O_PC : OUT std_logic_vector(XLENM1 downto 0) + ); + END COMPONENT; + + COMPONENT control_unit + PORT ( + I_clk : in STD_LOGIC; + I_reset : in STD_LOGIC; + I_aluop : in STD_LOGIC_VECTOR (6 downto 0); + O_state : out STD_LOGIC_VECTOR (6 downto 0); + + I_int: in STD_LOGIC; + O_int_ack: out STD_LOGIC; + + I_int_enabled: in 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; + + I_ready: in STD_LOGIC; + O_execute: out STD_LOGIC; + I_dataReady: in STD_LOGIC + ); + END COMPONENT; + + + COMPONENT decoder_RV32 + PORT( + I_clk : IN std_logic; + I_en : IN std_logic; + I_dataInst : IN std_logic_vector(31 downto 0); + O_selRS1 : OUT std_logic_vector(4 downto 0); + O_selRS2 : OUT std_logic_vector(4 downto 0); + O_selD : OUT std_logic_vector(4 downto 0); + O_dataIMM : OUT std_logic_vector(31 downto 0); + O_regDwe : OUT std_logic; + O_aluOp : OUT std_logic_vector(6 downto 0); + O_aluFunc : OUT std_logic_vector(15 downto 0); -- ALU function + O_memOp : out STD_LOGIC_VECTOR(4 downto 0) + ); + END COMPONENT; + + + component alu_RV32I is + Port ( I_clk : in STD_LOGIC; + I_en : in STD_LOGIC; + I_dataA : in STD_LOGIC_VECTOR (XLEN32M1 downto 0); + I_dataB : in STD_LOGIC_VECTOR (XLEN32M1 downto 0); + I_dataDwe : in STD_LOGIC; + I_aluop : in STD_LOGIC_VECTOR (4 downto 0); + I_aluFunc : in STD_LOGIC_VECTOR (15 downto 0); + I_PC : in STD_LOGIC_VECTOR (XLEN32M1 downto 0); + I_dataIMM : in STD_LOGIC_VECTOR (XLEN32M1 downto 0); + O_dataResult : out STD_LOGIC_VECTOR (XLEN32M1 downto 0); + O_branchTarget : out STD_LOGIC_VECTOR (XLEN32M1 downto 0); + O_dataWriteReg : out STD_LOGIC; + O_shouldBranch : out std_logic + + ); + end component; + + COMPONENT register_set + PORT( + I_clk : IN std_logic; + I_en : IN std_logic; + I_dataD : IN std_logic_vector(31 downto 0); + I_selRS1 : IN std_logic_vector(4 downto 0); + I_selRS2 : IN std_logic_vector(4 downto 0); + I_selD : IN std_logic_vector(4 downto 0); + I_we : IN std_logic; + O_dataA : OUT std_logic_vector(31 downto 0); + O_dataB : OUT std_logic_vector(31 downto 0) + ); + END COMPONENT; + + + + COMPONENT mem_controller + PORT( + I_clk : IN std_logic; + I_reset : IN std_logic; + O_ready : OUT std_logic; + I_execute : IN std_logic; + I_dataWe : IN std_logic; + I_address : IN std_logic_vector(XLENM1 downto 0); + I_data : IN std_logic_vector(XLENM1 downto 0); + I_dataByteEn : IN std_logic_vector(1 downto 0); + O_data : OUT std_logic_vector(XLENM1 downto 0); + O_dataReady : OUT std_logic; + MEM_I_ready : IN std_logic; + MEM_O_cmd : OUT std_logic; + MEM_O_we : OUT std_logic; + MEM_O_byteEnable : OUT std_logic_vector(1 downto 0); + MEM_O_addr : OUT std_logic_vector(XLENM1 downto 0); + MEM_O_data : OUT std_logic_vector(XLENM1 downto 0); + MEM_I_data : IN std_logic_vector(XLENM1 downto 0); + MEM_I_dataReady : IN std_logic + ); + END COMPONENT; + + + signal state : std_logic_vector(6 downto 0) := (others => '0'); + + + signal pcop: std_logic_vector(1 downto 0); + signal in_pc: std_logic_vector(XLENM1 downto 0); + + signal aluFunc: std_logic_vector(15 downto 0); + signal memOp: std_logic_vector(4 downto 0); + + signal branchTarget:std_logic_vector(XLENM1 downto 0) := (others => '0'); + + signal instruction : std_logic_vector(XLENM1 downto 0) := (others => '0'); + signal dataA : std_logic_vector(XLENM1 downto 0) := (others => '0'); + signal dataB : std_logic_vector(XLENM1 downto 0) := (others => '0'); + signal dataDwe : std_logic := '0'; + signal aluop : std_logic_vector(6 downto 0) := (others => '0'); + signal dataIMM : std_logic_vector(XLENM1 downto 0) := (others => '0'); + signal selRS1 : std_logic_vector(4 downto 0) := (others => '0'); + signal selRS2 : std_logic_vector(4 downto 0) := (others => '0'); + signal selD : std_logic_vector(4 downto 0) := (others => '0'); + signal dataregWrite: std_logic := '0'; + signal dataResult : std_logic_vector(XLENM1 downto 0) := (others => '0'); + signal dataWriteReg : std_logic := '0'; + signal shouldBranch : std_logic := '0'; + signal memMode : std_logic := '0'; + signal ram_req_size : std_logic := '0'; + + signal reg_en: std_logic := '0'; + signal reg_we: std_logic := '0'; + + signal registerWriteData : std_logic_vector(XLENM1 downto 0) := (others=>'0'); + + signal en_fetch : std_logic := '0'; + signal en_decode : std_logic := '0'; + signal en_alu : std_logic := '0'; + signal en_memory : std_logic := '0'; + signal en_regwrite : std_logic := '0'; + signal en_stall : std_logic := '0'; + + signal PC : std_logic_vector(XLENM1 downto 0) := (others => '0'); + + signal memctl_ready : std_logic; + signal memctl_execute : std_logic := '0'; + signal memctl_dataWe : std_logic; + signal memctl_address : std_logic_vector(XLENM1 downto 0); + signal memctl_in_data : std_logic_vector(XLENM1 downto 0); + signal memctl_dataByteEn : std_logic_vector(1 downto 0); + signal memctl_out_data : std_logic_vector(XLENM1 downto 0) := (others => '0'); + signal memctl_dataReady : std_logic := '0'; + signal memctl_size : std_logic_vector(1 downto 0); + signal memctl_signExtend: std_logic := '0'; + + signal PCintVec: STD_LOGIC := '0'; + + signal int_idata: STD_LOGIC_VECTOR(XLENM1 downto 0); + signal int_set_idata: STD_LOGIC; + signal int_enabled: std_logic; + signal int_set_irpc: STD_LOGIC; + + signal core_clock:STD_LOGIC := '0'; + +begin + core_clock <= I_clk; + + memctl: mem_controller PORT MAP ( + I_clk => I_clk, + I_reset => I_reset, + + O_ready => memctl_ready, + I_execute => memctl_execute, + I_dataWe => memctl_dataWe, + I_address => memctl_address, + I_data => memctl_in_data, + I_dataByteEn => memctl_dataByteEn, + O_data => memctl_out_data, + O_dataReady => memctl_dataReady, + + MEM_I_ready => MEM_I_ready, + MEM_O_cmd => MEM_O_cmd, + MEM_O_we => MEM_O_we, + MEM_O_byteEnable => MEM_O_byteEnable, + MEM_O_addr => MEM_O_addr, + MEM_O_data => MEM_O_data, + MEM_I_data => MEM_I_data, + MEM_I_dataReady => MEM_I_dataReady + ); + + pcunit: pc_unit Port map ( + I_clk => core_clock, + I_nPC => in_pc, + I_nPCop => pcop, + I_intVec => PCintVec, + O_PC => PC + ); + + control: control_unit PORT MAP ( + I_clk => core_clock, + I_reset => I_reset, + I_aluop => aluop, + + I_int => I_int, + O_int_ack => O_int_ack, + + I_int_enabled => int_enabled, + I_int_mem_data=>MEM_I_data, + O_idata=> int_idata, + O_set_idata=> int_set_idata, + O_set_ipc=> PCintVec, + O_set_irpc => int_set_irpc, + I_ready => memctl_ready, + O_execute => memctl_execute, + I_dataReady => memctl_dataReady, + + O_state => state + ); + + + decoder: decoder_RV32 PORT MAP ( + I_clk => core_clock, + I_en => en_decode, + I_dataInst => instruction, + O_selRS1 => selRS1, + O_selRS2 => selRS2, + O_selD => selD, + O_dataIMM => dataIMM, + O_regDwe => dataDwe, + O_aluOp => aluOp, + O_aluFunc => aluFunc, + O_memOp => memOp + ); + + alu: alu_RV32I PORT MAP ( + I_clk => core_clock, + I_en => en_alu, + I_dataA => dataA, + I_dataB => dataB, + I_dataDwe => dataDwe, + I_aluop => aluop(6 downto 2), + I_aluFunc => aluFunc, + I_PC => PC, + I_dataIMM => dataIMM, + O_dataResult => dataResult, + O_branchTarget => branchTarget, + O_dataWriteReg => dataWriteReg, + O_shouldBranch => shouldBranch + ); + + reg: register_set PORT MAP ( + I_clk => core_clock, + I_en => reg_en, + I_dataD => registerWriteData, + O_dataA => dataA, + O_dataB => dataB, + I_selRS1 => selRS1, + I_selRS2 => selRS2, + I_selD => selD, + I_we => reg_we + ); + + -- Register file controls + reg_en <= en_decode or en_regwrite; + reg_we <= dataWriteReg and en_regwrite; + + -- These are the pipeline stage enable bits + en_fetch <= state(0); + en_decode <= state(1); + en_alu <= state(3); + en_memory <= state(4); + en_regwrite <= state(5); + en_stall <= state(6); + + -- This decides what the next PC should be + pcop <= PCU_OP_RESET when I_reset = '1' else + PCU_OP_ASSIGN when shouldBranch = '1' and state(5) = '1' else + PCU_OP_INC when shouldBranch = '0' and state(5) = '1' else + PCU_OP_NOP; + + -- The input PC is just always the branch target output from ALU + in_pc <= branchTarget; + + -- The debug output just allows some internal state to be visible outside the core black box + O_DBG <= "000" & memctl_dataReady & "000" & MEM_I_dataReady & "0" & state & registerWriteData(15 downto 0); + + -- Below statements are for memory interface use. + memctl_address <= dataResult when en_memory = '1' else PC; + ram_req_size <= memMode when en_memory = '1' else '0'; + memctl_dataByteEn <= memctl_size when en_memory = '1' else F2_MEM_LS_SIZE_W; + memctl_in_data <= dataB; + memctl_dataWe <= '1' when en_memory = '1' and memOp(4 downto 3) = "11" else '0'; + memctl_size <= memOp(1 downto 0); + memctl_signExtend <= memOp(2); + + -- This chooses to write registers with memory data or ALU data + registerWriteData <= memctl_out_data when memOp(4 downto 3) = "10" else dataResult; + + -- The instructions are delivered from memctl + instruction <= memctl_out_data; + +end Behavioral; + diff --git a/vhdl/mem_controller.vhd b/vhdl/mem_controller.vhd new file mode 100644 index 0000000..b41feb0 --- /dev/null +++ b/vhdl/mem_controller.vhd @@ -0,0 +1,101 @@ +---------------------------------------------------------------------------------- +-- Project Name: RPU +-- Description: Memory controller unit of RPU +-- +-- Very simple. Allows for delays in reads, whilsts writes go through immediately. +-- MEM_ signals are expected to be exposted to any SoC fabric. +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +library work; +use work.constants.all; + + +entity mem_controller is + Port ( + I_clk : in STD_LOGIC; + I_reset : in STD_LOGIC; + + O_ready : out STD_LOGIC; + I_execute: in STD_LOGIC; + I_dataWe : in STD_LOGIC; + I_address : in STD_LOGIC_VECTOR (XLENM1 downto 0); + I_data : in STD_LOGIC_VECTOR (XLENM1 downto 0); + I_dataByteEn : in STD_LOGIC_VECTOR(1 downto 0); + O_data : out STD_LOGIC_VECTOR (XLENM1 downto 0); + O_dataReady: out STD_LOGIC; + + MEM_I_ready: in STD_LOGIC; + MEM_O_cmd: out STD_LOGIC; + MEM_O_we : out STD_LOGIC; + MEM_O_byteEnable : out STD_LOGIC_VECTOR (1 downto 0); + MEM_O_addr : out STD_LOGIC_VECTOR (XLENM1 downto 0); + MEM_O_data : out STD_LOGIC_VECTOR (XLENM1 downto 0); + MEM_I_data : in STD_LOGIC_VECTOR (XLENM1 downto 0); + MEM_I_dataReady : in STD_LOGIC + ); +end mem_controller; + +architecture Behavioral of mem_controller is + + signal we : std_logic := '0'; + signal addr : STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000000"; + signal indata: STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000000"; + signal byteEnable: STD_LOGIC_VECTOR ( 1 downto 0) := "11"; + signal cmd : STD_LOGIC := '0'; + signal state: integer := 0; + + signal ready: STD_LOGIC := '0'; + +begin + + process (I_clk, I_execute) + begin + if rising_edge(I_clk) then + if I_reset = '1' then + we <= '0'; + cmd <= '0'; + state <= 0; + O_dataReady <= '0'; + elsif state = 0 and I_execute = '1' and MEM_I_ready = '1' then + we <= I_dataWe; + addr <= I_address; + indata <= I_data; + byteEnable <= I_dataByteEn; + cmd <= '1'; + O_dataReady <= '0'; + if I_dataWe = '0' then + state <= 3;-- read + else + state <= 2;-- write + end if; + elsif state = 3 then + cmd <= '0'; + state <= 1; + elsif state = 1 then + cmd <= '0'; + if MEM_I_dataReady = '1' then + O_dataReady <= '1'; + O_data <= MEM_I_data; + state <= 2; + end if; + elsif state = 2 then + cmd <= '0'; + state <= 0; + O_dataReady <= '0'; + end if; + end if; + end process; + + O_ready <= ( MEM_I_ready and not I_execute ) when state = 0 else '0'; + + MEM_O_cmd <= cmd; + MEM_O_byteEnable <= byteEnable; + MEM_O_data <= indata; + MEM_O_addr <= addr; + MEM_O_we <= we; + +end Behavioral; + diff --git a/vhdl/pc_unit.vhd b/vhdl/pc_unit.vhd new file mode 100644 index 0000000..e824323 --- /dev/null +++ b/vhdl/pc_unit.vhd @@ -0,0 +1,52 @@ +---------------------------------------------------------------------------------- +-- Project Name: RPU +-- Description: Program Counter unit of RPU +-- +-- Simple black box for holding and manipulating the PC +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +use IEEE.NUMERIC_STD.ALL; + +library work; +use work.constants.all; + +entity pc_unit is + Port ( + I_clk : in STD_LOGIC; + I_nPC : in STD_LOGIC_VECTOR (XLENM1 downto 0); + I_nPCop : in STD_LOGIC_VECTOR (1 downto 0); + I_intVec: in STD_LOGIC; + O_PC : out STD_LOGIC_VECTOR (XLENM1 downto 0) + ); +end pc_unit; + +architecture Behavioral of pc_unit is + signal current_pc: std_logic_vector( XLENM1 downto 0) := ADDR_RESET; +begin + + process (I_clk) + begin + if rising_edge(I_clk) then + case I_nPCop is + when PCU_OP_NOP => -- NOP, keep PC the same/halt + if I_intVec = '1' then -- in a NOP, you can get intterupts. check. + current_pc <= ADDR_INTVEC;-- set PC to interrupt vector; + end if; + when PCU_OP_INC => -- increment + current_pc <= std_logic_vector(unsigned(current_pc) + 4); -- 32bit byte addressing + when PCU_OP_ASSIGN => -- set from external input + current_pc <= I_nPC; + when PCU_OP_RESET => -- Reset + current_pc <= ADDR_RESET; + when others => + end case; + end if; + end process; + + O_PC <= current_pc; + +end Behavioral; + diff --git a/vhdl/register_set.vhd b/vhdl/register_set.vhd new file mode 100644 index 0000000..9208463 --- /dev/null +++ b/vhdl/register_set.vhd @@ -0,0 +1,49 @@ +---------------------------------------------------------------------------------- +-- Project Name: RISC-V CPU +-- Description: Register file unit +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +library work; +use work.constants.all; + +entity register_set is + Port ( + I_clk : in STD_LOGIC; + I_en : in STD_LOGIC; + I_dataD : in STD_LOGIC_VECTOR (XLENM1 downto 0); -- Data to write to regD + I_selRS1 : in STD_LOGIC_VECTOR (4 downto 0); -- Select line for regRS1 + I_selRS2 : in STD_LOGIC_VECTOR (4 downto 0); -- Select line for regRS2 + I_selD : in STD_LOGIC_VECTOR (4 downto 0); -- Select line for regD + I_we : in STD_LOGIC; -- Write enable for regD + O_dataA : out STD_LOGIC_VECTOR (XLENM1 downto 0);-- regRS1 data out + O_dataB : out STD_LOGIC_VECTOR (XLENM1 downto 0) -- regRS2 data out + ); +end register_set; + +architecture Behavioral of register_set is + type store_t is array (0 to 31) of std_logic_vector(XLENM1 downto 0); + signal regs: store_t := (others => X"00000000"); + signal dataAout: STD_LOGIC_VECTOR (XLENM1 downto 0) := (others=>'0'); + signal dataBout: STD_LOGIC_VECTOR (XLENM1 downto 0) := (others=>'0'); +begin + + process(I_clk, I_en) + begin + if rising_edge(I_clk) and I_en='1' then + dataAout <= regs(to_integer(unsigned(I_selRS1))); + dataBout <= regs(to_integer(unsigned(I_selRS2))); + if (I_we = '1') then + regs(to_integer(unsigned(I_selD))) <= I_dataD; + end if; + end if; + end process; + + O_dataA <= dataAout; + O_dataB <= dataBout; + +end Behavioral; + diff --git a/vhdl/unit_alu_RV32_I.vhd b/vhdl/unit_alu_RV32_I.vhd new file mode 100644 index 0000000..881c7cb --- /dev/null +++ b/vhdl/unit_alu_RV32_I.vhd @@ -0,0 +1,215 @@ +---------------------------------------------------------------------------------- +-- Project Name: RISC-V CPU +-- Description: ALU unit suitable for RV32I operational use +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +use IEEE.NUMERIC_STD.ALL; + +library work; +use work.constants.all; + +entity alu_RV32I is + Port ( + I_clk : in STD_LOGIC; + I_en : in STD_LOGIC; + I_dataA : in STD_LOGIC_VECTOR (XLEN32M1 downto 0); + I_dataB : in STD_LOGIC_VECTOR (XLEN32M1 downto 0); + I_dataDwe : in STD_LOGIC; + I_aluop : in STD_LOGIC_VECTOR (4 downto 0); + I_aluFunc : in STD_LOGIC_VECTOR (15 downto 0); + I_PC : in STD_LOGIC_VECTOR (XLEN32M1 downto 0); + I_dataIMM : in STD_LOGIC_VECTOR (XLEN32M1 downto 0); + O_dataResult : out STD_LOGIC_VECTOR (XLEN32M1 downto 0); + O_branchTarget : out STD_LOGIC_VECTOR (XLEN32M1 downto 0); + O_dataWriteReg : out STD_LOGIC; + O_shouldBranch : out std_logic + ); +end alu_RV32I; + +architecture Behavioral of alu_RV32I is + -- The internal register for results of operations. + -- 32 bit + carry/overflow + + signal s_branchTarget : STD_LOGIC_VECTOR (XLEN32M1 downto 0) := (others => '0'); + signal s_result: STD_LOGIC_VECTOR(XLEN32M1+2 downto 0) := (others => '0'); + signal s_shouldBranch: STD_LOGIC := '0'; +begin + + process (I_clk, I_en) + begin + if rising_edge(I_clk) and I_en = '1' then + O_dataWriteReg <= I_dataDwe; + case I_aluop is + when OPCODE_OPIMM => + s_shouldBranch <= '0'; + case I_aluFunc(2 downto 0) is + when F3_OPIMM_ADDI => + s_result(31 downto 0) <= std_logic_vector(signed( I_dataA) + signed( I_dataIMM)); + + when F3_OPIMM_XORI => + s_result(31 downto 0) <= I_dataA xor I_dataIMM; + + when F3_OPIMM_ORI => + s_result(31 downto 0) <= I_dataA or I_dataIMM; + + when F3_OPIMM_ANDI => + s_result(31 downto 0) <= I_dataA and I_dataIMM; + + when F3_OPIMM_SLTI => + if signed(I_dataA) < signed(I_dataIMM) then + s_result(31 downto 0) <= X"00000001"; + else + s_result(31 downto 0) <= X"00000000"; + end if; + + when F3_OPIMM_SLTIU => + if unsigned(I_dataA) < unsigned(I_dataIMM) then + s_result(31 downto 0) <= X"00000001"; + else + s_result(31 downto 0) <= X"00000000"; + end if; + + when F3_OPIMM_SLLI => + s_result(31 downto 0) <= std_logic_vector(shift_left(unsigned(I_dataA), to_integer(unsigned(I_dataIMM(4 downto 0))))); + + when F3_OPIMM_SRLI => + case I_aluFunc(9 downto 3) is + when F7_OPIMM_SRLI => + s_result(31 downto 0) <= std_logic_vector(shift_right(unsigned(I_dataA), to_integer(unsigned(I_dataIMM(4 downto 0))))); + when F7_OPIMM_SRAI => + s_result(31 downto 0) <= std_logic_vector(shift_right(signed(I_dataA), to_integer(unsigned(I_dataIMM(4 downto 0))))); + when others=> + end case; + when others => + end case; + + when OPCODE_OP => + case I_aluFunc(9 downto 0) is + when F7_OP_ADD & F3_OP_ADD => + s_result(31 downto 0) <= std_logic_vector(signed( I_dataA) + signed( I_dataB)); + + when F7_OP_SUB & F3_OP_SUB => + s_result(31 downto 0) <= std_logic_vector(signed( I_dataA) - signed( I_dataB)); + + when F7_OP_SLT & F3_OP_SLT => + if signed(I_dataA) < signed(I_dataB) then + s_result(31 downto 0) <= X"00000001"; + else + s_result(31 downto 0) <= X"00000000"; + end if; + + when F7_OP_SLTU & F3_OP_SLTU => + if unsigned(I_dataA) < unsigned(I_dataB) then + s_result(31 downto 0) <= X"00000001"; + else + s_result(31 downto 0) <= X"00000000"; + end if; + + when F7_OP_XOR & F3_OP_XOR => + s_result(31 downto 0) <= I_dataA xor I_dataB; + + when F7_OP_OR & F3_OP_OR => + s_result(31 downto 0) <= I_dataA or I_dataB; + + when F7_OP_AND & F3_OP_AND => + s_result(31 downto 0) <= I_dataA and I_dataB; + + when F7_OP_SLL & F3_OP_SLL => + s_result(31 downto 0) <= std_logic_vector(shift_left(unsigned(I_dataA), to_integer(unsigned(I_dataB(4 downto 0))))); + + when F7_OP_SRL & F3_OP_SRL => + s_result(31 downto 0) <= std_logic_vector(shift_right(unsigned(I_dataA), to_integer(unsigned(I_dataB(4 downto 0))))); + + when F7_OP_SRA & F3_OP_SRA => + s_result(31 downto 0) <= std_logic_vector(shift_right(signed(I_dataA), to_integer(unsigned(I_dataB(4 downto 0))))); + + when others=> + s_result <= "00" & X"CDC1FEF1"; + end case; + + s_shouldBranch <= '0'; + + when OPCODE_LOAD | OPCODE_STORE => + s_shouldBranch <= '0'; + s_result(31 downto 0) <= std_logic_vector(signed( I_dataA) + signed( I_dataIMM)); + + when OPCODE_JALR => + s_branchTarget <= std_logic_vector(signed( I_dataA) + signed( I_dataIMM)); + s_shouldBranch <= '1'; + s_result(31 downto 0) <= std_logic_vector(signed( I_PC) + 4); + + when OPCODE_JAL => + s_branchTarget <= std_logic_vector(signed( I_PC) + signed( I_dataIMM)); + s_shouldBranch <= '1'; + s_result(31 downto 0) <= std_logic_vector(signed( I_PC) + 4); + + when OPCODE_LUI => + s_shouldBranch <= '0'; + s_result(31 downto 0) <= I_dataIMM; + + when OPCODE_AUIPC => + s_shouldBranch <= '0'; + s_result(31 downto 0) <= std_logic_vector( signed( I_PC) + signed( I_dataIMM)); + + when OPCODE_BRANCH => + s_branchTarget <= std_logic_vector(signed( I_PC) + signed( I_dataIMM)); + case I_aluFunc(2 downto 0) is + when F3_BRANCH_BEQ => + if I_dataA = I_dataB then + s_shouldBranch <= '1'; + else + s_shouldBranch <= '0'; + end if; + + when F3_BRANCH_BNE => + if I_dataA /= I_dataB then + s_shouldBranch <= '1'; + else + s_shouldBranch <= '0'; + end if; + + when F3_BRANCH_BLT => + if signed(I_dataA) < signed(I_dataB) then + s_shouldBranch <= '1'; + else + s_shouldBranch <= '0'; + end if; + + when F3_BRANCH_BGE => + if signed(I_dataA) >= signed(I_dataB) then + s_shouldBranch <= '1'; + else + s_shouldBranch <= '0'; + end if; + + when F3_BRANCH_BLTU => + if unsigned(I_dataA) < unsigned(I_dataB) then + s_shouldBranch <= '1'; + else + s_shouldBranch <= '0'; + end if; + + when F3_BRANCH_BGEU => + if unsigned(I_dataA) >= unsigned(I_dataB) then + s_shouldBranch <= '1'; + else + s_shouldBranch <= '0'; + end if; + + when others => + end case; + + when others => + s_result <= "00" & X"CDCDFEFE"; + end case; + end if; + end process; + + O_dataResult <= s_result(XLEN32M1 downto 0); + O_shouldBranch <= s_shouldBranch; + O_branchTarget <= s_branchTarget; + +end Behavioral; \ No newline at end of file diff --git a/vhdl/unit_decoder_RV32I.vhd b/vhdl/unit_decoder_RV32I.vhd new file mode 100644 index 0000000..7ec7b9e --- /dev/null +++ b/vhdl/unit_decoder_RV32I.vhd @@ -0,0 +1,127 @@ +---------------------------------------------------------------------------------- +-- Project Name: RISC-V CPU +-- Description: decoder unit RV32I +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +library work; +use work.constants.all; + +entity decoder_RV32 is + Port ( + I_clk : in STD_LOGIC; + I_en : in STD_LOGIC; + I_dataInst : in STD_LOGIC_VECTOR (31 downto 0); -- Instruction to be decoded + O_selRS1 : out STD_LOGIC_VECTOR (4 downto 0); -- Selection out for regrs1 + O_selRS2 : out STD_LOGIC_VECTOR (4 downto 0); -- Selection out for regrs2 + O_selD : out STD_LOGIC_VECTOR (4 downto 0); -- Selection out for regD + O_dataIMM : out STD_LOGIC_VECTOR (31 downto 0); -- Immediate value out + O_regDwe : out STD_LOGIC; -- RegD wrtite enable + O_aluOp : out STD_LOGIC_VECTOR (6 downto 0); -- ALU opcode + O_aluFunc : out STD_LOGIC_VECTOR (15 downto 0); -- ALU function + O_memOp : out STD_LOGIC_VECTOR(4 downto 0) -- Memory operation + ); +end decoder_RV32; + +architecture Behavioral of decoder_RV32 is +begin + -- Register selects for reads are async + O_selRS1 <= I_dataInst(R1_START downto R1_END); + O_selRS2 <= I_dataInst(R2_START downto R2_END); + + process (I_clk, I_en) + begin + if rising_edge(I_clk) and I_en = '1' then + + O_selD <= I_dataInst(RD_START downto RD_END); + + O_aluOp <= I_dataInst(OPCODE_START downto OPCODE_END); + + O_aluFunc <= "000000" & I_dataInst(FUNCT7_START downto FUNCT7_END) + & I_dataInst(FUNCT3_START downto FUNCT3_END); + + case I_dataInst(OPCODE_START downto OPCODE_END_2) is + when OPCODE_LUI => + O_regDwe <= '1'; + O_memOp <= "00000"; + O_dataIMM <= I_dataInst(IMM_U_START downto IMM_U_END) + & "000000000000"; + when OPCODE_AUIPC => + O_regDwe <= '1'; + O_memOp <= "00000"; + O_dataIMM <= I_dataInst(IMM_U_START downto IMM_U_END) + & "000000000000"; + when OPCODE_JAL => + if I_dataInst(RD_START downto RD_END) = "00000" then + O_regDwe <= '0'; + else + O_regDwe <= '1'; + end if; + O_memOp <= "00000"; + if I_dataInst(IMM_U_START) = '1' then + O_dataIMM <= "111111111111" & I_dataInst(19 downto 12) & I_dataInst(20) & I_dataInst(30 downto 21) & '0'; + else + O_dataIMM <= "000000000000" & I_dataInst(19 downto 12) & I_dataInst(20) & I_dataInst(30 downto 21) & '0'; + end if; + when OPCODE_JALR => + if I_dataInst(RD_START downto RD_END) = "00000" then + O_regDwe <= '0'; + else + O_regDwe <= '1'; + end if; + O_memOp <= "00000"; + if I_dataInst(IMM_U_START) = '1' then + O_dataIMM <= X"FFFF" & "1111" & I_dataInst(IMM_I_START downto IMM_I_END); + else + O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_I_START downto IMM_I_END); + end if; + when OPCODE_OPIMM => + O_regDwe <= '1'; + O_memOp <= "00000"; + if I_dataInst(IMM_U_START) = '1' then + O_dataIMM <= X"FFFF" & "1111" & I_dataInst(IMM_I_START downto IMM_I_END); + else + O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_I_START downto IMM_I_END); + end if; + when OPCODE_LOAD => + O_regDwe <= '1'; + O_memOp <= "10" & I_dataInst(FUNCT3_START downto FUNCT3_END); + if I_dataInst(IMM_U_START) = '1' then + O_dataIMM <= X"FFFF" & "1111" & I_dataInst(IMM_I_START downto IMM_I_END); + else + O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_I_START downto IMM_I_END); + end if; + when OPCODE_STORE => + O_regDwe <= '0'; + O_memOp <= "11" & I_dataInst(FUNCT3_START downto FUNCT3_END); + if I_dataInst(IMM_U_START) = '1' then + O_dataIMM <= X"FFFF" & "1111" & I_dataInst(IMM_S_A_START downto IMM_S_A_END) & I_dataInst(IMM_S_B_START downto IMM_S_B_END); + else + O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_S_A_START downto IMM_S_A_END) & I_dataInst(IMM_S_B_START downto IMM_S_B_END); + end if; + when OPCODE_BRANCH => + O_regDwe <= '0'; + O_memOp <= "00000"; + if I_dataInst(IMM_U_START) = '1' then + O_dataIMM <= X"FFFF" & "1111" & I_dataInst(7) & I_dataInst(30 downto 25) & I_dataInst(11 downto 8) & '0'; + else + O_dataIMM <= X"0000" & "0000" & I_dataInst(7) & I_dataInst(30 downto 25) & I_dataInst(11 downto 8) & '0'; + end if; + when OPCODE_MISCMEM => + O_regDwe <= '0'; + O_memOp <= "01000"; + O_dataIMM <= I_dataInst; + when others => + O_memOp <= "00000"; + O_regDwe <= '1'; + O_dataIMM <= I_dataInst(IMM_I_START downto IMM_S_B_END) + & "0000000"; + end case; + end if; + end process; + +end Behavioral; +