----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date: 09/30/2025 05:40:20 PM
-- Design Name: 
-- Module Name: eth_frame_packer - Behavioral
-- Project Name: 
-- Target Devices: 
-- Tool Versions: 
-- Description: 
-- 
-- Dependencies: 
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
----------------------------------------------------------------------------------


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

entity eth_frame_packer is
  port (
    clk           : in  std_logic;
    resetn        : in  std_logic;

    pause         : in  std_logic := '0';
    cfg_dst_mac   : in  std_logic_vector(47 downto 0);  -- DA
    cfg_src_mac   : in  std_logic_vector(47 downto 0);  -- SA
    cfg_eth_type  : in  std_logic_vector(15 downto 0);  -- EtherType/Length

    -- AXIS payload in (512-bit, 64B beats; multiples of 64B, aligned)
    s_axis_tdata  : in  std_logic_vector(511 downto 0);
    s_axis_tvalid : in  std_logic;
    s_axis_tready : out std_logic;
    s_axis_tlast  : in  std_logic;

    -- AXIS to CMAC TX client (512-bit)
    m_axis_tdata  : out std_logic_vector(511 downto 0);
    m_axis_tkeep  : out std_logic_vector(63 downto 0);
    m_axis_tvalid : out std_logic;
    m_axis_tready : in  std_logic;
    m_axis_tlast  : out std_logic;
    m_axis_tuser  : out std_logic;   -- error flag to CMAC
    
    tx_cnt        : out std_logic_vector(31 downto 0);
    underrun_cnt  : out std_logic_vector(31 downto 0) := (others => '0')
  );
end entity;

architecture rtl of eth_frame_packer is

  ---------------------------------------------------------------------------
  -- Functions (VHDL-93 safe)
  ---------------------------------------------------------------------------
  function mk_header(
    da  : std_logic_vector(47 downto 0);
    sa  : std_logic_vector(47 downto 0);
    ety : std_logic_vector(15 downto 0)
  ) return std_logic_vector is
    variable h : std_logic_vector(111 downto 0);
  begin
    -- DA [0..5]
    h(  7 downto   0) := da(47 downto 40);
    h( 15 downto   8) := da(39 downto 32);
    h( 23 downto  16) := da(31 downto 24);
    h( 31 downto  24) := da(23 downto 16);
    h( 39 downto  32) := da(15 downto  8);
    h( 47 downto  40) := da( 7 downto  0);
    -- SA [6..11]
    h( 55 downto  48) := sa(47 downto 40);
    h( 63 downto  56) := sa(39 downto 32);
    h( 71 downto  64) := sa(31 downto 24);
    h( 79 downto  72) := sa(23 downto 16);
    h( 87 downto  80) := sa(15 downto  8);
    h( 95 downto  88) := sa( 7 downto  0);
    -- Type/Len [12..13]
    h(103 downto  96) := ety(15 downto 8);
    h(111 downto 104) := ety( 7 downto 0);
    return h;
  end function;

  ---------------------------------------------------------------------------
  -- 
  ---------------------------------------------------------------------------
  type state_type is (IDLE, RUN, FLUSH);
  signal state          : state_type;
  signal state_r        : state_type := IDLE;
  signal carry_r        : std_logic_vector(111 downto 0) := (others => '0');
  signal carry          : std_logic_vector(111 downto 0);
  signal m_tdata_r      : std_logic_vector(511 downto 0) := (others => '0');
  signal m_tdata        : std_logic_vector(511 downto 0);
  signal m_tkeep_r      : std_logic_vector(63 downto 0) := (others => '1');
  signal m_tkeep        : std_logic_vector(63 downto 0);
  signal m_tlast_r      : std_logic := '0';
  signal m_tlast        : std_logic;
  signal m_tvalid_r     : std_logic := '0';
  signal m_tvalid       : std_logic;
  signal m_tuser_r      : std_logic := '0';
  signal m_tuser        : std_logic;
  signal header         : std_logic_vector(111 downto 0);
  signal underrun_cnt_r : std_logic_vector(31 downto 0) := (others => '0');
  signal tx_cnt_r       : std_logic_vector(31 downto 0) := (others => '0');

begin

  header         <= mk_header(cfg_dst_mac, cfg_src_mac, cfg_eth_type);

  ---------------------------------------------------------------------------
  -- Drive Outputs
  ---------------------------------------------------------------------------
  m_axis_tuser   <= m_tuser_r;
  m_axis_tdata   <= m_tdata_r;
  m_axis_tkeep   <= m_tkeep_r;
  m_axis_tlast   <= m_tlast_r;
  m_axis_tvalid  <= m_tvalid_r;
  underrun_cnt   <= underrun_cnt_r;
  tx_cnt         <= tx_cnt_r;

  ---------------------------------------------------------------------------
  -- Combinational next-state / next-data 
  ---------------------------------------------------------------------------
  process(state_r, carry_r, s_axis_tlast, s_axis_tvalid, m_axis_tready, m_tvalid_r,
          m_tdata_r, m_tkeep_r, m_tlast_r, m_tuser_r, header, s_axis_tdata, pause)
  begin
    -- Defaults
    state         <=  state_r;
    carry         <=  carry_r;
    m_tdata       <=  m_tdata_r;
    m_tkeep       <=  m_tkeep_r;
    m_tlast       <=  m_tlast_r;
    m_tuser       <=  m_tuser_r;
    m_tvalid      <=  m_tvalid_r;
    s_axis_tready <= '0';

    case state_r is
      -----------------------------------------------------------------------
      when IDLE =>
      
        --s_axis_tready <= m_axis_tready or not(m_tvalid_r);
       
        --m_tvalid <= (s_axis_tvalid = '1' and (m_axis_tready = '1' or m_tvalid_r = '0')) or (not(m_axis_tready) and m_tvalid_r);
        if(m_axis_tready = '1')then
          m_tvalid <= '0';
        end if;

        if (s_axis_tvalid = '1' and (m_axis_tready = '1' or m_tvalid_r = '0') and pause = '0') then
        
          s_axis_tready <= '1';
        
          -- Emit first beat = [14B header] ++ [first 50B payload]
          m_tdata   <= s_axis_tdata(399 downto 0) & header;
          m_tkeep   <= (others => '1');
          m_tvalid  <= '1';
          m_tuser   <= '0';
          m_tlast   <= '0';

          -- Next carry = bytes 50..63
          carry <= s_axis_tdata(511 downto 400);

          if s_axis_tlast = '1' then
            state <= FLUSH;
          else
            state <= RUN;
          end if;
          
        end if;

      -----------------------------------------------------------------------
      when RUN =>
      
        s_axis_tready <= m_axis_tready;
      
        if(s_axis_tvalid = '0')then
          m_tuser <= '1'; -- s_axis_tvalid must be '1' or else this is an error condition, tx data underrun.
        end if;
      
        if (m_axis_tready = '1') then 
        
          -- Steady-state emit = [prev carry 14B] ++ [new payload 0..49]
          m_tdata   <= s_axis_tdata(399 downto 0) & carry_r;
          --m_tkeep   <= (others => '1');
          --m_tvalid  <= '1';
          --m_tlast   <= '0';

          -- Next carry = bytes 50..63
          carry <= s_axis_tdata(511 downto 400);

          if s_axis_tlast = '1' then
            state <= FLUSH;
          else
            state <= RUN;
          end if;
        end if;

      -----------------------------------------------------------------------
      when FLUSH =>
      
        --s_axis_tready <= '0';
      
        -- if(s_axis_tvalid = '0')then
          -- m_tuser <= '1';
        -- end if;
      
        if (m_axis_tready = '1') then
        
          -- Emit the final 14B tail
          m_tdata   <= std_logic_vector(to_unsigned(0, 400)) & carry_r;
          m_tkeep   <= x"0000000000003FFF";
          m_tlast   <= '1';
          --m_tvalid  <= '1';

          state <= IDLE;
        end if;
        
      --when others =>

    end case;
  end process;

  ---------------------------------------------------------------------------
  -- Registers
  ---------------------------------------------------------------------------
  seq_proc : process(clk)
  begin
    if rising_edge(clk) then
      if resetn = '0' then
        state_r         <= IDLE;
        m_tvalid_r      <= '0';
        underrun_cnt_r  <= (others => '0');
        tx_cnt_r        <= (others => '0');
      else
        state_r         <= state;
        m_tvalid_r      <= m_tvalid;
        
        if(m_tlast_r = '1' and m_tvalid_r = '1' and m_tuser_r = '1' and m_axis_tready = '1')then
          underrun_cnt_r  <= std_logic_vector(unsigned(underrun_cnt_r)  +1);
        end if;

        if(m_tlast_r = '1' and m_tvalid_r = '1' and m_axis_tready = '1')then
          tx_cnt_r  <= std_logic_vector(unsigned(tx_cnt_r)  +1);
        end if;
        
      end if;
      carry_r   <= carry;
      m_tdata_r <= m_tdata;
      m_tkeep_r <= m_tkeep;
      m_tlast_r <= m_tlast;
      m_tuser_r <= m_tuser;             
    end if;
  end process;

end architecture;

