Skip to content
Snippets Groups Projects
Select Git revision
  • d4a911bf2b4adfbc6f0da7d163fbf7c024a6bd9d
  • master default protected
  • L2SDP-LIFT
  • L2SDP-1113
  • HPR-158
5 results

dp_block_validate_err.vhd

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    dp_block_validate_err.vhd 14.33 KiB
    -------------------------------------------------------------------------------
    --
    -- Copyright 2021
    -- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
    -- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
    --
    -- Licensed under the Apache License, Version 2.0 (the "License");
    -- you may not use this file except in compliance with the License.
    -- You may obtain a copy of the License at
    --
    --     http://www.apache.org/licenses/LICENSE-2.0
    --
    -- Unless required by applicable law or agreed to in writing, software
    -- distributed under the License is distributed on an "AS IS" BASIS,
    -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    -- See the License for the specific language governing permissions and
    -- limitations under the License.
    --
    -------------------------------------------------------------------------------
    
    -------------------------------------------------------------------------------
    -- Author: R vd Walle
    -- Purpose:
    --   Validate the error field of a DP block.
    -- Description:
    --  . The dp_block_validate_err.vhd checks the in_sosi.err field at the end of a
    --    block. Therefore the block needs to be stored, before it can be validated.
    --    The stored block is then either forwarded when the in_sosi.err = 0, or else
    --    it is discarded.
    --  . The dp_block_validate_err.vhd has to maintain the a total number of in_sosi
    --    blocks counter and a number of discarded blocks counter per bit in the
    --    in_sosi.err field. The counters can be read via the MM interface.
    -- Remarks:
    --   . Note that a block can have more than one bit set in the err field. This can
    --     result in multiple counters increasing per block. Therefore, it should not be
    --     assumed that the sum of the err counters is the total amount of discarded
    --     blocks.
    --   . The g_cnt_w is used for the err_counts and discarded count, where typically
    --     a 32 b value is more than enough, so one MM word, because error blocks do
    --     not occur often.
    --   . The g_blk_cnt_w is used to count blocks and can have at most 64 b so two
    --     MM words, because 32 b can easily be too small to count blocks for hours
    --     or longer. The MM map always uses two MM words for total_block_count for
    --     any value of g_blk_cnt_w.
    --   . g_max/min_block_size indicate the minimum / maximum length of incoming blocks.
    --     The ratio of max / min is used to determine a fifo size for the outgoing
    --     sosi.valid signals. To minimize logic the g_min_block_size can be set to
    --     the expected minimum block size.
    --   . g_fifo_size can be set to g_max_block_size if there is no backpressure.
    --     If there is back pressure on the src_in, the fifo_fill_eop can be used to
    --     to account for this backpressure by using an g_fifo_size > g_max_block_size.
    --   . Typically externally connect snk_in.sync to ref_sync or use another reference
    --     pulse. If ref_sync is kept '1', then the MM counts show the current values.
    --     Use a ref_sync pulse to let the MM counts show the values at that specific
    --     instant, independent of when they are read.
    --
    -------------------------------------------------------------------------------
    -- REGMAP
    -------------------------------------------------------------------------------
    --  wi                  Bits    R/W Name                                 Default
    --  ====================================================================================
    --  0                   [31..0] RO  err_count_index_0                     0x0
    --  1                   [31..0] RO  err_count_index_1                     0x0
    --  .                    .      .   .                                     .
    --  .                    .      .   .                                     .
    --  .                    .      .   .                                     .
    --  g_nof_err_counts-1  [31..0] RO  err_count_index_[g_nof_err_counts-1]  0x0
    --  g_nof_err_counts    [31..0] RO  total_discarded_blocks                0x0
    --  g_nof_err_counts+1  [31..0] RO  total_block_count                     0x0
    --                      [63.32]
    --  g_nof_err_counts+3  [31..0] RW  clear                                 0x0 read or write to clear counters
    --  ====================================================================================
    -------------------------------------------------------------------------------
    library IEEE, common_lib;
    use IEEE.std_logic_1164.all;
    use IEEE.numeric_std.all;
    use work.dp_stream_pkg.all;
    use common_lib.common_pkg.all;
    use common_lib.common_mem_pkg.all;
    
    entity dp_block_validate_err is
      generic (
        g_cnt_w              : natural  := c_word_w;  -- max is c_word_w due to mm word width
        g_blk_cnt_w          : natural  := c_longword_w;  -- max is c_longword_w due to two mm word width
        g_max_block_size     : positive := 250;  -- largest possible incoming block size.
        g_min_block_size     : positive := 1;  -- smallest possible incoming block size.
        g_nof_err_counts     : natural  := 8;
        -- fifo generics
        g_fifo_size          : positive := 256;  -- fifo size to buffer incoming blocks, should be >= g_max_block_size
        g_data_w             : natural  := 16;
        g_bsn_w              : natural  := 1;
        g_empty_w            : natural  := 1;
        g_channel_w          : natural  := 1;
        g_use_bsn            : boolean  := false;
        g_use_empty          : boolean  := false;
        g_use_channel        : boolean  := false;
        g_use_sync           : boolean  := false;
        g_use_complex        : boolean  := false
      );
      port (
        dp_rst       : in  std_logic;
        dp_clk       : in  std_logic;
    
        ref_sync     : in  std_logic := '1';
    
        -- ST sink
        snk_out      : out t_dp_siso := c_dp_siso_rdy;
        snk_in       : in  t_dp_sosi;
        -- ST source
        src_in       : in  t_dp_siso := c_dp_siso_rdy;
        src_out      : out t_dp_sosi;
    
        mm_rst       : in  std_logic;
        mm_clk       : in  std_logic;
    
        reg_mosi     : in  t_mem_mosi := c_mem_mosi_rst;
        reg_miso     : out t_mem_miso := c_mem_miso_rst
      );
    end dp_block_validate_err;
    
    architecture rtl of dp_block_validate_err is
    
      constant c_nof_err_ok : natural := ceil_div(g_max_block_size, g_min_block_size);
      constant c_nof_regs   : natural := g_nof_err_counts + 1 + 2 + 1;
      constant c_clear_adr  : natural := c_nof_regs - 1;
    
      type t_cnt_err_arr is array (integer range <>) of std_logic_vector(g_cnt_w - 1 downto 0);
    
      -- Define the actual size of the MM slave register
      constant c_mm_reg : t_c_mem := (latency  => 1,
                                      adr_w    => ceil_log2(c_nof_regs),
                                      dat_w    => c_word_w,  -- Use MM bus data width = c_word_w = 32 for all MM registers
                                      nof_dat  => c_nof_regs,
                                      init_sl  => '0');
    
      -- Registers in st_clk domain
      signal ref_sync_reg  : std_logic := '0';
      signal count_reg     : std_logic_vector(c_mm_reg.nof_dat * c_mm_reg.dat_w - 1 downto 0) := (others => '0');
    
      signal nxt_cnt_en       : std_logic;
      signal cnt_en           : std_logic := '0';
      signal cnt_this_eop     : std_logic;
    
      signal mm_cnt_clr       : std_logic;
      signal cnt_clr          : std_logic;
      signal cnt_blk          : std_logic_vector(g_blk_cnt_w - 1 downto 0);
      signal cnt_blk_en       : std_logic;
      signal cnt_discarded    : std_logic_vector(g_cnt_w - 1 downto 0);
      signal cnt_discarded_en : std_logic;
      signal cnt_err_arr      : t_cnt_err_arr(g_nof_err_counts - 1 downto 0);
      signal cnt_err_en_arr   : std_logic_vector(g_nof_err_counts - 1 downto 0);
    
      signal hold_cnt_blk        : std_logic_vector(g_blk_cnt_w - 1 downto 0) := (others => '0');
      signal hold_cnt_discarded  : std_logic_vector(g_cnt_w - 1 downto 0) := (others => '0');
      signal hold_cnt_err_arr    : t_cnt_err_arr(g_nof_err_counts - 1 downto 0) := (others => (others => '0'));
    
      signal err_ok          : std_logic;
      signal err_ok_reg      : std_logic;
      signal fifo_err_ok     : std_logic;
      signal fifo_err_ok_val : std_logic;
      signal out_valid       : std_logic;
      signal out_valid_reg   : std_logic;
    
      signal block_sosi       : t_dp_sosi;
      signal block_siso       : t_dp_siso;
      signal block_sosi_piped : t_dp_sosi;
    
    begin
    
      mm_cnt_clr <= (reg_mosi.rd or reg_mosi.wr) when TO_UINT(reg_mosi.address(c_mm_reg.adr_w - 1 downto 0)) = c_clear_adr else '0';
      u_common_spulse : entity common_lib.common_spulse
        port map (
          in_rst    => mm_rst,
          in_clk    => mm_clk,
          in_pulse  => mm_cnt_clr,
          out_rst   => dp_rst,
          out_clk   => dp_clk,
          out_pulse => cnt_clr
        );
    
      -- . register snk_in.sync to ease timing closure for ref_sync fanout
      ref_sync_reg <= ref_sync when rising_edge(dp_clk);
    
      -- . clear block counters immediately at cnt_clr
      -- . start block counters after sync, e.g. to align block counters in different nodes in
      --   case the snk_in was (already) active during the cnt_clr
      nxt_cnt_en <= '0' when cnt_clr = '1' else '1' when ref_sync_reg = '1' else cnt_en;
      cnt_en <= nxt_cnt_en when rising_edge(dp_clk);
      cnt_this_eop <= cnt_en and snk_in.eop;
    
      -- block counter
      cnt_blk_en <= cnt_this_eop;
      u_blk_counter : entity common_lib.common_counter
      generic map (
        g_width => g_blk_cnt_w,
        g_clip  => true
      )
      port map (
        rst => dp_rst,
        clk => dp_clk,
    
        cnt_clr => cnt_clr,
        cnt_en  => cnt_blk_en,
        count   => cnt_blk
      );
    
      -- discarded block counter
      cnt_discarded_en <= cnt_this_eop when TO_UINT(snk_in.err(g_nof_err_counts - 1 downto 0)) > 0 else '0';
      u_discarded_counter : entity common_lib.common_counter
      generic map (
        g_width => g_cnt_w,
        g_clip  => true
      )
      port map (
        rst => dp_rst,
        clk => dp_clk,
    
        cnt_clr => cnt_clr,
        cnt_en  => cnt_discarded_en,
        count   => cnt_discarded
      );
    
      -- error counters
      gen_err_counters : for I in 0 to g_nof_err_counts - 1 generate
        cnt_err_en_arr(I) <= cnt_this_eop and snk_in.err(I);
        u_blk_counter : entity common_lib.common_counter
        generic map (
          g_width => g_cnt_w,
          g_clip  => true
        )
        port map (
          rst => dp_rst,
          clk => dp_clk,
    
          cnt_clr => cnt_clr,
          cnt_en  => cnt_err_en_arr(I),
          count   => cnt_err_arr(I)
        );
      end generate;
    
      -- Hold counter values at ref_sync_reg to have stable values for MM read for comparision between nodes
      p_hold_counters : process(dp_clk)
      begin
        if rising_edge(dp_clk) then
          if cnt_clr = '1' then
            hold_cnt_blk <= (others => '0');
            hold_cnt_discarded <= (others => '0');
            hold_cnt_err_arr <= (others => (others => '0'));
          elsif ref_sync_reg = '1' then
            hold_cnt_blk <= cnt_blk;
            hold_cnt_discarded <= cnt_discarded;
            hold_cnt_err_arr <= cnt_err_arr;
          end if;
        end if;
      end process;
    
      -- Register mapping
      gen_reg : for I in 0 to g_nof_err_counts - 1 generate
        count_reg((I + 1) * c_word_w - 1 downto I * c_word_w) <= RESIZE_UVEC(hold_cnt_err_arr(I), c_word_w);
      end generate;
    
      count_reg((g_nof_err_counts + 1) * c_word_w - 1 downto g_nof_err_counts * c_word_w ) <= RESIZE_UVEC(hold_cnt_discarded, c_word_w);
    
      gen_blk_cnt_32b : if g_blk_cnt_w <= c_word_w generate
        count_reg((g_nof_err_counts + 2) * c_word_w - 1 downto (g_nof_err_counts + 1) * c_word_w ) <= RESIZE_UVEC(hold_cnt_blk, c_word_w);  -- low part
        count_reg((g_nof_err_counts + 3) * c_word_w - 1 downto (g_nof_err_counts + 2) * c_word_w ) <= (others => '0');  -- high part (not used)
      end generate;
    
      gen_blk_cnt_64b : if g_blk_cnt_w > c_word_w generate
        count_reg((g_nof_err_counts + 2) * c_word_w - 1 downto (g_nof_err_counts + 1) * c_word_w ) <= hold_cnt_blk(c_word_w - 1 downto 0);  -- low part
        count_reg((g_nof_err_counts + 3) * c_word_w - 1 downto (g_nof_err_counts + 2) * c_word_w ) <= RESIZE_UVEC(hold_cnt_blk(g_blk_cnt_w - 1 downto c_word_w), c_word_w);  -- high part
      end generate;
    
      u_reg : entity common_lib.common_reg_r_w_dc
      generic map (
        g_cross_clock_domain => true,
        g_readback           => false,
        g_reg                => c_mm_reg
      )
      port map (
        -- Clocks and reset
        mm_rst      => mm_rst,
        mm_clk      => mm_clk,
        st_rst      => dp_rst,
        st_clk      => dp_clk,
    
        -- Memory Mapped Slave in mm_clk domain
        sla_in      => reg_mosi,
        sla_out     => reg_miso,
    
        -- MM registers in st_clk domain
        reg_wr_arr  => OPEN,
        reg_rd_arr  => OPEN,
        in_reg      => count_reg,  -- read only
        out_reg     => open  -- no write
      );
    
      u_fifo_fill_eop : entity work.dp_fifo_fill_eop
      generic map (
        g_data_w       => g_data_w,
        g_bsn_w        => g_bsn_w,
        g_empty_w      => g_empty_w,
        g_channel_w    => g_channel_w,
        g_use_bsn      => g_use_bsn,
        g_use_empty    => g_use_empty,
        g_use_channel  => g_use_channel,
        g_use_sync     => g_use_sync,
        g_use_complex  => g_use_complex,
        g_fifo_fill    => g_max_block_size,
        g_fifo_size    => g_fifo_size
      )
      port map (
        wr_rst => dp_rst,
        wr_clk => dp_clk,
        rd_rst => dp_rst,
        rd_clk => dp_clk,
    
        -- ST sink
        snk_out => snk_out,
        snk_in  => snk_in,
        -- ST source
        src_in  => block_siso,
        src_out => block_sosi
      );
    
      u_pipeline : entity work.dp_pipeline
      generic map (
        g_pipeline   => 1  -- 0 for wires, > 0 for registers,
      )
      port map (
        rst          => dp_rst,
        clk          => dp_clk,
        -- ST sink
        snk_out      => block_siso,
        snk_in       => block_sosi,
        -- ST source
        src_in       => src_in,
        src_out      => block_sosi_piped
      );
    
      p_dp_clk : process(dp_rst, dp_clk)
      begin
        if dp_rst = '1' then
          err_ok_reg      <= '0';
          out_valid_reg   <= '0';
        elsif rising_edge(dp_clk) then
          err_ok_reg      <= err_ok;
          out_valid_reg   <= out_valid;
        end if;
      end process;
    
      err_ok <= not vector_or(snk_in.err(g_nof_err_counts - 1 downto 0)) when snk_in.eop = '1' else err_ok_reg;
    
      u_fifo_err_ok : entity common_lib.common_fifo_sc
      generic map (
        g_dat_w => 1,
        g_nof_words => c_nof_err_ok
      )
      port map (
        rst       => dp_rst,
        clk       => dp_clk,
        wr_dat(0) => err_ok,
        wr_req    => snk_in.eop,
        rd_req    => block_sosi.sop,
        rd_dat(0) => fifo_err_ok,
        rd_val    => fifo_err_ok_val
      );
    
      out_valid <= fifo_err_ok when fifo_err_ok_val = '1' else out_valid_reg;
    
      p_src_out : process(block_sosi_piped, out_valid)
      begin
        src_out       <= block_sosi_piped;
        src_out.valid <= block_sosi_piped.valid and out_valid;
        src_out.sop   <= block_sosi_piped.sop   and out_valid;
        src_out.eop   <= block_sosi_piped.eop   and out_valid;
        src_out.sync  <= block_sosi_piped.sync  and out_valid;
      end process;
    
    end rtl;