library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_signed.all;
use ieee.math_real.all;
library work;
use work.cnn_types.all;

entity DotProduct is
  generic(
    BITWIDTH       : integer;
    SUM_WIDTH        : integer;
    DOT_PRODUCT_SIZE : integer;
    KERNEL_VALUE     : pixel_array;
    BIAS_VALUE       : std_logic_vector
    );
  port(
    clk      : in  std_logic;
    reset_n  : in  std_logic;
    enable   : in  std_logic;
    in_data  : in  pixel_array (0 to DOT_PRODUCT_SIZE - 1);
    in_dv    : in  std_logic;
    in_fv    : in  std_logic;
    out_data : out std_logic_vector (SUM_WIDTH-1 downto 0);
    out_dv   : out std_logic;
    out_fv   : out std_logic
    );
end DotProduct;

architecture rtl of DotProduct is
  --------------------------------------------------------------
  ---- Implementation with the multiplier and adder components
  --------------------------------------------------------------
  component MCM
     generic (
       BITWIDTH       : integer;
       DOT_PRODUCT_SIZE : integer;
       KERNEL_VALUE     : pixel_array
       );
     port (
       clk       : in  std_logic;
       reset_n   : in  std_logic;
       enable    : in  std_logic;
       in_data   : in  pixel_array (0 to DOT_PRODUCT_SIZE - 1);
       in_valid  : in  std_logic;
       out_data  : out prod_array (0 to DOT_PRODUCT_SIZE - 1);
       out_valid : out std_logic
       );
  end component MCM;
  --
  component MOA
     generic (
       BITWIDTH   : integer;
       SUM_WIDTH    : integer;
       NUM_OPERANDS : integer;
       BIAS_VALUE   : std_logic_vector
       );
     port (
       clk       : in  std_logic;
       reset_n   : in  std_logic;
       enable    : in  std_logic;
       in_data   : in  prod_array (0 to NUM_OPERANDS - 1);
       in_valid  : in  std_logic;
       out_data  : out std_logic_vector (SUM_WIDTH-1 downto 0);
       out_valid : out std_logic
       );
  end component MOA;
  --
     signal p_prod_data  : prod_array (0 to DOT_PRODUCT_SIZE - 1);
     signal p_prod_valid : std_logic;
     signal s_out_valid  : std_logic;
   begin
   MCM_i : MCM
     generic map (
       BITWIDTH       => BITWIDTH,
       DOT_PRODUCT_SIZE => DOT_PRODUCT_SIZE,
       KERNEL_VALUE     => KERNEL_VALUE
       )
     port map (
       clk       => clk,
       reset_n   => reset_n,
       enable    => enable,
       in_data   => in_data,
       in_valid  => (in_dv and in_fv),
       out_data  => p_prod_data,
       out_valid => p_prod_valid
       );
   MOA_i : MOA
     generic map (
       BITWIDTH   => BITWIDTH,
       SUM_WIDTH    => SUM_WIDTH,
       NUM_OPERANDS => DOT_PRODUCT_SIZE,
       BIAS_VALUE   => BIAS_VALUE
       )
     port map (
       clk       => clk,
       reset_n   => reset_n,
       enable    => enable,
       in_data   => p_prod_data,
       in_valid  => p_prod_valid,
       out_data  => out_data,

--  begin
--    process(clk)
--    begin
--      if (reset_n = '0') then
--        pip_acc <= (others => (others => '0'));
--        out_dv  <= '0';
--        out_fv  <= '0';
--
--      elsif(rising_edge(clk)) then
--        if (enable = '1') then
--          if (in_dv = '1' and in_fv = '1') then
--            pip_acc(0)(2*BITWIDTH-1 downto 0) <= in_data(0) *  KERNEL_VALUE(0);
--            acc_loop : for i in 1 to DOT_PRODUCT_SIZE-1 loop
--              pip_acc(i) <= pip_acc(i-1) + (in_data(i) * KERNEL_VALUE(i));
--            end loop acc_loop;
--          end if;
--          out_dv <= in_dv;
--          out_fv <= in_fv;
--        end if;
--      end if;
--    end process;
--    out_data <= pip_acc(DOT_PRODUCT_SIZE-1) + BIAS_VALUE;
end architecture;

       out_valid => s_out_valid
       );
   out_dv <= s_out_valid;
   out_fv <= s_out_valid;


-- Converts the camera output to signals usable by Haddoc layers
-- TODO : Support a variable PIXEL_BIT_WIDTH

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_signed.all;

library work;
use work.cnn_types.all;

entity InputLayer is
  generic(
    BITWIDTH      : integer;          -- Bit-width of the activations and FMs
    INPUT_BIT_WIDTH : integer;          -- Bit-width of the input pixel
    NB_OUT_FLOWS    : integer           -- Number of channels in input
    );

  port(
    clk      : in  std_logic;
    reset_n  : in  std_logic;
    enable   : in  std_logic;
    in_data  : in  std_logic_vector(INPUT_BIT_WIDTH-1 downto 0);
    in_dv    : in  std_logic;
    in_fv    : in  std_logic;
    out_data : out pixel_array(0 to NB_OUT_FLOWS-1);
    out_dv   : out std_logic;
    out_fv   : out std_logic

    );
end entity;

architecture bhv of InputLayer is
begin
  MONOCHROME_INPUT : if NB_OUT_FLOWS = 1 generate
    -- Grayscale input
    process(clk)
    begin
      if (reset_n = '0') then
        out_data <= (others => (others => '0'));
      else
        if (enable = '1') then
          out_data(0) <= '0' & in_data(INPUT_BIT_WIDTH-1 downto INPUT_BIT_WIDTH-BITWIDTH+1);
        end if;
        out_dv <= in_dv;

      end if;
    end process;
  end generate MONOCHROME_INPUT;

  COLOR_INPUT : if NB_OUT_FLOWS = 3 generate
    -- RGB 8 bits color depth
    process(clk)
    begin
      if (reset_n = '0') then
        out_data <= (others => (others => '0'));
      else
        if (enable = '1') then
          -- MSBs of each channel
          out_data(0) <= '0' & in_data(INPUT_BIT_WIDTH/1-1 downto INPUT_BIT_WIDTH/1-BITWIDTH+1);
          out_data(1) <= '0' & in_data(INPUT_BIT_WIDTH/2-1 downto INPUT_BIT_WIDTH/2-BITWIDTH+1);
          out_data(2) <= '0' & in_data(INPUT_BIT_WIDTH/3-1 downto INPUT_BIT_WIDTH/3-BITWIDTH+1);
        end if;
        out_dv <= in_dv;
        out_fv <= in_fv;
      end if;
    end process;
  end generate COLOR_INPUT;
end bhv;


-- Implementation of a Multiple-Constant-Multiplier:
-- This IP multiplies an input array with CONSTANT coefficients (3D convolution kernels)

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_signed.all;
use ieee.math_real.all;
library work;
use work.cnn_types.all;

entity MCM is
  generic(
    BITWIDTH       : integer;
    DOT_PRODUCT_SIZE : integer;
    KERNEL_VALUE     : pixel_array
    );
  port(
    clk       : in  std_logic;
    reset_n   : in  std_logic;
    enable    : in  std_logic;
    in_data   : in  pixel_array (0 to DOT_PRODUCT_SIZE - 1);
    in_valid  : in  std_logic;
    out_data  : out prod_array (0 to DOT_PRODUCT_SIZE - 1);
    out_valid : out std_logic
    );
end MCM;

architecture rtl of MCM is
  -- Generate DOT_PRODUCT_SIZE Multipliers
  signal mult : prod_array (0 to DOT_PRODUCT_SIZE - 1);

begin
  ---------------------------------
  -- Assynchronous implmentation --
  ---------------------------------
  -- mcm_loop : for i in 0 to DOT_PRODUCT_SIZE - 1 generate
  --     out_data(i) <=  KERNEL_VALUE(i) * in_data(i);
  -- end generate mcm_loop;
  -- out_valid <= in_valid;

  ---------------------------------
  --  synchronous implmentation  --
  ---------------------------------

  process(clk)
  begin
    if(reset_n = '0') then
      out_data  <= (others => (others => '0'));
      out_valid <= '0';
    elsif (rising_edge(clk) and enable = '1') then

      out_valid <= in_valid;
    end if;
  end process;

end architecture;

      if (in_valid = '1') then
        mcm_loop : for i in 0 to DOT_PRODUCT_SIZE - 1 loop
          out_data(i) <= KERNEL_VALUE(i) * in_data(i);


-- Design of a Multi-Operand-Adder block
-- This is a naive implementation with binary adder trees
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_signed.all;
library work;
use work.cnn_types.all;

entity MOA is
  generic(
    BITWIDTH     : integer;
    SUM_WIDTH    : integer;
    NUM_OPERANDS : integer;
    BIAS_VALUE   : std_logic_vector
    );

  port(
    clk       : in  std_logic;
    reset_n   : in  std_logic;
    enable    : in  std_logic;
    in_data   : in  prod_array (0 to NUM_OPERANDS - 1);
    in_valid  : in  std_logic;
    out_data  : out std_logic_vector (SUM_WIDTH-1 downto 0);
    out_valid : out std_logic
    );
end MOA;

architecture rtl of MOA is
-- Implementation of Multi Operand Adder with Adder trees

-----------------------------------
-- Removing MOA to Evaluate FMax --
-----------------------------------
-- begin
-- out_valid <= in_valid;
-- out_data  <= "00000000" & in_data(0);
-- end architecture;

---------------------------------
-- Assynchronous implmentation --
---------------------------------
--signal s_acc   : std_logic_vector (SUM_WIDTH-1 downto 0);
--signal pip_acc : prod_array (0 to NUM_OPERANDS - 1);
--begin
--  add_process : process(clk)
--    variable v_acc : std_logic_vector (SUM_WIDTH-1 downto 0) := (others => '0');
--  begin
--    if (reset_n = '0') then
--      v_acc     := (others => '0');
--      out_valid <= '0';
--
--    elsif (rising_edge(clk)) then
--      if (enable = '1') then
--        if (in_valid = '1') then
--          acc_loop : for i in 0 to NUM_OPERANDS-1 loop
--            v_acc := v_acc + in_data(i);
--          end loop acc_loop;
--          v_acc := v_acc + BIAS_VALUE;
--        end if;
--      end if;
--      s_acc     <= v_acc;
--      v_acc     := (others => '0');
--      out_valid <= in_valid;
--    end if;
--  end process;
--  out_data <= s_acc;

-----------------------------
-- Pipelined implmentation --
-----------------------------
 signal pip_acc : sum_array (0 to NUM_OPERANDS - 1) := (others => (others => '0'));
 begin
   process(clk)
   begin
     if (reset_n = '0') then
       pip_acc   <= (others => (others => '0'));
       out_valid <= '0';

           acc_loop : for i in 1 to NUM_OPERANDS-1 loop
             pip_acc(i) <= pip_acc(i-1) + in_data(i);
           end loop acc_loop;
         else
           pip_acc   <= (others => (others => '0'));
         end if;
         out_data <= pip_acc(NUM_OPERANDS-1) + BIAS_VALUE;
         out_valid <= in_valid;
       end if;
     end if;
   end process;

end architecture;

     elsif(rising_edge(clk)) then
       if (enable = '1') then


    out_dv   : out std_logic;
    out_fv   : out std_logic
    );
end neighExtractor;

architecture rtl of neighExtractor is

  -- signals
  signal pixel_out : pixel_array(0 to KERNEL_SIZE-1);
  signal tmp_data  : pixel_array (0 to (KERNEL_SIZE * KERNEL_SIZE)- 1);
  signal all_valid : std_logic;
  signal s_valid   : std_logic;
  signal buffer_fv : std_logic_vector(KERNEL_SIZE-1 downto 0);
  signal tmp_dv    : std_logic;
  signal tmp_fv    : std_logic;
  signal delay_dv  : std_logic;
  signal delay_fv  : std_logic;

  -- components
  component taps
    generic (
      BITWIDTH  : integer;
      TAPS_WIDTH  : integer;
      KERNEL_SIZE : integer
      );

    port (
      clk       : in  std_logic;
      reset_n   : in  std_logic;
      enable    : in  std_logic;
      in_data   : in  std_logic_vector (BITWIDTH-1 downto 0);
      taps_data : out pixel_array (0 to KERNEL_SIZE -1);
      out_data  : out std_logic_vector (BITWIDTH-1 downto 0)
      );
  end component;

  component bit_taps
    generic (
      TAPS_WIDTH : integer
      );

    port (
      clk      : in  std_logic;
      reset_n  : in  std_logic;
      enable   : in  std_logic;
      in_data  : in  std_logic;
      out_data : out std_logic
      );
  end component;

begin

  -- All valid : Logic and
  all_valid <= in_dv and in_fv;
  s_valid   <= all_valid and enable;
  ----------------------------------------------------
  -- Instantiates taps
  ----------------------------------------------------

  taps_inst : for i in 0 to KERNEL_SIZE-1 generate
    -- First line
    gen_1 : if i = 0 generate
      gen1_inst : taps
        generic map(
          BITWIDTH  => BITWIDTH,
          TAPS_WIDTH  => IMAGE_WIDTH-1,
          KERNEL_SIZE => KERNEL_SIZE
          )
        port map(
          clk       => clk,
          reset_n   => reset_n,
          enable    => s_valid,
          in_data   => in_data,
          taps_data => tmp_data(0 to KERNEL_SIZE-1),
          out_data  => pixel_out(0)
          );
    end generate gen_1;

    -- line i
    gen_i : if i > 0 and i < KERNEL_SIZE-1 generate
      geni_inst : taps
        generic map(
          BITWIDTH  => BITWIDTH,
          TAPS_WIDTH  => IMAGE_WIDTH-1,
          KERNEL_SIZE => KERNEL_SIZE
          )
        port map(
          clk       => clk,
          reset_n   => reset_n,
          enable    => s_valid,
          in_data   => pixel_out(i-1),
          taps_data => tmp_data(i * KERNEL_SIZE to KERNEL_SIZE*(i+1)-1),
          out_data  => pixel_out(i)
          );
    end generate gen_i;

    -- Last line
    gen_last : if i = (KERNEL_SIZE-1) generate
      gen_last_inst : taps
        generic map(
          BITWIDTH  => BITWIDTH,
          TAPS_WIDTH  => KERNEL_SIZE,
          KERNEL_SIZE => KERNEL_SIZE
          )
        port map(
          clk       => clk,
          reset_n   => reset_n,
          enable    => s_valid,
          in_data   => pixel_out(i-1),
          taps_data => tmp_data((KERNEL_SIZE-1) * KERNEL_SIZE to KERNEL_SIZE*KERNEL_SIZE - 1),
          out_data  => open
          );
    end generate gen_last;
  end generate taps_inst;

  --------------------------------------------------------------------------
  -- Manage out_dv and out_fv
  --------------------------------------------------------------------------
  -- Embrace your self : Managing the image borders is quite a pain

  dv_proc : process(clk)

    constant NBITS_DELAY : integer                           := integer(ceil(log2(real(IMAGE_WIDTH))));
    variable x_cmp       : unsigned (NBITS_DELAY-1 downto 0) := (others => '0');
    variable y_cmp       : unsigned (NBITS_DELAY-1 downto 0) := (others => '0');

  begin
    if (reset_n = '0') then
      x_cmp  := (others => '0');
      tmp_dv <= '0';
      tmp_fv <= '0';

    elsif (rising_edge(clk)) then

      out_data <= tmp_data;
      delay_fv <= in_fv;

      if(enable = '1') then
        if (in_fv = '1') then
          if(in_dv = '1') then
---------------------------------------------------------------------------------------------------------------------------------------------------------
            if (y_cmp = to_unsigned (IMAGE_WIDTH - 1, NBITS_DELAY)) then
              if (x_cmp = to_unsigned (IMAGE_WIDTH, NBITS_DELAY)) then
                tmp_dv <= '0';
                x_cmp  := (others => '0');
                y_cmp  := (others => '0');
              -- elsif(x_cmp< to_unsigned (KERNEL_SIZE - 1, NBITS_DELAY)) then
              --     tmp_dv <='0';
              --     x_cmp := x_cmp + to_unsigned(1,NBITS_DELAY);
              else
                tmp_dv <= '1';
                x_cmp  := x_cmp + to_unsigned(1, NBITS_DELAY);
              end if;

            elsif (y_cmp < to_unsigned (KERNEL_SIZE-1, NBITS_DELAY)) then
              tmp_fv <= '0';
              tmp_dv <= '0';

              if (x_cmp = to_unsigned (IMAGE_WIDTH, NBITS_DELAY)) then
                x_cmp := (others => '0');
                y_cmp := y_cmp + to_unsigned(1, NBITS_DELAY);
              else
                x_cmp := x_cmp + to_unsigned(1, NBITS_DELAY);
              end if;

            else
              -- Start of frame
              if (x_cmp = to_unsigned (IMAGE_WIDTH-1, NBITS_DELAY)) then
                tmp_dv <= '1';
                x_cmp  := (others => '0');
                y_cmp  := y_cmp + to_unsigned(1, NBITS_DELAY);
              elsif (x_cmp < to_unsigned (KERNEL_SIZE - 1, NBITS_DELAY)) then
                tmp_dv <= '0';
                x_cmp  := x_cmp + to_unsigned(1, NBITS_DELAY);
              else
                tmp_fv <= '1';
                tmp_dv <= '1';
                x_cmp  := x_cmp + to_unsigned(1, NBITS_DELAY);
              end if;

            end if;
---------------------------------------------------------------------------------------------------------------------------------------------------------
          else
            tmp_dv <= '0';
          end if;

        else
          x_cmp  := (others => '0');
          y_cmp  := (others => '0');
          tmp_dv <= '0';
          tmp_fv <= '0';
        end if;

      -- When enable = 0
      else
        x_cmp  := (others => '0');
        y_cmp  := (others => '0');
        tmp_dv <= '0';
        tmp_fv <= '0';
      end if;
    end if;
  end process;

  out_dv <= tmp_dv;
  out_fv <= delay_fv;
end architecture;

    enable   : in  std_logic;
    in_data  : in  std_logic_vector((BITWIDTH-1) downto 0);
    in_dv    : in  std_logic;


---------------------------------------------------------------------------------
-- Design Name   : poolLayer
-- Coder         : Kamel ABDELOUAHAB
-- Institution   : Institut Pascal  - 2016
---------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library work;
use work.cnn_types.all;

entity PoolLayer is
  generic(
    BITWIDTH   : integer;
    IMAGE_WIDTH  : integer;
    KERNEL_SIZE  : integer;
    NB_OUT_FLOWS : integer
    );
  port(
    clk      : in  std_logic;
    reset_n  : in  std_logic;
    enable   : in  std_logic;
    in_data  : in  pixel_array(0 to NB_OUT_FLOWS - 1);
    in_dv    : in  std_logic;
    in_fv    : in  std_logic;
    out_data : out pixel_array(0 to NB_OUT_FLOWS - 1);
    out_dv   : out std_logic;
    out_fv   : out std_logic
    );
end entity;

architecture STRUCTURAL of PoolLayer is
  --------------------------------------------------------------------------------
  -- COMPONENTS
  --------------------------------------------------------------------------------
  component maxPool
    generic(
      BITWIDTH  : integer;
      IMAGE_WIDTH : integer;
      KERNEL_SIZE : integer
      );
    port(
      clk      : in  std_logic;
      reset_n  : in  std_logic;
      enable   : in  std_logic;
      in_data  : in  std_logic_vector (BITWIDTH - 1 downto 0);
      in_dv    : in  std_logic;
      in_fv    : in  std_logic;
      out_data : out std_logic_vector (BITWIDTH - 1 downto 0);
      out_dv   : out std_logic;
      out_fv   : out std_logic
      );

  end component;
--------------------------------------------------------------------------------
begin

  generate_maxPool : for i in 0 to NB_OUT_FLOWS-1 generate
    first_maxpool : if i = 0 generate
      maxPool_0 : maxPool
        generic map(
          BITWIDTH  => BITWIDTH,
          IMAGE_WIDTH => IMAGE_WIDTH,
          KERNEL_SIZE => KERNEL_SIZE
          )
        port map(
          clk      => clk,
          reset_n  => reset_n,
          enable   => enable,
          in_data  => in_data(0),
          in_dv    => in_dv,
          in_fv    => in_fv,
          out_data => out_data(0),
          out_dv   => out_dv,
          out_fv   => out_fv
          );
    end generate first_maxpool;

    other_maxPool : if i > 0 generate
      maxPool_i : maxPool
        generic map(
          BITWIDTH  => BITWIDTH,
          IMAGE_WIDTH => IMAGE_WIDTH,
          KERNEL_SIZE => KERNEL_SIZE
          )
        port map(
          clk      => clk,
          reset_n  => reset_n,
          enable   => enable,

          in_dv    => in_dv,
          in_fv    => in_fv,
          out_data => out_data(i),
          out_dv   => open,
          out_fv   => open
          );
    end generate other_maxPool;
  end generate generate_maxPool;
end STRUCTURAL;


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library work;
use work.cnn_types.all;

entity TanhLayer is
  generic(
    BITWIDTH   : integer;

architecture Bhv of TanhLayer is
-- Piecewise implementation of TanH

begin
  --sum_s    <= signed(in_data);
  out_data <= std_logic_vector(to_signed(-V2, BITWIDTH)) when (signed(in_data) < to_signed(-T2, SUM_WIDTH)) else
              std_logic_vector(to_signed( V2, BITWIDTH)) when (signed(in_data) > to_signed( T2, SUM_WIDTH)) else
              std_logic_vector(SHIFT_RIGHT(signed(in_data),BITWIDTH)(BITWIDTH-1 downto 0));
end architecture;

    SUM_WIDTH  : integer
    );
  port(
    in_data  : in  std_logic_vector (SUM_WIDTH-1 downto 0);


    in_data   : in  std_logic_vector (BITWIDTH-1 downto 0);
    taps_data : out pixel_array (0 to KERNEL_SIZE -1);
    out_data  : out std_logic_vector (BITWIDTH-1 downto 0)
    );
end Taps;

architecture bhv of Taps is

  signal cell : pixel_array (0 to TAPS_WIDTH-1);

begin

  process(clk)
    variable i : integer := 0;
  begin

    if (reset_n = '0') then
      cell      <= (others => (others => '0'));
      out_data  <= (others => '0');
      taps_data <= (others => (others => '0'));

    elsif (rising_edge(clk)) then
      if (enable = '1') then
        cell(0) <= in_data;
        for i in 1 to (TAPS_WIDTH-1) loop
          cell(i) <= cell(i-1);
        end loop;
        taps_data <= cell(0 to KERNEL_SIZE-1);
        out_data  <= cell(TAPS_WIDTH-1);
      end if;
    end if;
  end process;
end bhv;


------------------------------------------------------------------------------
-- Title      : TensorExtractor
-- Project    : Haddoc2
------------------------------------------------------------------------------------------------------------
-- File       : TensorExtractor.vhd
-- Author     : K. Abdelouahab
-- Company    : Institut Pascal
-- Last update: 2018-08-23
-------------------------------------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.cnn_types.all;

entity TensorExtractor is
  generic(
    BITWIDTH  : integer;
    IMAGE_WIDTH : integer;
    KERNEL_SIZE : integer;
    NB_IN_FLOWS : integer
    );
  port(
    clk      : in  std_logic;
    reset_n  : in  std_logic;
    enable   : in  std_logic;
    in_data  : in  pixel_array (0 to NB_IN_FLOWS - 1);
    in_dv    : in  std_logic;
    in_fv    : in  std_logic;
    out_data : out pixel_array (0 to NB_IN_FLOWS * KERNEL_SIZE * KERNEL_SIZE- 1);
    out_dv   : out std_logic;
    out_fv   : out std_logic
    );
end TensorExtractor;

architecture rtl of TensorExtractor is
  -- components
  component neighExtractor
    generic(
      BITWIDTH  : integer;
      IMAGE_WIDTH : integer;
      KERNEL_SIZE : integer
      );

    port(
      clk      : in  std_logic;
      reset_n  : in  std_logic;
      enable   : in  std_logic;
      in_data  : in  std_logic_vector(BITWIDTH-1 downto 0);
      in_dv    : in  std_logic;
      in_fv    : in  std_logic;
      out_data : out pixel_array (0 to KERNEL_SIZE * KERNEL_SIZE- 1);
      out_dv   : out std_logic;
      out_fv   : out std_logic
      );
  end component;

-- begin
begin
  neighExtractor_gen : for c in 0 to NB_IN_FLOWS-1 generate

    SINGLE_CHANNEL : if c = 0 generate
      neighExtractor_0 : neighExtractor
        generic map (
          BITWIDTH  => BITWIDTH,
          IMAGE_WIDTH => IMAGE_WIDTH,
          KERNEL_SIZE => KERNEL_SIZE
          )

          clk      => clk,
          reset_n  => reset_n,
          enable   => enable,
          in_data  => in_data(0),
          in_dv    => in_dv,
          in_fv    => in_fv,
          out_data => out_data(0 to KERNEL_SIZE * KERNEL_SIZE - 1),
          out_dv   => out_dv,
          out_fv   => out_fv
          );
    end generate SINGLE_CHANNEL;

    MULTI_CHANNEL : if c > 0 generate
      neighExtractor_i : neighExtractor
        generic map (
          BITWIDTH  => BITWIDTH,
          IMAGE_WIDTH => IMAGE_WIDTH,
          KERNEL_SIZE => KERNEL_SIZE
          )
        port map (
          clk      => clk,
          reset_n  => reset_n,
          enable   => enable,
          in_data  => in_data(c),
          in_dv    => in_dv,
          in_fv    => in_fv,
          out_data => out_data(c * KERNEL_SIZE * KERNEL_SIZE to (c+1) * KERNEL_SIZE * KERNEL_SIZE - 1),
          out_dv   => open,
          out_fv   => open
          );
    end generate MULTI_CHANNEL;
  end generate neighExtractor_gen;
end architecture;


------------------------------------------------------------------------------
-- Title      : cnn_types
-- Project    : Haddoc2
------------------------------------------------------------------------------
-- File       : cnn_types.vhd
-- Author     : K. Abdelouahab
-- Company    : Institut Pascal
-- Last update: 2018-08-23
------------------------------------------------------------------------------
-- Description: A package defining the types, functions and constants used in
--              the Haddoc2 IP VHDL library
------------------------------------------------------------------------------

library ieee;
use ieee.numeric_std.all;
use ieee.std_logic_1164.all;
use ieee.math_real.all;
use work.bitwidths.all;

package cnn_types is
  ------------------------------------------------------------------------------
  -- Types
  type pixel_array is array (integer range <>) of std_logic_vector (GENERAL_BITWIDTH-1 downto 0);
  type sum_array is array (integer range <>) of std_logic_vector (SUM_WIDTH-1 downto 0);
  type prod_array is array (integer range <>) of std_logic_vector (2*GENERAL_BITWIDTH-1 downto 0);
  type pixel_matrix is array (integer range <>, integer range <>) of std_logic_vector (GENERAL_BITWIDTH-1 downto 0);
  ------------------------------------------------------------------------------
  -- Constants to implement piecewize TanH : cf DotProduct.vhd
  constant SCALE_FACTOR : integer := 2 **(GENERAL_BITWIDTH-1) - 1;
  constant A1           : integer := GENERAL_BITWIDTH - 1;
  constant A2           : integer := GENERAL_BITWIDTH;
  constant T1           : integer := SCALE_FACTOR * SCALE_FACTOR / 2;
  constant T2           : integer := SCALE_FACTOR * SCALE_FACTOR * 5/4;
  constant V1           : integer := SCALE_FACTOR / 4;

  ------------------------------------------------------------------------------
  -- Functions:
  -- extractRow : Extracts a row of pixel_array data from a pixel_matrix.
  function extractRow(target_row : integer;
                      nb_row     : integer;
                      nb_col     : integer;
                      in_matrix  : pixel_matrix)
  return pixel_array;
------------------------------------------------------------------------------
end cnn_types;

package body cnn_types is
  function extractRow(target_row : integer;
                      nb_row     : integer;
                      nb_col     : integer;
                      in_matrix  : pixel_matrix)
  return pixel_array is
  variable out_vec : pixel_array (0 to nb_col - 1);
  begin
    for index_col in 0 to (nb_col - 1) loop
      out_vec(index_col) := in_matrix(target_row, index_col);
    end loop;
    return out_vec;
  end extractRow;
end cnn_types;

constant V2           : integer := SCALE_FACTOR - 10;


  port(
    clk      : in  std_logic;
    reset_n  : in  std_logic;
    enable   : in  std_logic;
    in_data  : in  std_logic_vector (BITWIDTH - 1 downto 0);
    in_dv    : in  std_logic;
    in_fv    : in  std_logic;
    out_data : out std_logic_vector (BITWIDTH - 1 downto 0);
    out_dv   : out std_logic;
    out_fv   : out std_logic
    );
end entity;

architecture rtl of maxPool is
  --------------------------------------------------------------------------
  -- Signals
  --------------------------------------------------------------------------
  signal connect_data : std_logic_vector (BITWIDTH - 1 downto 0);
  signal connect_dv   : std_logic;
  signal connect_fv   : std_logic;

  --------------------------------------------------------------------------
  -- components
  --------------------------------------------------------------------------
  component poolH
    generic(
      BITWIDTH  : integer;
      IMAGE_WIDTH : integer;
      KERNEL_SIZE : integer
      );

    port(
      clk      : in  std_logic;
      reset_n  : in  std_logic;
      enable   : in  std_logic;
      in_data  : in  std_logic_vector (BITWIDTH - 1 downto 0);
      in_dv    : in  std_logic;
      in_fv    : in  std_logic;
      out_data : out std_logic_vector (BITWIDTH - 1 downto 0);
      out_dv   : out std_logic;
      out_fv   : out std_logic
      );

  end component;
  --------------------------------------------------------------------------
  component poolV
    generic(
      BITWIDTH  : integer;
      IMAGE_WIDTH : integer;
      KERNEL_SIZE : integer
      );

    port(
      clk      : in  std_logic;
      reset_n  : in  std_logic;
      enable   : in  std_logic;
      in_data  : in  std_logic_vector (BITWIDTH - 1 downto 0);
      in_dv    : in  std_logic;
      in_fv    : in  std_logic;
      out_data : out std_logic_vector (BITWIDTH - 1 downto 0);
      out_dv   : out std_logic;
      out_fv   : out std_logic
      );

  end component;
  --------------------------------------------------------------------------

begin
  poolv_inst : poolV
    generic map (
      BITWIDTH  => BITWIDTH,
      KERNEL_SIZE => KERNEL_SIZE,
      IMAGE_WIDTH => IMAGE_WIDTH
      )

    port map (
      clk      => clk,
      reset_n  => reset_n,
      enable   => enable,
      in_data  => in_data,
      in_dv    => in_dv,
      in_fv    => in_fv,
      out_data => connect_data,
      out_dv   => connect_dv,
      out_fv   => connect_fv
      );

  --------------------------------------------------------------------------

  poolh_inst : poolH
    generic map (
      BITWIDTH  => BITWIDTH,
      KERNEL_SIZE => KERNEL_SIZE,
      IMAGE_WIDTH => IMAGE_WIDTH
      )

    port map (
      clk      => clk,
      reset_n  => reset_n,
      enable   => enable,
      in_data  => connect_data,
      in_dv    => connect_dv,
      in_fv    => connect_fv,
      out_data => out_data,
      out_dv   => out_dv,
      out_fv   => out_fv
      );

end rtl;

entity maxPool is
  generic(
    BITWIDTH  : integer;
    IMAGE_WIDTH : integer;


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;

entity poolH is
  generic(
    BITWIDTH  : integer;
    IMAGE_WIDTH : integer;
    KERNEL_SIZE : integer
    );
  port(
    clk      : in  std_logic;
    reset_n  : in  std_logic;

    in_data  : in  std_logic_vector (BITWIDTH - 1 downto 0);
    in_dv    : in  std_logic;
    in_fv    : in  std_logic;
    out_data : out std_logic_vector (BITWIDTH - 1 downto 0);
    out_dv   : out std_logic;
    out_fv   : out std_logic
    );
end entity;

architecture rtl of poolH is
  --------------------------------------------------------------------------
  -- Signals
  --------------------------------------------------------------------------
  type buffer_data_type is array (integer range <>) of signed (BITWIDTH-1 downto 0);

  signal buffer_data      : buffer_data_type (KERNEL_SIZE - 1 downto 0);
  signal max_value_signal : signed(BITWIDTH-1 downto 0);
  signal buffer_fv        : std_logic_vector(KERNEL_SIZE downto 0);
  signal delay_fv         : std_logic := '0';
  signal tmp_dv           : std_logic := '0';

begin

  process (clk)
    variable x_cmp : unsigned (15 downto 0) := (others => '0');
  begin
    if (reset_n = '0') then
      tmp_dv           <= '0';
      buffer_data      <= (others => (others => '0'));
      max_value_signal <= (others => '0');
      x_cmp            := (others => '0');
    elsif (rising_edge(clk)) then
      if (enable = '1') then
        if (in_fv = '1') then
          if (in_dv = '1') then
            -- Bufferize data --------------------------------------------------------
            buffer_data(KERNEL_SIZE - 1) <= signed(in_data);
            BUFFER_LOOP : for i in (KERNEL_SIZE - 1) downto 1 loop
              buffer_data(i-1) <= buffer_data(i);
            end loop;

            -- Compute max -----------------------------------------------------------
            if (buffer_data(0) > buffer_data(1)) then
              max_value_signal <= buffer_data(0);
            else
              max_value_signal <= buffer_data(1);
            end if;

            -- H Subsample -------------------------------------------------------------
            if (x_cmp = to_unsigned(KERNEL_SIZE, 16)) then
              tmp_dv <= '1';
              x_cmp  := to_unsigned(1, 16);
            else
              tmp_dv <= '0';
              x_cmp  := x_cmp + to_unsigned(1, 16);
            end if;
          --------------------------------------------------------------------------
          else
            -- Data is not valid
            tmp_dv <= '0';
          end if;

        else
          -- Frame is not valid
          tmp_dv           <= '0';
          buffer_data      <= (others => (others => '0'));
          max_value_signal <= (others => '0');
          x_cmp            := (others => '0');
        end if;
      end if;
    end if;
  end process;
  --------------------------------------------------------------------------
  delay : process(clk)
  begin
    if (reset_n = '0') then
      delay_fv  <= '0';
      buffer_fv <= (others => '0');
    elsif (rising_edge(clk)) then
      if (enable = '1') then
        buffer_fv <= buffer_fv(buffer_fv'high -1 downto 0) & in_fv;
        delay_fv  <= buffer_fv(buffer_fv'high);
      end if;
    end if;
  end process;

  out_data <= std_logic_vector(max_value_signal);
  out_fv   <= delay_fv;
  out_dv   <= tmp_dv;
end architecture;


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;

entity poolV is

  generic(
    BITWIDTH  : integer;
    IMAGE_WIDTH : integer;
    KERNEL_SIZE : integer
    );

  port(
    clk      : in  std_logic;
    reset_n  : in  std_logic;
    enable   : in  std_logic;
    in_data  : in  std_logic_vector (BITWIDTH - 1 downto 0);
    in_dv    : in  std_logic;
    in_fv    : in  std_logic;
    out_data : out std_logic_vector (BITWIDTH - 1 downto 0);
    out_dv   : out std_logic;
    out_fv   : out std_logic
    );
end entity;

architecture rtl of poolV is
  --------------------------------------------------------------------------
  -- Signals
  --------------------------------------------------------------------------
  type buffer_data_type is array (integer range <>) of signed (BITWIDTH-1 downto 0);

  signal buffer_line      : buffer_data_type (IMAGE_WIDTH - 1 downto 0);
  signal buffer_data      : buffer_data_type (KERNEL_SIZE - 1 downto 0);
  signal max_value_signal : signed (BITWIDTH - 1 downto 0);
  signal buffer_fv        : std_logic_vector(KERNEL_SIZE downto 0);
  signal delay_fv         : std_logic := '0';
  signal tmp_dv           : std_logic := '0';

begin

  process (clk)
    variable x_cmp : unsigned (15 downto 0);

  begin
    if (reset_n = '0') then
      tmp_dv           <= '0';
      buffer_data      <= (others => (others => '0'));
      buffer_line      <= (others => (others => '0'));
      max_value_signal <= (others => '0');
      x_cmp            := (others => '0');

    elsif (rising_edge(clk)) then
      if (enable = '1') then
        if (in_fv = '1') then
          if (in_dv = '1') then

            -- Bufferize line --------------------------------------------------------
            buffer_line(IMAGE_WIDTH - 1) <= signed(in_data);
            BUFFER_LOOP : for i in (IMAGE_WIDTH - 1) downto 1 loop
              buffer_line(i-1) <= buffer_line(i);
            end loop;

            buffer_data(0) <= signed(in_data);
            buffer_data(1) <= buffer_line(0);

            -- Compute max : Case2 , just a comparator --------------------------------
            if (buffer_data(0) > buffer_data(1)) then
              max_value_signal <= buffer_data(0);
            else
              max_value_signal <= buffer_data(1);
            end if;

            -- V Subsample -------------------------------------------------------------
            if (x_cmp < to_unsigned(IMAGE_WIDTH+1, 16)) then
              tmp_dv <= '0';
              x_cmp  := x_cmp + to_unsigned(1, 16);
            elsif (x_cmp > to_unsigned(IMAGE_WIDTH + IMAGE_WIDTH, 16)) then
              tmp_dv <= '0';
              x_cmp  := to_unsigned(2, 16);
            else
              tmp_dv <= '1';
              x_cmp  := x_cmp + to_unsigned(1, 16);
            end if;
          --------------------------------------------------------------------------
          else
            -- Data is not valid
            tmp_dv <= '0';
          end if;
        else
          buffer_data      <= (others => (others => '0'));
          buffer_line      <= (others => (others => '0'));
          max_value_signal <= (others => '0');
          x_cmp            := (others => '0');

      end if;
    end if;
  end process;
  --------------------------------------------------------------------------
  delay : process(clk)
  begin
    if (reset_n = '0') then
      delay_fv  <= '0';
      buffer_fv <= (others => '0');
    elsif (rising_edge(clk)) then
      if (enable = '1') then
        buffer_fv <= buffer_fv(buffer_fv'high -1 downto 0) & in_fv;
        delay_fv  <= buffer_fv(buffer_fv'high);
      end if;
    end if;
  end process;

  out_data <= std_logic_vector(max_value_signal);
  out_fv   <= delay_fv;
  out_dv   <= tmp_dv;

end architecture;