Boilerplate CPU. VHDL files only.

This commit is contained in:
Yohan Boujon 2023-10-03 14:37:37 +02:00
parent e6c33c07b5
commit be238afa0a
9 changed files with 465 additions and 0 deletions

68
.gitignore vendored Normal file
View file

@ -0,0 +1,68 @@
# Vivado project files
*.cache/
*.data/
*.hw/
*.ip_user_files/
*.runs/
*.sim/
*.srcs/
*.sdk/
# Vivado settings and logs
*.jou
*.log
*.str
*.bak
# Generated files
project_*.xpr
*.bit
*.bin
*.bmm
*.dcp
*.html
*.xdc
*.ltx
*.ngc
*.tcl
*.xgui
*.xise
*.xml
*_bd.tcl
*_top.xdc
*_wrapper.bmm
*_xmd.xdc
*_xmd.ini
# Compiled files
webtalk.log
xgui/
hdl/
isim/
project_*.cache/
project_*.runs/
project_*.srcs/
project_*.xpr.user
*_vivado_*
# IDE specific files
.DS_Store
*.suo
*.user
*.sln
*.ncb
*.aps
*.vsp
*.pidb
*.opensdf
*.VC.db
# Ignore user-specific settings and configurations
*.xilinx
*.sws
*.cache
*.str
*.history
# Ignore backup files created by text editors
*~

86
src/alu.vhd Normal file
View file

@ -0,0 +1,86 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity alu is
port(
a: in STD_LOGIC_VECTOR(7 downto 0);
b: in STD_LOGIC_VECTOR(7 downto 0);
op: in STD_LOGIC_VECTOR(2 downto 0);
s: out STD_LOGIC_VECTOR(7 downto 0);
flags : out STD_LOGIC_VECTOR(3 downto 0)
);
end alu;
-- Flags
-- C -> Carry (bit 0)
-- N -> Negative (bit 1)
-- Z -> Zero (bit 2)
-- O -> Overflow (when using MUL) (bit 3)
-- Operation Bits (OP2, OP1, OP0) Operation
-- 000 ADD
-- 001 SUB
-- 010 AND
-- 011 OR
-- 100 XOR
-- 101 NOT (return 0x1 if true, else return 0x0)
-- 110 MUL
architecture behavior_alu of alu is
-- Internal variables
shared variable buffer_s_16 : STD_LOGIC_VECTOR(15 downto 0);
shared variable buffer_s : STD_LOGIC_VECTOR(7 downto 0);
shared variable carry_s : STD_LOGIC_VECTOR(8 downto 0);
shared variable buffer_flags : STD_LOGIC_VECTOR(3 downto 0);
begin
process(a, b, op) is
begin
buffer_flags := "0000";
case op is
when "000" =>
-- calculcating a + b by concatening 8 bits to 9 bits and checking the MSB
carry_s := ('0' & a) + ('0' & b);
buffer_s := carry_s(7 downto 0);
buffer_flags(0) := carry_s(8);
-- Checking negative
if (SIGNED(buffer_s) < (0)) then
buffer_flags(1) := '1';
end if;
when "001" =>
carry_s := ('0' & a) - ('0' & b);
buffer_s := carry_s(7 downto 0);
-- borrowing when negative
buffer_flags(1) := carry_s(8);
when "010" =>
buffer_s := a AND b;
when "011" =>
buffer_s := a OR b;
when "100" =>
buffer_s := a XOR b;
when "101" =>
buffer_s := NOT a;
when "110" =>
buffer_s_16 := (a * b);
buffer_s := buffer_s_16(7 downto 0);
-- In the context of a multiplication overflow can be interpreted in two manners
-- A basic overflow for both signed/unsigned. A negative flag for signed.
if (buffer_s_16 > X"FF") then
buffer_flags(3) := '1';
end if;
when others =>
buffer_s := "00000000";
end case;
-- checking for 0 value
if (buffer_s = 0) then
buffer_flags(2) := '1';
end if;
-- Writing from the buffer to the output
s <= buffer_s;
flags <= buffer_flags;
end process;
end behavior_alu;

54
src/alu_tb.vhd Normal file
View file

@ -0,0 +1,54 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity test_alu is
end test_alu;
architecture bench of test_alu is
component alu is
port(
a: in STD_LOGIC_VECTOR(7 downto 0);
b: in STD_LOGIC_VECTOR(7 downto 0);
op: in STD_LOGIC_VECTOR(2 downto 0);
s: out STD_LOGIC_VECTOR(7 downto 0);
flags : out STD_LOGIC_VECTOR(3 downto 0)
);
end component;
for all : alu use entity work.alu;
signal in1, in2, out1 : STD_LOGIC_VECTOR(7 downto 0);
signal out2 : STD_LOGIC_VECTOR(3 downto 0);
signal operation : STD_LOGIC_VECTOR(2 downto 0);
-- Test ADD -> 4+(-16)/4+240, then 128+156 -> C = 1
-- Test SUB -> 32-6 then 4-10 -> N =1
-- Test AND -> 0b00001111 & 0b11110000 then 0b01010000 & 0b11110001
-- Test OR -> 0b00001111 | 0b11110000 then 0b01010000 | 0b11110001
-- Test XOR -> 0b00001111 ^ 0b11110000 then 0b01010000 ^ 0b11110001
-- Test NOT -> 0b00001111
-- Test MUL -> 6*3 then 128*3 O = 1
begin
testeur: alu PORT MAP(in1, in2, operation, out1, out2);
in1 <= "00000100", "10000000" after 2 ns,
"00100000" after 4 ns, "00000100" after 6 ns,
"00001111" after 8 ns, "01010000" after 10 ns,
"00001111" after 12 ns, "01010000" after 14 ns,
"00001111" after 16 ns, "01010000" after 18 ns,
"00001111" after 20ns,
"00000110" after 24ns, "10000000" after 26ns;
in2 <= "11110000", "10011100" after 2 ns,
"00000110" after 4 ns, "00001010" after 6 ns,
"11110000" after 8 ns, "11110001" after 10 ns,
"11110000" after 12 ns, "11110001" after 14 ns,
"11110000" after 16 ns, "11110001" after 18 ns,
-- in2 is not used for not
"00000011" after 24ns, "00000011" after 26ns;
operation <= "000", "001" after 4ns, "010" after 8ns, "011" after 12ns, "100" after 16ns, "101" after 20ns, "110" after 24ns;
end bench;

31
src/data_memory.vhd Normal file
View file

@ -0,0 +1,31 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;
entity DataMemory is
Port ( CLK : in STD_LOGIC;
RST : in STD_LOGIC;
RW_ENABLE : in STD_LOGIC;
ADDR : in STD_LOGIC_VECTOR(7 downto 0);
DATA_IN : in STD_LOGIC_VECTOR(7 downto 0);
DATA_OUT : out STD_LOGIC_VECTOR(7 downto 0));
end DataMemory;
architecture Behavioral of DataMemory is
type MemoryArray is array (0 to 255) of STD_LOGIC_VECTOR(7 downto 0);
signal Memory : MemoryArray := (others => X"00");
begin
process(CLK, RST)
begin
if RST = '1' then
Memory <= (others => X"00"); -- Reset the memory to 0x00
elsif rising_edge(CLK) then
if RW_ENABLE = '1' then -- Read
DATA_OUT <= Memory(to_integer(unsigned(ADDR)));
else -- Write
Memory(to_integer(unsigned(ADDR))) <= DATA_IN;
end if;
end if;
end process;
end Behavioral;

61
src/data_memory_tb.vhd Normal file
View file

@ -0,0 +1,61 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;
entity DataMemory_TB is
end DataMemory_TB;
architecture Behavioral of DataMemory_TB is
signal CLK : STD_LOGIC := '0';
signal RST : STD_LOGIC := '0';
signal RW_ENABLE : STD_LOGIC := '0';
signal ADDR : STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
signal DATA_IN : STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
signal DATA_OUT : STD_LOGIC_VECTOR(7 downto 0);
constant CLOCK_PERIOD : time := 10 ns; -- Define your clock period here
begin
-- Instantiate the DataMemory component
UUT: entity work.DataMemory
port map (
CLK => CLK,
RST => RST,
RW_ENABLE => RW_ENABLE,
ADDR => ADDR,
DATA_IN => DATA_IN,
DATA_OUT => DATA_OUT
);
-- Clock generation process
CLK_GEN: process
begin
while now < 1000 ns loop
CLK <= not CLK;
wait for CLOCK_PERIOD / 2;
end loop;
wait;
end process;
-- Stimulus process
STIMULUS: process
begin
RST <= '1'; -- Reset the memory
wait for 20 ns;
RST <= '0';
wait for 10 ns;
-- Write to memory
RW_ENABLE <= '0';
ADDR <= "00000001";
DATA_IN <= "01010101";
wait for 20 ns;
-- Read from memory
RW_ENABLE <= '1';
ADDR <= "00000001";
wait for 20 ns;
wait;
end process;
end Behavioral;

View file

@ -0,0 +1,40 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity instruction is
port(
instruction: in STD_LOGIC_VECTOR(7 downto 0);
code: out STD_LOGIC_VECTOR(31 downto 0);
clk: in STD_LOGIC
);
-- Array of STD_LOGIC_VECTOR
type code_array is array(0 to 256) of
STD_LOGIC_VECTOR(31 downto 0);
-- Initialize the code memory
function init return code_array is
variable init_result: code_array;
begin
--do something (e.g. read data from a file, perform some initialization calculation, ...)
-- Exemple :
for i in code_array'range loop
init_result(i) := std_logic_vector(conv_unsigned(i, 32));
end loop;
return init_result;
end function init;
end instruction;
architecture behavior_instr of instruction is
-- Memory variable
signal code_memory: code_array := init;
begin
process(instruction, clk) is
begin
if clk'event AND clk = '1' then
code <= code_memory(CONV_INTEGER(UNSIGNED(instruction)));
end if;
end process;
end behavior_instr;

View file

@ -0,0 +1,31 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity test_instr is
end test_instr;
architecture bench of test_instr is
component instruction is
port(
instruction: in STD_LOGIC_VECTOR(7 downto 0);
code: out STD_LOGIC_VECTOR(31 downto 0);
clk: in STD_LOGIC
);
end component;
for all : instruction use entity work.instruction;
signal inAddress : STD_LOGIC_VECTOR(7 downto 0);
signal outCode : STD_LOGIC_VECTOR(31 downto 0);
signal inClock : STD_LOGIC := '0';
begin
testeur: instruction PORT MAP(inAddress, outCode, inClock);
inClock <= not inClock after 1ns;
inAddress <= X"00", X"0a" after 10ns;
end bench;

52
src/register.vhd Normal file
View file

@ -0,0 +1,52 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity reg is
port(
address_A: in STD_LOGIC_VECTOR(3 downto 0);
address_B: in STD_LOGIC_VECTOR(3 downto 0);
address_W: in STD_LOGIC_VECTOR(3 downto 0);
W_Enable: in STD_LOGIC;
W_Data: in STD_LOGIC_VECTOR(7 downto 0);
reset: in STD_LOGIC;
clk: in STD_LOGIC;
A_Data: out STD_LOGIC_VECTOR(7 downto 0);
B_Data: out STD_LOGIC_VECTOR(7 downto 0)
);
end reg;
architecture behavior_reg of reg is
-- Array of STD_LOGIC_VECTOR
type memory_array is array(0 to 15) of
STD_LOGIC_VECTOR(7 downto 0);
-- Memory variable
signal memory: memory_array;
begin
-- Convert address_A and address_B to integers
-- Using Bypass to avoid delay between Read Data and Write Data if they are at the same time called (WEnable = 1)
A_Data <= memory(CONV_INTEGER(unsigned(address_A))) when (W_Enable = '0' or address_A /= address_W)
else W_Data;
B_Data <= memory(CONV_INTEGER(unsigned(address_B))) when (W_Enable = '0' or address_B /= address_W)
else W_Data;
-- Write data synchronously
process(address_W, W_Enable, W_Data, reset, clk) is
begin
-- Reset the memory if shutdown
if reset = '0' then
memory <= (others => "00000000");
end if;
-- Else Doing writing routine at each clock tick
if reset = '1' then
if clk'event and clk='1' then
if W_Enable = '1' then
memory(CONV_INTEGER(unsigned(address_W))) <= W_Data;
end if;
end if;
end if;
end process;
end behavior_reg;

42
src/register_tb.vhd Normal file
View file

@ -0,0 +1,42 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity test_reg is
end test_reg;
architecture bench of test_reg is
component reg is
port(
address_A: in STD_LOGIC_VECTOR(3 downto 0);
address_B: in STD_LOGIC_VECTOR(3 downto 0);
address_W: in STD_LOGIC_VECTOR(3 downto 0);
W_Enable: in STD_LOGIC;
W_Data: in STD_LOGIC_VECTOR(7 downto 0);
reset: in STD_LOGIC;
clk: in STD_LOGIC;
A_Data: out STD_LOGIC_VECTOR(7 downto 0);
B_Data: out STD_LOGIC_VECTOR(7 downto 0)
);
end component;
for all : reg use entity work.reg;
signal inA, inB, inW : STD_LOGIC_VECTOR(3 downto 0);
signal outA, outB, inDataW : STD_LOGIC_VECTOR(7 downto 0);
signal inWenabler, inReset, inClock : STD_LOGIC := '0';
begin
testeur: reg PORT MAP(inA, inB, inW, inWenabler, inDataW, inReset, inClock, outA, outB);
inClock <= not inClock after 1ns;
inReset <= '0', '1' after 1ns, '0' after 200ns, '1' after 202ns;
inA <= "0000", "0001" after 48ns, "0010" after 64ns, "0000" after 70ns, "0001" after 80ns;
inB <= "0000", "0010" after 48ns, "0001" after 64ns, "1111" after 70ns;
inW <= "0000", "0001" after 24ns, "0010" after 32ns, "0000" after 56ns;
inWenabler <= '0', '1' after 24ns, '0' after 56ns;
inDataW <= "00000000", "01010101" after 24ns, "10101010" after 32ns, "11111111" after 48ns, "00000000" after 56ns;
end bench;