diff --git a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd index fb263f5b4ccdd775cd50e95c9b7d44f38751bab2..d5c96ea99d864ddf79b199ce24fe9c532dc776bf 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd @@ -335,7 +335,8 @@ begin g_use_mm_output => true, g_rd_latency => 1, -- Required for st_xst -- for mms_dp_bsn_monitor_v2 - g_nof_clk_per_sync => c_sdp_N_clk_sync_timeout_xsub, -- Using c_sdp_N_clk_sync_timeout_xsub as g_nof_clk_per_sync is used for BSN monitor timeout. + -- Using c_sdp_N_clk_sync_timeout_xsub as g_nof_clk_per_sync is used for BSN monitor timeout. + g_nof_clk_per_sync => c_sdp_N_clk_sync_timeout_xsub, g_nof_input_bsn_monitors => g_P_sq, g_use_bsn_output_monitor => true ) diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_beamformer_output.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_beamformer_output.vhd index 688042034a72ccd9de6c174478ae02b121eb4516..4012eabd8272c77d113e651524ae650d6c441aa3 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_beamformer_output.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_beamformer_output.vhd @@ -244,17 +244,14 @@ begin ----------------------------------------------------------------------------- u_dp_packet_merge : entity dp_lib.dp_packet_merge generic map( + g_use_ready => false, -- no flow control g_nof_pkt => c_nof_blocks_per_packet, g_bsn_increment => 1 ) port map( rst => dp_rst, clk => dp_clk, - - snk_out => OPEN, snk_in => dp_repack_beamlet_src_out, - - src_in => c_dp_siso_rdy, src_out => dp_packet_merge_src_out ); diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd index 6eb6479310aaf40180e891d1d9f5fe5e049f64b3..ebd79dc6d47bbcaa1214e1a8e1f58b93717e6c49 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd @@ -435,6 +435,7 @@ architecture str of sdp_station is constant c_validate_channel : boolean := true; constant c_validate_channel_mode : string := "="; constant c_sync_timeout : natural := sel_a_b(g_sim, g_sim_sync_timeout, c_sdp_N_clk_sync_timeout); + constant c_sync_timeout_xst : natural := sel_a_b(g_sim, g_sim_sync_timeout, c_sdp_N_clk_sync_timeout_xsub); -- Use same Tx FIFO size for all lanes in the ring to ease the code, no need to optimize Tx FIFO RAM usage per lane. -- The tr_10GbE uses dp_fifo_fill_eop, so rely on releasing packets (beamlets, crosslets) at eop instead @@ -1224,7 +1225,7 @@ begin g_bsn_at_sync_check_channel => c_bsn_at_sync_check_channel, g_validate_channel => c_validate_channel, g_validate_channel_mode => c_validate_channel_mode, - g_sync_timeout => c_sync_timeout + g_sync_timeout => c_sync_timeout_xst ) port map ( mm_rst => mm_rst, diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg index 8d500138501b9273d8bad9a735033150efd979c9..1d3533b137fdbd8d27f60afb547b4febd902487b 100644 --- a/libraries/base/dp/hdllib.cfg +++ b/libraries/base/dp/hdllib.cfg @@ -1,8 +1,8 @@ hdl_lib_name = dp hdl_library_clause_name = dp_lib -hdl_lib_uses_synth = mm common common_mult easics -hdl_lib_uses_sim = -hdl_lib_technology = +hdl_lib_uses_synth = mm common common_mult easics +hdl_lib_uses_sim = +hdl_lib_technology = synth_files = src/vhdl/dp_stream_pkg.vhd @@ -10,7 +10,7 @@ synth_files = src/vhdl/dp_example_dut.vhd src/vhdl/dp_packetizing_pkg.vhd src/vhdl/dp_packet_pkg.vhd - + src/vhdl/dp_eop_extend.vhd src/vhdl/dp_validate.vhd src/vhdl/dp_ready.vhd @@ -30,6 +30,7 @@ synth_files = src/vhdl/dp_pipeline.vhd src/vhdl/dp_pipeline_arr.vhd src/vhdl/dp_pipeline_ready.vhd + src/vhdl/dp_add_flow_control.vhd src/vhdl/dp_block_resize.vhd src/vhdl/dp_block_validate_length.vhd src/vhdl/dp_block_validate_bsn_at_sync.vhd @@ -147,7 +148,7 @@ synth_files = src/vhdl/dp_packet_unmerge.vhd src/vhdl/dp_offload_tx_legacy.vhd - src/vhdl/dp_offload_tx_len_calc.vhd + src/vhdl/dp_offload_tx_len_calc.vhd src/vhdl/dp_sync_insert.vhd src/vhdl/dp_sync_insert_v2.vhd src/vhdl/dp_sync_recover.vhd @@ -192,20 +193,20 @@ synth_files = src/vhdl/dp_selector_arr.vhd src/vhdl/dp_selector.vhd src/vhdl/mms_dp_scale.vhd - + tb/vhdl/dp_stream_player.vhd tb/vhdl/dp_sosi_recorder.vhd tb/vhdl/dp_stream_rec_play.vhd tb/vhdl/dp_statistics.vhd - + tb/vhdl/tb_dp_pkg.vhd - -test_bench_files = - + +test_bench_files = + tb/vhdl/dp_phy_link.vhd tb/vhdl/dp_stream_stimuli.vhd tb/vhdl/dp_stream_verify.vhd - + tb/vhdl/tb_dp_strobe_total_count.vhd tb/vhdl/tb_dp_block_select.vhd tb/vhdl/tb_dp_block_validate_length.vhd @@ -266,6 +267,7 @@ test_bench_files = tb/vhdl/tb_dp_concat_field_blk.vhd tb/vhdl/tb_dp_packet.vhd tb/vhdl/tb_dp_packet_merge.vhd + tb/vhdl/tb_dp_packet_merge_unmerge.vhd tb/vhdl/tb_dp_packetizing.vhd tb/vhdl/tb_dp_pad_insert_remove.vhd tb/vhdl/tb_dp_pipeline.vhd @@ -344,6 +346,7 @@ test_bench_files = tb/vhdl/tb_tb_dp_packetizing.vhd tb/vhdl/tb_tb_dp_packet.vhd tb/vhdl/tb_tb_dp_packet_merge.vhd + tb/vhdl/tb_tb_dp_packet_merge_unmerge.vhd tb/vhdl/tb_tb_dp_concat_field_blk.vhd tb/vhdl/tb_tb_dp_pipeline.vhd tb/vhdl/tb_tb_dp_pipeline_ready.vhd @@ -360,7 +363,7 @@ test_bench_files = tb/vhdl/tb_tb_dp_throttle_xon.vhd tb/vhdl/tb_tb_dp_counter.vhd tb/vhdl/tb_tb_dp_xonoff.vhd - + tb/vhdl/tb_tb_tb_dp_backpressure.vhd tb/vhdl/tb_dp_offload_tx_v3.vhd tb/vhdl/tb_tb_dp_offload_tx_v3.vhd @@ -368,7 +371,7 @@ test_bench_files = tb/vhdl/tb_dp_selector_arr.vhd tb/vhdl/tb_mms_dp_scale.vhd -regression_test_vhdl = +regression_test_vhdl = tb/vhdl/tb_dp_fifo_to_mm.vhd tb/vhdl/tb_dp_fifo_xonoff.vhd tb/vhdl/tb_dp_latency_adapter.vhd @@ -403,8 +406,8 @@ regression_test_vhdl = tb/vhdl/tb_tb_dp_reverse_n_data.vhd tb/vhdl/tb_tb_dp_reverse_n_data_fc.vhd tb/vhdl/tb_tb_dp_example_dut.vhd - tb/vhdl/tb_tb_dp_fifo_dc.vhd - tb/vhdl/tb_tb_dp_fifo_dc_mixed_widths.vhd + tb/vhdl/tb_tb_dp_fifo_dc.vhd + tb/vhdl/tb_tb_dp_fifo_dc_mixed_widths.vhd tb/vhdl/tb_tb_dp_fifo_fill.vhd tb/vhdl/tb_tb_dp_fifo_fill_sc.vhd tb/vhdl/tb_tb_dp_fifo_fill_eop.vhd @@ -417,6 +420,7 @@ regression_test_vhdl = tb/vhdl/tb_tb3_dp_mux.vhd tb/vhdl/tb_tb_dp_packet.vhd tb/vhdl/tb_tb_dp_packet_merge.vhd + tb/vhdl/tb_tb_dp_packet_merge_unmerge.vhd tb/vhdl/tb_tb_dp_concat_field_blk.vhd tb/vhdl/tb_tb_dp_pad_insert_remove.vhd tb/vhdl/tb_tb_dp_pipeline.vhd diff --git a/libraries/base/dp/src/vhdl/dp_add_flow_control.vhd b/libraries/base/dp/src/vhdl/dp_add_flow_control.vhd new file mode 100644 index 0000000000000000000000000000000000000000..739917a35ef9a1b434fd9992a04d6b3eebe57d5d --- /dev/null +++ b/libraries/base/dp/src/vhdl/dp_add_flow_control.vhd @@ -0,0 +1,153 @@ +-------------------------------------------------------------------------------- +-- +-- Copyright (C) 2023 +-- 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/>. +-- +-------------------------------------------------------------------------------- + +-- Purpose: Provide flow control +-- Description: +-- . This component can add src_in.ready flow control to a logic function +-- that no flow control. +-- - If g_use_ready = false, then src_in is ignored, so no flow control is +-- added and the dp_add_flow_control component reduces to wires. +-- - If g_use_ready = true then use flow control by src_in.ready. +-- . If g_pipeline_ready = false then dp_add_flow_control = dp_pipeline, +-- and then snk_in and src_in.ready are used combinatorially by +-- dp_pipeline. +-- . If g_pipeline_ready = true then dp_add_flow_control = dp_pipeline_ready, +-- and then the registered input snk_in_reg is used and the src_in.ready +-- is registered. +-- +-- . The block diagram shows how the dp_add_flow_control is used. +-- +-- g_use_ready +-- g_pipeline_ready +-- g_in_latency +-- g_out_latency +-- _____________________ +-- | dp_add_flow_control | +-- | | +-- snk_out <--------------| snk_out src_in |<---------- src_in +-- | | +-- d.src_out -->| snk_in src_out|--> src_out +-- r.src_out -->| snk_in_reg | +-- |_____________________| +-- +-- ________ _______ +-- / \ | | +-- snk_in --------->| p_comb | --> d -->| p_reg |--> r +-- \________/ |_______| +-- | | +-- \-----------<------/ +-- +-- . The logic function is implemented in combinatorial process p_comb and the +-- internal state of it is kept in a clocked process p_reg. +-- . The p_reg increases the ready latency (RL) of p_reg.src_out by 1, because +-- p_comb does not use src_in.ready. Instead p_comb drives d.src_out based +-- on the snk_in.sop, eop, valid. +-- - With g_use_ready = true and g_pipeline_ready = true use a +-- dp_pipeline_ready to restore the RL from 2 to 1 for the src_out output. +-- With the dp_pipeline_ready a few register stages are needed to store +-- src_out to be able to adapt the RL and to be able to register the +-- src_in.ready. The advantage of dp_pipeline_ready is that it register the +-- src_in.ready, so that may ease achieving timing closure. +-- - The alternative with g_use_ready = true and g_pipeline_ready = false is +-- to use a dp_pipeline the d.src_out in combination with src_in.ready, to +-- keep the RL at 1 by means of dp_pipeline. The dp_pipeline adds a +-- register to store src_out. The p_reg.src_out is kept as well, to keep +-- the internal state of the function. +-- . If the p_comb also needs to apply flow control itself, then p_comb can +-- output a ready or d.ready signal that is or-ed with the input +-- src_in.ready and then applied to the src_in input of dp_add_flow_control. +-- . The g_in_latency and g_out_latency are only used when g_use_ready = true +-- and g_pipeline_ready = true. When g_pipeline_ready = false, then the +-- assumption is that snk_in and src_out both have RL = 1. +-- Remark: +-- . Default use g_use_ready = true and g_pipeline_ready = false for solution +-- that fits both flow control or no flow control with the least logic. The +-- flow control logic may even get entirely optimized away during synthesis +-- when snk_in.ready = '1' fixed. + +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_add_flow_control is + generic ( + g_use_ready : boolean := true; + g_pipeline_ready : boolean := false; + g_in_latency : natural := 1; + g_out_latency : natural := 1 + ); + port ( + rst : in std_logic; + clk : in std_logic; + + snk_out : out t_dp_siso; + snk_in : in t_dp_sosi; -- combinatorial input + snk_in_reg : in t_dp_sosi; -- registered input + + src_in : in t_dp_siso := c_dp_siso_rst; + src_out : out t_dp_sosi + ); +end dp_add_flow_control; + +architecture str of dp_add_flow_control is +begin + no_flow_control : if g_use_ready = false generate + -- Only wires + snk_out <= c_dp_siso_rdy; + src_out <= snk_in_reg; + end generate; + + use_flow_control : if g_use_ready = true generate + gen_dp_pipeline : if g_pipeline_ready = false generate + u_dp_pipeline : entity work.dp_pipeline + port map ( + rst => rst, + clk => clk, + -- ST sink + snk_out => snk_out, + snk_in => snk_in, + -- ST source + src_in => src_in, + src_out => src_out + ); + end generate; + + gen_dp_pipeline_ready : if g_pipeline_ready = true generate + u_dp_pipeline_ready : entity work.dp_pipeline_ready + generic map ( + g_in_latency => g_in_latency + 1, -- + 1 to account of registered input + g_out_latency => g_out_latency + ) + port map ( + rst => rst, + clk => clk, + -- ST sink + snk_out => snk_out, + snk_in => snk_in_reg, + -- ST source + src_in => src_in, + src_out => src_out + ); + end generate; + end generate; +end str; diff --git a/libraries/base/dp/src/vhdl/dp_packet_merge.vhd b/libraries/base/dp/src/vhdl/dp_packet_merge.vhd index 78269fecf91b5630c8e666924ea27304019fed4c..14dfd8a984cda38fe936af12e313163de94bf2e8 100644 --- a/libraries/base/dp/src/vhdl/dp_packet_merge.vhd +++ b/libraries/base/dp/src/vhdl/dp_packet_merge.vhd @@ -42,140 +42,11 @@ -- dynamic nof_pkt control. When the nof_pkt control input is used, g_nof_pkt sets -- the maximum number of packets that can be merged. -- . the nof_pkt control input is read on completion of the current block. The current --- working value is kept in r (t_reg). nof_pkt = 0 is ignored. +-- working value is kept in r (t_reg). -- . with g_align_at_sync=true the merge can be forced to restart at a snk_in.sync. --- --- Issues: --- --- Design steps: --- A) No flow control --- . Start with the Gaisler p_comb, p_reg template: --- --- /----------------------------\ --- | nxt_r r | --- | /------\ . . | --- \-->| | . . | --- snk_in -------->| | ---> p_reg -----*--> src_out --- snk_out <--------|p_comb| <------------------- src_in --- \------/ . --- . --- snk --> src : latency + 1 --- --- The following signals are registered in r: outputs, internal states. Typically do not yet --- add pipelining registers, because these are not functional and can be added later using --- common_pipeline or dp_pipeline components. For the design start with a timing diagram of the --- input and output for merging e.g. 3 packets. Draw the sop and eop and also draw the case that --- there is a data invalid gap between packets. There needs to be a packet counter so, also --- draw this signal. --- . Using the timing diagram now assing each of the v fields. Try to keep the implementation --- for each field seperate, i.e. do not combine multiple fields into one if then else statement. --- The reason is that keeping each field implementation seperate makes the code more clear and --- also eases making modifications later on. Mark the implementation section by a comment line. --- . Typically use r in the if condition and at the right of the := assignment. Depending only --- on r for implementing v has the advantage that the order of the code section is not important, --- i.e. similar as with seperate combinatorial processes. However for some cases it can be --- beneficial to use v in the condition or in the assignment, e.g. as with v_busy. --- . If the component will not support flow control, then the snk - src latency may be made --- > 1. However to prepare for adding flow control later on it is prefered to keep the input - --- output latency at 1. Keeping the snk - src latency at 1 typically also makes the --- implementation more clear, because if more latency is needed then it may be better to --- partition the function over two or more components. The snk - src latency of 1 is not --- functional, because the nxt_r with latency 0 also could be used as output. However the --- registering of outputs can be functional. From digital implementation point of view it is --- appropriate though to continue with the r outputs and assign these to the entity outputs. --- . When the architecture HDL implementation looks clean coded then compile it. When it compiles --- OK commit it. --- . Next step is to add a test bench. For dp_packet_merge it is beneficial to first also implement --- the reverse function dp_packet_unmerge, because that allows the test bench to have the same --- stimuli input counter data also appear as counter data at the output for verification. --- Furthermore even though the reverse function may not be needed yet in an application it --- happens quite often that it will become useful in the future. --- . For the stimuli and verification use the counter data procedures from tb_dp_pkg.vhd. The --- test bench consists of stimuli --> DUT --> reverse DUT --> verification. First keep data --- valid active and use nof_pkt > 1. When that works OK, commit the DUT and the test bench. --- After that try the extreme values for control, i.e. nof_pkt = 0 should yield no output and --- nof_pkt = 1 should yield output = input. Then add dynamic in_en to get data not valid gaps --- during and between the packets. Also try dynamic changing of nof_pkt. When that all --- works commit the VHDL files. --- --- B) Add flow control using dp_latency_adapter: --- --- /----------------------------\ --- | nxt_r r | --- | /------\ . . | /-------\ --- \-->| | . . | |dp | --- snk_in -------->| | ---> p_reg -----*---->|latency|---> src_out --- snk_out <--------|p_comb| <---------------------|adapter|<--- src_in --- . \------/ . \-------/ . --- . . . --- . . . --- . . . --- RL=1 RL=2 RL=1 --- --- C) Add flow control using dp_hold_input --- . Adding flow control implies adding dp_hold_input to connect to the snk_in/skn_out and using --- pend_src_out/hold_src_in instead in p_comb: --- --- |-----| <---------------------------------------------\ --- | | nxt_r r | --- | | /------\ . . | --- | dp | | | . . | --- snk_in --> |hold | --> pend_src_out --> | | ---> p_reg -----*--> src_out --- snk_out <-- |input| <-- hold_src_in <-- |p_comb| <------------------- src_in --- . |-----| . \------/ . --- . . . --- RL=1 RL=0 RL=1 --- --- The ready latency (RL) at the input is RL=1. The RL at the output must also be RL=1. The --- dp_hold_input effectively achieves that the pend_src_out/hold_src_in have RL=0. --- The snk - src latency of the function is 1, so at the output the RL then becomes RL=1 again. --- Inside p_comb the v output valid now only get assigned with the pend_src_out valid when --- src_in.ready='1'. Internally in dp_hold_input the next_src_out.valid = pend_src_out.valid --- AND src_in.ready, and similar for the sop, eop amd sync. Therefore the p_comb can also use --- next_src_out instead of pend_src_out in combination with src_in.ready. The advantage of --- using next_src_out is that it makes the code more compact. The advantange of using --- pend_src_out and src_in.ready explicitly is that it may make it more clear how src_in.ready --- is used. --- . Typically the p_comb can simply pass on src_in to hold_src_in, however it may also control --- hold_src_in by itself if this function also needs some extra flow control, e.g. to halt or --- flush the input. --- . Most components only need to pass on the src_in.xon to hold_src_in.xon. Hence this can be --- the last statement in p_comb. --- . In the test bench now also apply the src_in.ready to verify that the component behaves --- correctly for allways active ready and random ready flow control. --- . Make a multi-test bench tb_tb_dp_packet_merge to verify different tb settings for the DUT --- stimuli and add it to the overall DP module tb_tb_tb_dp_backpressure regression test bench. --- --- D) Potential alternative to add flow control using dp_pipeline --- --- . This scheme has not been verified yet, but it could work: --- --- /-----------------------------\ --- | nxt_r r | dp_pipeline_stream --- | /------\ . . | <--select none or one--> --- \-->| |---o-> p_reg -----/ ____ /-----\ --- | | | |dp | |dp | --- snk_in -------->| | \------------------->|pipe|-->|pipe |--> src_out --- snk_out <--------|p_comb|<-----------------------|line|<--|line |--- src_in --- . \------/ . |____| |ready| . --- . RL=0 . \-----/ . --- RL=1 RL=1 RL=1 --- --- . dp_pipeline_stream = wires or dp_pipeline or dp_pipeline_ready --- - dp_pipeline pipelines the sosi --- - dp_pipeline_ready pipelines the siso and also the sosi --- . without flow control there is no siso, so then dp_pipeline_stream degenerates --- to common_pipeline. --- . The nxt_r contains combinatorially (= functionally) already what src_out will become in --- the next cycle and because it is combinatorially related to the input it preserves the --- RL=1 (whereas p_reg has RL=2). Leading nxt_r through dp_pipeline includes the flow control --- to it. --- . Compared to using dp_hold_input using dp_pipeline costs twice more registers, because --- dp_pipeline internally has a register stage and p_reg is still needed. Still synthesis --- may be able to optimize away some redundant registers. --- . The advantage of this scheme it that it allows designing the function without flow --- control according to scheme A). Then make nxt_r available instead of r and lead nxt_r --- through dp_pipeline to register it and to add the flow control. +-- . Optional flow control dependent on g_use_ready and g_pipeline_ready, see +-- description of dp_add_flow_control. + library IEEE,common_lib; use IEEE.std_logic_1164.all; @@ -185,10 +56,12 @@ use work.dp_stream_pkg.all; entity dp_packet_merge is generic ( - g_nof_pkt : natural; - g_align_at_sync : boolean := false; - g_bsn_increment : natural := 0; - g_bsn_err_bi : natural := 0 -- bit index (bi) in scr_out.err for snk_in.bsn error + g_use_ready : boolean := true; + g_pipeline_ready : boolean := false; + g_nof_pkt : natural; + g_align_at_sync : boolean := false; + g_bsn_increment : natural := 0; + g_bsn_err_bi : natural := 0 -- bit index (bi) in scr_out.err for snk_in.bsn error ); port ( rst : in std_logic; @@ -200,7 +73,7 @@ entity dp_packet_merge is snk_out : out t_dp_siso; snk_in : in t_dp_sosi; - src_in : in t_dp_siso; + src_in : in t_dp_siso := c_dp_siso_rdy; src_out : out t_dp_sosi ); end dp_packet_merge; @@ -217,52 +90,32 @@ architecture rtl of dp_packet_merge is src_out : t_dp_sosi; end record; - constant c_use_dp_latency_adapter : boolean := true; -- when TRUE adjust RL from 2 to 1, else use dp_hold_input to keep RL at 1. - signal r, nxt_r : t_reg; - - signal dp_latency_adapter_snk_out : t_dp_siso; - signal dp_latency_adapter_snk_in : t_dp_sosi; - signal dp_latency_adapter_src_in : t_dp_siso; - signal dp_latency_adapter_src_out : t_dp_sosi; begin - -- Map t_reg outputs to entity outputs + -- Map logic function outputs to entity outputs nof_pkt_out <= TO_UVEC(r.nof_pkt, ceil_log2(g_nof_pkt + 1)); - no_dp_latency_adapter : if c_use_dp_latency_adapter = false generate - snk_out <= src_in; - src_out <= r.src_out; - - -- can put dp_hold_input here -- - end generate; - - gen_dp_latency_adapter : if c_use_dp_latency_adapter = true generate - snk_out <= dp_latency_adapter_snk_out; - dp_latency_adapter_snk_in <= r.src_out; - - u_dp_latency_adapter : entity work.dp_latency_adapter + u_dp_add_flow_control : entity work.dp_add_flow_control generic map ( - g_in_latency => 2, - g_out_latency => 1 + g_use_ready => g_use_ready, + g_pipeline_ready => g_pipeline_ready ) port map ( - rst => rst, - clk => clk, - -- ST sink - snk_out => dp_latency_adapter_snk_out, - snk_in => dp_latency_adapter_snk_in, - -- ST source - src_in => dp_latency_adapter_src_in, - src_out => dp_latency_adapter_src_out - ); + rst => rst, + clk => clk, - dp_latency_adapter_src_in <= src_in; - src_out <= dp_latency_adapter_src_out; - end generate; + snk_out => snk_out, + snk_in => nxt_r.src_out, -- combinatorial input + snk_in_reg => r.src_out, -- registered input + + src_in => src_in, + src_out => src_out + ); - -- p_reg + -- p_reg function state register r <= nxt_r when rising_edge(clk); + -- Logic function p_comb : process(rst, r, snk_in, nof_pkt) variable v : t_reg; begin @@ -355,7 +208,7 @@ begin if snk_in.eop = '1' then if r.pkt_cnt = 0 then - v.src_out.err := snk_in.err; + v.src_out.err := snk_in.err; -- take err field of first packet else v.src_out.err := r.src_out.err or snk_in.err; -- OR the err fields of the packets to reflect combined error status. end if; diff --git a/libraries/base/dp/src/vhdl/dp_packet_unmerge.vhd b/libraries/base/dp/src/vhdl/dp_packet_unmerge.vhd index 1fe7ebd08f82c15f48ac1e5622692e84b2afd121..9a8401456e7753a45023be5912ec77afb4eb0af7 100644 --- a/libraries/base/dp/src/vhdl/dp_packet_unmerge.vhd +++ b/libraries/base/dp/src/vhdl/dp_packet_unmerge.vhd @@ -19,17 +19,38 @@ -- -------------------------------------------------------------------------------- --- Purpose: Unmerge each input packet into g_nof_pkt output packets. Below is the --- waveform when g_nof_pkt = 3. The numbers in snk_in.sop and snk_in.eop --- match pkt_cnt. --- _ _ _ --- snk_in.sop _____|0|___________________________|1|___________________________|2|_ --- _ _ --- snk_in.sop _________________________________|0|___________________________|1|___ --- _ _ _ _ _ _ _ --- src_out.sop _____|0|_______|1|_______|2|_______|0|_______|1|_______|2|_______|0|_ --- _ _ _ _ _ _ --- src_out.eop _____________|0|_______|1|_______|2|_______|0|_______|1|_______|2|___ +-- 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 must be an integer +-- multiple of g_pkt_len. The number of output packets g_nof_pkt_max is only +-- used to determine the maximum supported pkt_cnt range. The actual number +-- of output packets has to be <= g_nof_pkt_max, and is determined by input +-- packet length / g_pkt_len. Hence the dp_packet_unmerge can dynamically +-- handle different sizes of input packets, provided that their length is an +-- integer multiple of g_pkt_len. +-- . 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. library IEEE,common_lib; use IEEE.std_logic_1164.all; @@ -39,8 +60,11 @@ use work.dp_stream_pkg.all; entity dp_packet_unmerge is generic ( - g_nof_pkt : natural := 1; -- Nof packets to unmerge each incoming packet to - g_pkt_len : natural := 1 -- Length of the unmerged packets + g_use_ready : boolean := true; + g_pipeline_ready : boolean := false; + g_nof_pkt_max : natural := 1; -- Maximum nof 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; @@ -49,14 +73,133 @@ entity dp_packet_unmerge is snk_out : out t_dp_siso; snk_in : in t_dp_sosi; - src_in : in t_dp_siso; + 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 + -- Internal state of logic function + type t_reg is record + pkt_cnt : natural range 0 to g_nof_pkt_max + 1; + val_cnt : natural range 0 to g_pkt_len + 1; + src_out : t_dp_sosi; + end record; + + constant c_reg_rst : t_reg := (0, 0, c_dp_sosi_rst); + + signal r : t_reg; + signal d : t_reg; + begin - -- Temporary void component that assigns output = input - snk_out <= src_in; - src_out <= snk_in; + -- Map logic function outputs to entity outputs + 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) + variable v : t_reg; + begin + -- Default + v := r; + + -- Function + -- _ _ _ + -- 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; + + -- 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; + if v.val_cnt = g_pkt_len - 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; diff --git a/libraries/base/dp/tb/vhdl/tb_dp_packet_merge.vhd b/libraries/base/dp/tb/vhdl/tb_dp_packet_merge.vhd index 6466f7e36b7f408b3d2687d849b74e35654a6358..42624ba3ea0b2b1778240d19a80e9e19e62df7b4 100644 --- a/libraries/base/dp/tb/vhdl/tb_dp_packet_merge.vhd +++ b/libraries/base/dp/tb/vhdl/tb_dp_packet_merge.vhd @@ -21,7 +21,7 @@ ------------------------------------------------------------------------------- -- Purpose: --- . Test bench for dp_packet_merge and dp_packet_unmerge +-- . Test bench for dp_packet_merge -- Description: -- . The p_stimuli_st uses proc_dp_gen_block_data to generate g_nof_repeat packets for the DUT. The output of the DUT needs -- to be similar e.g. by means of an inverse DUT component so that the proc_dp_verify_* procedures can be used to verify @@ -29,6 +29,8 @@ -- verify that the test bench has yielded output results at all and that the output valid, sop, eop, and ready fits the -- streaming interface specification. -- . A seperate p_stimuli_mm process may be used to verify the nof_pkt input of the DUT. +-- . Use separate tb_dp_packet_merge_unmerge to verify dp_packet_unmerge, to have easier control +-- over expected output channel, err and empty of dp_packet_unmerge. -- Usage: -- > as 10 -- > run -all @@ -59,6 +61,7 @@ entity tb_dp_packet_merge is g_flow_control_stimuli : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control g_flow_control_verify : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control -- specific + g_pipeline_ready : boolean := true; g_data_w : natural := 4; g_nof_repeat : natural := 24; g_nof_pkt : natural := 3; @@ -67,13 +70,13 @@ entity tb_dp_packet_merge is g_align_at_sync : boolean := false; g_verify_bsn_err : boolean := false; g_bsn_increment : natural := 2; - g_bsn_err_at_pkt_index : natural := 6; -- force wrong snk_in.bsn for packet with this index, in range(g_nof_repeat) - g_use_dp_packet_unmerge : boolean := false + g_bsn_err_at_pkt_index : natural := 6 -- force wrong snk_in.bsn for packet with this index, in range(g_nof_repeat) ); end tb_dp_packet_merge; architecture tb of tb_dp_packet_merge is constant c_rl : natural := 1; + constant c_use_ready : boolean := g_flow_control_verify /= e_active; constant c_pulse_active : natural := 1; constant c_pulse_period : natural := 7; @@ -205,7 +208,7 @@ begin end loop; -- Determine expected sosi field values after end of stimuli - ---- . e_qual (account for merge size g_nof_pkt) + -- . e_qual (account for merge size g_nof_pkt) -- . e_at_least v_sosi.bsn := std_logic_vector( unsigned(c_bsn_init) + c_verify_at_least * g_nof_pkt); v_sosi.channel := TO_DP_CHANNEL(c_channel_init + c_verify_at_least * g_nof_pkt); @@ -297,10 +300,12 @@ begin -- Merge every g_nof_pkt incomming packets into output packets u_dp_packet_merge : entity work.dp_packet_merge generic map ( - g_nof_pkt => g_nof_pkt, - g_align_at_sync => g_align_at_sync, - g_bsn_increment => g_bsn_increment, - g_bsn_err_bi => c_bsn_err_bi + g_use_ready => c_use_ready, + g_pipeline_ready => g_pipeline_ready, + g_nof_pkt => g_nof_pkt, + g_align_at_sync => g_align_at_sync, + g_bsn_increment => g_bsn_increment, + g_bsn_err_bi => c_bsn_err_bi ) port map ( rst => rst, @@ -316,30 +321,8 @@ begin ------------------------------------------------------------------------------ -- Optional reverse DUT dp_packet_unmerge ------------------------------------------------------------------------------ - no_dp_packet_unmerge : if g_use_dp_packet_unmerge = false generate - dp_packet_merge_src_in <= verify_snk_out; - verify_snk_in <= dp_packet_merge_src_out; - end generate; - - gen_dp_packet_unmerge : if g_use_dp_packet_unmerge = true generate - u_dp_packet_unmerge : entity work.dp_packet_unmerge - generic map ( - g_nof_pkt => g_nof_pkt - ) - port map ( - rst => rst, - clk => clk, - - snk_out => dp_packet_merge_src_in, - snk_in => dp_packet_merge_src_out, - - src_in => dp_packet_unmerge_src_in, - src_out => dp_packet_unmerge_src_out - ); - - dp_packet_unmerge_src_in <= verify_snk_out; - verify_snk_in <= dp_packet_unmerge_src_out; - end generate; + dp_packet_merge_src_in <= verify_snk_out; + verify_snk_in <= dp_packet_merge_src_out; -- Map to slv to ease monitoring in wave window stimuli_data <= stimuli_src_out.data(g_data_w - 1 downto 0); diff --git a/libraries/base/dp/tb/vhdl/tb_dp_packet_merge_unmerge.vhd b/libraries/base/dp/tb/vhdl/tb_dp_packet_merge_unmerge.vhd new file mode 100644 index 0000000000000000000000000000000000000000..24e34cbaf24916cfd18356d48c1a80dfd0351acd --- /dev/null +++ b/libraries/base/dp/tb/vhdl/tb_dp_packet_merge_unmerge.vhd @@ -0,0 +1,333 @@ +-- -------------------------------------------------------------------------- +-- Copyright 2023 +-- 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: E. Kooistra +-- Purpose: +-- . Test bench for dp_packet_merge and dp_packet_unmerge +-- Description: +-- . The p_stimuli_st uses proc_dp_gen_block_data to generate g_nof_repeat packets for the DUT. The output of the DUT needs +-- to be similar e.g. by means of an inverse DUT component so that the proc_dp_verify_* procedures can be used to verify +-- that the counter data in the sosi data fields is passed on correctly. Furthermore the proc_dp_verify_* procedures +-- verify that the test bench has yielded output results at all and that the output valid, sop, eop, and ready fits the +-- streaming interface specification. +-- Usage: +-- > as 10 +-- > run -all +-- + +library IEEE, common_lib; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use common_lib.common_pkg.all; +use common_lib.common_lfsr_sequences_pkg.all; +use common_lib.tb_common_pkg.all; +use work.dp_stream_pkg.all; +use work.tb_dp_pkg.all; + +entity tb_dp_packet_merge_unmerge is + generic ( + -- general + -- . always active, random or pulse flow control + g_flow_control_stimuli : t_dp_flow_control_enum := e_active; + g_flow_control_verify : t_dp_flow_control_enum := e_active; + -- specific + g_pipeline_ready : boolean := true; + g_data_w : natural := 16; + g_nof_repeat : natural := 24; + g_nof_pkt : natural := 3; + g_pkt_len : natural := 29; + g_pkt_gap : natural := 0; + g_bsn_increment : natural := 0 + ); +end tb_dp_packet_merge_unmerge; + +architecture tb of tb_dp_packet_merge_unmerge is + constant c_rl : natural := 1; + constant c_use_ready : boolean := g_flow_control_verify /= e_active; + + constant c_pulse_active : natural := 1; + constant c_pulse_period : natural := 7; + + constant c_sync_period : natural := 10; + constant c_sync_offset : natural := 7; + + constant c_data_max : unsigned(g_data_w - 1 downto 0) := (others => '1'); + constant c_data_init : integer := -1; + constant c_bsn_init : std_logic_vector(c_dp_stream_bsn_w - 1 downto 0) := X"0000000000000000"; -- X"0877665544332211" + + constant c_nof_pkt_not_zero : natural := sel_a_b(g_nof_pkt = 0, 1, g_nof_pkt); + constant c_nof_merged_sop : natural := sel_a_b(g_nof_pkt = 0, 0, ceil_div(g_nof_repeat, c_nof_pkt_not_zero)); + -- verify that at least some packets have been merged, not exact to allow variation by p_stimuli_mm + constant c_verify_at_least : natural := largest(1,c_nof_merged_sop / 2); + + signal tb_end : std_logic := '0'; + signal clk : std_logic := '1'; + signal rst : std_logic := '1'; + signal sl1 : std_logic := '1'; + + -- use different slv lengths to have different random sequences + signal random_0 : std_logic_vector(14 downto 0) := (others => '0'); + signal random_1 : std_logic_vector(15 downto 0) := (others => '0'); + signal pulse_0 : std_logic; + signal pulse_1 : std_logic; + signal pulse_en : std_logic := '1'; + + signal stimuli_en : std_logic := '1'; + signal stimuli_src_in : t_dp_siso; + signal stimuli_src_out : t_dp_sosi; + signal stimuli_data : std_logic_vector(g_data_w - 1 downto 0); + + signal dp_packet_merge_snk_in : t_dp_siso; + signal dp_packet_merge_src_out : t_dp_sosi; + signal dp_packet_merge_siso : t_dp_siso; + signal dp_packet_merge_sosi : t_dp_sosi; + + signal prev_verify_snk_out : t_dp_siso; + signal verify_snk_out : t_dp_siso := c_dp_siso_rdy; + signal verify_snk_in : t_dp_sosi; + signal verify_data : std_logic_vector(g_data_w - 1 downto 0); + signal prev_verify_snk_in : t_dp_sosi; + + signal input_pkt_cnt : natural := 0; + signal merged_pkt_cnt : natural := 0; + signal exp_channel : natural := 0; + signal exp_error : natural := 0; + signal exp_empty : natural := 0; + + signal verify_hold_sop : std_logic := '0'; + signal verify_en_valid : std_logic := '0'; + signal verify_en_sop : std_logic := '0'; + signal verify_en_eop : std_logic := '0'; + signal verify_done : std_logic := '0'; + signal verify_value_en : std_logic := sel_a_b(g_nof_pkt = 0, '0', '1'); + + signal expected_verify_snk_in : t_dp_sosi; +begin + clk <= (not clk) or tb_end after clk_period / 2; + rst <= '1', '0' after clk_period * 7; + + random_0 <= func_common_random(random_0) when rising_edge(clk); + random_1 <= func_common_random(random_1) when rising_edge(clk); + + proc_common_gen_duty_pulse(c_pulse_active, c_pulse_period, '1', rst, clk, pulse_en, pulse_0); + proc_common_gen_duty_pulse(c_pulse_active, c_pulse_period + 1, '1', rst, clk, pulse_en, pulse_1); + + ------------------------------------------------------------------------------ + -- STREAM CONTROL + ------------------------------------------------------------------------------ + + stimuli_en <= '1' when g_flow_control_stimuli = e_active else + random_0(random_0'high) when g_flow_control_stimuli = e_random else + pulse_0 when g_flow_control_stimuli = e_pulse; + + verify_snk_out.ready <= '1' when g_flow_control_verify = e_active else + random_1(random_1'high) when g_flow_control_verify = e_random else + pulse_1 when g_flow_control_verify = e_pulse; + + ------------------------------------------------------------------------------ + -- DATA GENERATION + ------------------------------------------------------------------------------ + + -- Generate data path input data + p_stimuli_st : process + constant c_channel : natural := 0; + constant c_err : natural := 0; + variable v_sosi : t_dp_sosi := c_dp_sosi_rst; + variable v_bsn : std_logic_vector(c_dp_stream_bsn_w - 1 downto 0); + begin + -- Adjust initial sosi field values by -1 to compensate for auto increment + v_sosi.bsn := INCR_UVEC(c_bsn_init, -1); + v_sosi.data := INCR_UVEC(TO_DP_DATA(c_data_init), -1); + + stimuli_src_out <= c_dp_sosi_rst; + proc_common_wait_until_low(clk, rst); + proc_common_wait_some_cycles(clk, 5); + + -- Generate c_nof_repeat packets + for I in 0 to g_nof_repeat - 1 loop + -- Auto increment v_sosi field values for this packet + v_sosi.bsn := INCR_UVEC(v_sosi.bsn, g_bsn_increment); + -- insert sync starting at BSN = c_sync_offset and with period c_sync_period + v_sosi.sync := sel_a_b((unsigned(v_sosi.bsn) mod c_sync_period) = c_sync_offset, '1', '0'); + v_sosi.data := INCR_UVEC(v_sosi.data, g_pkt_len); + v_sosi.data := RESIZE_DP_DATA(v_sosi.data(g_data_w - 1 downto 0)); -- wrap when >= 2**g_data_w + + -- Send packet + proc_dp_gen_block_data(g_data_w, TO_UINT(v_sosi.data), g_pkt_len, + c_channel, c_err, v_sosi.sync, v_sosi.bsn, + clk, stimuli_en, stimuli_src_in, stimuli_src_out); + + -- Insert optional gap between the packets + proc_common_wait_some_cycles(clk, g_pkt_gap); + end loop; + + -- Determine expected sosi field values after end of stimuli + -- . e_qual (account for merge size g_nof_pkt) + -- . e_at_least + v_sosi.bsn := std_logic_vector(unsigned(c_bsn_init) + c_verify_at_least * g_nof_pkt); + + -- . account for g_pkt_len + v_sosi.data := INCR_UVEC(v_sosi.data, g_pkt_len - 1); + v_sosi.data := RESIZE_DP_DATA(v_sosi.data(g_data_w - 1 downto 0)); -- wrap when >= 2**g_data_w + expected_verify_snk_in <= v_sosi; + + -- Signal end of stimuli + -- . latency from stimuli to verify depends on the flow control, so wait + -- sufficiently long for last packet to have passed through + proc_common_wait_some_cycles(clk, 100); + if verify_value_en = '1' then + proc_common_gen_pulse(clk, verify_done); + end if; + proc_common_wait_some_cycles(clk, 50); + tb_end <= '1'; + wait; + end process; + + ------------------------------------------------------------------------------ + -- DATA VERIFICATION + ------------------------------------------------------------------------------ + + -- Start verify after first valid, sop or eop + verify_en_valid <= '1' when verify_snk_in.valid = '1' and rising_edge(clk); + verify_en_sop <= '1' when verify_snk_in.sop = '1' and rising_edge(clk); + verify_en_eop <= '1' when verify_snk_in.eop = '1' and rising_edge(clk); + + -- Verify that the stimuli have been applied at all + proc_dp_verify_value("verify_snk_in.valid", clk, verify_done, sl1, verify_en_valid); + proc_dp_verify_value("verify_snk_in.sop", clk, verify_done, sl1, verify_en_sop); + proc_dp_verify_value("verify_snk_in.eop", clk, verify_done, sl1, verify_en_eop); + proc_dp_verify_value("verify_snk_in.data", e_equal, clk, verify_done, expected_verify_snk_in.data, verify_snk_in.data); + proc_dp_verify_value("verify_snk_in.bsn", e_at_least, clk, verify_done, expected_verify_snk_in.bsn, verify_snk_in.bsn); + + -- Verify that the output is incrementing data, like the input stimuli + proc_dp_verify_data("verify_snk_in.data", c_rl, c_data_max, c_unsigned_1, + clk, verify_en_valid, verify_snk_out.ready, + verify_snk_in.valid, verify_snk_in.data, prev_verify_snk_in.data); + + -- Verify that the output is incrementing BSN, like the input stimuli + proc_dp_verify_data("verify_snk_in.bsn", c_rl, c_unsigned_0, c_unsigned_0, + clk, verify_en_sop, verify_snk_out.ready, + verify_snk_in.sop, verify_snk_in.bsn, prev_verify_snk_in.bsn); + + -- Verify output packet ctrl + proc_dp_verify_sop_and_eop(clk, verify_snk_in.valid, verify_snk_in.sop, verify_snk_in.eop, verify_hold_sop); + + -- Verify output ready latency + proc_dp_verify_valid(clk, verify_en_valid, verify_snk_out.ready, prev_verify_snk_out.ready, verify_snk_in.valid); + + -- Verify output verify_snk_in.sosi fields + p_verify_fields : process(clk) + begin + if rising_edge(clk) then + -- Use verify_snk_in.valid, because the assumption with dp_packet_unmerge + -- is that the channel (at sop) and err, empty (at eop) are valid during + -- the entire unmerged packet. + if verify_snk_in.valid = '1' then + -- Verify that output channel yields index of unmerged packet, in range(g_nof_pkt) + assert unsigned(verify_snk_in.channel) = exp_channel + report "Wrong unmerged verify_snk_in.channel" + severity ERROR; + + -- Verify that output error is same per g_nof_pkt unmerged packets + assert unsigned(verify_snk_in.err) = exp_error + report "Wrong unmerged verify_snk_in.err" + severity ERROR; + + -- Verify that output empty is same per g_nof_pkt unmerged packets + assert unsigned(verify_snk_in.empty) = exp_empty + report "Wrong unmerged verify_snk_in.empty" + severity ERROR; + end if; + end if; + end process; + + ------------------------------------------------------------------------------ + -- DUT dp_packet_merge + ------------------------------------------------------------------------------ + + -- Merge every g_nof_pkt incomming packets into output packets + u_dp_packet_merge : entity work.dp_packet_merge + generic map ( + g_nof_pkt => g_nof_pkt, + g_align_at_sync => false, -- not used in this tb + g_bsn_increment => g_bsn_increment, + g_bsn_err_bi => 0 -- not used in this tb + ) + port map ( + rst => rst, + clk => clk, + + snk_out => stimuli_src_in, + snk_in => stimuli_src_out, + + src_in => dp_packet_merge_snk_in, + src_out => dp_packet_merge_src_out + ); + + dp_packet_merge_snk_in <= dp_packet_merge_siso; + + ------------------------------------------------------------------------------ + -- TB stimuli for dp_packet_unmerge + ------------------------------------------------------------------------------ + p_stimuli_unmerge : process(dp_packet_merge_src_out) + begin + dp_packet_merge_sosi <= dp_packet_merge_src_out; + -- Use counter as err field stimulus, per merged packet. Use + -- offset to distinguish err and empty values + dp_packet_merge_sosi.err <= TO_DP_ERROR(merged_pkt_cnt); + dp_packet_merge_sosi.empty <= TO_DP_EMPTY(merged_pkt_cnt + 1); + end process; + + ------------------------------------------------------------------------------ + -- TB signals for verification of dp_packet_unmerge + ------------------------------------------------------------------------------ + input_pkt_cnt <= input_pkt_cnt + 1 when rising_edge(clk) and stimuli_src_out.eop = '1'; + merged_pkt_cnt <= merged_pkt_cnt + 1 when rising_edge(clk) and dp_packet_merge_src_out.eop = '1'; + + -- get expected value, aligned with output packet boundaries + exp_channel <= input_pkt_cnt mod g_nof_pkt when rising_edge(clk) and verify_snk_in.eop = '1'; + exp_error <= merged_pkt_cnt when rising_edge(clk) and verify_snk_in.eop = '1'; + exp_empty <= exp_error + 1; + + ------------------------------------------------------------------------------ + -- Optional reverse DUT dp_packet_unmerge + ------------------------------------------------------------------------------ + u_dp_packet_unmerge : entity work.dp_packet_unmerge + generic map ( + g_use_ready => c_use_ready, + g_pipeline_ready => g_pipeline_ready, + g_nof_pkt_max => g_nof_pkt, + g_pkt_len => g_pkt_len, + g_bsn_increment => g_bsn_increment + ) + port map ( + rst => rst, + clk => clk, + + snk_out => dp_packet_merge_siso, + snk_in => dp_packet_merge_sosi, + + src_in => verify_snk_out, + src_out => verify_snk_in + ); + + -- Map to slv to ease monitoring in wave window + stimuli_data <= stimuli_src_out.data(g_data_w - 1 downto 0); + verify_data <= verify_snk_in.data(g_data_w - 1 downto 0); +end tb; diff --git a/libraries/base/dp/tb/vhdl/tb_tb_dp_packet_merge.vhd b/libraries/base/dp/tb/vhdl/tb_tb_dp_packet_merge.vhd index 46fbd725ba93af2716dab57266cc4a6f73bb8af1..8ae19f6b60bc83899124ef30cf2f886af2627a20 100644 --- a/libraries/base/dp/tb/vhdl/tb_tb_dp_packet_merge.vhd +++ b/libraries/base/dp/tb/vhdl/tb_tb_dp_packet_merge.vhd @@ -27,7 +27,7 @@ use dp_lib.tb_dp_pkg.all; -- Purpose: Verify multiple variations of tb_dp_packet_merge -- Description: -- Usage: --- > as 3 +-- > as 8, to see that correct instances are generated -- > run -all entity tb_tb_dp_packet_merge is @@ -41,6 +41,7 @@ begin -- g_flow_control_stimuli : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control -- g_flow_control_verify : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control -- -- specific + -- g_pipeline_ready : boolean := true; -- g_data_w : natural := 4; -- g_nof_repeat : natural := 20; -- g_nof_pkt : natural := 3; @@ -50,29 +51,29 @@ begin -- g_verify_bsn_err : boolean := false; -- g_bsn_increment : natural := 0; -- g_bsn_err_at_pkt_index : natural := 3; - -- g_use_dp_packet_unmerge : boolean := false - u_act_act_8_nof_0 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 0, 29, 0, false, false, 1, 0, false); - u_act_act_8_nof_1 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 1, 29, 0, false, false, 1, 0, false); - u_act_act_8_nof_2 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 2, 29, 0, false, false, 1, 0, false); - u_act_act_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 3, 29, 0, false, false, 1, 0, false); - u_act_act_8_nof_4 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 4, 29, 0, false, false, 1, 0, false); - u_act_act_8_nof_5 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 5, 29, 0, false, false, 1, 0, false); - u_act_act_8_nof_6 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 6, 29, 0, false, false, 1, 0, false); - u_act_act_8_nof_7 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 7, 29, 0, false, false, 1, 0, false); + u_act_act_8_nof_0 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 0, 29, 0, false, false, 1, 0); + u_act_act_8_nof_1 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 1, 29, 0, false, false, 1, 0); + u_act_act_8_nof_2 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 2, 29, 0, false, false, 1, 0); + u_act_act_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 3, 29, 0, false, false, 1, 0); + u_act_act_8_nof_4 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 4, 29, 0, false, false, 1, 0); + u_act_act_8_nof_5 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 5, 29, 0, false, false, 1, 0); + u_act_act_8_nof_6 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 6, 29, 0, false, false, 1, 0); + u_act_act_8_nof_7 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 7, 29, 0, false, false, 1, 0); - u_rnd_act_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_random, e_active, 8, c_nof_repeat, 3, 29, 0, false, false, 2, 0, false); - u_rnd_rnd_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_random, e_random, 8, c_nof_repeat, 3, 29, 0, false, false, 3, 0, false); - u_pls_act_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_pulse, e_active, 8, c_nof_repeat, 3, 29, 0, false, false, 4, 0, false); - u_pls_rnd_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_pulse, e_random, 8, c_nof_repeat, 3, 29, 0, false, false, 5, 0, false); - u_pls_pls_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_pulse, e_pulse, 8, c_nof_repeat, 3, 29, 0, false, false, 6, 0, false); + u_rnd_act_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_random, e_active, true, 8, c_nof_repeat, 3, 29, 0, false, false, 2, 0); + u_rnd_rnd_8_nof_3_comb : entity work.tb_dp_packet_merge generic map ( e_random, e_random, false, 8, c_nof_repeat, 3, 29, 0, false, false, 3, 0); + u_rnd_rnd_8_nof_3_reg : entity work.tb_dp_packet_merge generic map ( e_random, e_random, true, 8, c_nof_repeat, 3, 29, 0, false, false, 3, 0); + u_pls_act_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_pulse, e_active, true, 8, c_nof_repeat, 3, 29, 0, false, false, 4, 0); + u_pls_rnd_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_pulse, e_random, true, 8, c_nof_repeat, 3, 29, 0, false, false, 5, 0); + u_pls_pls_8_nof_3 : entity work.tb_dp_packet_merge generic map ( e_pulse, e_pulse, true, 8, c_nof_repeat, 3, 29, 0, false, false, 6, 0); - u_rnd_act_8_nof_1 : entity work.tb_dp_packet_merge generic map ( e_random, e_active, 8, c_nof_repeat, 1, 29, 0, false, false, 1, 0, false); - u_rnd_act_8_nof_3_gap : entity work.tb_dp_packet_merge generic map ( e_random, e_active, 8, c_nof_repeat, 3, 29, 17, false, false, 1, 0, false); + u_rnd_act_8_nof_1 : entity work.tb_dp_packet_merge generic map ( e_random, e_active, true, 8, c_nof_repeat, 1, 29, 0, false, false, 1, 0); + u_rnd_act_8_nof_3_gap : entity work.tb_dp_packet_merge generic map ( e_random, e_active, true, 8, c_nof_repeat, 3, 29, 17, false, false, 1, 0); - u_act_act_8_nof_3_no_err : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 3, 29, 0, false, true, 0, 10, false); - u_act_act_8_nof_3_err_10 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 3, 29, 0, false, true, 1, 10, false); - u_act_act_8_nof_3_err_11 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 3, 29, 0, false, true, 1, 11, false); - u_act_act_8_nof_3_err_12 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 3, 29, 0, false, true, 1, 12, false); - u_act_act_8_nof_3_err_13 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, 8, c_nof_repeat, 3, 29, 0, false, true, 1, 13, false); + u_act_act_8_nof_3_no_err : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 3, 29, 0, false, true, 0, 10); + u_act_act_8_nof_3_err_10 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 3, 29, 0, false, true, 1, 10); + u_act_act_8_nof_3_err_11 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 3, 29, 0, false, true, 1, 11); + u_act_act_8_nof_3_err_12 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 3, 29, 0, false, true, 1, 12); + u_act_act_8_nof_3_err_13 : entity work.tb_dp_packet_merge generic map ( e_active, e_active, true, 8, c_nof_repeat, 3, 29, 0, false, true, 1, 13); end tb; diff --git a/libraries/base/dp/tb/vhdl/tb_tb_dp_packet_merge_unmerge.vhd b/libraries/base/dp/tb/vhdl/tb_tb_dp_packet_merge_unmerge.vhd new file mode 100644 index 0000000000000000000000000000000000000000..7fd9034f6fb81169d4c7b189a370006c2f78b9d7 --- /dev/null +++ b/libraries/base/dp/tb/vhdl/tb_tb_dp_packet_merge_unmerge.vhd @@ -0,0 +1,70 @@ +-- -------------------------------------------------------------------------- +-- Copyright 2023 +-- 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. +-- +-- ---------------------------------------------------------------------------- + +library IEEE, dp_lib; +use IEEE.std_logic_1164.all; +use dp_lib.tb_dp_pkg.all; + +-- Author: E. Kooistra +-- Purpose: Verify multiple variations of tb_dp_packet_merge_unmerge +-- Description: +-- Usage: +-- > as 8, to see that correct instances are generated +-- > run -all + +entity tb_tb_dp_packet_merge_unmerge is +end tb_tb_dp_packet_merge_unmerge; + +architecture tb of tb_tb_dp_packet_merge_unmerge is + constant c_nof_repeat : natural := 100; + signal tb_end : std_logic := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' +begin + -- -- general + -- g_flow_control_stimuli : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control + -- g_flow_control_verify : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control + -- -- specific + -- g_pipeline_ready : boolean := true; + -- g_data_w : natural := 4; + -- g_nof_repeat : natural := 20; + -- g_nof_pkt : natural := 3; + -- g_pkt_len : natural := 29; + -- g_pkt_gap : natural := 0; + -- g_bsn_increment : natural := 0; + + u_act_act_8_nof_0 : entity work.tb_dp_packet_merge_unmerge generic map ( e_active, e_active, true, 8, c_nof_repeat, 0, 29, 0, 1); + u_act_act_8_nof_1 : entity work.tb_dp_packet_merge_unmerge generic map ( e_active, e_active, true, 8, c_nof_repeat, 1, 29, 0, 1); + u_act_act_8_nof_2 : entity work.tb_dp_packet_merge_unmerge generic map ( e_active, e_active, true, 8, c_nof_repeat, 2, 29, 0, 1); + u_act_act_8_nof_3 : entity work.tb_dp_packet_merge_unmerge generic map ( e_active, e_active, true, 8, c_nof_repeat, 3, 29, 0, 1); + u_act_act_8_nof_4 : entity work.tb_dp_packet_merge_unmerge generic map ( e_active, e_active, true, 8, c_nof_repeat, 4, 29, 0, 1); + u_act_act_8_nof_4_bsn_0 : entity work.tb_dp_packet_merge_unmerge generic map ( e_active, e_active, true, 8, c_nof_repeat, 4, 29, 0, 0); + --u_act_act_8_nof_4_bsn_2 : entity work.tb_dp_packet_merge_unmerge generic map ( e_active, e_active, true, 8, c_nof_repeat, 4, 29, 0, 2); + u_act_act_8_nof_5 : entity work.tb_dp_packet_merge_unmerge generic map ( e_active, e_active, true, 8, c_nof_repeat, 5, 29, 0, 1); + u_act_act_8_nof_6 : entity work.tb_dp_packet_merge_unmerge generic map ( e_active, e_active, true, 8, c_nof_repeat, 6, 29, 0, 1); + u_act_act_8_nof_7 : entity work.tb_dp_packet_merge_unmerge generic map ( e_active, e_active, true, 8, c_nof_repeat, 7, 29, 0, 1); + + u_rnd_act_8_nof_3 : entity work.tb_dp_packet_merge_unmerge generic map ( e_random, e_active, true, 8, c_nof_repeat, 3, 29, 0, 1); + u_rnd_rnd_8_nof_3_comb : entity work.tb_dp_packet_merge_unmerge generic map ( e_random, e_random, false, 8, c_nof_repeat, 3, 29, 0, 1); + u_rnd_rnd_8_nof_3_reg : entity work.tb_dp_packet_merge_unmerge generic map ( e_random, e_random, true, 8, c_nof_repeat, 3, 29, 0, 1); + u_pls_act_8_nof_3 : entity work.tb_dp_packet_merge_unmerge generic map ( e_pulse, e_active, true, 8, c_nof_repeat, 3, 29, 0, 1); + u_pls_rnd_8_nof_3 : entity work.tb_dp_packet_merge_unmerge generic map ( e_pulse, e_random, true, 8, c_nof_repeat, 3, 29, 0, 1); + u_pls_pls_8_nof_3 : entity work.tb_dp_packet_merge_unmerge generic map ( e_pulse, e_pulse, true, 8, c_nof_repeat, 3, 29, 0, 1); + + u_rnd_act_8_nof_1 : entity work.tb_dp_packet_merge_unmerge generic map ( e_random, e_active, true, 8, c_nof_repeat, 1, 29, 0, 1); + u_rnd_act_8_nof_3_gap : entity work.tb_dp_packet_merge_unmerge generic map ( e_random, e_active, true, 8, c_nof_repeat, 3, 29, 17, 1); +end tb;