library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.fixed_pkg.all;
use work.DigEng.all;
use work.array_type.ALL;

entity Neuron_with_Encoder_toplevel_tb is
--  Port ( );
end Neuron_with_Encoder_toplevel_tb;

architecture Behavioral of Neuron_with_Encoder_toplevel_tb is

constant clk_period : time := 10ns;
constant Dendrite_NO : NATURAL := 4; 
constant decode_period : NATURAL := 256;  -- the number of clk period that the decoder will wait and collect data before generate a output
-- neuron parameters
constant Resting_potential : NATURAL := 40;
constant Refract_potential_start : NATURAL := 37; -- the membrane potential(MP) will drop to this value and get into the refract period. MP will increase by 1 in each clk peirod.
constant Refract_potential_end : NATURAL := 40; -- when the MP reach Refract_potential_end, refract period end and the neuron restart to be sentive to incoming spikes.
constant thredhold_potential : SFIXED := to_SFIXED(45, 7, -12);
constant Leaky_rate : SFIXED := to_SFIXED(0.00390625, 3, -12);
constant Recharge_rate : SFIXED := to_SFIXED(0.00390625, 3, -12);


signal clk : STD_LOGIC;
signal rst : STD_LOGIC;
signal en : STD_LOGIC;
signal neuron_input : array_2d_STD(0 to Dendrite_NO - 1)(7 downto 0);
signal decoded_output : STD_LOGIC_VECTOR (log2(decode_period)-1 downto 0);
signal weights_rst : STD_LOGIC;

signal custom_weight_en : STD_LOGIC;
signal custom_weight : array_2d_SFIXED (0 to Dendrite_NO - 1)(1 downto -12);

begin

 clk_process : process
    begin
        clk <= '0';
        wait for clk_period/2;
        clk <= '1';
        wait for clk_period/2;
    end process;
    
    UUT : entity work.Neuron_with_Encoder_toplevel
        Generic map(Dendrite_NO => Dendrite_NO,
            Resting_potential => Resting_potential,
            Refract_potential_start => Refract_potential_start,
            Refract_potential_end => Refract_potential_end,
            thredhold_potential => thredhold_potential,
            Leaky_rate => Leaky_rate,
            Recharge_rate => Recharge_rate)

        Port map (clk => clk, 
            rst => rst, 
            en => en, 
            input => neuron_input,
            output => decoded_output,
            weight_rst => weights_rst,
            custom_weight_en => custom_weight_en,
            custom_weight => custom_weight);
            
    test_process : process
    begin
        
        wait for 100ns;
        wait until falling_edge(clk);
        
        -- initialize all value of inputs to 0
        rst <= '0';
        en <= '0';
        neuron_input <= (others => (others => '0')); 
        weights_rst <= '0';
        custom_weight_en <= '0';
        custom_weight <= (others => (others => '0'));
        wait for clk_period*10;
        
        -- initial reset
        rst <= '1';
        wait for clk_period;
        rst <= '0';
        wait for clk_period *10 ;
        
        en <= '1';
        
        -- if you want to randomly initialise weights, uncomment below
--        wait for clk_period * 81; -- Let RNG to initialize. change this value to try different initial weights.
--        weights_rst <= '1';
--        wait for clk_period;
--        weights_rst <= '0';
--        wait for clk_period;
        -- random initialisation end
        
        -- if you want to use customise wieghts, uncomment below
        wait for clk_period *10;
        custom_weight_en <= '1';
        custom_weight <= (to_SFIXED(1.0, 1, -12),
                          to_SFIXED(0.5, 1, -12),
                          to_SFIXED(-0.5, 1, -12),
                          to_SFIXED(-1, 1, -12));
        -- Customising weights code end
        
        
        wait for clk_period * 20;


        neuron_input <= (STD_LOGIC_VECTOR(to_UNSIGNED(64, 8)), 
                         STD_LOGIC_VECTOR(to_UNSIGNED(192, 8)), 
                         STD_LOGIC_VECTOR(to_UNSIGNED(128, 8)), 
                         STD_LOGIC_VECTOR(to_UNSIGNED(16, 8)));
          -- you can change the input value to any value within 255
        wait for clk_period*10*decode_period;
        neuron_input <= (STD_LOGIC_VECTOR(to_UNSIGNED(0, 8)), 
                 STD_LOGIC_VECTOR(to_UNSIGNED(0, 8)), 
                 STD_LOGIC_VECTOR(to_UNSIGNED(0, 8)), 
                 STD_LOGIC_VECTOR(to_UNSIGNED(0, 8)));

        wait;
    
   end process;
   
end Behavioral;
