Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
dp_packet_unmerge.vhd 9.29 KiB
--------------------------------------------------------------------------------
--
-- Copyright (C) 2012
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
--------------------------------------------------------------------------------

-- Author: E. Kooistra
-- Purpose: Unmerge each input packet into output packets of length g_pkt_len.
-- Description:
-- . The merged packet length of the snk_in input packets is unmerged into
--   ceil(input packet length / pkt_len). The unmerged output packets have
--   length pkt_len, but if input packet length mod pkt_len /= 0, then the
--   last packet will contain the remaining data of the input packet and have
--   length < pkt_len. Corner cases:
--   - If pkt_len > input packet length, then the output = input
--   - The length of the last output packet is input packet length mod
--     pkt_len, so it can have minimal lenght 1.
-- . The dynamic pkt_len value is captured at the input eop, or between input
--   packets, to have a stable value during the unmerge.
-- . The number of output packets g_nof_pkt is only used to determine the
--   maximum supported pkt_cnt range. The actual number of output packets has
--   to be <= g_nof_pkt, and is determined by input packet length /
--   pkt_len. Hence the dp_packet_unmerge can dynamically handle different
--   sizes of input packets.
-- . The pkt_cnt is passed on as src_out.channel index.
-- . The g_bsn_increment sets the BSN increment for the unmerged output
--   packets relative to the BSN of the snk_in input packet. When
--   g_bsn_increment = 0, then all unmerged output packets get the same BSN as
--   the input packet.
-- . The input snk_in.err and snk_in.empty are valid at the snk_in.eop, but
--   that is too late to apply them to the unmerged packets. Therefor assume
--   that the snk_in.err and snk_in_eop are already valid at the snk_in.sop
--   and remain valid until the snk_in.eop. Hence these signals are then valid
--   when snk_in.valid = '1'. Use same snk_in.err and snk_in.empty value for
--   all unmerged packets.
--                      _                       _                       _
--   snk_in.sop   _____| |_____________________| |_____________________| |_
--                                            _                       _
--   snk_in.eop   ___________________________| |_____________________| |___
--                      _       _       _       _       _       _       _
--   src_out.sop  _____|0|_____|1|_____|2|_____|0|_____|1|_____|2|_____|0|_
--                            _       _       _       _       _       _
--   src_out.eop  ___________|0|_____|1|_____|2|_____|0|_____|1|_____|2|___
--
-- . Optional flow control dependent on g_use_ready and g_pipeline_ready, see
--   description of dp_add_flow_control.
-- . g_pkt_len statically sets the length of the unmerged packets in absence of
--   dynamic pkt_len control. When the pkt_len control input is used, g_pkt_len
--   sets the maximum number length of the unmerged packets.

library IEEE,common_lib;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use common_lib.common_pkg.all;
use work.dp_stream_pkg.all;

entity dp_packet_unmerge is
  generic (
    g_use_ready       : boolean := true;
    g_pipeline_ready  : boolean := false;
    g_nof_pkt         : natural := 1;  -- Number of packets to unmerge each incoming packet to
    g_pkt_len         : natural := 1;  -- Length of the unmerged packets
    g_bsn_increment   : natural := 0
  );
  port (
    rst         : in  std_logic;
    clk         : in  std_logic;

    pkt_len     : in  std_logic_vector(ceil_log2(g_pkt_len + 1) - 1 downto 0) := to_uvec(g_pkt_len, ceil_log2(g_pkt_len + 1));
    pkt_len_out : out std_logic_vector(ceil_log2(g_pkt_len + 1) - 1 downto 0);  -- Valid at src_out.sop

    snk_out     : out t_dp_siso;
    snk_in      : in  t_dp_sosi;

    src_in      : in  t_dp_siso := c_dp_siso_rdy;
    src_out     : out t_dp_sosi
  );
end dp_packet_unmerge;

architecture rtl of dp_packet_unmerge is
  constant c_nof_pkt_max : natural := g_nof_pkt + 1;
  constant c_pkt_len_max : natural := g_pkt_len + 1;
  constant c_pkt_len_w   : natural := ceil_log2(c_pkt_len_max);

  -- Internal state of logic function
  type t_reg is record
    pkt_cnt     : natural range 0 to c_nof_pkt_max;
    pkt_len     : natural range 0 to c_pkt_len_max;
    val_cnt     : natural range 0 to c_pkt_len_max;
    busy        : std_logic;
    src_out     : t_dp_sosi;
  end record;

  constant c_reg_rst : t_reg := (0, 0, 0, '0', c_dp_sosi_rst);

  signal r : t_reg;
  signal d : t_reg;

begin
  -- Map logic function outputs to entity outputs
  pkt_len_out <= to_uvec(r.pkt_len, c_pkt_len_w);

  u_dp_add_flow_control : entity work.dp_add_flow_control
    generic map (
      g_use_ready       => g_use_ready,
      g_pipeline_ready  => g_pipeline_ready
    )
    port map (
      rst         => rst,
      clk         => clk,

      snk_out     => snk_out,
      snk_in      => d.src_out,  -- combinatorial input
      snk_in_reg  => r.src_out,  -- registered input

      src_in      => src_in,
      src_out     => src_out
    );

  -- p_reg function state register
  r <= d when rising_edge(clk);

  -- Logic function
  p_comb : process(rst, r, snk_in, pkt_len)
    variable v : t_reg;
  begin
    -- Default
    v := r;

    -- Function: unmerge input packet into output packets
    --                    _                 _                 _
    --   snk_in.sop    __| |_______________| |_______________| |_______________|
    --                                   _                 _                 _
    --   snk_in.eop    _________________| |_______________| |_______________| |_
    --                    _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _
    --   snk_in.valid  __|
    --   v.val_cnt        0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1
    --   v.pkt_cnt        0     1     2     0     1     2     0     1     2
    --                    ______________   _______________   _______________   _
    --   v.busy        __|              |_|               |_|               |_|
    --                    _     _     _     _     _     _     _     _     _
    --   v.src_out.sop __|0|___|1|___|2|___|0|___|1|___|2|___|0|___|1|___|2|___|
    --                       _     _     _     _     _     _     _     _     _
    --   v.src_out.eop _____|0|___|1|___|2|___|0|___|1|___|2|___|0|___|1|___|2|_

    -- input valid counter
    if snk_in.sop = '1' then
      v.val_cnt := 0;
    elsif snk_in.valid = '1' then
      if r.val_cnt < g_pkt_len - 1 then
        v.val_cnt := r.val_cnt + 1;
      else
        v.val_cnt := 0;
      end if;
    end if;

    -- output packet counter
    if snk_in.sop = '1' then
      v.pkt_cnt := 0;
    elsif snk_in.valid = '1' and v.val_cnt = 0 then
      v.pkt_cnt := r.pkt_cnt + 1;
    end if;

    -- capture pkt_len between input packets when there is no unmerge busy
    if snk_in.sop = '1' then
      v.busy := '1';
    end if;
    if snk_in.eop = '1' then
      v.busy := '0';
    end if;

    if v.busy = '0' then
      v.pkt_len := to_uint(pkt_len);
    end if;

    -- output packet bsn, sync, sop and eop
    -- . Default passes on snk_in.sync, valid, data, re, im
    -- . The sync, valid can be passed on from snk_in, without checking
    --   src_in.ready, because the fact that snk_in.valid is '1' is
    --   sufficient for src_out.valid to be '1' too. The RL is taken care
    --   of by gen_use_src_in_ready.
    v.src_out         := snk_in;  -- passes on snk_in.sync, data, re, im
    v.src_out.bsn     := r.src_out.bsn;
    v.src_out.channel := r.src_out.channel;
    v.src_out.empty   := r.src_out.empty;
    v.src_out.err     := r.src_out.err;
    v.src_out.sop     := '0';
    v.src_out.eop     := '0';

    -- . output sop, eop
    if snk_in.valid = '1' then
      if v.val_cnt = 0 then
        v.src_out.sop := '1';
      end if;
      -- Unmerge packets of length r.pkt_len
      if v.val_cnt = r.pkt_len - 1 then
        v.src_out.eop := '1';
      end if;
      -- Unmerge last packet with length <= r.pkt_len
      if snk_in.eop = '1' then
        v.src_out.eop := '1';
      end if;
    end if;

    -- . output bsn
    if snk_in.sop = '1' then
      v.src_out.bsn := snk_in.bsn;
    elsif v.src_out.sop = '1' then
      v.src_out.bsn := incr_uvec(r.src_out.bsn, g_bsn_increment);
    end if;

    -- . output channel
    v.src_out.channel := TO_DP_CHANNEL(v.pkt_cnt);

    -- . output err, empty. assume they are valid during entire input packet,
    --   so not only at the snk_in.eop
    if snk_in.valid = '1' then
      v.src_out.empty := snk_in.empty;
      v.src_out.err   := snk_in.err;
    end if;

    -- Synchronous reset
    if rst = '1' then
      v := c_reg_rst;
    end if;

    d <= v;
  end process;
end rtl;