-- Soma Model by J.Richardson: Summer 2022 V.2.0
-- Based on original code by Shimeng Wu and Impliments the Membrane Potential Model by Iakymchuk et al;
-- as seen in the paper : 
-- "Simplified spiking neural network architecture and STDP learning algorithm applied to image classification"

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

entity Soma is
    --The Membrane Potential the Neuron will start at, reset to, charge up to, and leak down to. 
    Generic (Resting_potential : NATURAL := 5; 
    -- The Membrane Potential the neuron drops to after firing, entering a refractory state where
    -- incoming spikes do not effect the potential. It will recharge from here to Refract_potential_end
    -- at the Recharge_rate; this creates a psedo-counter that determines the length of the refractory 
    -- period in clock cycles. 
    Refract_potential_start : NATURAL := 0;
    -- The Mamebrane potential the neuron must charge to in it's refractory period in order to exit it's
    -- refractory state.
    Refract_potential_end : NATURAL := 5;
    -- The Membrane Potential the neuron must reach in order to fire a spike
    thredhold_potential : SFIXED := to_SFIXED(1.5, 7, -12);
    -- The quantity the membrane potentaial loses eith each clock cycle that the Membrane Potential
    -- is above the resting potential 
    Leaky_rate : SFIXED := to_SFIXED(0.000244140625, 3, -12);
    Recharge_rate : SFIXED := to_SFIXED(0.000244140625, 3, -12);
    -- The quantity the membrane potentaial gains eith each clock cycle that the Membrane Potential
    -- is below the resting potential 
    dendrite_NO : NATURAL := 12;     
    weight_size_max : NATURAL := 3;  
    weight_size_min : integer := -6);
    
    Port ( clk : in STD_LOGIC;
           en : in STD_LOGIC;
           rst : in STD_LOGIC;
           EPSP : in SFIXED ((log2(NATURAL(2**weight_size_max * dendrite_NO))) downto weight_size_min);
           Spike_out : out STD_LOGIC);
end Soma;

architecture Behavioral of Soma is

signal EPSP_int : SFIXED ((log2(NATURAL(2**weight_size_max * dendrite_NO))) downto weight_size_min);
signal MP : SFIXED (log2(NATURAL(to_integer(Thredhold_potential))) + 1 downto -12); -- Membrane Potential
signal Refract : boolean;
signal fire : boolean;
signal Spike_out_int : STD_LOGIC;

begin   
    EPSP_int <= EPSP;
    Soma : process (clk)
    begin
        -- Sync to Clock Edge
        if rising_edge(clk) then
            -- Perform Reset if rst is enabled
            if (rst = '1') then
                -- Resets to resting Potential By default
                MP <= to_SFIXED(Resting_potential, log2(NATURAL(to_integer(Thredhold_potential))) + 1, -12);
                Refract <= False;
                fire <= False;
                Spike_out_int <= '0';
            elsif (en = '1') then
 
                -- When MP is below thredhold potential and above resting potential, and not in refract state, 
                -- incoming spikes have effect and MP leaks to resting.
                if ((MP < thredhold_potential) and (MP > Resting_potential) and (Refract = false)) then
                -- NEW CODE START--  
                -- New code prevents MP leak from overshooting Resting_potential
                   -- Check for overshoot only operates if no spikes are coming in
                   if (EPSP_int = (EPSP_int'range => '0')) then 
                      -- Checks if difference between MP and Resting_potential is less than Leaky_rate
                      if((MP-Resting_potential)< Leaky_rate) then
                         -- If less than Leaky_rate, MP only leaks by difference
                         MP <= to_SFIXED(Resting_potential, log2(NATURAL(to_integer(Thredhold_potential))) + 1, -12);
                      elsif ((MP-Resting_potential)>= Leaky_rate) then
                         MP <= resize(arg => MP - Leaky_rate, size_res => MP);
                      end if;
                   elsif (resize(arg => MP + EPSP_int - Leaky_rate, size_res => MP) >= thredhold_potential) then
                      MP <= resize(arg => MP + EPSP_int - Leaky_rate, size_res => MP);
                      Refract <= true;
                      Spike_out_int <= '1';
                      fire <= true;
                   else 
                      MP <= resize(arg => MP + EPSP_int - Leaky_rate, size_res => MP);
                   end if;
                -- NEW CODE END--   
                     
                -- When MP is below thredhold potential and below resting potential, and not in refract state, 
                -- incoming spikes have effect and MP recharges to resting.
                elsif ((MP < thredhold_potential) and (MP < Resting_potential) and (Refract = false)) then               
                -- NEW CODE START--
                -- New code prevents MP charge from overshooting Resting_potential
                   -- Check for overshoot only operates if no spikes are coming in
                   if (EPSP_int = (EPSP_int'range => '0')) then 
                      -- Checks if difference between MP and Resting_potential is less than Recharge_rate
                      if((Resting_potential-MP)< Recharge_rate) then
                         -- If less than Recharge_rate, MP only charges by difference
                         MP <= to_SFIXED(Resting_potential, log2(NATURAL(to_integer(Thredhold_potential))) + 1, -12);
                      elsif ((Resting_potential-MP)>= Recharge_rate) then
                         -- If more, recharge by Recharge_rate
                         MP <= resize(arg => MP + Recharge_rate, size_res => MP);
                      end if;
                   elsif (resize(arg => MP + EPSP_int + Recharge_rate, size_res => MP) >= thredhold_potential) then
                      MP <= resize(arg => MP + EPSP_int + Recharge_rate, size_res => MP);
                      Refract <= true;
                      Spike_out_int <= '1';
                      fire <= true;
                   elsif (resize(arg => MP + EPSP_int + Recharge_rate, size_res => MP) < 0) then
                      MP <= to_SFIXED(0, log2(NATURAL(to_integer(Thredhold_potential))) + 1, -12);
                   else 
                      MP <= resize(arg => MP + EPSP_int + Recharge_rate, size_res => MP);
                   end if;
                -- NEW CODE END--              
                
                -- When MP is below thredhold potential and equal to resting potential, and not in refract state, 
                -- incoming spikes have effect and MP sits at resting.    
                elsif ((MP < thredhold_potential) and (MP = Resting_potential) and (Refract = false)) then
                    if (resize(arg => MP + EPSP_int, size_res => MP) >= thredhold_potential) then
                       MP <= resize(arg => MP + EPSP_int, size_res => MP);
                       Refract <= true;
                       Spike_out_int <= '1';
                       fire <= true;
                    else 
                       MP <= resize(arg => MP + EPSP_int, size_res => MP);
                    end if;

                -- When in refractory state, two MP levels are used to make a pseudo-counter;
                -- Refract_potential_start and Refract_potential_end.
                -- The difference between the Refract_potential_start & Refract_potential_end, over 
                -- the Recharge_rate determines the refactory time in clock cycles.   
                elsif (MP >= thredhold_potential) and (Refract = true) then
                    MP <= to_SFIXED(Refract_potential_start, log2(NATURAL(to_integer(Thredhold_potential))) + 1, -12);                 
                elsif ((MP < Refract_potential_end) and (Refract = true)) then
                    MP <= resize(arg => MP + 1, size_res => MP);
                    -- Recharging completed, refract period finished, exit refract state. 
                    if resize(arg => MP + 1, size_res => MP) = Refract_potential_end then              
                        Refract <= false;                                                 
                    end if;                                                               
                end if; 
                
                -- fire a spike if signal fire is true.
                if (fire = true) then
                    Spike_out_int <= '0';
                    fire <= false;
                end if;
            end if;
        end if;
    end process;
                    
    Spike_out <= Spike_out_int;
    
end Behavioral;










