Select Git revision
MACScheduler.cc
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;