---------------------------------------------------------------------------
-- File           : ea_control_patt_mon.vhd
-- Author(s)      : Nikola Avramovic
-- Created        : March 16th, 2019
---------------------------------------------------------------------------
-- Description    : Contorl module is used to receive command and to set
-- ouput resters
---------------------------------------------------------------------------
--
---------------------------------------------------------------------------
library ieee; 
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
   use work.p_functions.all;
   use work.p_parameters_and_constants.all;
   use work.p_monitor_interface.all;
   use work.p_patt_mon_interface.all;

entity ea_control_patt_mon is 
   generic(
      G_PATTERN_MON_NO : natural
      );
   port (clk           : in std_logic;
         reset         : in std_logic;
         -- to/from input command module
         cmd_decoderi  : in  TYPE_CMD_DECODER_TO_PATTERN_MON;
         cmd_decodero  : out TYPE_PATTERN_MON_TO_CMD_DECODER;
         -- to/from linear feedback shift register
         lfsri         : in  TYPE_LFSR_TO_CTRL_PATTERN;
         lfsro         : out TYPE_CTRL_PATTERN_TO_LFSR;
         -- to/from linear monitor
         lineari       : in  TYPE_LINEAR_TO_CTRL_PATTERN;
         linearo       : out TYPE_CTRL_PATTERN_TO_LINEAR;
         -- from scoreboard
         scoreboardi   : in  TYPE_SCOREBOARD_TO_CTRL_PATTERN;
         scoreboardo   : out TYPE_CTRL_PATTERN_TO_SCOREBOARD;
         -- to/from multiplexor
         patt_muxi     : in  TYPE_PATTERN_MUX_TO_CTRL_PATTERN;
         patt_muxo     : out TYPE_CTRL_PATTERN_TO_PATTERN_MUX;
         -- to cyclic redundancy check
         crc32o        : out TYPE_CTRL_PATTERN_TO_CRC32;
         -- to/from status object
         --statusi       : in  TYPE_STATUS_TO_CTRL_PATTERN;
         statuso       : out TYPE_PATTERN_MON_TO_STATUS 
   );
end ea_control_patt_mon;

architecture rtl of ea_control_patt_mon is  
   
   type TYPE_STATE is (IDLE, INIT, SEND_ONE_FRAME, SEND_CRC32, CHECK_FRAME_NO);

   type TYPE_REGS is record   
      state             : TYPE_STATE;
      -- init values
      cmd_seed          : natural;
      cmd_seed_increment: natural;
      cmd_frame_length  : natural;
      cmd_frame_no      : natural;
      cmd_crc_en        : boolean;
      cmd_linear_en     : boolean;
      cmd_lfsr_en       : boolean;
      -- command use for output modules
      seed              : natural;
      seed_increment    : natural;
      frame_length      : natural;
      frame_no          : natural;
      crc_en            : boolean;
      linear_en         : boolean;
      lfsr_en           : boolean;
      busy              : boolean;
      seed_en           : boolean;
      mux_sel           : TYPE_MUX_SELECT;
      one_frame_err     : boolean;
      start_seqence     : natural;
      first_frame       : boolean;
      crc_counter       : natural;
      cmd_scoreboard_en : boolean;
   end record;
   
   constant CONST_REGS_RESET : TYPE_REGS := (
      state             => IDLE,
      -- init values
      cmd_seed          => 0,
      cmd_seed_increment=> 0,
      cmd_frame_length  => 0,
      cmd_frame_no      => 0,
      cmd_crc_en        => false,
      cmd_linear_en     => false,
      cmd_lfsr_en       => false,
      -- command use for output modules
      seed              => 0,
      seed_increment    => 0,
      frame_length      => 0,
      frame_no          => 0,
      crc_en            => false,
      linear_en         => false,
      lfsr_en           => false,
      busy              => false,
      seed_en           => false,
      mux_sel           => NONE,
      one_frame_err     => false,
      start_seqence     => 0,
      first_frame       => false,
      crc_counter       => 0,
      cmd_scoreboard_en => false
   ); 
   
   signal r :        TYPE_REGS := CONST_REGS_RESET;
   
begin
   sync: process (reset, clk) 
      variable s        : TYPE_REGS;
   begin
      if reset = '1' then
         r                     <= CONST_REGS_RESET;

      elsif rising_edge(clk) then
         -- initialize the variables
         s                     := r;
         
         case r.state is
            when IDLE =>
               s.frame_no        := 0;
               s.busy            := false;
               s.seed_en         := false; 

               -- init pattern control registers
               if cmd_decoderi.init_pattern = G_PATTERN_MON_NO then
                  s.state        := INIT;
                  s.cmd_seed         := cmd_decoderi.seed;
                  s.cmd_lfsr_en      := cmd_decoderi.lfsr_en;
                  s.cmd_frame_length := cmd_decoderi.frame_length;
                  s.cmd_crc_en       := cmd_decoderi.crc_en;
                  s.cmd_linear_en    := cmd_decoderi.linear_en;
                  s.cmd_seed_increment := cmd_decoderi.seed_increment;
                  s.cmd_scoreboard_en:= cmd_decoderi.scoreboard_en;

                  if cmd_decoderi.frame_no > 0 then
                     s.cmd_frame_no  := cmd_decoderi.frame_no;

                  end if;

               end if;

            when INIT =>

               if cmd_decoderi.start_receiving then
                  s.state        := SEND_ONE_FRAME;
                  s.busy         := true;
                  s.first_frame  := true;
                  s.seed_en      := true;

                  -- init control register according to the command registers
                  s.seed         := r.cmd_seed;
                  s.lfsr_en      := r.cmd_lfsr_en;
                  s.frame_length := r.cmd_frame_length;
                  s.crc_en       := r.cmd_crc_en;
                  s.linear_en    := r.cmd_linear_en;
                  s.seed_increment := r.cmd_seed_increment;
                  s.frame_no     := r.cmd_frame_no;

               end if;

            when SEND_ONE_FRAME =>   
               -- disable seed_en after first byte
               -- seed enable should be set just one cycle, to set seed of first byte
               if lfsri.first_byte then 
                  s.seed_en   := false;        

               end if;

               if r.lfsr_en and not lfsri.busy then
                  s.mux_sel      := LFSR_DATA;
                  s.lfsr_en      := true;
                  s.crc_en       := cmd_decoderi.crc_en;

                  -- send CRC after data
                  if lfsri.last_byte and r.crc_en then
                     s.state     := SEND_CRC32;
                     s.mux_sel   := CRC32_DATA;
                     s.crc_en    := false;
                     s.lfsr_en   := false;

                  elsif lfsri.last_byte and not r.crc_en then
                     s.state     := CHECK_FRAME_NO;
                     s.lfsr_en   := false;

                  end if;

               elsif r.linear_en and not lineari.busy then 
                  s.mux_sel      := INCREMENT_DATA;
                  s.crc_en       := cmd_decoderi.crc_en;

                  if r.first_frame then 
                     s.start_seqence:= r.seed;         -- seed is base number

                  end if;

                  if lineari.last_byte and r.crc_en then
                     s.state     := SEND_CRC32;
                     s.mux_sel   := CRC32_DATA;
                     s.crc_en    := false;
                     s.linear_en := false;

                  elsif lineari.last_byte and not r.crc_en then
                     s.state     := CHECK_FRAME_NO;
                     s.linear_en := false;

                  end if;

               end if; 

            when SEND_CRC32 =>
               s.seed_en         := false;

               -- wait to send whol crc (4 bytes)
               if patt_muxi.busy or r.crc_counter <= C_CRC32_NO_OF_BYTS then
                  s.state        := SEND_CRC32;
                  s.crc_counter  := r.crc_counter + 1;

               else
                  s.state        := CHECK_FRAME_NO;
                  s.mux_sel      := NONE;

               end if;

            when CHECK_FRAME_NO =>
               s.seed_en         := false;
               s.crc_counter     := 0;

               -- wait for modules to be rady
               if lineari.busy or lfsri.busy or patt_muxi.busy then
                  s.state        := CHECK_FRAME_NO; 

               elsif s.frame_no > 1 then
                  s.first_frame  := false;
                  s.frame_no     := r.frame_no - 1;
                  s.state        := SEND_ONE_FRAME;
                  s.seed         := r.seed + r.seed_increment; -- increase seed for next frames
                  s.start_seqence:= r.start_seqence + r.seed_increment;

                  -- set lfsr en according to the command for next frame
                  -- it could not be set booth monitor type
                  if r.cmd_lfsr_en and r.cmd_linear_en then
                     s.linear_en := false;
                     s.lfsr_en   := false;

                  elsif r.cmd_lfsr_en then
                     s.lfsr_en   := true;

                  elsif r.cmd_linear_en then
                     s.linear_en := true;

                  end if;

               else
                  s.state        := IDLE;                  
   
               end if;
         end case;

         -- Assign the register record -------------------------------------------
         r                       <= s;

      end if;  
   end process;

   cmd_decodero <= (busy                   => r.busy);
   
   lfsro        <= (frame_length           => r.frame_length,
                    seed                   => r.seed,
                    seed_en                => r.seed_en,
                    lfsr_en                => r.lfsr_en);
   
   linearo      <= (frame_length           => r.frame_length,
                    start_seqence          => r.start_seqence,
                    linear_en              => r.linear_en);
   
   crc32o       <= (crc32_en               => r.crc_en);
   
   patt_muxo    <= (mux_sel                => r.mux_sel);

   scoreboardo  <= (scoreboard_en          => r.cmd_scoreboard_en);
   
   statuso      <= (one_frame_err          => r.one_frame_err,
                    error_frame_counter    => scoreboardi.error_frame_counter,
                    correct_frame_counter  => scoreboardi.correct_frame_counter);

end architecture rtl;      