Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • acaute/tp_mlp_on_fpga
1 result
Show changes
Commits on Source (2)
Showing
with 2380 additions and 0 deletions
TP_MLP.hw/
TP_MLP.runs/
TP_MLP.ip_user_files/
TP_MLP.gen/
TP_MLP.cache/
TP_MLP.sim/
data/
*.xpr
*.wcfg
*.pt
mnist_mlp/data_files/
mnist_mlp/bin/
mnist_mlp/__pycache__/
mnist_mlp/lib/
mnist_mlp/lib64i/
create_clock -name clk -period 10 -waveform {0 5} [get_ports clk]
# Switches
#set_property PACKAGE_PIN V17 [get_ports {sw[0]}]
s#et_property IOSTANDARD LVCMOS33 [get_ports {sw[0]}]
#set_property PACKAGE_PIN V16 [get_ports {sw[1]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[1]}]
#set_property PACKAGE_PIN W16 [get_ports {sw[2]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[2]}]
#set_property PACKAGE_PIN W17 [get_ports {sw[3]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[3]}]
#set_property PACKAGE_PIN W15 [get_ports {sw[4]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[4]}]
#set_property PACKAGE_PIN V15 [get_ports {sw[5]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[5]}]
#set_property PACKAGE_PIN W14 [get_ports {sw[6]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[6]}]
#set_property PACKAGE_PIN W13 [get_ports {sw[7]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[7]}]
#set_property PACKAGE_PIN V2 [get_ports {sw[8]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[8]}]
#set_property PACKAGE_PIN T3 [get_ports {sw[9]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[9]}]
#set_property PACKAGE_PIN T2 [get_ports {sw[10]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[10]}]
#set_property PACKAGE_PIN R3 [get_ports {sw[11]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[11]}]
#set_property PACKAGE_PIN W2 [get_ports {sw[12]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[12]}]
#set_property PACKAGE_PIN U1 [get_ports {sw[13]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[13]}]
#set_property PACKAGE_PIN T1 [get_ports {sw[14]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[14]}]
#set_property PACKAGE_PIN R2 [get_ports {sw[15]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {sw[15]}]
# LEDs
set_property PACKAGE_PIN U16 [get_ports {LED[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[0]}]
set_property PACKAGE_PIN E19 [get_ports {LED[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[1]}]
set_property PACKAGE_PIN U19 [get_ports {LED[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[2]}]
set_property PACKAGE_PIN V19 [get_ports {LED[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[3]}]
set_property PACKAGE_PIN W18 [get_ports {LED[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[4]}]
set_property PACKAGE_PIN U15 [get_ports {LED[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[5]}]
set_property PACKAGE_PIN U14 [get_ports {LED[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[6]}]
set_property PACKAGE_PIN V14 [get_ports {LED[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[7]}]
set_property PACKAGE_PIN V13 [get_ports {LED[8]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[8]}]
set_property PACKAGE_PIN V3 [get_ports {LED[9]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[9]}]
set_property PACKAGE_PIN W3 [get_ports {LED[10]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[10]}]
set_property PACKAGE_PIN U3 [get_ports {LED[11]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[11]}]
set_property PACKAGE_PIN P3 [get_ports {LED[12]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[12]}]
set_property PACKAGE_PIN N3 [get_ports {LED[13]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[13]}]
set_property PACKAGE_PIN P1 [get_ports {LED[14]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[14]}]
set_property PACKAGE_PIN L1 [get_ports {LED[15]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[15]}]
#7 segment display
set_property PACKAGE_PIN W7 [get_ports {seg[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[0]}]
set_property PACKAGE_PIN W6 [get_ports {seg[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[1]}]
set_property PACKAGE_PIN U8 [get_ports {seg[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[2]}]
set_property PACKAGE_PIN V8 [get_ports {seg[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[3]}]
set_property PACKAGE_PIN U5 [get_ports {seg[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[4]}]
set_property PACKAGE_PIN V5 [get_ports {seg[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[5]}]
set_property PACKAGE_PIN U7 [get_ports {seg[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[6]}]
#set_property PACKAGE_PIN V7 [get_ports dp]
# set_property IOSTANDARD LVCMOS33 [get_ports dp]
set_property PACKAGE_PIN U2 [get_ports {an[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an[0]}]
#set_property PACKAGE_PIN U4 [get_ports {an[1]}]
# set_property IOSTANDARD LVCMOS33 [get_ports {an[1]}]
#set_property PACKAGE_PIN V4 [get_ports {an[2]}]
# set_property IOSTANDARD LVCMOS33 [get_ports {an[2]}]
#set_property PACKAGE_PIN W4 [get_ports {an[3]}]
# set_property IOSTANDARD LVCMOS33 [get_ports {an[3]}]
##Buttons
#set_property PACKAGE_PIN U18 [get_ports btnC]
#set_property IOSTANDARD LVCMOS33 [get_ports btnC]
#set_property PACKAGE_PIN T18 [get_ports btnU]
#set_property IOSTANDARD LVCMOS33 [get_ports btnU]
set_property PACKAGE_PIN W19 [get_ports btnL]
set_property IOSTANDARD LVCMOS33 [get_ports btnL]
set_property PACKAGE_PIN T17 [get_ports btnR]
set_property IOSTANDARD LVCMOS33 [get_ports btnR]
#set_property PACKAGE_PIN U17 [get_ports btnD]
#set_property IOSTANDARD LVCMOS33 [get_ports btnD]
##Pmod Header JA
##Sch name = JA1
#set_property PACKAGE_PIN J1 [get_ports {JA[0]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JA[0]}]
##Sch name = JA2
#set_property PACKAGE_PIN L2 [get_ports {JA[1]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JA[1]}]
##Sch name = JA3
#set_property PACKAGE_PIN J2 [get_ports {JA[2]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JA[2]}]
##Sch name = JA4
#set_property PACKAGE_PIN G2 [get_ports {JA[3]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JA[3]}]
##Sch name = JA7
#set_property PACKAGE_PIN H1 [get_ports {JA[4]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JA[4]}]
##Sch name = JA8
#set_property PACKAGE_PIN K2 [get_ports {JA[5]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JA[5]}]
##Sch name = JA9
#set_property PACKAGE_PIN H2 [get_ports {JA[6]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JA[6]}]
##Sch name = JA10
#set_property PACKAGE_PIN G3 [get_ports {JA[7]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JA[7]}]
##Pmod Header JB
##Sch name = JB1
#set_property PACKAGE_PIN A14 [get_ports {JB[0]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JB[0]}]
##Sch name = JB2
#set_property PACKAGE_PIN A16 [get_ports {JB[1]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JB[1]}]
##Sch name = JB3
#set_property PACKAGE_PIN B15 [get_ports {JB[2]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JB[2]}]
##Sch name = JB4
#set_property PACKAGE_PIN B16 [get_ports {JB[3]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JB[3]}]
##Sch name = JB7
#set_property PACKAGE_PIN A15 [get_ports {JB[4]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JB[4]}]
##Sch name = JB8
#set_property PACKAGE_PIN A17 [get_ports {JB[5]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JB[5]}]
##Sch name = JB9
#set_property PACKAGE_PIN C15 [get_ports {JB[6]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JB[6]}]
##Sch name = JB10
#set_property PACKAGE_PIN C16 [get_ports {JB[7]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JB[7]}]
##Pmod Header JC
##Sch name = JC1
#set_property PACKAGE_PIN K17 [get_ports {JC[0]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JC[0]}]
##Sch name = JC2
#set_property PACKAGE_PIN M18 [get_ports {JC[1]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JC[1]}]
##Sch name = JC3
#set_property PACKAGE_PIN N17 [get_ports {JC[2]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JC[2]}]
##Sch name = JC4
#set_property PACKAGE_PIN P18 [get_ports {JC[3]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JC[3]}]
##Sch name = JC7
#set_property PACKAGE_PIN L17 [get_ports {JC[4]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JC[4]}]
##Sch name = JC8
#set_property PACKAGE_PIN M19 [get_ports {JC[5]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JC[5]}]
##Sch name = JC9
#set_property PACKAGE_PIN P17 [get_ports {JC[6]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JC[6]}]
##Sch name = JC10
#set_property PACKAGE_PIN R18 [get_ports {JC[7]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {JC[7]}]
#Pmod Header JXADC
#Sch name = XA1_P
#set_property PACKAGE_PIN J3 [get_ports {vauxp6}]
# set_property IOSTANDARD LVCMOS33 [get_ports {vauxp6}]
##Sch name = XA2_P
#set_property PACKAGE_PIN L3 [get_ports {vauxp14}]
# set_property IOSTANDARD LVCMOS33 [get_ports {vauxp14}]
##Sch name = XA3_P
#set_property PACKAGE_PIN M2 [get_ports {vauxp7}]
# set_property IOSTANDARD LVCMOS33 [get_ports {vauxp7}]
##Sch name = XA4_P
#set_property PACKAGE_PIN N2 [get_ports {vauxp15}]
# set_property IOSTANDARD LVCMOS33 [get_ports {vauxp15}]
##Sch name = XA1_N
#set_property PACKAGE_PIN K3 [get_ports {vauxn6}]
# set_property IOSTANDARD LVCMOS33 [get_ports {vauxn6}]
##Sch name = XA2_N
#set_property PACKAGE_PIN M3 [get_ports {vauxn14}]
# set_property IOSTANDARD LVCMOS33 [get_ports {vauxn14}]
##Sch name = XA3_N
#set_property PACKAGE_PIN M1 [get_ports {vauxn7}]
# set_property IOSTANDARD LVCMOS33 [get_ports {vauxn7}]
##Sch name = XA4_N
#set_property PACKAGE_PIN N1 [get_ports {vauxn15}]
# set_property IOSTANDARD LVCMOS33 [get_ports {vauxn15}]
##VGA Connector
#set_property PACKAGE_PIN G19 [get_ports {vgaRed[0]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaRed[0]}]
#set_property PACKAGE_PIN H19 [get_ports {vgaRed[1]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaRed[1]}]
#set_property PACKAGE_PIN J19 [get_ports {vgaRed[2]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaRed[2]}]
#set_property PACKAGE_PIN N19 [get_ports {vgaRed[3]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaRed[3]}]
#set_property PACKAGE_PIN N18 [get_ports {vgaBlue[0]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaBlue[0]}]
#set_property PACKAGE_PIN L18 [get_ports {vgaBlue[1]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaBlue[1]}]
#set_property PACKAGE_PIN K18 [get_ports {vgaBlue[2]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaBlue[2]}]
#set_property PACKAGE_PIN J18 [get_ports {vgaBlue[3]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaBlue[3]}]
#set_property PACKAGE_PIN J17 [get_ports {vgaGreen[0]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaGreen[0]}]
#set_property PACKAGE_PIN H17 [get_ports {vgaGreen[1]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaGreen[1]}]
#set_property PACKAGE_PIN G17 [get_ports {vgaGreen[2]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaGreen[2]}]
#set_property PACKAGE_PIN D17 [get_ports {vgaGreen[3]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {vgaGreen[3]}]
#set_property PACKAGE_PIN P19 [get_ports Hsync]
#set_property IOSTANDARD LVCMOS33 [get_ports Hsync]
#set_property PACKAGE_PIN R19 [get_ports Vsync]
#set_property IOSTANDARD LVCMOS33 [get_ports Vsync]
##USB-RS232 Interface
#set_property PACKAGE_PIN B18 [get_ports RsRx]
#set_property IOSTANDARD LVCMOS33 [get_ports RsRx]
#set_property PACKAGE_PIN A18 [get_ports RsTx]
#set_property IOSTANDARD LVCMOS33 [get_ports RsTx]
##USB HID (PS/2)
#set_property PACKAGE_PIN C17 [get_ports PS2Clk]
#set_property IOSTANDARD LVCMOS33 [get_ports PS2Clk]
#set_property PULLUP true [get_ports PS2Clk]
#set_property PACKAGE_PIN B17 [get_ports PS2Data]
#set_property IOSTANDARD LVCMOS33 [get_ports PS2Data]
#set_property PULLUP true [get_ports PS2Data]
##Quad SPI Flash
##Note that CCLK_0 cannot be placed in 7 series devices. You can access it using the
##STARTUPE2 primitive.
#set_property PACKAGE_PIN D18 [get_ports {QspiDB[0]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {QspiDB[0]}]
#set_property PACKAGE_PIN D19 [get_ports {QspiDB[1]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {QspiDB[1]}]
#set_property PACKAGE_PIN G18 [get_ports {QspiDB[2]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {QspiDB[2]}]
#set_property PACKAGE_PIN F18 [get_ports {QspiDB[3]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {QspiDB[3]}]
#set_property PACKAGE_PIN K19 [get_ports QspiCSn]
#set_property IOSTANDARD LVCMOS33 [get_ports QspiCSn]
\ No newline at end of file
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/12/2024 04:43:13 PM
-- Design Name:
-- Module Name: tb_layer - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all; -- For integer operations and conversions
use work.function_pkg.all; -- Import the function package
use work.test_config_pkg.all;
entity tb_layer is
--generic entity, that way you can generate a bunch of them in a loop for layer instanciation--
generic (
DATA_WIDTH : natural := 32; -- bit width
NUM_OF_NEURONS : natural := 16; -- number of neurons in the layer
NUM_OF_INPUTS : natural := 784; -- number of neurons in the layer
NUM_LAYER : natural := 0; -- indic of the layer
DATA_PATH : string := "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/weights/"
);
end tb_layer;
architecture Behavioral of tb_layer is
-- instantiate layer
component layer
generic ( -- default for first layer of a standard mnist network
NUM_OF_LAYERS : natural := 3;
DATA_WIDTH : natural := 32; -- bit width
NUM_OF_NEURONS : natural := 16; -- number of neurons in the layer
NUM_OF_INPUTS : natural := 784; -- number of neurons in the layer
NUM_LAYER : natural := 0; -- indic of the layer
DATA_PATH : string := "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/weights/"
);
port (
clk : IN std_logic;
rst : IN std_logic;
enable_l_i : IN std_logic; -- enable the layer
start_l_i : IN std_logic; -- start the layer
idle_l_o : OUT std_logic; -- layer in IDLE state
ready_l_o : OUT std_logic; -- layer ready to start
n_addr : IN std_logic_vector(log2_unsigned(MAX_OUTPUTS_SIZE)-1 downto 0); -- read neuron address from next layer
r_addr : OUT std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- read neuron address for previous layer
input : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0);
output : OUT std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- will be necessary to pipeline layers
valid_value_i : IN std_logic; -- valid input value
valid_value_o : OUT std_logic -- valid output value
);
end component;
component std_memory
generic(
ADD_WIDTH : natural := log2_unsigned(MAX_INPUTS_SIZE);
NUM_OF_INPUTS : natural := 3;
NUM_NEURON : natural := 5;
NUM_LAYER : natural := 1;
DATA_WIDTH : natural := 16;
DATA_FILE : string := ""
);
port(
clk : IN std_logic;
radd : IN std_logic_vector(ADD_WIDTH-1 downto 0);
enable : IN std_logic;
wout : OUT std_logic_vector(DATA_WIDTH-1 downto 0)
);
end component;
signal clk : std_logic := '0';
signal rst : std_logic := '1';
signal valid_input : std_logic := '0';
constant clk_period : time := 10 ns;
constant input_file : string := "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/inputs/in_1_lbl_6.mif";
signal mem_en_l : std_logic := '0'; -- enable input memory
signal output_data : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
signal input_data : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
signal l_en : std_logic := '0';
signal ready_l : std_logic := '0';
signal start_l : std_logic := '0';
signal idle_l : std_logic := '0';
signal n_addr : std_logic_vector(log2_unsigned(NUM_OF_NEURONS)-1 downto 0);
signal r_addr : std_logic_vector(log2_unsigned(NUM_OF_INPUTS)-1 downto 0);
signal valid_out : std_logic;
begin
-- Instantiate
-- unit under test
uut: layer
generic map(
DATA_WIDTH => DATA_WIDTH,
NUM_OF_INPUTS => NUM_OF_INPUTS, -- number of inputs in the layer
NUM_OF_NEURONS => NUM_OF_NEURONS, -- number of the neuron to identification purposes
NUM_LAYER => NUM_LAYER, -- number of the layer for identification purposes
DATA_PATH => "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/weights/"
)
port map (
clk => clk,
rst => rst,
enable_l_i => l_en, -- enable layer MAC
start_l_i => start_l,
idle_l_o => idle_l,
ready_l_o => ready_l, -- enable layer output
n_addr => n_addr,-- read neuron address from next layer
r_addr => r_addr, -- read neuron address for previous layer
input => input_data,
output => output_data, -- will be necessary to pipeline layers
valid_value_i => valid_input,
valid_value_o => valid_out
);
-- instantiate memory bench for inputs
input_memory_inst : std_memory
generic map(
DATA_WIDTH => DATA_WIDTH,
ADD_WIDTH => log2_unsigned(NUM_OF_INPUTS),
NUM_OF_INPUTS => NUM_OF_INPUTS,
NUM_NEURON => 23,
NUM_LAYER =>1,
DATA_FILE => input_file -- weight file of the neuron
)
port map (
clk => clk,
radd => std_logic_vector(signed(r_addr)),
enable => mem_en_l,
wout => input_data
);
-- Start of the module code
-- Clock process definitions
clk_process :process
begin
clk <= '0';
wait for clk_period/2;
clk <= '1';
wait for clk_period/2;
end process;
stimulus_process :process
begin
wait for 40 ns;
rst <= '0';
wait for 20 ns;
l_en <= '1'; -- enable the layer
start_l <= '1';
valid_input <= '1';
mem_en_l <= '1';
end process;
end Behavioral;
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/17/2024 04:12:47 PM
-- Design Name:
-- Module Name: tb_network - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
entity tb_network is
generic(
DATA_WIDTH : integer := 32
);
-- Port ( ); = none
end tb_network;
architecture Behavioral of tb_network is
component network is
generic(
INPUTS_PATH : string := "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/inputs/in_1_lbl_6.mif"
);
port (
clk : IN std_logic;
rst : IN std_logic;
en : IN std_logic;
inputs : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0);
outputs : OUT std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- will be necessary to pipeline layers
valid_value_i : IN std_logic
);
end component;
--signals
signal clk : std_logic := '0';
constant clk_period : time := 10 ns;
signal rst : std_logic := '1';
signal enable : std_logic := '0';
signal inputs : std_logic_vector(DATA_WIDTH-1 DOWNTO 0) := (others => '0');
signal outputs : std_logic_vector(DATA_WIDTH-1 DOWNTO 0) := (others => '0');
signal valid_value_i : std_logic := '0';
begin
uut : network
generic map(
INPUTS_PATH => "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/inputs/in_1_lbl_6.mif" -- always the same input for now
)
port map(
clk => clk,
rst => rst,
en => enable,
inputs => inputs,
outputs => outputs,
valid_value_i => valid_value_i
);
-- Clock process definitions
clk_process :process
begin
clk <= '0';
wait for clk_period/2;
clk <= '1';
wait for clk_period/2;
end process;
rst_process :process
begin
wait for 40 ns;
rst <= '0';
wait for 20 ns;
enable <= '1'; -- enable the layer
end process;
end Behavioral;
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/09/2024 04:43:15 PM
-- Design Name:
-- Module Name: tb_perceptron - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all; -- For integer operations and conversions
use work.function_pkg.all;
use work.test_config_pkg.all;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity tb_perceptron is
--generic entity, that way you can generate a bunch of them in a loop for layer instanciation--
generic (
DATA_WIDTH : natural := 32;
NUM_OF_INPUTS : natural := 784; -- number of inputs in the layer
NUM_NEURON : natural := 23; -- number of the neuron to identification purposes
NUM_LAYER : natural := 23 -- number of the layer for identification purposes
);
end tb_perceptron;
architecture Behavioral of tb_perceptron is
component perceptron
generic (
DATA_WIDTH : natural := 32;
NUM_OF_INPUTS : natural := 784; -- number of inputs in the layer
NUM_NEURON : natural := 23; -- number of the neuron to identification purposes
NUM_LAYER : natural := 23; -- number of the layer for identification purposes
DATA_FILE : string := ""
);
port (
clk : IN std_logic;
rst : IN std_logic;
enable_n_i : IN std_logic; -- enable the neuron
state : IN state_t; -- neuron state from the layer state machine
idle_n_o : OUT std_logic; -- neuron in IDLE state
ready_n_o : OUT std_logic; -- neuron ready
value_i : IN std_logic_vector(DATA_WIDTH-1 downto 0); -- single input for data access reasons
valid_value_i : IN std_logic;
value_o : OUT std_logic_vector(DATA_WIDTH-1 downto 0);
valid_o : OUT std_logic -- valid signal for the control purposes
);
end component;
component std_memory
generic(
ADD_WIDTH : natural := 4;
NUM_OF_INPUTS : natural := 3;
NUM_NEURON : natural := 5;
NUM_LAYER : natural := 1;
DATA_WIDTH : natural := 16;
DATA_FILE : string := ""
);
port(
clk : IN std_logic;
radd : IN std_logic_vector(ADD_WIDTH-1 downto 0);
enable : IN std_logic;
wout : OUT std_logic_vector(DATA_WIDTH-1 downto 0)
);
end component;
-- signals
signal clk : std_logic := '0';
signal rst : std_logic := '1';
constant clk_period : time := 10 ns;
signal input : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
signal input_valid : std_logic;
signal output : std_logic_vector(DATA_WIDTH-1 downto 0);
signal valid_o : std_logic;
signal cnt_layer : std_logic_vector(log2_unsigned(NUM_OF_INPUTS)-1 downto 0); -- counter for addressing inputs in the reg
signal en : std_logic;
signal mem_en : std_logic := '0';
signal idle_s : std_logic;
signal start_s : std_logic;
signal ready : std_logic;
signal valid_o_tb : std_logic;
-- for the pipeline
signal state : state_t := IDLE;
begin
-- Instantiate
uut: perceptron
generic map(
DATA_WIDTH => 32,
NUM_OF_INPUTS => 784, -- number of inputs in the layer
NUM_NEURON => 23, -- number of the neuron to identification purposes
NUM_LAYER => 23, -- number of the layer for identification purposes
DATA_FILE => "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/weights/w_l0_n0.mif"
)
port map (
clk => clk,
rst => rst,
enable_n_i => en, -- enable the neuron
state => state, -- neuron state from the layer state machine
idle_n_o => idle_s, -- neuron in IDLE state
ready_n_o => ready, -- neuron ready
value_i => input, -- single input for data access reasons
valid_value_i => input_valid,
value_o => output,
valid_o => valid_o -- valid signal for the control purposes
);
input_memory : std_memory
generic map(
DATA_WIDTH => DATA_WIDTH,
ADD_WIDTH => log2_unsigned(NUM_OF_INPUTS),
NUM_OF_INPUTS => NUM_OF_INPUTS + 1, -- +1 to account for the bias in last position
NUM_NEURON => NUM_NEURON,
NUM_LAYER => NUM_LAYER,
DATA_FILE => "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/inputs/in_2_lbl_6.mif" -- weight file of the neuron
)
port map (
clk => clk,
radd => cnt_layer,
enable => mem_en,
wout => input
);
-- input <= std_logic_vector(to_signed(to_fixed(1.0, 25), DATA_WIDTH));
-- Clock process definitions
clk_process :process
begin
clk <= '0';
wait for clk_period/2;
clk <= '1';
wait for clk_period/2;
end process;
rst_process :process
begin
wait for 40 ns;
rst <= '0';
wait for 20 ns;
start_s <= '1';
en <= '1';
wait for 20 ns;
start_s <= '0';
wait;
end process;
Stimulus : process(clk, rst)
begin
if rst = '1' then
cnt_layer <= (others => '0');
report "reset";
elsif rising_edge(clk) then
if to_integer(unsigned(cnt_layer)) < NUM_OF_INPUTS-1 and rst = '0' and state = EXECUTING then
-- input <= inputs_mem(cnt_layer);
cnt_layer <= std_logic_vector(signed(cnt_layer) + 1);
elsif state /= START or state /= EXECUTING then
cnt_layer <= (others => '0');
end if;
end if;
end process;
state_machine : process(clk, rst)
begin
if en = '1' and start_s = '1' and state /= EXECUTING and state /= START then
state <= START;
valid_o_tb <= '0';
input_valid <= '1';
mem_en <= '1';
elsif en = '0' or rst = '1' then
state <= IDLE;
valid_o_tb <= '0';
input_valid <= '0';
mem_en <= '0';
elsif en = '1' and state = START then
state <= EXECUTING;
valid_o_tb <= '0';
mem_en <= '1';
elsif en = '1' and valid_o = '1' then
state <= FINISHED;
valid_o_tb <= '1';
input_valid <= '0';
mem_en <= '0';
end if;
end process;
end Behavioral;
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/19/2024 10:38:31 AM
-- Design Name:
-- Module Name: test_fixe_point - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all; -- For integer operations and conversions
use work.function_pkg.all;
entity test_fixe_point is
-- Port ( );
end test_fixe_point;
architecture Behavioral of test_fixe_point is
signal A : std_logic_vector(31 downto 0) := STD_LOGIC_VECTOR(to_signed(to_fixed(-5.0, 25),32));
signal B : std_logic_vector(31 downto 0) := STD_LOGIC_VECTOR(to_signed(to_fixed(5.0, 25),32));
signal C : std_logic_vector(63 downto 0) := (others => '0');
signal D : std_logic_vector(31 downto 0) := (others => '0');
begin
C <= std_logic_vector(signed(A)*signed(B));
D <= radix_multiply(A, B, 25);
end Behavioral;
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/09/2024 12:12:33 PM
-- Design Name:
-- Module Name: test_text_io - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
-- Example of using textio, not used in the project
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_textio.all;
library std;
use std.textio.all;
ENTITY tb IS
END tb;
ARCHITECTURE tb_behavior OF tb IS
component text_io
port(
clk : IN std_logic;
rst : IN std_logic
);
end component;
signal clk : std_logic := '0';
signal rst : std_logic := '1';
constant clk_period : time := 10 ns;
BEGIN
-- Instantiate test
uut: text_io PORT MAP (
clk => clk,
rst => rst
);
-- Clock process definitions
clk_process :process
begin
clk <= '0';
wait for clk_period/2;
clk <= '1';
wait for clk_period/2;
end process;
Stimulus :process
begin
wait for 20 ns;
rst <= '0';
wait;
end process;
END tb_behavior;
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/23/2024 11:28:03 AM
-- Design Name:
-- Module Name: whole_system_tb - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use work.config_pkg.all; -- Import the configuration package
use work.function_pkg.all; -- Import the function package
entity whole_system_tb is
-- Port ( );
end whole_system_tb;
architecture Behavioral of whole_system_tb is
component interface
generic(
NUM_INPUTS : natural := 16 -- max 16, because only 16 leds and it is cool to see which input you are selecting.
);
port(
clk : IN std_logic;
rst : IN std_logic;
btnL : IN std_logic; -- button left
btnR : IN std_logic; -- button right
seg : OUT std_logic_vector(6 downto 0);
LED : OUT std_logic_vector(NUM_INPUTS-1 downto 0);
an : OUT std_logic
);
end component;
signal clk : std_logic := '0';
signal rst : std_logic := '0';
constant clk_period : time := 10 ns;
signal btnL : std_logic := '0';
signal btnR : std_logic := '0';
signal seg : std_logic_vector(6 downto 0);
signal LED : std_logic_vector(15 downto 0); -- 16 inputs
signal an : std_logic := '1';
begin
uut : interface
generic map(
NUM_INPUTS => 16
)
port map(
clk => clk,
rst => rst,
btnL => btnL,
btnR => btnR,
seg => seg,
LED => LED,
an => an
);
-- Clock process definitions
clk_process :process
begin
clk <= '0';
wait for clk_period/2;
clk <= '1';
wait for clk_period/2;
end process;
stimulus : process
begin
wait for 40 ns;
btnR <= '1';
wait for 20 ns;
btnR <= '0'; -- enable the layer
wait;
end process;
end Behavioral;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
package config_pkg is
constant NUM_OF_LAYERS : natural := 3;
constant NUM_OF_OUTPUTS : natural := 10;
constant DATA_WIDTH : natural := 32;
constant INT_WIDTH : natural := 10;
constant DATA_PATH : string := "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/weights/";
constant MAX_INPUTS_SIZE : natural := 784;
constant MAX_OUTPUTS_SIZE : natural := 16;
type num_of_neurons_t is array (0 to NUM_OF_LAYERS - 1) of integer;
type num_of_inputs_t is array (0 to NUM_OF_LAYERS - 1) of integer;
type activation_functions_t is array (0 to NUM_OF_LAYERS-1) of natural;
type state_t is (IDLE, EXECUTING, FINISHED, START);
-- LAYERS PARAMETERS :
constant NUM_OF_NEURONS : num_of_neurons_t := (
16,
16,
10
);
constant NUM_OF_INPUTS : num_of_inputs_t := (
784,
16,
16
);
constant ACTIVATION_FUNCTONS : activation_functions_t := ( -- activation function for 1st layer is always linear
0,
2,
2
);
end package config_pkg;
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/09/2024 04:06:54 PM
-- Design Name:
-- Module Name: activation_function - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all; -- For integer operations and conversions
use work.function_pkg.all; -- Import the function package
-- implementation of the hardtanh activation function
entity hartanh is
generic (
DATA_WIDTH : natural := 32;
INT_WIDTH : natural := 23
);
port (
clk : IN std_logic; -- reset
rst : IN std_logic; -- reset
value_i : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- input vector
valid_value_i : IN std_logic; -- valid input
value_o : OUT std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- output vector
valid_value_o : OUT std_logic -- valid output
);
end hartanh;
architecture Behavioral of hartanh is
constant DEN_WIDTH : natural := DATA_WIDTH - INT_WIDTH;
begin
hardtanh : process(clk, rst)
begin
if rst = '1' then
value_o <= (value_o'range => '0');
valid_value_o <= '0';
elsif rising_edge(clk) then
if signed(value_i) < to_signed(to_fixed(1.0, DEN_WIDTH), DATA_WIDTH) and signed(value_i) > to_signed(to_fixed(-1.0, DEN_WIDTH), DATA_WIDTH) and valid_value_i = '1' then -- sign bit to 0
value_o <= value_i;
valid_value_o <= '1';
elsif signed(value_i) < to_signed(to_fixed(-1.0, DEN_WIDTH), DATA_WIDTH) and valid_value_i = '1' then -- sign bit to 1
value_o <= std_logic_vector(to_signed(to_fixed(-1.0, DEN_WIDTH), DATA_WIDTH));
valid_value_o <= '1';
elsif signed(value_i) > to_signed(to_fixed(1.0, DEN_WIDTH), DATA_WIDTH) and valid_value_i = '1' then
value_o <= std_logic_vector(to_signed(to_fixed(1.0, DEN_WIDTH), DATA_WIDTH));
valid_value_o <= '1';
else
value_o <= (others => '0');
valid_value_o <= '0';
end if;
end if;
end process;
end Behavioral;
\ No newline at end of file
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/20/2024 03:24:02 PM
-- Design Name:
-- Module Name: Interface - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
-- this module controle the interface between the FPGA's outputs and inputs
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all; -- for shifts and others
use work.config_pkg.all; -- Import the configuration package
use work.function_pkg.all; -- Import the function package
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity interface is
generic(
NUM_INPUTS : natural := 16 -- max 16, because only 16 leds and it is cool to see which input you are selecting.
);
port(
clk : IN std_logic;
rst : IN std_logic;
btnL : IN std_logic; -- button left for changing input
btnR : IN std_logic; -- button right for changing input
seg : OUT std_logic_vector(6 downto 0); -- seven segment control vector
LED : OUT std_logic_vector(NUM_INPUTS-1 downto 0); -- LED controle vector, to know which input is selected
an : OUT std_logic -- for selecting the seven segment on the board as they are multiplexed.
);
end interface;
architecture Behavioral of Interface is
component std_memory
generic(
ADD_WIDTH : natural := 4;
NUM_OF_INPUTS : natural := 3;
NUM_NEURON : natural := 5;
NUM_LAYER : natural := 1;
DATA_WIDTH : natural := 16;
DATA_FILE : string := ""
);
port(
clk : IN std_logic;
radd : IN std_logic_vector(ADD_WIDTH-1 downto 0);
enable : IN std_logic;
wout : OUT std_logic_vector(DATA_WIDTH-1 downto 0)
);
end component;
component network
port (
clk : IN std_logic;
rst : IN std_logic;
en : IN std_logic;
inputs : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0);
output : OUT std_logic_vector(log2_unsigned(NUM_OF_OUTPUTS)-1 downto 0) := (others => '0'); -- will be necessary to pipeline layers
r_addr : OUT std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- read address from input ROM
valid_value_i : IN std_logic;
valid_value_o : OUT std_logic
);
end component;
type mux_input_t is array (NUM_INPUTS-1 downto 0) of std_logic_vector(DATA_WIDTH -1 downto 0);
signal mem_en : std_logic_vector(NUM_INPUTS - 1 downto 0) := (NUM_INPUTS - 1 downto 1 => '0' ) & '1'; -- enable each input memories accordingly to their index
signal net_inputs : mux_input_t := (others => (others => '0')); -- input for the network
signal net_input : std_logic_vector(DATA_WIDTH -1 downto 0) := (others => '0');
signal addr_mux : std_logic_vector(log2_unsigned(NUM_INPUTS)-1 downto 0) := (log2_unsigned(NUM_INPUTS)-1 downto 1 => '0') & '1';
signal enable_network : std_logic := '0'; -- enable the nework
signal output_network : std_logic_vector(log2_unsigned(NUM_OF_OUTPUTS)-1 downto 0) := (others => '0'); -- outputs for the network
signal r_addr_network : std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0) := (others => '0');
signal valid_input : std_logic := '0';
signal valid_output : std_logic := '0';
signal seg_vect : std_logic_vector(6 downto 0) := "1000000";
begin
gen_inputs_memory : for i in 0 to NUM_INPUTS-1 generate -- generate NUM_INPUTS memories with their respectfull input image initialized in memory.
begin
input_memory_inst : std_memory
generic map(
DATA_WIDTH => DATA_WIDTH,
ADD_WIDTH => log2_unsigned(MAX_INPUTS_SIZE),
NUM_OF_INPUTS => NUM_OF_INPUTS(0),
NUM_NEURON => 23,
NUM_LAYER =>i,
DATA_FILE => "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/inputs/in_" & integer'image(i) & ".mif" -- weight file of the neuron
)
port map (
clk => clk,
radd => r_addr_network,
enable => mem_en(i),
wout => net_inputs(i)
);
end generate gen_inputs_memory;
inst_network : network
port map(
clk => clk,
rst => rst,
en => enable_network,
inputs => net_input,
output => output_network,-- will be necessary to pipeline layers
r_addr => r_addr_network, -- read address from input ROM
valid_value_i => valid_input,
valid_value_o => valid_output
);
change_input : process(btnL, btnR)
begin
if rising_edge(btnL) then
mem_en <= std_logic_vector(rotate_left(unsigned(mem_en), 1));
addr_mux
if enable_network = '0' then
enable_network <= '1';
end if;
elsif rising_edge(btnR) then
mem_en <= std_logic_vector(rotate_right(unsigned(mem_en), 1));
if enable_network = '0' then
enable_network <= '1';
end if;
end if;
end process;
LED <= mem_en; -- for seeing which input is selected
seven_segment : process(clk)
begin
if rising_edge(clk) then
if valid_output = '0' then
seg <= "1101010"; -- n for none
else
case output_network is -- change in function of the number of classes (here MNIST custom)
when "0000" => seg <= "0000001"; -- "0"
when "0001" => seg <= "1001111"; -- "1"
when "0010" => seg <= "0010010"; -- "2"
when "0011" => seg <= "0000110"; -- "3"
when "0100" => seg <= "1001100"; -- "4"
when "0101" => seg <= "0100100"; -- "5"
when "0110" => seg <= "0100000"; -- "6"
when "0111" => seg <= "0001111"; -- "7"
when "1000" => seg <= "0000000"; -- "8"
when "1001" => seg <= "0000100"; -- "9"
when others => seg <= "0001001"; -- big n
end case;
end if;
end if;
end process;
an <= '1'; -- select the first seven segment of the board
end Behavioral;
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/09/2024 04:06:54 PM
-- Design Name:
-- Module Name: activation_function - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity ReLU is
generic (
DATA_WIDTH : natural := 32
);
port (
clk : IN std_logic; -- clock
rst : IN std_logic; -- reset
value_i : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- input vector
valid_value_i : IN std_logic; -- valid input
value_o : OUT std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- output vector
valid_value_o : OUT std_logic
);
end ReLU;
architecture Behavioral of ReLU is
begin
relu : process(clk, rst)
begin
if rst = '1' then
value_o <= (value_o'range => '0');
valid_value_o <= '0';
elsif rising_edge(clk) then
if (value_i(DATA_WIDTH-1) = '0') and valid_value_i = '1' then -- sign bit to 0
value_o <= value_i;
valid_value_o <= '1';
elsif (value_i(DATA_WIDTH-1) = '1') and valid_value_i = '1' then -- sign bit to 1
value_o <= (others => '0');
valid_value_o <= '1';
else
value_o <= (others => '0');
valid_value_o <= '0';
end if;
end if;
end process;
end Behavioral;
\ No newline at end of file
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all; -- For integer operations and conversions
-- this package contain usefull functions for this use ieee.fixed_float_types
package function_pkg is
-- compute the log2 of a number, usefull for automatic adress bit_width computation
function log2_unsigned (nbData : in natural) return natural;
function to_fixed (number : real; fractional_bit_length : natural ) return integer;
function radix_multiply ( left, right : std_logic_vector; radix : natural) return std_logic_vector;
end package function_pkg;
package body function_pkg is
----------------------------------------------------------------------------------------
-- log2(x), used for finding the optimal add width for a given number of datapoints
function log2_unsigned(nbData : in natural) return natural is
variable temp : natural := nbData;
variable n : natural := 0;
begin
while temp > 1 loop
temp := temp / 2 ;
n := n + 1 ;
end loop ;
return n+1;
end function;
-----------------------------------------------------------------------------------------
-- convert reals into fixe points
function to_fixed (number : real; fractional_bit_length : natural ) return integer is
begin
return integer(number*2.0**(fractional_bit_length));
end to_fixed;
-----------------------------------------------------------------------------------------
-- multiply fixed points
function radix_multiply ( left, right : std_logic_vector; radix : natural) return std_logic_vector is
constant word_length : natural := left'length + right'length;
variable result : signed(word_length-1 downto 0);
begin
result := signed(left) * signed(right);
return std_logic_vector(result(left'length+radix-1 downto radix));
end radix_multiply;
end package body function_pkg;
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/14/2024 09:13:49 PM
-- Design Name:
-- Module Name: hardmax - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all; -- For integer operations and conversions
use work.config_pkg.all; -- Import the configuration package
use work.function_pkg.all; -- Import the function package
entity hardmax is -- more or less hardagrmax
generic (
DATA_WIDTH : natural := 32;
NUM_OF_INPUTS : natural := 23 -- number of inputs for the hardmax module
);
port (
clk : IN std_logic;
rst : IN std_logic;
enable_l_i : IN std_logic; -- enable the layer
start_l_i : IN std_logic; -- start the layer
idle_l_o : OUT std_logic; -- layer in IDLE state
ready_l_o : OUT std_logic; -- layer ready to start
r_addr : OUT std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- read neuron address for previous layer
input : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0);
output : OUT std_logic_vector(log2_unsigned(NUM_OF_INPUTS)-1 DOWNTO 0); -- will be necessary to pipeline layers
valid_value_i : IN std_logic; -- valid input value
valid_value_o : OUT std_logic -- valid output
);
end hardmax;
architecture Behavioral of hardmax is
signal cnt : std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0) := (others => '0');
signal found_max_value : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '1'); -- 0b1111...1111 = biggest negative number in fixe point
signal found_max_index : std_logic_vector(log2_unsigned(NUM_OF_INPUTS)-1 downto 0) := (others => '1'); -- 0b1111...1111 = biggest negative number in fixe point
signal state : state_t := IDLE;
signal max_valid : std_logic := '0';
signal ready : std_logic := '0';
begin
max_finder : process(clk, rst)
begin
if rst = '1' then
found_max_value <= (found_max_value'range => '1');
elsif rising_edge(clk) and state = EXECUTING then
if signed(input) >= signed(found_max_value) then
found_max_value <= input;
found_max_index <= cnt(log2_unsigned(NUM_OF_INPUTS)-1 downto 0);
end if;
end if;
end process;
get_input : process(clk, rst) -- get values from previous layer
begin
if rst = '1' then
cnt <= (cnt'range => '0');
elsif rising_edge(clk) then
if enable_l_i = '1' and state = EXECUTING then
if unsigned(cnt) < NUM_OF_INPUTS-1 then
cnt <= std_logic_vector(unsigned(cnt)+1);
elsif unsigned(cnt) = NUM_OF_INPUTS-1 then
max_valid <= '1';
end if;
elsif(enable_l_i = '1' or valid_value_i = '1') then
cnt <= (others => '0');
max_valid <= '0';
end if;
end if;
end process;
state_machine : process(clk, rst)
begin
if rst = '0' then
if rising_edge(clk) and rst = '0' then
if enable_l_i = '1' and start_l_i = '1' and valid_value_i = '1' and state /= EXECUTING and state /= START and ready = '1' then
state <= START;
idle_l_o <= '0';
ready <= '0';
output <= (others => '0');
elsif enable_l_i = '1' and state = START then
state <= EXECUTING;
idle_l_o <= '0';
output <= (others => '0');
elsif enable_l_i = '1' and max_valid = '1' then
state <= FINISHED;
idle_l_o <= '0';
ready <= '1';
valid_value_o <= '1';
output <= found_max_index;
elsif enable_l_i ='0' then
state <= IDLE;
idle_l_o <= '1';
ready <= '1';
valid_value_o <= '0';
output <= (others => '0');
end if;
end if;
else -- rest a 1
state <= IDLE;
idle_l_o <= '1';
ready <= '1';
valid_value_o <= '0';
output <= (others => '0');
end if;
end process;
r_addr <= cnt;
ready_l_o <= ready;
end Behavioral;
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/11/2024 10:27:05 AM
-- Design Name:
-- Module Name: layer - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all; -- For integer operations and conversions
use work.function_pkg.all; -- Import the function package
use work.config_pkg.all;
entity layer is
generic ( -- default for first layer of a standard mnist network
NUM_OF_LAYERS : natural := 3;
DATA_WIDTH : natural := 32; -- bit width
NUM_OF_NEURONS : natural := 16; -- number of neurons in the layer
NUM_OF_INPUTS : natural := 784; -- number of neurons in the layer
NUM_LAYER : natural := 0; -- indic of the layer
DATA_PATH : string := "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/weights/";
ACTIVATION_TYPE : natural := 2
);
port (
clk : IN std_logic;
rst : IN std_logic;
enable_l_i : IN std_logic; -- enable the layer
start_l_i : IN std_logic; -- start the layer
idle_l_o : OUT std_logic; -- layer in IDLE state
ready_l_o : OUT std_logic; -- layer ready to start
n_addr : IN std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- read neuron address from next layer
r_addr : OUT std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- read neuron address for previous layer
input : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0);
output : OUT std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- will be necessary to pipeline layers
valid_value_i : IN std_logic; -- valid input value
valid_value_o : OUT std_logic -- valid output value
);
end layer;
architecture Behavioral of layer is
-- add all components, even if they won't be synthesized because of your parameters
component ReLU
generic (
DATA_WIDTH : natural := 32
);
port (
clk : IN std_logic; -- clock
rst : IN std_logic; -- reset
value_i : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- input vector
valid_value_i : IN std_logic; -- valid input
value_o : OUT std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- output vector
valid_value_o : OUT std_logic
);
end component;
component hartanh
generic (
DATA_WIDTH : natural := 32;
INT_WIDTH : natural := 23
);
port (
clk : IN std_logic; -- clock
rst : IN std_logic; -- reset
value_i : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- input vector
valid_value_i : IN std_logic; -- valid input
value_o : OUT std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- output vector
valid_value_o : OUT std_logic -- valid output
);
end component;
component perceptron
generic (
DATA_WIDTH : natural := 32;
NUM_OF_INPUTS : natural; -- number of inputs in the layer
NUM_NEURON : natural := 23; -- number of the neuron to identification purposes
NUM_LAYER : natural := 23; -- number of the layer for identification purposes
DATA_FILE : string := ""
);
port (
clk : IN std_logic;
rst : IN std_logic;
enable_n_i : IN std_logic; -- enable the neuron
state : IN state_t; -- neuron state from the layer state machine
idle_n_o : OUT std_logic; -- neuron in IDLE state
ready_n_o : OUT std_logic; -- neuron ready
value_i : IN std_logic_vector(DATA_WIDTH-1 downto 0); -- single input for data access reasons
valid_value_i : IN std_logic;
value_o : OUT std_logic_vector(DATA_WIDTH-1 downto 0);
valid_o : OUT std_logic -- valid signal for the control purposes
);
end component;
type neuron_array_t is array (0 to NUM_OF_NEURONS-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
signal value_percep_i : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
signal valid_value_percep_i : std_logic := '0'; -- valid value broadcast to the perceptron
signal valid_value_layer_i : std_logic := '0'; -- valid value from the inputs of the layer
signal value_o : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
signal cnt : std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0) := (others => '0'); -- counter for addressing inputs in previous layer
signal neuron_values : neuron_array_t := (others => (others => '0')); -- register bench that contain current outputs of perceptrons
signal neuron_values_hold : neuron_array_t := (others => (others => '0')); -- register bench contain last iteration value (used for pipelining layers containing different
-- ctrl signals for pipelining
signal valid_verif : std_logic_vector(NUM_OF_NEURONS-1 downto 0) := (others => '0');
signal valid_value_layer_o : std_logic := '0'; -- entire layer has valid value (not usefull if all perceptron has the same number of MAC cycles
signal flush_output : std_logic := '0'; -- flush the hold input for the new one on rising edge
signal start_n : std_logic := '0'; -- enable MAC operations (start and stop the layer)
signal idle_n : std_logic := '0'; -- enable MAC operations (start and stop the layer)
signal all_n_ready : std_logic_vector(NUM_OF_NEURONS-1 downto 0) := (others => '0'); -- enable MAC operations (start and stop the layer)
signal ready_l : std_logic := '0'; -- layer ready
signal state : state_t := IDLE;
begin
gen_neurons : for i in 0 to NUM_OF_NEURONS-1 generate -- generate neuron + control inna loop
signal valid_value_dum_o : std_logic;
begin
neuron_inst : perceptron
generic map (
DATA_WIDTH => DATA_WIDTH,
NUM_OF_INPUTS => NUM_OF_INPUTS, -- usefull inside
NUM_NEURON => i,
NUM_LAYER => NUM_LAYER,
DATA_FILE => DATA_PATH & "w_l" & integer'image(NUM_LAYER) & "_n" & integer'image(i) & ".mif" -- create a string like path + w_l + num_layer + _n + num_neuron + .csv
-- see the python script for the standart syntaxe for the files.
-- biases are stored as the last value in this file
)
port map (
clk => clk,
rst => rst,
enable_n_i => enable_l_i,
state => state,
idle_n_o => idle_n,
ready_n_o => all_n_ready(i),
value_i => value_percep_i, -- single input for data access reasons
valid_value_i => valid_value_percep_i,
value_o => neuron_values(i), -- assign the value of the perceptron to the array (is this synthetisable ? )
valid_o => valid_value_dum_o -- valid signal for the control purposes
);
valid_verif(i) <= valid_value_dum_o; -- assign valid bit in the valid bus for the whole layer
end generate gen_neurons;
-- Activation function
gen_hardtanh : if ACTIVATION_TYPE = 2 generate -- hard hyperbolic tangeante
hardtanh_inst : hartanh
generic map(
DATA_WIDTH => DATA_WIDTH,
INT_WIDTH => INT_WIDTH
)
port map(
clk => clk, -- clock
rst => rst, -- reset
value_i => input, -- input vector
valid_value_i => valid_value_i, -- valid input
value_o => value_percep_i,-- output vector
valid_value_o => valid_value_percep_i
);
end generate;
gen_ReLU : if ACTIVATION_TYPE = 1 generate -- classic, array of strings cannot have dynamic size so length of 8 for all
relu_inst : relu
generic map(
DATA_WIDTH => DATA_WIDTH
)
port map(
clk => clk, -- clock
rst => rst, -- reset
value_i => input, -- input vector
valid_value_i => valid_value_i, -- valid input
value_o => value_percep_i,-- output vector
valid_value_o => valid_value_percep_i
);
end generate;
gen_Linear : if ACTIVATION_TYPE = 0 generate -- LINEAR activation (for inputs typically)
-- handling inputs for neurons
valid_value_percep_i <= valid_value_i when rst = '0' else
'0';
value_percep_i <= input when rst = '0' else
(others => '0');
end generate;
state_machine : process(clk, rst)
begin
if rst = '0' then
if rising_edge(clk) and rst = '0' then
if enable_l_i = '1' and start_l_i = '1' and valid_value_i = '1' and state /= EXECUTING and state /= START and ready_l = '1' then
state <= START;
idle_l_o <= '0';
ready_l_o <= '0';
elsif enable_l_i = '1' and state = START then
state <= EXECUTING;
idle_l_o <= '0';
elsif enable_l_i = '1' and valid_value_layer_o = '1' then
state <= FINISHED;
idle_l_o <= '0';
ready_l_o <= '1';
valid_value_o <= '1';
elsif enable_l_i ='0' then
report("idle");
state <= IDLE;
idle_l_o <= '1';
ready_l_o <= '1';
valid_value_o <= '0';
end if;
end if;
else -- rest a 1
state <= IDLE;
idle_l_o <= '1';
ready_l_o <= '1';
valid_value_o <= '0';
end if;
end process;
-- handling valid signals from neurons
valid_value_layer_o <= '1' when (valid_verif and (valid_verif'range => '1')) = (valid_verif'range => '1') and enable_l_i = '1' else
'0';
-- handling ready signals from neurons
ready_l <= '1' when (all_n_ready and (all_n_ready'range => '1')) = (all_n_ready'range => '1') and enable_l_i = '1' else
'0';
release_output : process(clk, rst) -- output values for next layer and
begin
if rst = '1' then
neuron_values_hold <= (others => (others => '0'));
elsif state = start then
neuron_values_hold <= neuron_values;
end if;
end process;
get_input : process(clk, rst) -- get values from previous layer
begin
if rst = '1' then
cnt <= (cnt'range => '0');
elsif rising_edge(clk) then
if state = EXECUTING then
if unsigned(cnt) < NUM_OF_INPUTS-1 then
cnt <= std_logic_vector(unsigned(cnt)+1);
end if;
elsif state = START then
cnt <= (others => '0');
end if;
end if;
end process;
r_addr <= cnt ;
-- outputing values
give_output : process(clk, rst)
begin
if rst = '1' then
output <= (others => '0');
elsif rising_edge(clk) and state = EXECUTING then
output <= neuron_values_hold(to_integer(unsigned(n_addr)));
end if;
end process;
end Behavioral;
\ No newline at end of file
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
use work.config_pkg.all; -- Import the configuration package
use work.function_pkg.all; -- Import the function package
entity network is
port (
clk : IN std_logic;
rst : IN std_logic;
en : IN std_logic;
inputs : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0);
output : OUT std_logic_vector(log2_unsigned(NUM_OF_OUTPUTS)-1 downto 0) := (others => '0'); -- will be necessary to pipeline layers
r_addr : OUT std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- read address from input ROM
valid_value_i : IN std_logic;
valid_value_o : OUT std_logic
);
end network;
architecture Behavioral of network is
-- connections between layer :
---------------------------------------------------------------------
-- I used arrays for the signals. It might not be optimal but I cannot use the index in the generate loop as a wildcard for custom signal names so here's my solution.
type std_logic_t is array (NUM_OF_LAYERS - 1 downto 0) of std_logic;
type std_logic_vect_log2_neurons_t is array (NUM_OF_LAYERS - 1 downto 0) of std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- of the biggest layer
type std_logic_vect_log2_inputs_t is array (NUM_OF_LAYERS - 1 downto 0) of std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- of the biggest layer
type std_logic_vector_data_width_t is array (NUM_OF_LAYERS - 1 downto 0) of std_logic_vector(DATA_WIDTH-1 DOWNTO 0);
type std_logic_vector_log2_num_layers_t is array (NUM_OF_LAYERS - 1 downto 0) of std_logic_vector(log2_unsigned(NUM_OF_LAYERS+1)-1 downto 0);
type layers_connections_record_t is record
-- layer :
idle_l_o : std_logic_vector(NUM_OF_LAYERS downto 0); -- layer in IDLE state
ready_l_o : std_logic_vector(NUM_OF_LAYERS downto 0); -- layer ready to start
n_addr : std_logic_vect_log2_neurons_t; -- read neuron address from next layer
r_addr : std_logic_vect_log2_inputs_t; -- read neuron address for previous layer
input : std_logic_vector_data_width_t;
output : std_logic_vector_data_width_t; -- will be necessary to pipeline layers
valid_value_i : std_logic_vector(NUM_OF_LAYERS downto 0);
valid_value_o : std_logic_vector(NUM_OF_LAYERS downto 0);
end record layers_connections_record_t;
-- init constant for layers_connection_record
constant init_layers_connections : layers_connections_record_t := (
idle_l_o => (others => '0'), -- idle state
ready_l_o => (others => '0'), -- ready state
n_addr => (others => (others => '0')), -- input address
r_addr => (others => (others => '0')), -- output address
input => (others => (others => '0')),
output => (others => (others => '0')),
valid_value_i => (others => '0'),
valid_value_o=> (others => '0')
);
-----------------------------------------------------------------------------
-- layer component def
component layer
generic ( -- default for first layer of a standard mnist network
NUM_OF_LAYERS : natural := 3;
DATA_WIDTH : natural := 32; -- bit width
NUM_OF_NEURONS : natural := 16; -- number of neurons in the layer
NUM_OF_INPUTS : natural := 784; -- number of neurons in the layer
NUM_LAYER : natural := 0; -- indic of the layer
DATA_PATH : string := "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/weights/";
ACTIVATION_TYPE : natural := 2
);
port (
clk : IN std_logic;
rst : IN std_logic;
enable_l_i : IN std_logic; -- enable the layer
start_l_i : IN std_logic; -- start the layer
idle_l_o : OUT std_logic; -- layer in IDLE state
ready_l_o : OUT std_logic; -- layer ready to start
n_addr : IN std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- read neuron address from next layer
r_addr : OUT std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- read neuron address for previous layer
input : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0);
output : OUT std_logic_vector(DATA_WIDTH-1 DOWNTO 0); -- will be necessary to pipeline layers
valid_value_i : IN std_logic; -- valid input value
valid_value_o : OUT std_logic -- valid output value
);
end component;
component hardmax
generic (
DATA_WIDTH : natural := 32;
NUM_OF_INPUTS : natural := 23 -- number of inputs for the hardmax module
);
port (
clk : IN std_logic;
rst : IN std_logic;
enable_l_i : IN std_logic; -- enable the layer
start_l_i : IN std_logic; -- start the layer
idle_l_o : OUT std_logic; -- layer in IDLE state
ready_l_o : OUT std_logic; -- layer ready to start
r_addr : OUT std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- read neuron address for previous layer
input : IN std_logic_vector(DATA_WIDTH-1 DOWNTO 0);
output : OUT std_logic_vector(log2_unsigned(NUM_OF_INPUTS)-1 DOWNTO 0); -- will be necessary to pipeline layers
valid_value_i : IN std_logic; -- valid input value
valid_value_o : OUT std_logic -- valid output
);
end component;
-- general signals definition ;
signal enable_network : std_logic := '0';
signal net_output : std_logic_vector(log2_unsigned(NUM_OF_NEURONS(NUM_OF_LAYERS-1))-1 downto 0) := (others => '0');
signal LC : layers_connections_record_t := init_layers_connections; -- define all layers connection and init them to 0. see config_pkg (lc for layer_conenctions)
-- such as :
-- - l_en_i : for enabling the layer
-- - input : for the input of the layer
-- - outputs : for the outputs of the alias
-- - valid_value_i : for the input of the layer
-- to continue ...
-- attribute ram_style : string;
-- attribute ram_style of LC : signal is "distributed"; -- to try for implementation
-- control signal
type state_n_t is (IDLE, EXECUTING, PUSH, FLUSH);
signal addr_input : std_logic_vector(log2_unsigned(MAX_INPUTS_SIZE)-1 downto 0); -- addr for the input of the first layer
signal net_input : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
signal valid_value_input : std_logic := '0'; -- value of the said input
signal start_n : std_logic := '0'; -- start network
signal all_layers_ready : std_logic := '0';
signal state_n : state_n_t := IDLE;
signal mem_in_en : std_logic := '0'; -- enabling input memory
begin
gen_layers : for i in 0 to NUM_OF_LAYERS-1 generate -- generate the whole networks
begin
-- layer gen
layer_inst : layer
generic map (
NUM_OF_LAYERS => NUM_OF_LAYERS,
DATA_WIDTH => DATA_WIDTH,
NUM_OF_NEURONS => work.config_pkg.NUM_OF_NEURONS(i), -- defined in config_pkg.vhd
NUM_OF_INPUTS => work.config_pkg.NUM_OF_INPUTS(i), -- defined in config_pkg.vhd
NUM_LAYER => i,
DATA_PATH => DATA_PATH,
ACTIVATION_TYPE => ACTIVATION_FUNCTONS(i)
)
port map (
clk => clk, -- in
rst => rst, -- in
enable_l_i => enable_network, -- in, enable layer MAC
start_l_i => start_n, -- in, start layer
idle_l_o => LC.idle_l_o(i), -- out, idle state
ready_l_o => LC.ready_l_o(i), -- out, ready state
n_addr => LC.n_addr(i), --in
r_addr => LC.r_addr(i), -- out
input => LC.input(i), -- in
output => LC.output(i), -- out
valid_value_i => LC.valid_value_i(i), -- in
valid_value_o => LC.valid_value_o(i) -- out
);
-- input layers connexions :
input_layer : if i = 0 generate
-- standart
LC.n_addr(i) <= LC.r_addr(i+1);
LC.input(i+1) <= LC.output(i);
LC.valid_value_i(i+1) <= LC.valid_value_o(i);
-- input layer specific
addr_input <= LC.r_addr(i);
LC.input(i) <= net_input;
LC.valid_value_i(i) <= valid_value_input;
end generate input_layer;
-- intermediate layers connexions :
intermediate_layer : if i > 0 and i < NUM_OF_LAYERS-1 generate
LC.n_addr(i) <= LC.r_addr(i+1);
LC.input(i+1) <= LC.output(i);
LC.valid_value_i(i+1) <= LC.valid_value_o(i);
end generate intermediate_layer;
-- output layers connexions :
output_layer : if i = NUM_OF_LAYERS-1 generate
-- for now, don't care about the output
end generate output_layer;
end generate gen_layers;
-- handling ready signals from layers
all_layers_ready <= '1' when (LC.ready_l_o and (LC.ready_l_o'range => '1')) = (LC.ready_l_o'range => '1') and enable_network = '1' else
'0';
-- control
enable_network <= en;
pipeline_control_state_machine : process(clk, rst)
begin
if rising_edge(clk) then
if (rst = '1' or enable_network = '0') and state_n /= FLUSH and state_n /= IDLE then -- for now when network disable => flush the whole pipeline
state_n <= FLUSH;
start_n <= '0';
mem_in_en <= '0';
valid_value_input <= '0';
elsif all_layers_ready = '1' and enable_network = '1' and state_n /= PUSH then
state_n <= PUSH;
start_n <= '1';
mem_in_en <= '1';
valid_value_input <= '1';
elsif state_n = PUSH and enable_network = '1' then
state_n <= EXECUTING;
start_n <= '0';
mem_in_en <= '1';
valid_value_input <= '1';
elsif state_n = FLUSH then
state_n <= IDLE;
start_n <= '0';
mem_in_en <= '0';
valid_value_input <= '0';
end if;
end if;
end process;
-- last activation activation (only hardmax for now)
inst_hardmax : hardmax
generic map(
DATA_WIDTH => DATA_WIDTH,
NUM_OF_INPUTS => NUM_OF_NEURONS(NUM_OF_LAYERS-1) -- number of inputs for the hardmax module
)
port map(
clk => clk,
rst => rst,
enable_l_i => enable_network, -- enable the hardmax
start_l_i => start_n, -- start the hardmax
idle_l_o => LC.idle_l_o(NUM_OF_LAYERS), -- hardmax in IDLE state
ready_l_o => LC.ready_l_o(NUM_OF_LAYERS), -- hardmax ready to start
r_addr => LC.n_addr(NUM_OF_LAYERS-1), -- read neuron address for previous layer
input => LC.output(NUM_OF_LAYERS-1),
output => net_output,
valid_value_i => LC.valid_value_o(NUM_OF_LAYERS-1), -- valid input value
valid_value_o => LC.valid_value_o(NUM_OF_LAYERS)-- valid output
);
-- inputs and outputs handling
r_addr <= LC.r_addr(0); -- r_addr takes the r_addr of the first layer.
output <= net_output; -- takes the output of the hardmax.
valid_value_o <= LC.valid_value_o(NUM_OF_LAYERS);-- valid output
net_input <= inputs;
end Behavioral;
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 09/11/2024 01:37:00 PM
// Design Name:
// Module Name: std_memory
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
// Using verilog because VHDL doesn't support synthesizable init from files :(
module std_memory #(parameter ADD_WIDTH=4, NUM_OF_INPUTS=3, NUM_NEURON=5,NUM_LAYER=1, DATA_WIDTH=16, DATA_FILE = "/home/achoul/AI_ON_FPGA/TP_MLP/mnist_mlp/data_files/weights/w_l0_n0.csv")
(
input wire clk,
input wire [ADD_WIDTH-1:0] radd,
input wire enable, // enable wire
output reg [DATA_WIDTH-1:0] wout);
reg [DATA_WIDTH-1:0] mem [NUM_OF_INPUTS-1:0];
initial
begin
$readmemb(DATA_FILE, mem); // change to $readmemh for hexadecimal files
end
always_ff @(posedge clk)
begin
if (enable) begin
wout = mem[radd];
end else begin
wout = {DATA_WIDTH{1'b0}}; // 0 if not enable
end
end
endmodule
\ No newline at end of file
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 09/09/2024 03:07:15 PM
-- Design Name:
-- Module Name: perceptron - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all; -- For integer operations and conversions
use work.function_pkg.all; -- Import the function use ieee.fixed_float_types
use work.config_pkg.all; -- Import the function use ieee.fixed_float_types
entity perceptron is
--generic entity, that way you can generate a bunch of them in a loop for layer instanciation--
generic (
DATA_WIDTH : natural := 32;
NUM_OF_INPUTS : natural := 16; -- number of inputs in the layer
NUM_NEURON : natural := 23; -- number of the neuron to identification purposes
NUM_LAYER : natural := 23; -- number of the layer for identification purposes
DATA_FILE : string := ""
);
port (
clk : IN std_logic;
rst : IN std_logic;
enable_n_i : IN std_logic; -- enable the neuron
state : IN state_t; -- neuron state from the layer state machine
idle_n_o : OUT std_logic; -- neuron in IDLE state
ready_n_o : OUT std_logic; -- neuron ready
value_i : IN std_logic_vector(DATA_WIDTH-1 downto 0); -- single input for data access reasons
valid_value_i : IN std_logic;
value_o : OUT std_logic_vector(DATA_WIDTH-1 downto 0);
valid_o : OUT std_logic -- valid signal for the control purposes
);
end perceptron;
architecture Behavioral of perceptron is
type state_t is (IDLE, EXECUTING, FINISHED, START);
-- signals definition
-- do not use varibles, not synthetizable
signal input : std_logic_vector((DATA_WIDTH)-1 downto 0) := (others => '0');
signal weight : std_logic_vector((DATA_WIDTH)-1 downto 0):= (others => '0');
signal accu : std_logic_vector((DATA_WIDTH)-1 downto 0):= (others => '0');
signal acc_valid : std_logic;
signal cnt : std_ulogic_vector(log2_unsigned(NUM_OF_INPUTS)-1 downto 0) := (others => '0'); -- counter for addressing weights in the reg, change the attribution on cnt size
signal addr : std_ulogic_vector(log2_unsigned(NUM_OF_INPUTS)-1 downto 0) := (others => '0');
signal mem_en : std_logic := '0'; -- enable memory
component std_memory
generic(
ADD_WIDTH : natural := 4;
NUM_OF_INPUTS : natural := 3;
NUM_NEURON : natural := 5;
NUM_LAYER : natural := 1;
DATA_WIDTH : natural := 16;
DATA_FILE : string := ""
);
port(
clk : IN std_logic;
radd : IN std_ulogic_vector(ADD_WIDTH-1 downto 0);
enable : IN std_logic;
wout : OUT std_logic_vector(DATA_WIDTH-1 downto 0)
);
end component;
begin -- begin architecture
-- instantiate weights memory
weight_memory_inst : std_memory
generic map(
DATA_WIDTH => DATA_WIDTH,
ADD_WIDTH => log2_unsigned(NUM_OF_INPUTS),
NUM_OF_INPUTS => NUM_OF_INPUTS + 1, -- +1 to account for the bias in last position
NUM_NEURON => NUM_NEURON,
NUM_LAYER => NUM_LAYER,
DATA_FILE => DATA_FILE -- weight file of the neuron
)
port map (
clk => clk,
radd => cnt,
enable => mem_en,
wout => weight
);
-- Start of the module code
input <= value_i when valid_value_i = '1' else
(input'range => '0');
MAC : process(clk, rst)
begin
if rising_edge(clk) then
if state = IDLE then -- do nothing state ~= reset for now
accu <= (others => '0');
acc_valid <= '0';
cnt <= (others => '0');
idle_n_o <= '1';
ready_n_o <= '1';
valid_o <= '0';
mem_en <= '0';
value_o <= (others => '0');
elsif state = EXECUTING then
if (to_integer(unsigned(cnt)) < NUM_OF_INPUTS+1) and valid_value_i = '1' and enable_n_i = '1' then
accu <= std_logic_vector(signed(accu) + signed(radix_multiply(input, weight, DATA_WIDTH - INT_WIDTH))); -- standard MAC (multiply accumulate)
cnt <= std_ulogic_vector(unsigned(cnt) + 1);
valid_o <= '0';
if unsigned(cnt) >= NUM_OF_INPUTS then
mem_en <= '0';
else
mem_en <= '1';
end if;
elsif (to_integer(unsigned(cnt)) = NUM_OF_INPUTS +1) and enable_n_i = '1' then
accu <= std_logic_vector(signed(accu) + signed(weight)); -- add bias
cnt <= std_ulogic_vector(unsigned(cnt) + 1);
valid_o <= '0';
mem_en <= '0';
elsif (to_integer(unsigned(cnt)) > NUM_OF_INPUTS+1) and enable_n_i = '1' then -- standby after
valid_o <= '1';
mem_en <= '0';
value_o <= accu; -- truncate the output (diwall overflow)
end if;
idle_n_o <= '0';
ready_n_o <= '0';
elsif state = FINISHED then
cnt <= (others => '0');
idle_n_o <= '0';
ready_n_o <= '1';
valid_o <= '1';
elsif state = START then
mem_en <= '1';
accu <= (others => '0');
acc_valid <= '0';
cnt <= (others => '0');
idle_n_o <= '0';
ready_n_o <= '0';
valid_o <= '0';
value_o <= (others => '0');
end if;
end if;
end process;
end Behavioral;
File added
import numpy as np
import argparse
import os
import csv
import torch.nn as nn
import torch
def extract_fcl_layers(model): # create a list containing layers info
layer_info = []
previous_layer_was_fcl = False
for layer in model.modules():
if isinstance(layer, nn.Linear) : # Check if the layer is a fully connected layer
layer_info.append({
'layer': layer,
'in_features': layer.in_features,
'out_features': layer.out_features,
'activation': None # Placeholder for activation function
})
cnt = 0
for layer in model.modules():
if cnt == 0 :
layer_info[cnt]['activation'] = 0
cnt += 1
elif isinstance(layer, (nn.ReLU, nn.Hardtanh)) and cnt < len(layer_info): # add support for parameters for hardtanh and relu and others ?
# If the previous layer was fully connected, this is the activation function
if layer.__class__.__name__ == "ReLU" :
layer_info[cnt]['activation'] = 1 # Update the last added layer with activation
elif layer.__class__.__name__ == "Hardtanh" :
layer_info[cnt]['activation'] = 2 # Update the last added layer with activation
else :
raise Exception(f"activation {layer.__class__.__name__} not supported")
cnt += 1
return layer_info
def gen_config_pkg(model, bit_width, int_width, dirname = "default", config_name = "config_test"):
# generate a csv file for each neurons with quantized weights in fixe point (note that you can choose which part represent the fraction and integer part)
# model => model you want to exctract weights from
# dirname => name of the directory where the config package is stored
# bit_width => size of each data word (default = 32 bits)
layers_info = extract_fcl_layers(model)
num_layers = len(layers_info)
num_outputs = layers_info[num_layers-1]['out_features']
out_features_list = []
in_features_list = []
for i, layer in enumerate(layers_info): # generate constant for each layers (TODO : add activation type, adding sigmoid would be cool, and hard_tanh)
out_features_list.append(layer['out_features'])
in_features_list.append(layer['in_features'])
max_input_size = max(in_features_list)
max_ouput_size = max(out_features_list)
if not os.path.exists(dirname):
os.makedirs(dirname)
abs_path = os.path.abspath(dirname)
subdir_configs = os.path.join(dirname, "configs")
if not os.path.exists(subdir_configs):
os.makedirs(subdir_configs) # makes a subdirectory to store weights
else :
print("warning : config file already generate with the same name")
# Generate the VHDL package file
with open(os.path.join(subdir_configs, 'config_pkg.vhd'), 'w') as f:
f.write('library IEEE;\n')
f.write('use IEEE.STD_LOGIC_1164.ALL;\n\n')
f.write('package config_pkg is\n')
f.write(f' constant NUM_OF_LAYERS : natural := {num_layers};\n')
f.write(f' constant NUM_OF_OUTPUTS : natural := {num_outputs};\n')
f.write(f' constant DATA_WIDTH : natural := {bit_width};\n')
f.write(f' constant INT_WIDTH : natural := {int_width};\n')
f.write(f' constant DATA_PATH : string := "{abs_path}/weights/";\n')
f.write(f' constant MAX_INPUTS_SIZE : natural := {max_input_size};\n')
f.write(f' constant MAX_OUTPUTS_SIZE : natural := {max_ouput_size};\n')
f.write(f'\n')
f.write(f' type num_of_neurons_t is array (0 to NUM_OF_LAYERS - 1) of integer;\n')
f.write(f' type num_of_inputs_t is array (0 to NUM_OF_LAYERS - 1) of integer;\n')
f.write(f' type activation_functions_t is array (0 to NUM_OF_LAYERS-1) of natural;\n')
f.write(f' type state_t is (IDLE, EXECUTING, FINISHED, START);\n')
f.write(f'\n')
f.write(f' -- LAYERS PARAMETERS :\n')
f.write(f'\n')
f.write(f' constant NUM_OF_NEURONS : num_of_neurons_t := (\n')
for i, layer in enumerate(layers_info): # generate constant for each layers (TODO : add activation type, adding sigmoid would be cool, and hard_tanh)
out_features = layer['out_features']
if i == len(layers_info)-1 :
f.write(f' {out_features}\n') # last value
else :
f.write(f' {out_features},\n') # intermediate values
f.write(f' );\n')
f.write(f'\n')
f.write(f' constant NUM_OF_INPUTS : num_of_inputs_t := (\n')
for i, layer in enumerate(layers_info): # generate constant for each layers (TODO : add activation type, adding sigmoid would be cool, and hard_tanh)
in_features = layer['in_features']
if i == len(layers_info)-1 :
f.write(f' {in_features}\n') # last value
else :
f.write(f' {in_features},\n') # intermediate values
f.write(f' );\n')
f.write(f'\n')
f.write(f' constant ACTIVATION_FUNCTONS : activation_functions_t := ( -- activation function for 1st layer is always linear\n')
for i, layer in enumerate(layers_info): # generate constant for each layers (TODO : add activation type, adding sigmoid would be cool, and hard_tanh)
activation = layer['activation']
if i == len(layers_info)-1 :
f.write(f' {activation}\n') # last value
else :
f.write(f' {activation},\n') # intermediate values
f.write(f' );\n')
f.write(f'\n')
f.write('end package config_pkg;\n')