diff --git a/libraries/base/dp/src/vhdl/dp_fifo_fill_eop.vhd b/libraries/base/dp/src/vhdl/dp_fifo_fill_eop.vhd index c8e3d4e667ff1a49202863cf048f982bb954f2f3..b4b591c923ec32c6ddbfbcdb67deae31d4f2fcae 100644 --- a/libraries/base/dp/src/vhdl/dp_fifo_fill_eop.vhd +++ b/libraries/base/dp/src/vhdl/dp_fifo_fill_eop.vhd @@ -1,36 +1,38 @@ --------------------------------------------------------------------------------- +------------------------------------------------------------------------------- -- --- Copyright (C) 2019 +-- Copyright 2020 -- 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. -- --- 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: R. van der Walle -- Purpose: -- The FIFO starts outputting data when the output is ready and it has been -- filled with more than g_fifo_fill words or an eop signal has been received. --- Given a fixed frame length, this is useful when the in_val is throttled while --- the out_val should not be inactive valid between out_sop to out_eop. --- This is necessary for frame transport over a PHY link without separate data --- valid signal. +-- This is useful when the in_val is throttled while the out_val should not be +-- inactive valid between out_sop to out_eop. This is necessary for frame +-- transport over a PHY link without separate data valid signal. -- Description: -- Modified version of dp_fifo_fill_core. In addition to a frame being available -- after the fifo has been filled sufficiently, a frame is also available when -- the in_eop has been received earlier than the specified g_fifo_fill. For -- more details, please consult the description of dp_fill_fifo_core. +------------------------------------------------------------------------------- LIBRARY IEEE, common_lib, technology_lib; USE IEEE.std_logic_1164.ALL; @@ -82,7 +84,6 @@ ENTITY dp_fifo_fill_eop IS ); END dp_fifo_fill_eop; - ARCHITECTURE rtl OF dp_fifo_fill_eop IS CONSTANT c_fifo_rl : NATURAL := sel_a_b(g_fifo_fill=0, 1, g_fifo_rl); @@ -99,6 +100,8 @@ ARCHITECTURE rtl OF dp_fifo_fill_eop IS CONSTANT s_fill : STD_LOGIC_VECTOR(1 DOWNTO 0) := "01"; CONSTANT s_output : STD_LOGIC_VECTOR(1 DOWNTO 0) := "10"; CONSTANT s_xoff : STD_LOGIC_VECTOR(1 DOWNTO 0) := "11"; + + CONSTANT c_nof_spulse : NATURAL := 3; SIGNAL state : STD_LOGIC_VECTOR(1 DOWNTO 0); -- t_state SIGNAL nxt_state : STD_LOGIC_VECTOR(1 DOWNTO 0); -- t_state @@ -120,9 +123,17 @@ ARCHITECTURE rtl OF dp_fifo_fill_eop IS SIGNAL hold_src_in : t_dp_siso; SIGNAL pend_src_out : t_dp_sosi; - SIGNAL received_eop : BOOLEAN := FALSE; - SIGNAL nxt_received_eop : BOOLEAN := FALSE; - SIGNAL crossed_domain_snk_in_eop : STD_LOGIC := '0'; + SIGNAL reg_wr_eop_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL reg_rd_eop_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL wr_eop_done : STD_LOGIC; + SIGNAL wr_eop_new : STD_LOGIC; + SIGNAL prev_snk_in_eop : STD_LOGIC; + SIGNAL rd_eop_new : STD_LOGIC; + SIGNAL wr_eop_busy : STD_LOGIC; + SIGNAL wr_eop_cnt : NATURAL := 0; + SIGNAL rd_eop_cnt : NATURAL := 0; + SIGNAL eop_cnt : INTEGER := 0; + SIGNAL nxt_eop_cnt : INTEGER := 0; BEGIN -- Output monitor FIFO filling @@ -136,7 +147,6 @@ BEGIN rd_fill_ctrl <= rd_fill_32b(c_fifo_size_w-1 DOWNTO 0); gen_dp_fifo_sc : IF g_use_dual_clock=FALSE GENERATE - crossed_domain_snk_in_eop <= snk_in.eop; u_dp_fifo_sc : ENTITY work.dp_fifo_sc GENERIC MAP ( g_technology => g_technology, @@ -172,19 +182,27 @@ BEGIN ); wr_fifo_usedw <= rd_fifo_usedw; + + -- No need to transfer eop counter across clock domains + rd_eop_cnt <= wr_eop_cnt; + rd_eop_new <= '1'; + p_sc: PROCESS(wr_clk, wr_rst) + BEGIN + IF wr_rst='1' THEN + wr_eop_cnt <= 0; + ELSIF rising_edge(wr_clk) THEN + IF snk_in.eop = '1' THEN + wr_eop_cnt <= 1; + ELSE + wr_eop_cnt <= 0; + END IF; + END IF; + END PROCESS; + + END GENERATE; gen_dp_fifo_dc : IF g_use_dual_clock=TRUE GENERATE - u_common_spulse : ENTITY common_lib.common_spulse - PORT MAP ( - in_rst => wr_rst, - in_clk => wr_clk, - in_pulse => snk_in.eop, - out_rst => rd_rst, - out_clk => rd_clk, - out_pulse => crossed_domain_snk_in_eop - ); - u_dp_fifo_dc : ENTITY work.dp_fifo_dc GENERIC MAP ( g_technology => g_technology, @@ -221,6 +239,52 @@ BEGIN src_in => rd_siso, -- for RL = 0 rd_siso.ready acts as read acknowledge, -- for RL = 1 rd_siso.ready acts as read request src_out => rd_sosi ); + + -- Transfer eop counter across clock domains + reg_wr_eop_cnt <= TO_UVEC(wr_eop_cnt, c_word_w); + rd_eop_cnt <= TO_UINT(reg_rd_eop_cnt); + u_common_reg_cross_domain : ENTITY common_lib.common_reg_cross_domain + PORT MAP ( + in_rst => wr_rst, + in_clk => wr_clk, + in_dat => reg_wr_eop_cnt, + in_new => wr_eop_new, + in_done => wr_eop_done, + out_rst => rd_rst, + out_clk => rd_clk, + out_dat => reg_rd_eop_cnt, + out_new => rd_eop_new + ); + + p_dc: PROCESS(wr_clk, wr_rst) + VARIABLE v_wr_eop_cnt: NATURAL; + BEGIN + IF wr_rst='1' THEN + wr_eop_busy <= '0'; + wr_eop_cnt <= 0; + wr_eop_new <= '0'; + ELSIF rising_edge(wr_clk) THEN + v_wr_eop_cnt := wr_eop_cnt; + IF wr_eop_done = '1' THEN + wr_eop_busy <= '0'; + END IF; + + IF wr_eop_busy = '0' THEN + wr_eop_busy <= '1'; + wr_eop_new <= '1'; + END IF; + + IF wr_eop_new = '1' THEN + wr_eop_new <= '0'; + v_wr_eop_cnt := 0; + END IF; + + IF snk_in.eop = '1' THEN + v_wr_eop_cnt := v_wr_eop_cnt + 1; + END IF; + wr_eop_cnt <= v_wr_eop_cnt; + END IF; + END PROCESS; END GENERATE; no_fill : IF g_fifo_fill=0 GENERATE @@ -231,33 +295,27 @@ BEGIN gen_fill : IF g_fifo_fill>0 GENERATE src_out <= i_src_out; - + p_rd_clk: PROCESS(rd_clk, rd_rst) BEGIN IF rd_rst='1' THEN xon_reg <= '0'; state <= s_idle; i_src_out <= c_dp_sosi_rst; - received_eop <= FALSE; ELSIF rising_edge(rd_clk) THEN xon_reg <= nxt_xon_reg; state <= nxt_state; i_src_out <= nxt_src_out; - IF crossed_domain_snk_in_eop = '1' THEN - received_eop <= TRUE; - ELSE - received_eop <= nxt_received_eop; - END IF; + eop_cnt <= nxt_eop_cnt; END IF; END PROCESS; - + nxt_xon_reg <= src_in.xon; -- register xon to easy timing closure gen_rl_0 : IF g_fifo_rl=0 GENERATE - p_state : PROCESS(state, rd_sosi, src_in, xon_reg, rd_fifo_usedw, rd_fill_ctrl, received_eop) + p_state : PROCESS(state, rd_sosi, src_in, xon_reg, rd_fifo_usedw, rd_fill_ctrl, rd_eop_cnt, eop_cnt, rd_eop_new) BEGIN nxt_state <= state; - nxt_received_eop <= received_eop; rd_siso <= src_in; -- default acknowledge (RL=1) this input when output is ready -- The output register stage increase RL=0 to 1, so it matches RL = 1 for src_in.ready @@ -267,6 +325,10 @@ BEGIN nxt_src_out.eop <= '0'; nxt_src_out.sync <= '0'; + IF rd_eop_new = '1' THEN + nxt_eop_cnt <= eop_cnt + rd_eop_cnt; + END IF; + CASE state IS WHEN s_idle => IF xon_reg='0' THEN @@ -285,14 +347,18 @@ BEGIN nxt_state <= s_xoff; ELSE -- stop reading until the FIFO has been filled sufficiently - IF UNSIGNED(rd_fifo_usedw)<UNSIGNED(rd_fill_ctrl) AND NOT received_eop THEN + IF UNSIGNED(rd_fifo_usedw)<UNSIGNED(rd_fill_ctrl) AND eop_cnt <= 0 THEN rd_siso <= c_dp_siso_hold; -- stop the input, hold the pend_src_out.sop ELSE -- if the output is ready, then start outputting the frame IF src_in.ready='1' THEN nxt_src_out <= rd_sosi; -- output sop that is still at FIFO output (RL=0) - nxt_received_eop <= FALSE; nxt_state <= s_output; + IF rd_eop_new = '1' THEN + nxt_eop_cnt <= eop_cnt + rd_eop_cnt - 1; + ELSE + nxt_eop_cnt <= eop_cnt -1; + END IF; END IF; END IF; END IF; @@ -335,10 +401,9 @@ BEGIN src_out_reg => i_src_out ); - p_state : PROCESS(state, src_in, xon_reg, pend_src_out, rd_fifo_usedw, rd_fill_ctrl, received_eop) + p_state : PROCESS(state, src_in, xon_reg, pend_src_out, rd_fifo_usedw, rd_fill_ctrl, rd_eop_cnt, eop_cnt, rd_eop_new) BEGIN nxt_state <= state; - nxt_received_eop <= received_eop; hold_src_in <= src_in; -- default request (RL=1) new input when output is ready @@ -348,6 +413,10 @@ BEGIN nxt_src_out.sop <= '0'; nxt_src_out.eop <= '0'; nxt_src_out.sync <= '0'; + + IF rd_eop_new = '1' THEN + nxt_eop_cnt <= eop_cnt + rd_eop_cnt; + END IF; CASE state IS WHEN s_idle => @@ -367,14 +436,18 @@ BEGIN nxt_state <= s_xoff; ELSE -- stop reading until the FIFO has been filled sufficiently - IF UNSIGNED(rd_fifo_usedw)<UNSIGNED(rd_fill_ctrl) AND NOT received_eop THEN + IF UNSIGNED(rd_fifo_usedw)<UNSIGNED(rd_fill_ctrl) AND eop_cnt <= 0 THEN hold_src_in <= c_dp_siso_hold; -- stop the input, hold the pend_src_out.sop ELSE -- if the output is ready, then start outputting the input frame IF src_in.ready='1' THEN nxt_src_out <= pend_src_out; -- output sop that is still pending in dp_hold_input - nxt_received_eop <= FALSE; nxt_state <= s_output; + IF rd_eop_new = '1' THEN + nxt_eop_cnt <= eop_cnt + rd_eop_cnt - 1; + ELSE + nxt_eop_cnt <= eop_cnt -1; + END IF; END IF; END IF; END IF;