---------------------------------------------------------------------------
-- File           : ea_cmd_gen_decoder.vhd
-- Author(s)      : Nikola Avramovic
-- Created        : March 11th, 2019
---------------------------------------------------------------------------
-- Description    : 
--  This moduel should decode commads trom software.
--  According the these commands should be set output control signals 
--  for pattern and port generator.
---------------------------------------------------------------------------

---------------------------------------------------------------------------
library ieee, work;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
   use work.p_interfaces.all;
   use work.p_addresses.all;
   use work.p_parameters_and_constants.all;
   use work.p_commands.all;
   use work.p_functions.all;
   use work.p_generator_interface.all;

entity ea_cmd_gen_decoder is
   generic(
      G_NO_OF_PATT_GEN    : positive  := 2
      );
   port  (
      clk               : in  std_logic;
      reset             : in  std_logic;
      -- to/from NIOS II processor
      fifo_cmdi         : in  TYPE_FIFO_CMD_TO_CMD_DEC;
      fifo_cmdo         : out TYPE_CMD_DEC_TO_FIFO_CMD;
      -- to/from pattern generator
      pattern_geni      : in  TYPE_PATTERN_GEN_TO_CMD_DECODER_ARRAY(G_NO_OF_PATT_GEN downto 1);
      pattern_geno      : out TYPE_CMD_DECODER_TO_PATTERN_GEN_ARRAY(G_NO_OF_PATT_GEN downto 1);
      -- to/from port generator
      port_geni         : in  TYPE_PORT_GEN_TO_CMD_DECODER;
      port_geno         : out TYPE_CMD_DECODER_TO_PORT_GEN
   );
end entity ea_cmd_gen_decoder;

architecture rtl of ea_cmd_gen_decoder is

   type TYPE_REGS is record
      fsm_state         : TYPE_CMD_GEN_STATE;
      first_byte        : std_logic_vector(C_AVALON_MM_DATA_WIDTH-1 downto 0);
      second_byte       : std_logic_vector(C_AVALON_MM_DATA_WIDTH-1 downto 0);
      cmd_word          : std_logic_vector (C_WORD_LENGTH-1 downto 0);
      read_req          : boolean;
      crc_en            : boolean;
      cmd_dec_error     : boolean;
      word_ready        : boolean;
      first_or_second   : boolean;
      cmd_ready         : boolean;
      cmd_value_ready   : boolean;
      init_pattern      : natural;
      seed              : natural;
      seed_increment    : natural;
      frame_length      : natural;
      frame_no          : natural;
      lfsr_en           : boolean;
      increment_payload : boolean;
      start_sending     : boolean;
      init_port         : natural;
      set_frag          : boolean;
      priority_input    : natural;
      no_of_fragment    : natural;
      packet_crc_en     : boolean;
      frame_wait_priority: natural;
      frame_wait_frag   : natural;
      preamble_length   : natural;
      terminated        : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
      sfd               : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
      smd_i             : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
      smd_c             : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
   end record TYPE_REGS;

   constant CONST_REGS_RESET : TYPE_REGS := (
      fsm_state         => IDLE,
      first_byte        => (others => '0'),
      second_byte       => (others => '0'),
      cmd_word          => (others => '0'),
      read_req          => false,
      crc_en            => true,  -- crc needs to be set by default
      cmd_dec_error     => false,
      word_ready        => false,
      first_or_second   => false,
      cmd_ready         => false,
      cmd_value_ready   => false,
      init_pattern      => 0,
      seed              => 0,
      seed_increment    => 1,
      frame_length      => 64,    -- frame length could be min 64, max 1500 (plas header)
      frame_no          => 0,     -- number of frames
      lfsr_en           => false, -- set random generator
      increment_payload => false, -- send data with non random payload
      start_sending     => false, -- start with transaction
      init_port         => 0,
      set_frag          => false,
      priority_input    => 0,
      no_of_fragment    => 0, 
      packet_crc_en     => true, 
      frame_wait_priority=> 0,
      frame_wait_frag   => 0,
      preamble_length   => 7,
      terminated        => X"FD",
      sfd               => X"AB",
      smd_i             => X"E6",
      smd_c             => X"E7"
   );

   signal r             : TYPE_REGS;

begin
   -- synchronous process 
   seq : process (clk, reset)
      variable s        : TYPE_REGS;
   begin
      if reset = '1' then
         r                 <= CONST_REGS_RESET;

      elsif rising_edge(clk) then
         
         s                 := r;

         -- set default value for next cycle
         s.read_req        := false;
         s.word_ready      := false;
         s.cmd_value_ready := false;
         s.cmd_ready       := false;

         -- when fifo is not empty start reading data
         if not fifo_cmdi.fifo_empty then
            s.read_req     := true;
         end if;

         -- read data from fifo command
         if fifo_cmdi.valid then

            if not r.first_or_second then
               s.first_byte      := fifo_cmdi.data_out;
               s.first_or_second := true;

            -- read second byte for word command
            else 
               s.second_byte     := fifo_cmdi.data_out;
               s.first_or_second := false;
               s.word_ready      := true;

            end if;
         end if;

         -- add two bytes into word reg, to read command or command data
         s.cmd_word              := r.first_byte & r.second_byte;

         -- command signes mast not be used as command value
         if r.word_ready then
            if choose_gen_command(s.cmd_word) /= NONE then 
               s.cmd_ready              := true;

            else
               s.cmd_value_ready        := true;

            end if;
         end if;

         -- command FSM. Idle state wait for some data(fifo_data), 
         -- start cmd set default values, every other cmd state sets output register(s) releted to itself
         -- end state sets start of transcation in pattern/port 
         case r.fsm_state is
            when IDLE => 
               s.start_sending       := false;
               --s.increment_payload   := false;
               --s.lfsr_en             := false;

               if r.cmd_word = C_GEN_CMD_START then
                  s.fsm_state        := START;

               end if;

            when START => 
               -- set default values, if something is missing in command
               -- e.g. frame no, it needs at least one to send something
               s.seed                := 3;     -- 3 is random value, could be whatevery I want 
               s.frame_length        := 64;    -- set at least min frame
               s.frame_no            := 1;     -- send one frame
               s.lfsr_en             := false; -- turn off random generator
               s.increment_payload   := true;  -- turn on generator for liner increasing payload

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := START;
                  end if;

               end if;

            when INIT_PATTERN_GEN => 
               -- add value command value
               if r.cmd_value_ready then
                  s.init_pattern     := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := INIT_PATTERN_GEN;
                  end if;

               end if;

            when INIT_PORT_GEN => 
               -- add value command value
               if r.cmd_value_ready then
                  s.init_port        := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := INIT_PORT_GEN;
                  end if;

               end if;

            when FRAME_NO => 
               -- add value command value
               if r.cmd_value_ready then
                  s.frame_no         := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := FRAME_NO;
                  end if;

               end if;

            when FRAME_LEN => 
               -- add value command value
               if r.cmd_value_ready then
                  s.frame_length     := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := FRAME_LEN;
                  end if;

               end if;

            when FRAME_SEED => 
               -- add value command value
               if r.cmd_value_ready then
                  s.seed             := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := FRAME_SEED;
                  end if;

               end if;

            when SEED_INCREMENT => 
               -- add value command value
               if r.cmd_value_ready then
                  s.seed_increment   := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := SEED_INCREMENT;
                  end if;

               end if;

            when RANDOM_DATA => 
               -- add value command value
               if r.cmd_value_ready then
                  s.cmd_value_ready  := false;

                  if to_integer(unsigned(r.cmd_word)) = 1 then
                     s.lfsr_en           := true;
                     s.increment_payload := false; -- disable linear generator

                  elsif to_integer(unsigned(r.cmd_word)) = 0 then
                     s.lfsr_en           := false;

                  else
                     s.cmd_dec_error     := true;

                  end if;
               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := RANDOM_DATA;
                  end if;

               end if;

            when NON_RANDOM_DATA =>
               -- add value command value
               if r.cmd_value_ready then
                  s.cmd_value_ready  := false;

                  if to_integer(unsigned(r.cmd_word)) = 1 then
                     s.increment_payload := true;
                     s.lfsr_en           := false; --disable lfsr

                  elsif to_integer(unsigned(r.cmd_word)) = 0 then
                     s.increment_payload := false;

                  else
                     s.cmd_dec_error     := true;

                  end if;
               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := NON_RANDOM_DATA;
                  end if;

               end if;

            when CRC_EN =>
               -- add value command value
               if r.cmd_value_ready then
                  s.cmd_value_ready  := false;

                  if to_integer(unsigned(r.cmd_word)) = 1 then
                     s.crc_en            := true;

                  elsif to_integer(unsigned(r.cmd_word)) = 0 then
                     s.crc_en            := false;

                  else
                     s.cmd_dec_error     := true;

                  end if;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := CRC_EN;
                  end if;

               end if;

            when SET_FRAG =>
               -- add value command value
               if r.cmd_value_ready then
                  s.cmd_value_ready  := false;

                  if to_integer(unsigned(r.cmd_word)) = 1 then
                     s.set_frag          := true;

                  elsif to_integer(unsigned(r.cmd_word)) = 0 then
                     s.set_frag          := false;

                  else
                     s.cmd_dec_error     := true;

                  end if;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := SET_FRAG;
                  end if;

               end if;

            when PRIORITY_INPUT =>
               -- add value command value
               if r.cmd_value_ready then
                  s.priority_input   := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := PRIORITY_INPUT;
                  end if;

               end if;

            when NO_OF_FARGMENT =>
               -- add value command value
               if r.cmd_value_ready then
                  s.no_of_fragment   := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := NO_OF_FARGMENT;
                  end if;

               end if;

            when PACKET_CRC_EN =>
               -- add value command value
               if r.cmd_value_ready then
                  s.cmd_value_ready  := false;

                  if to_integer(unsigned(r.cmd_word)) = 1 then
                     s.packet_crc_en := true;

                  elsif to_integer(unsigned(r.cmd_word)) = 0 then
                     s.packet_crc_en := false;

                  else
                     s.cmd_dec_error := true;

                  end if;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := PACKET_CRC_EN;
                  end if;

               end if;

            when FRAME_WAIT_PRIORITY =>
               -- add value command value
               if r.cmd_value_ready then
                  s.frame_wait_priority := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := FRAME_WAIT_PRIORITY;
                  end if;

               end if;

            when FRAME_WAIT_FRAG =>
               -- add value command value
               if r.cmd_value_ready then
                  s.frame_wait_frag  := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := FRAME_WAIT_FRAG;
                  end if;

               end if;

            when PREAMBLE_LENGTH =>
               -- add value command value
               if r.cmd_value_ready then
                  s.preamble_length  := to_integer(unsigned(r.cmd_word));
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := PREAMBLE_LENGTH;
                  end if;

               end if;

            when SFD =>
               -- add value command value
               if r.cmd_value_ready then
                  s.sfd              := r.cmd_word(C_BYTE_LENGTH-1 downto 0);
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := SFD;
                  end if;

               end if;

            when SMD_I =>
               -- add value command value
               if r.cmd_value_ready then
                  s.smd_i            := r.cmd_word(C_BYTE_LENGTH-1 downto 0);
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := SMD_I;
                  end if;

               end if;

            when SMD_C =>
               -- add value command value
               if r.cmd_value_ready then
                  s.smd_c            := r.cmd_word(C_BYTE_LENGTH-1 downto 0);
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := SMD_C;
                  end if;

               end if;
            when TERMINATED =>
               -- add value command value
               if r.cmd_value_ready then
                  s.terminated       := r.cmd_word(C_BYTE_LENGTH-1 downto 0);
                  s.cmd_value_ready  := false;

               end if;

               if r.cmd_ready then
                  s.fsm_state        := choose_gen_command(r.cmd_word);
                  s.cmd_ready        := false;

                  if s.fsm_state = NONE then
                     s.fsm_state     := TERMINATED;
                  end if;

               end if;

            when END_CMD => 
               -- send start transcation in proper pattern genertor; start transaction also on port generator 
               if pattern_geni(r.init_pattern).busy or port_geni.busy then
                  s.fsm_state        := END_CMD;

               else
                  s.start_sending    := true;
                  s.fsm_state        := IDLE;

               end if;

            when others =>
               null;
         end case;

         r                           <= s;

      end if;
   end process;

   fifo_cmdo                     <= (read_req          => r.read_req);

   pattern_geno(1)               <= (init_pattern      => r.init_pattern,
                                     seed              => r.seed,
                                     seed_increment    => r.seed_increment,
                                     frame_length      => r.frame_length,
                                     frame_no          => r.frame_no,
                                     crc_en            => r.crc_en,
                                     lfsr_en           => r.lfsr_en,
                                     linear_en         => r.increment_payload,
                                     start_sending     => r.start_sending);

   pattern_geno(2)               <= (init_pattern      => r.init_pattern,
                                     seed              => r.seed,
                                     seed_increment    => r.seed_increment,
                                     frame_length      => r.frame_length,
                                     frame_no          => r.frame_no,
                                     crc_en            => r.crc_en,
                                     lfsr_en           => r.lfsr_en,
                                     linear_en         => r.increment_payload,
                                     start_sending     => r.start_sending);


   port_geno                     <= (init_port         => r.init_port,
                                     set_frag          => r.set_frag,
                                     priority_input    => r.priority_input,
                                     no_of_fragment    => r.no_of_fragment,
                                     packet_crc_en     => r.packet_crc_en,
                                     frame_wait_priority=> r.frame_wait_priority,
                                     frame_wait_frag   => r.frame_wait_frag,
                                     preamble_length   => r.preamble_length,
                                     sfd               => r.sfd,
                                     smd_i             => r.smd_i,
                                     smd_c             => r.smd_c,
                                     terminated        => r.terminated,
                                     start_sending     => r.start_sending);

end architecture rtl;