This commit is contained in:
2024-01-29 19:13:01 +01:00
commit d339d53c39
23 changed files with 2995 additions and 0 deletions

53
src/alu.vhd Normal file
View File

@@ -0,0 +1,53 @@
-- alu.vhd
-- Created on: Mo 21. Nov 11:23:36 CET 2022
-- Author(s): Carl Ries, Yannick Reiß, Alexander Graf
-- Content: ALU
library IEEE;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.riscv_types.all;
entity alu is
port (
alu_opc : in aluOP; -- alu opcode.
input1 : in word; -- input1 of alu (reg1 / pc address) rs1
input2 : in word; -- input2 of alu (reg2 / immediate) rs2
result : out word -- alu output.
);
end alu;
-- Architecture implementation of alu: implements operatings mode
architecture implementation of alu is
begin
-- Process log that fetches the opcode and executes it
log : process (alu_opc, input1, input2) -- runs only, when all changed
begin
case alu_opc is
when uNOP => result <= std_logic_vector(to_unsigned(0, wordWidth)); -- no operations
when uADD => result <= std_logic_vector(unsigned(input1) + unsigned( input2 )); -- addition
when uSUB => result <= std_logic_vector(signed(input1) - signed( input2 )); -- subtraction
when uSLL => result <= std_logic_vector(unsigned(input1) sll 1); -- shift left logical
when uSLT =>
if(signed( input1 ) < signed( input2 )) then
result <= std_logic_vector(to_unsigned(1, wordWidth));
else
result <= std_logic_vector(to_unsigned(0, wordWidth));
end if; -- Set lower than
when uSLTU =>
if(unsigned( input1 ) < unsigned( input2 )) then
result <= std_logic_vector(to_unsigned(1, wordWidth));
else
result <= std_logic_vector(to_unsigned(0, wordWidth));
end if; -- Set lower than unsigned
when uXOR => result <= input1 xor input2; -- exclusive or
when uSRL => result <= std_logic_vector(unsigned(input1) srl 1); -- shift right logical
when uSRA => result <= std_logic_vector(to_stdlogicvector(to_bitvector(input1) sra 1)); -- shift right arithmetic
when uOR => result <= input1 or input2; -- or
when uAND => result <= input1 and input2; -- and
when others => result <= std_logic_vector(to_unsigned(0, wordWidth)); -- other operations return zero
end case;
end process;
end implementation;

69
src/branch.vhd Normal file
View File

@@ -0,0 +1,69 @@
-- branch.vhd
-- Created on: 19:01:2023
-- Author(s): Yannick Reiß
-- Copyright: WTFPL
-- Content: Entity branch - enable B-types in CPU
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library work;
use work.riscv_types.all;
entity Branch is
port (
op_code : in uOP;
reg1 : in word;
reg2 : in word;
jmp_enable : out one_bit
);
end Branch;
architecture arch of Branch is
begin
branch_process : process(op_code)
begin
case op_code is
when uBEQ =>
if reg1 = reg2 then
jmp_enable <= "1";
else
jmp_enable <= "0";
end if;
when uBNE =>
if not (reg1 = reg2) then
jmp_enable <= "1";
else
jmp_enable <= "0";
end if;
when uBLT =>
if signed(reg1) < signed(reg2) then
jmp_enable <= "1";
else
jmp_enable <= "0";
end if;
when uBGE =>
if signed(reg1) >= signed(reg2) then
jmp_enable <= "1";
else
jmp_enable <= "0";
end if;
when uBLTU =>
if unsigned(reg1) < unsigned(reg2) then
jmp_enable <= "1";
else
jmp_enable <= "0";
end if;
when uBGEU =>
if unsigned(reg1) >= unsigned(reg2) then
jmp_enable <= "1";
else
jmp_enable <= "0";
end if;
when others =>
jmp_enable <= "0";
end case;
end process; -- branch
end architecture; -- arch

359
src/cpu.vhd Normal file
View File

@@ -0,0 +1,359 @@
-- cpu.vhd
-- Created on: Mo 19. Dez 11:07:17 CET 2022
-- Author(s): Yannick Reiß, Carl Ries, Alexander Graf
-- Content: Entity cpu
library IEEE;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.riscv_types.all;
-- Entity cpu: path implementation of RISC-V cpu
entity cpu is
port(
clk : in std_logic; -- clk to control the unit
-- Led Output
led : out std_logic_vector(15 downto 0); -- output to 16 LEDS
-- RGB Output
RGB1 : out std_logic_vector(2 downto 0); -- output to RGB 1
RGB2 : out std_logic_vector(2 downto 0) -- output to RGB 2
);
end cpu;
-- Architecture implementation of c: control and connect different parts of cpu
architecture implementation of cpu is
component pc
port(
clk : in std_logic; -- Clock input for timing
en_pc : in one_bit; -- activates PC
addr_calc : in ram_addr_t; -- Address from ALU
doJump : in one_bit; -- Jump to Address
addr : out ram_addr_t -- Address to Decoder
);
end component;
component ram
port(
clk : in std_logic; -- Clock input for timing
instructionAdr : in ram_addr_t; -- Address instruction
dataAdr : in ram_addr_t; -- Address data
writeEnable : in one_bit; -- Read or write mode
dataIn : in word; -- Write data
instruction : out word; -- Get instruction
dataOut : out word -- Read data
);
end component;
component alu
port (
alu_opc : in aluOP; -- alu opcode.
input1 : in word; -- input1 of alu (reg1 / pc address) rs1
input2 : in word; -- input2 of alu (reg2 / immediate) rs2
result : out word -- alu output.
);
end component;
component decoder
port(
instrDecode : in instruction; -- Instruction from instruction memory
op_code : out uOP; -- alu opcode
regOp1 : out reg_idx; -- Rj: first register to read
regOp2 : out reg_idx; -- Rk: second register to read
regWrite : out reg_idx -- Ri: the register to write to
);
end component;
component imm
port (
instruction : in instruction;
opcode : in uOP;
immediate : out word
);
end component;
component registers
port(
clk : in std_logic; -- input for clock (control device)
en_reg_wb : in one_bit; -- enable register write back (?)
data_in : in word; -- Data to be written into the register
wr_idx : in reg_idx; -- register to write to
r1_idx : in reg_idx; -- first register to read from
r2_idx : in reg_idx; -- second register to read from
write_enable : in one_bit; -- enable writing to wr_idx
r1_out : out word; -- data from first register
r2_out : out word; -- data from second register
led_out : out word -- output led
);
end component;
component Branch
port(
op_code : in uOP;
reg1 : in word;
reg2 : in word;
jmp_enable : out one_bit
);
end component;
-- SIGNALS GLOBAL
signal s_clock : std_logic;
signal s_reg_wb_enable : one_bit; --enables: register writeback
signal s_reg_wr_enable : one_bit; --enables: register write to index
signal s_pc_enable : one_bit; --enables: pc
signal s_pc_jump_enable : one_bit; --enables: pc jump to address
signal s_ram_enable : one_bit; --enables: ram write enalbe
signal s_led_out : word := "10110011100001110111010110101110"; -- stores the exact output
-- decoder -> registers
signal s_idx_1 : reg_idx;
signal s_idx_2 : reg_idx;
signal s_idx_wr : reg_idx;
-- decoder -> imm ( + decoder)
signal s_opcode : uOP;
-- register -> alu
signal s_reg_data1 : word;
signal s_reg_data2 : word;
-- pc -> ram
signal s_instAdr : ram_addr_t;
signal s_cycle_cnt : cpuStates := stIF;
signal s_branch_jump_enable : one_bit;
-- alu -> ram + register
signal s_alu_data : word;
-- ram -> register
signal s_ram_data : word;
--ram -> decoder + imm
signal s_inst : instruction;
signal s_data_in_addr : ram_addr_t;
-- v dummy signals below v
--imm -> ???
signal s_immediate : word;
-- ??? -> alu
signal X_aluOP : aluOP;
-- ??? -> alu
signal X_addr_calc : ram_addr_t;
-- Clock signals
signal reset : std_logic;
signal locked : std_logic;
-------------------------
-- additional ALU signals
-------------------------
signal aluIn1 : word;
signal aluIn2 : word;
-------------------------
-- additional REG signals
-------------------------
signal reg_data_in : word;
begin
s_clock <= clk;
decoder_RISCV : decoder
port map(
instrDecode => s_inst,
op_code => s_opcode,
regOp1 => s_idx_1,
regOp2 => s_idx_2,
regWrite => s_idx_wr
);
registers_RISCV : registers
port map(
clk => s_clock,
en_reg_wb => s_reg_wb_enable,
data_in => reg_data_in,
wr_idx => s_idx_wr,
r1_idx => s_idx_1,
r2_idx => s_idx_2,
write_enable => s_reg_wr_enable,
r1_out => s_reg_data1,
r2_out => s_reg_data2,
led_out => s_led_out
);
imm_RISCV : imm
port map(
instruction => s_inst,
opcode => s_opcode,
immediate => s_immediate
);
pc_RISCV : pc
port map(
clk => s_clock,
en_pc => s_pc_enable,
addr_calc => X_addr_calc,
doJump => s_pc_jump_enable,
addr => s_instAdr
);
alu_RISCV : alu
port map(
alu_opc => X_aluOP, -- switch case from s_opcode
input1 => aluIn1,
input2 => aluIn2,
result => s_alu_data
);
ram_RISCV : ram
port map(
clk => s_clock, --
instructionAdr => s_instAdr, -- instruction from pc
dataAdr => s_data_in_addr, -- data address from alu
writeEnable => s_ram_enable, --
dataIn => s_reg_data2, -- data from register
instruction => s_inst, --
dataOut => s_ram_data
);
branch_RISCV : Branch
port map(
op_code => s_opcode,
reg1 => aluIn1,
reg2 => aluIn2,
jmp_enable => s_branch_jump_enable
);
------------------------
-- ALU opcode and input connection
------------------------
-- Process alu_control set alu opcode
-----------------------------------------
-- Output
-----------------------------------------
led <= s_led_out(15 downto 0);
RGB1 <= s_clock & s_clock & s_clock;
alu_control : process (s_immediate, s_opcode, s_reg_data1, s_reg_data2) -- runs only, when item in list changed
begin
-- Connect opcode
case s_opcode is
when uADD | uADDI => X_aluOP <= uADD;
when uSUB => X_aluOP <= uSUB;
when uSLL | uSLLI => X_aluOP <= uSLL;
when uSLT | uSLTI => X_aluOP <= uSLT;
when uSLTU | uSLTIU => X_aluOP <= uSLTU;
when uXOR | uXORI => X_aluOP <= uXOR;
when uSRL | uSRLI => X_aluOP <= uSRL;
when uSRA | uSRAI => X_aluOP <= uSRA;
when uOR | uORI => X_aluOP <= uOR;
when uAND | uANDI => X_aluOP <= uAND;
when others => X_aluOP <= uNOP;
end case;
-- connect input1
case s_opcode is
-- add nonstandard inputs for aluIn1 here
when others => aluIn1 <= s_reg_data1;
end case;
-- TODO: why line from pc to alu inp1?
-- connect input 2
case s_opcode is
when uADDI | uSLTI | uSLTIU | uXORI | uORI | uANDI => aluIn2 <= s_immediate;
when others => aluIn2 <= s_reg_data2; -- use rs2 as default
end case;
end process;
-- Process register_data_input select which input is needed for register
register_data_input : process (s_cycle_cnt, s_opcode, s_ram_data, s_alu_data) -- runs only, when item in list changed
begin
s_reg_wb_enable <= "0";
case s_opcode is
when uBEQ | uBNE | uBLT | uBGE | uBLTU | uBGEU | uSB | uSH | uSW | uECALL | uNOP => s_reg_wr_enable <= "0";
when others =>
if s_cycle_cnt = stEXEC then
s_reg_wr_enable <= "1";
else
s_reg_wr_enable <= "0";
end if;
end case;
case s_opcode is
when uLB | uLH | uLW | uLBU | uLHU => reg_data_in <= s_ram_data; -- use value from
-- RAM (Load instructions)
when others => reg_data_in <= s_alu_data; -- alu operations as default
end case;
end process;
-- Process pc input
pc_addr_input : process(s_opcode, s_cycle_cnt, s_instAdr, s_immediate)
begin
if s_cycle_cnt = stWB then
s_pc_enable <= "1";
else
s_pc_enable <= "0";
-- X_addr_calc <= s_instAdr; -- should not be necessary, every case option sets X_addr_calc
end if;
case s_opcode is
when uJALR | uJAL =>
s_pc_jump_enable <= "1";
X_addr_calc <= std_logic_vector(signed(s_immediate(11 downto 0)) + signed(s_instAdr));
-- Branch op_codes
when uBEQ | uBNE | uBLT | uBGE | uBLTU | uBGEU =>
-- always load address from immediate on B-Type
X_addr_calc <= std_logic_vector(signed(s_immediate(11 downto 0)) + signed(s_instAdr));
-- check for opcodes and evaluate condition
s_pc_jump_enable <= s_branch_jump_enable;
when others =>
s_pc_jump_enable <= "0";
X_addr_calc <= s_instAdr;
end case;
end process;
-- process ram
ram_input : process(s_opcode, s_cycle_cnt)
begin
s_data_in_addr <= std_logic_vector(signed(s_immediate(11 downto 0)) + signed(s_reg_data1(11 downto 0)));
if s_cycle_cnt = stWB then
case s_opcode is
when uSB | uSH | uSW => s_ram_enable <= "1";
when others => s_ram_enable <= "0";
end case;
else
s_ram_enable <= "0";
end if;
end process;
-- pc cycle control
pc_cycle_control : process(s_clock)
begin
if rising_edge(s_clock) then
case s_cycle_cnt is
when stIF => s_cycle_cnt <= stDEC;
RGB2 <= "001";
when stDEC => s_cycle_cnt <= stOF;
RGB2 <= "010";
when stOF => s_cycle_cnt <= stEXEC;
RGB2 <= "011";
when stEXEC => s_cycle_cnt <= stWB;
RGB2 <= "100";
when others => s_cycle_cnt <= stIF;
RGB2 <= "101";
end case;
end if;
end process pc_cycle_control;
end implementation;

125
src/decoder.vhd Normal file
View File

@@ -0,0 +1,125 @@
-- vsg_off
-- decoder_reloaded.vhd
-- Created on: Do 8. Dez 18:45:24 CET 2022
-- Author(s): Axel, Carsten und Jannis
-- Content: Decoder Version 2 (ständige vollbelegung)
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.riscv_types.all;
-- Entity decode: Decoder currently supporting read operations
entity decoder is
port(
instrDecode : in instruction; -- Instruction from instruction memory
op_code : out uOP; -- alu opcode
regOp1 : out reg_idx; -- Rj: first register to read
regOp2 : out reg_idx; -- Rk: second register to read
regWrite : out reg_idx -- Ri: the register to write to
);
end decoder;
-- Architecture schematic of decode: Split up instruction into registers
architecture decode of decoder is
begin
-- Process decode splits up instruction for alu
process (instrDecode(11 downto 7), instrDecode(14 downto 12),
instrDecode(19 downto 15), instrDecode(24 downto 20),
instrDecode(31 downto 25), instrDecode(6 downto 0)) -- runs only, when instrDecode changed
begin
-- ONLY DECODES RV32I Base Instruction Set
-- op_code (funct7 + funct3 + operand)
case instrDecode(6 downto 0) is
-- R-Type
when "0110011" =>
case instrDecode(14 downto 12) is
when "000" =>
if instrDecode(31 downto 25) = "0000000" then
op_code <= uADD;
else
op_code <= uSUB;
end if; -- ADD / SUB
when "001" => op_code <= uSLL;
when "010" => op_code <= uSLT;
when "011" => op_code <= uSLTU;
when "100" => op_code <= uXOR;
when "101" =>
if instrDecode(31 downto 25) = "0000000" then
op_code <= uSRL;
else
op_code <= uSRA;
end if;
when "110" => op_code <= uOR;
when "111" => op_code <= uAND;
when others => op_code <= uNOP;
end case;
-- I-Type
when "1100111" => op_code <= uJALR;
when "0000011" =>
case instrDecode(14 downto 12) is
when "000" => op_code <= uLB;
when "001" => op_code <= uLH;
when "010" => op_code <= uLW;
when "100" => op_code <= uLBU;
when "101" => op_code <= uLHU;
when others => op_code <= uNOP;
end case;
when "0010011" =>
case instrDecode(14 downto 12) is
when "000" => op_code <= uADDI;
when "001" => op_code <= uSLTI;
when "010" => op_code <= uSLTIU;
when "011" => op_code <= uXORI;
when "100" => op_code <= uORI;
when "101" => op_code <= uANDI;
when others => op_code <= uNOP;
end case;
-- S-Type
when "0100011" =>
case instrDecode(14 downto 12) is
when "000" => op_code <= uSB;
when "001" => op_code <= uSH;
when "010" => op_code <= uSW;
when others => op_code <= uNOP;
end case;
-- B-Type
when "1100011" =>
case instrDecode(14 downto 12) is
when "000" => op_code <= uBEQ;
when "001" => op_code <= uBNE;
when "100" => op_code <= uBLT;
when "101" => op_code <= uBGE;
when "110" => op_code <= uBLTU;
when "111" => op_code <= uBGEU;
when others => op_code <= uNOP;
end case;
-- U-Type
when "0110111" => op_code <= uLUI;
when "0010111" => op_code <= uAUIPC;
-- J-Type
when "1101111" => op_code <= uJAL;
-- Add more Operandtypes here
when others => op_code <= uNOP;
end case;
-- regOp1 (19-15)
regOp1 <= instrDecode(19 downto 15);
-- regOp2 (24-20)
regOp2 <= instrDecode(24 downto 20);
-- regWrite (11-7)
regWrite <= instrDecode(11 downto 7);
end process;
end decode;

54
src/imem.vhd Normal file
View File

@@ -0,0 +1,54 @@
-- imem.vhd
-- Created on: Do 29. Dez 20:44:53 CET 2022
-- Author(s): Yannick Reiß, Alexander Graf, Carl Ries
-- Content: Entity instruction memory as part of ram
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.riscv_types.all;
entity instr_memory is
generic (initMem : ram_t := (others => (others => '0')));
port (clk : in std_logic;
addr_a : in std_logic_vector(ram_addr_size - 3 downto 0);
data_read_a : out std_logic_vector(wordWidth - 1 downto 0);
write_b : in one_bit;
addr_b : in std_logic_vector(ram_addr_size - 3 downto 0);
data_read_b : out std_logic_vector(wordWidth - 1 downto 0);
data_write_b : in std_logic_vector(wordWidth - 1 downto 0)
);
end instr_memory;
-- START:
-- addi x1 x0 1
-- add x2 x0 x0
-- add x3 x0 x0
-- addi x4 x0 2047
-- slli x4 x4 5
-- REG2UP:
-- add x2 x2 x1
-- add x3 x0 x0
-- REG3UP:
-- add x3 x3 x1
-- bgeu x3 x4 REG2UP
-- jal REG3UP
architecture behavioral of instr_memory is
signal store : ram_t :=
(
x"00100093", x"00000133", x"000001b3", x"7ff00213", x"00521213", x"00110133", x"000001b3", x"001181b3", x"fe41fae3", x"ff9ff0ef", others => (others => '0')
);
begin
-- Two synchron read ports
data_read_a <= store(to_integer(unsigned(addr_a(9 downto 2))));
data_read_b <= store(to_integer(unsigned(addr_b(9 downto 2))));
end behavioral;

44
src/imm.vhd Normal file
View File

@@ -0,0 +1,44 @@
library IEEE;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.riscv_types.all;
entity imm is
port (
instruction : in instruction;
opcode : in uOP;
immediate : out word
);
end imm;
-- Architecture slicing of imm: slices immediate out of instruction
architecture slicing of imm is
begin
-- Process immediate slice
process (opcode, instruction)
begin
case opcode is
-- I-Type
when uLB | uLH | uLW | uLBU | uLHU | uADDI | uSLTI | uSLTIU | uXORI | uORI | uANDI => immediate <= std_logic_vector(to_unsigned(0, wordWidth - 12)) & instruction(31 downto 20);
-- S-Type
when uSB | uSH | uSW => immediate <= std_logic_vector(to_unsigned(0, wordWidth-12)) & instruction(31 downto 25) & instruction(11 downto 7);
-- B-Type
when uBEQ | uBNE | uBLT | uBGE | uBLTU | uBGEU => immediate <= std_logic_vector(to_unsigned(0, 19)) & instruction(31) & instruction(7) & instruction(30 downto 25) & instruction(11 downto 8) & "0";
-- U-Type
when uLUI | uAUIPC => immediate <= instruction(31 downto 12) & std_logic_vector(to_unsigned(0, 12));
-- J-Type
when uJAL => immediate <= std_logic_vector(to_unsigned(0, wordWidth - 21)) & instruction(31) & instruction(19 downto 12) & instruction(20) & instruction(30 downto 21) & "0";
when others => immediate <= x"C000FFEE";
end case;
end process;
end slicing;

46
src/pc.vhd Normal file
View File

@@ -0,0 +1,46 @@
-- pc.vhd
-- Created on: Mo 05. Dec 14:21:39 CET 2022
-- Author(s): Carl Ries, Yannick Reiß, Alexander Graf
-- Content: program counter
library IEEE;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.riscv_types.all;
-- Entity PC: entity defining the pins and ports of the programmcounter
entity pc is
port (clk : in std_logic; -- Clock input for timing
en_pc : in one_bit; -- activates PC
addr_calc : in ram_addr_t; -- Address from ALU
doJump : in one_bit; -- Jump to Address
addr : out ram_addr_t -- Address to Decoder
);
end PC;
architecture pro_count of pc is
signal addr_out : ram_addr_t := (others => '0');
signal addr_out_plus : ram_addr_t := (others => '0');
begin
process (clk)
begin
if rising_edge(clk) then
if en_pc = "1" then
-- count
if doJump = "1" then
addr_out <= addr_calc;
-- jump
else
addr_out <= addr_out_plus;
end if;
end if;
end if;
end process;
addr_out_plus <= (std_logic_vector(to_unsigned(to_integer(unsigned(addr_out)) + 4, ram_addr_size)));
addr <= addr_out;
end pro_count;

54
src/ram_block.vhd Executable file
View File

@@ -0,0 +1,54 @@
-- ram_block.vhd
-- Created on: Do 3. Nov 20:06:13 CET 2022
-- Author(s): Alexander Graf, Carl Ries, Yannick Reiß
-- Content: Entity ram_block
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.riscv_types.all;
entity ram_block is
generic (initMem : ram_t := (others => (others => '0')));
port (clk : in std_logic;
addr_a : in std_logic_vector(ram_addr_size - 3 downto 0);
data_read_a : out std_logic_vector(wordWidth - 1 downto 0);
write_b : in one_bit;
addr_b : in std_logic_vector(ram_addr_size - 3 downto 0);
data_read_b : out std_logic_vector(wordWidth - 1 downto 0);
data_write_b : in std_logic_vector(wordWidth - 1 downto 0)
);
end ram_block;
--
architecture behavioral of ram_block is
signal store : ram_t := initMem;
begin
process(clk)
begin
if rising_edge(clk) then
-- One synchron write port
if write_b = "1" then
store(to_integer(unsigned(addr_b(9 downto 2)))) <= data_write_b;
end if;
end if;
end process;
-- Two synchron read ports
data_read_a <= store(to_integer(unsigned(addr_a(9 downto 2))));
data_read_b <= store(to_integer(unsigned(addr_b(9 downto 2))));
end behavioral;

147
src/ram_entity_only.vhd Executable file
View File

@@ -0,0 +1,147 @@
-- Created on: Do 3. Nov 20:11:50 CET 2022
-- Author(s): Alexander Graf, Carl Ries, Yannick Reiß
-- Content: Entity ram and architecture of ram
library work;
use work.riscv_types.all;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- Entity ram: Ram storage
entity ram is
generic (zeros : ram_t := (others => (others => '0')));
port(
clk : in std_logic; -- Clock input for timing
instructionAdr : in ram_addr_t; -- Address instruction
dataAdr : in ram_addr_t; -- Address data
writeEnable : in one_bit; -- Read or write mode
dataIn : in word; -- Write data
instruction : out word; -- Get instruction
dataOut : out word -- Read data
);
end ram;
-- Architecture behavioral of ram: control different ram blocks
architecture behavioral of ram is
-- write signals
signal wr1 : one_bit := "0";
signal wr2 : one_bit := "0";
signal wr3 : one_bit := "0";
signal wr4 : one_bit := "0";
-- instruction signals
signal inst1 : std_logic_vector(wordWidth - 1 downto 0);
signal inst2 : std_logic_vector(wordWidth - 1 downto 0);
signal inst3 : std_logic_vector(wordWidth - 1 downto 0);
signal inst4 : std_logic_vector(wordWidth - 1 downto 0);
-- data signals
signal data1 : std_logic_vector(wordWidth - 1 downto 0);
signal data2 : std_logic_vector(wordWidth - 1 downto 0);
signal data3 : std_logic_vector(wordWidth - 1 downto 0);
signal data4 : std_logic_vector(wordWidth - 1 downto 0);
begin
block1 : entity work.instr_memory(behavioral)
port map (
clk => clk,
addr_a => instructionAdr(ram_addr_size - 3 downto 0),
write_b => wr1,
addr_b => dataAdr(ram_addr_size - 3 downto 0),
data_write_b => dataIn,
data_read_a => inst1,
data_read_b => data1
);
block2 : entity work.ram_block(behavioral)
port map (
clk => clk,
addr_a => instructionAdr(9 downto 0),
write_b => wr2,
addr_b => dataAdr(9 downto 0),
data_write_b => dataIn,
data_read_a => inst2,
data_read_b => data2
);
block3 : entity work.ram_block(behavioral)
port map (
clk => clk,
addr_a => instructionAdr(9 downto 0),
write_b => wr3,
addr_b => dataAdr(9 downto 0),
data_write_b => dataIn,
data_read_a => inst3,
data_read_b => data3
);
block4 : entity work.ram_block(behavioral)
port map (
clk => clk,
addr_a => instructionAdr(9 downto 0),
write_b => wr4,
addr_b => dataAdr(9 downto 0),
data_write_b => dataIn,
data_read_a => inst4,
data_read_b => data4
);
addr_block : process (data1, data2, data3, data4, dataAdr(11 downto 10),
inst1, inst2, inst3, inst4,
instructionAdr(11 downto 10), writeEnable) -- run process addr_block when list changes
begin
-- enable write
case dataAdr(11 downto 10) is
when "00" =>
wr1 <= writeEnable;
wr2 <= "0";
wr3 <= "0";
wr4 <= "0";
when "01" =>
wr1 <= "0";
wr2 <= writeEnable;
wr3 <= "0";
wr4 <= "0";
when "10" =>
wr1 <= "0";
wr2 <= "0";
wr3 <= writeEnable;
wr4 <= "0";
when "11" =>
wr1 <= "0";
wr2 <= "0";
wr3 <= "0";
wr4 <= writeEnable;
when others =>
wr1 <= "0";
wr2 <= "0";
wr3 <= "0";
wr4 <= "0";
end case;
-- instruction data
case instructionAdr(11 downto 10) is
when "00" => instruction <= inst1;
when "01" => instruction <= inst2;
when "10" => instruction <= inst3;
when others => instruction <= inst4;
end case;
-- data data
case dataAdr(11 downto 10) is
when "00" => dataOut <= data1;
when "01" => dataOut <= data2;
when "10" => dataOut <= data3;
when others => dataOut <= data4;
end case;
end process;
end behavioral;

59
src/registers.vhd Normal file
View File

@@ -0,0 +1,59 @@
-- registers.vhd
-- Created on: So 13. Nov 19:06:55 CET 2022
-- Author(s): Alexander Graf, Carl Ries, Yannick Reiß
-- Content: Entity registers
--------------------------------------------------------------
-- important constants and types from riscv_types (LN 104ff.)
--
-- constant reg_adr_size : integer := 5;
-- constant reg_size : integer := 32;
-- type regFile is array (reg_size - 1 downto 0) of word;
--------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.riscv_types.all;
-- Entity registers: entity defining the pins and ports of the registerblock
entity registers is
generic (initRegs : regFile := (others => (others => '0')));
port(
clk : in std_logic; -- input for clock (control device)
en_reg_wb : in one_bit; -- enable register write back (?)
data_in : in word; -- Data to be written into the register
wr_idx : in reg_idx; -- register to write to
r1_idx : in reg_idx; -- first register to read from
r2_idx : in reg_idx; -- second register to read from
write_enable : in one_bit; -- enable writing to wr_idx
r1_out : out word; -- data from first register
r2_out : out word; -- data from second register
led_out : out word -- output reg 2 to led
);
end registers;
-- Architecture structure of registers: read from two, write to one
architecture structure of registers is
signal registerbench : regFile := initRegs;
begin
-- react only on clock changes
process (clk) -- runs only, when clk changed
begin
if rising_edge(clk) then
-- check if write is enabled
if to_integer(unsigned(write_enable)) = 1 then
-- write data_in to wr_idx
registerbench(to_integer(unsigned(wr_idx))) <= data_in;
end if;
registerbench(0) <= std_logic_vector(to_unsigned(0, wordWidth));
end if;
end process;
-- read from both reading registers
r1_out <= registerbench(to_integer(unsigned(r1_idx)));
r2_out <= registerbench(to_integer(unsigned(r2_idx)));
led_out <= registerbench(2);
end structure;

136
src/riscv_types.vhd Executable file
View File

@@ -0,0 +1,136 @@
-- riscv_types.vhd
-- Created on: So 13. Nov 19:05:44 CET 2022
-- Author(s): Carl Ries, Yannick Reiß, Alexander Graf
-- Content: All types needed in processor
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package riscv_types is
-- internal opCodes/enums for instructions
type uOP is (uNOP, uLUI, uAUIPC, uJAL, uJALR, uBEQ, uBNE, uBLT, uBGE, uBLTU, uBGEU,
uLB, uLH, uLW, uLBU, uLHU, uSB, uSH, uSW, uADDI, uSLTI, uSLTIU,
uXORI, uORI, uANDI, uSLLI, uSRLI, uSRAI, uADD, uSUB, uSLL, uSLT,
uSLTU, uXOR, uSRL, uSRA, uOR, uAND, uECALL);
-- internal opCodes/enums for alu instructions
type aluOP is (uNOP, uADD, uSUB, uSLL, uSLT, uSLTU, uXOR, uSRL, uSRA, uOR, uAND);
-- internal instruction formats
type inst_formats is (R, I, S, B, U, J);
-- internal immediat formats
type imm_formats is (I, S, B, U, J);
-- cpu states
type cpuStates is (stIF, stDEC, stOF, stEXEC, stWB);
-- internal opCodes/enums for memory operation
type memOP is (uNOP, uLB, uLH, uLW, uLBU, uLHU, uSB, uSH, uSW);
-- internal opCodes/enums for branching
type branchOP is (uNOP, uEQ, uNE, uLT, uLTU, uGE, uGEU);
-- Size of words
constant wordWidth : integer := 32;
-- bit vectors for different types
subtype word is std_logic_vector(wordWidth - 1 downto 0); -- 32bit (word)
subtype half is std_logic_vector(16 - 1 downto 0); -- 16bit (half)
subtype byte is std_logic_vector(8 - 1 downto 0); -- 8bit (byte)
subtype four_bit is std_logic_vector(4 - 1 downto 0); -- 4bit vector
subtype two_bit is std_logic_vector(2 - 1 downto 0); -- 2bit vector
subtype one_bit is std_logic_vector(1 - 1 downto 0); -- 1bit vector
subtype instruction is std_logic_vector(wordWidth - 1 downto 0); -- instruction
subtype opcode is std_logic_vector(7 - 1 downto 0); -- 7bit opcode
subtype reg_idx is std_logic_vector(5 - 1 downto 0); -- register index
subtype funct3 is std_logic_vector(3 - 1 downto 0); -- 3bit sub opcode
subtype shamt is std_logic_vector(5 - 1 downto 0); -- shift amount
subtype upper_imm is std_logic_vector(31 downto 12); -- upper immediate
subtype imm_12 is std_logic_vector(12 - 1 downto 0); -- 12bit immediate
-- constants for the 7bit opcode field in a normal 32bit instruction.
-- for 32bit size instructions the last 2 bits always have to be '1'
-- xxxxx11
constant opc_LUI : opcode := "0110111"; -- load upper immediate
constant opc_AUIPC : opcode := "0010111"; -- add upper immediate to pc
constant opc_JAL : opcode := "1101111"; -- jump and link
constant opc_JALR : opcode := "1100111"; -- jump and link register
constant opc_BRANCH : opcode := "1100011"; -- branch --
constant opc_LOAD : opcode := "0000011"; -- load --
constant opc_STORE : opcode := "0100011"; -- store --
constant opc_ALUI : opcode := "0010011"; -- alu immediate --
constant opc_ALUR : opcode := "0110011"; -- alu register --
constant opc_FENCE : opcode := "0001111"; -- fence
constant opc_ECALL : opcode := "1110011"; -- ecall
constant opc_EBREAK : opcode := "1110011"; -- break
constant opc_NULL : opcode := "0000000"; -- invalid
-- constant for alu double funct3 entrys. (e.g. SUB instruction)
constant alu_flag : std_logic_vector(7 - 1 downto 0) := "0100000";
-- constants for the funct3 field on branches
constant branch_EQ : funct3 := "000";
constant branch_NE : funct3 := "001";
constant branch_LT : funct3 := "100";
constant branch_GE : funct3 := "101";
constant branch_LTU : funct3 := "110";
constant branch_GEU : funct3 := "111";
-- constants for the funct3 field on loads
constant load_B : funct3 := "000"; -- byte
constant load_H : funct3 := "001"; -- half
constant load_W : funct3 := "010"; -- word
constant load_LBU : funct3 := "100"; -- byte unsigned
constant load_LHU : funct3 := "101"; -- half unsigned
-- constants for the funct3 field on stores
constant store_B : funct3 := "000"; -- byte
constant store_H : funct3 := "001"; -- half
constant store_W : funct3 := "010"; -- word
-- constants for the funct3 field for alu
constant alu_ADD : funct3 := "000"; -- add
constant alu_SUB : funct3 := "000"; -- sub also needs alu_flag set
constant alu_SLT : funct3 := "010"; -- set less than
constant alu_SLTU : funct3 := "011"; -- set less than immediate
constant alu_AND : funct3 := "111"; -- and
constant alu_OR : funct3 := "110"; -- or
constant alu_XOR : funct3 := "100"; -- xor
constant alu_SLL : funct3 := "001"; -- shift left logical
constant alu_SRL : funct3 := "101"; -- shift right logical
constant alu_SRA : funct3 := "101"; -- shift right arithmetic
-- regFile constants and type
constant reg_adr_size : integer := 5;
constant reg_size : integer := 32;
type regFile is array (reg_size - 1 downto 0) of word;
-- ram constants and type
constant ram_size : natural := 4096;
constant ram_block_size : natural := 1024;
constant ram_addr_size : natural := 12;
subtype ram_addr_t is std_logic_vector(ram_addr_size -1 downto 0);
-- type ram_t is array(0 to ram_addr_size - 1) of word;
type ram_t is array(0 to 255) of word;
-- const for multiplexer sources
constant mul_wr_alures : two_bit := "00";
constant mul_wr_memread : two_bit := "01";
constant mul_wr_pc4 : two_bit := "10";
constant mul_alu_reg : one_bit := "0";
constant mul_alu_pc : one_bit := "1";
constant mul_alu_imm : one_bit := "1";
constant mul_pc_pc4 : one_bit := "0";
constant mul_pc_alu : one_bit := "1";
end riscv_types;
package body riscv_types is
end riscv_types;