diff --git a/applications/disturb2/designs/disturb2_unb2b_station/src/vhdl/disturb2_unb2b_station.vhd b/applications/disturb2/designs/disturb2_unb2b_station/src/vhdl/disturb2_unb2b_station.vhd index 03625482a6a03f36a216b5df8a359b15d453b7f5..72d54fc63b3a99a8d1146485b3c65b797414165c 100644 --- a/applications/disturb2/designs/disturb2_unb2b_station/src/vhdl/disturb2_unb2b_station.vhd +++ b/applications/disturb2/designs/disturb2_unb2b_station/src/vhdl/disturb2_unb2b_station.vhd @@ -797,8 +797,9 @@ BEGIN ram_st_xsq_cipo => ram_st_xsq_cipo ); + -- Use gn_id = ID MOD 32, so map ID to 0:31 range (c_disturb_W_gn_id = 5) + gn_id <= ID(c_disturb_W_gn_id-1 DOWNTO 0); - gn_id <= ID(c_disturb_W_gn_id-1 DOWNTO 0); ----------------------------------------------------------------------------- -- disturb nodes ----------------------------------------------------------------------------- diff --git a/applications/disturb2/libraries/disturb/src/vhdl/disturb_info.vhd b/applications/disturb2/libraries/disturb/src/vhdl/disturb_info.vhd index fb50b588a5d8195e86af0af60abdc43bcba33b16..87c09eda8b0fa95f9dd8a4975722bf9c3bf6de77 100644 --- a/applications/disturb2/libraries/disturb/src/vhdl/disturb_info.vhd +++ b/applications/disturb2/libraries/disturb/src/vhdl/disturb_info.vhd @@ -52,12 +52,11 @@ ENTITY disturb_info IS reg_miso : OUT t_mem_miso; -- inputs from other blocks - gn_index : IN NATURAL; f_adc : IN STD_LOGIC; fsub_type : IN STD_LOGIC; -- disturb info - disturb_info : OUT t_disturb_info + disturb_info : OUT t_disturb_info ); END disturb_info; diff --git a/applications/disturb2/libraries/disturb/src/vhdl/disturb_pkg.vhd b/applications/disturb2/libraries/disturb/src/vhdl/disturb_pkg.vhd index 2350b25a56aa602a08c12078e835404c5b577b15..4bee2f44c09e63117a5ca790d51fcb437448a06f 100644 --- a/applications/disturb2/libraries/disturb/src/vhdl/disturb_pkg.vhd +++ b/applications/disturb2/libraries/disturb/src/vhdl/disturb_pkg.vhd @@ -97,7 +97,7 @@ PACKAGE disturb_pkg is CONSTANT c_disturb_W_crosslet : NATURAL := 16; CONSTANT c_disturb_W_beamlet_sum : NATURAL := 18; CONSTANT c_disturb_W_beamlet : NATURAL := 8; - CONSTANT c_disturb_W_gn_id : NATURAL := 5; + CONSTANT c_disturb_W_gn_id : NATURAL := 5; -- Use gn_id = ID MOD 32, so map ID to 0:31 range (2**5 = 32) CONSTANT c_disturb_W_statistic : NATURAL := 64; CONSTANT c_disturb_W_statistic_sz : NATURAL := 2; -- = c_disturb_W_statistic / c_word_w CONSTANT c_disturb_W_sub_weight : NATURAL := 16; -- = w in s(w, p), s = signed @@ -202,8 +202,9 @@ PACKAGE disturb_pkg is -- https://git.astron.nl/desp/hdl/-/blob/master/boards/uniboard2b/libraries/unb2b_board/src/vhdl/ctrl_unb2b_board.vhd -- Can use same offload time for all statistics, because 1GbE mux will combine them + -- see https://support.astron.nl/confluence/display/L2M/L3+SDP+Testing+Notebook%3A+Statistics+offload --CONSTANT c_disturb_offload_time : NATURAL := 13000; -- from wave window 62855nS / 5nS = 12571 cycles. - CONSTANT c_disturb_offload_time : NATURAL := 600000; -- see L2SDP-452 + CONSTANT c_disturb_offload_time : NATURAL := 600000; -- 600000 * 5 ns = 3 ms, so gn 31 starts after 93 ms -- packet lengths, see ICD SC-SDP CONSTANT c_disturb_nof_bytes_per_statistic : NATURAL := 8; -- c_disturb_W_statistic_sz * c_word_sz = 2 * 4 = 8 diff --git a/applications/disturb2/libraries/disturb/src/vhdl/disturb_station.vhd b/applications/disturb2/libraries/disturb/src/vhdl/disturb_station.vhd index 6edc080c1883c354ea5dd80a5b1bf66b6a6cd1a8..2cc4854186555d125ae9d265cf255445739a02a9 100644 --- a/applications/disturb2/libraries/disturb/src/vhdl/disturb_station.vhd +++ b/applications/disturb2/libraries/disturb/src/vhdl/disturb_station.vhd @@ -431,6 +431,9 @@ ARCHITECTURE str OF disturb_station IS SIGNAL disturb_info : t_disturb_info := c_disturb_info_rst; SIGNAL ring_info : t_ring_info; + SIGNAL gn_index : NATURAL := 0; -- range 0:31 (c_disturb_W_gn_id = 5) + SIGNAL this_rn : STD_LOGIC_VECTOR(c_byte_w-1 DOWNTO 0); + ---------------------------------------------- -- BF ---------------------------------------------- @@ -561,12 +564,16 @@ ARCHITECTURE str OF disturb_station IS SIGNAL bst_udp_src_port : STD_LOGIC_VECTOR(c_network_udp_port_w-1 DOWNTO 0); SIGNAL xst_udp_src_port : STD_LOGIC_VECTOR(c_network_udp_port_w-1 DOWNTO 0); + SIGNAL disturb_info : t_disturb_info := c_disturb_info_rst; + SIGNAL ring_info : t_ring_info; + BEGIN + gn_index <= TO_UINT(gn_id); + ----------------------------------------------------------------------------- -- SDP Info register ----------------------------------------------------------------------------- - gn_index <= TO_UINT(gn_id); -- derive MAC, IP and UDP Port cep_eth_src_mac <= c_disturb_cep_eth_src_mac_47_16 & RESIZE_UVEC(this_bck_id, c_byte_w) & RESIZE_UVEC(this_chip_id, c_byte_w); -- Simply use chip_id since we only use 1 of the 6*4 = 24 10GbE port. cep_ip_src_addr <= c_disturb_cep_ip_src_addr_31_16 & RESIZE_UVEC(this_bck_id, c_byte_w) & INCR_UVEC(RESIZE_UVEC(this_chip_id, c_byte_w), 1); -- +1 to avoid IP = *.*.*.0 @@ -591,7 +598,6 @@ BEGIN reg_miso => reg_disturb_info_cipo, -- inputs from other blocks - gn_index => gn_index, f_adc => c_f_adc, fsub_type => c_fsub_type, @@ -603,6 +609,9 @@ BEGIN -- Ring info ----------------------------------------------------------------------------- u_ring_info : ENTITY ring_lib.ring_info + GENERIC MAP ( + g_ring_info_O_rn_w => c_disturb_W_gn_id -- wrap O_rn to gn_index range + ) PORT MAP ( mm_rst => mm_rst, mm_clk => mm_clk, diff --git a/applications/disturb2/libraries/disturb/tb/vhdl/tb_disturb_info.vhd b/applications/disturb2/libraries/disturb/tb/vhdl/tb_disturb_info.vhd index d73b62312ae3d9e2bfd8ff35ad65fa77c925d475..eb3706b84b91e7187117f873714a51f846b0de89 100644 --- a/applications/disturb2/libraries/disturb/tb/vhdl/tb_disturb_info.vhd +++ b/applications/disturb2/libraries/disturb/tb/vhdl/tb_disturb_info.vhd @@ -72,7 +72,6 @@ ARCHITECTURE tb OF tb_disturb_info IS SIGNAL reg_miso : t_mem_miso; -- signals used to change settings of disturb_info - SIGNAL gn_index : NATURAL := 15; SIGNAL f_adc : STD_LOGIC := '0'; SIGNAL fsub_type : STD_LOGIC := '0'; @@ -225,7 +224,6 @@ BEGIN reg_mosi => reg_mosi, reg_miso => reg_miso, - gn_index => gn_index, f_adc => f_adc, fsub_type => fsub_type, diff --git a/applications/lofar2/designs/lofar2_unb2b_ring/src/vhdl/lofar2_unb2b_ring.vhd b/applications/lofar2/designs/lofar2_unb2b_ring/src/vhdl/lofar2_unb2b_ring.vhd index 8d69605e8dea2e6d33de389dbcc854503753b70f..d6d240b2e8f67554150a3daab9bf2d7684bbe314 100644 --- a/applications/lofar2/designs/lofar2_unb2b_ring/src/vhdl/lofar2_unb2b_ring.vhd +++ b/applications/lofar2/designs/lofar2_unb2b_ring/src/vhdl/lofar2_unb2b_ring.vhd @@ -750,6 +750,7 @@ BEGIN ring_info => ring_info ); + -- Use full c_byte_w range of ID for gn_index and ring_info.O_rn gn_index <= TO_UINT(ID); this_rn <= TO_UVEC(gn_index - TO_UINT(ring_info.O_rn), c_byte_w) WHEN rising_edge(dp_clk); -- Using register to ease timing closure. diff --git a/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/lofar2_unb2b_sdp_station.vhd b/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/lofar2_unb2b_sdp_station.vhd index ed8aa99838b2caecf441bcca6ed632ee80d56b38..a5f605b3520af98c62aa61a552bad970cc807c3a 100644 --- a/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/lofar2_unb2b_sdp_station.vhd +++ b/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/lofar2_unb2b_sdp_station.vhd @@ -797,7 +797,9 @@ BEGIN ); - gn_id <= ID(c_sdp_W_gn_id-1 DOWNTO 0); + -- Use gn_id = ID MOD 32, so map ID to 0:31 range (c_sdp_W_gn_id = 5) + gn_id <= ID(c_sdp_W_gn_id-1 DOWNTO 0); + ----------------------------------------------------------------------------- -- sdp nodes ----------------------------------------------------------------------------- diff --git a/applications/lofar2/designs/lofar2_unb2c_ring/src/vhdl/lofar2_unb2c_ring.vhd b/applications/lofar2/designs/lofar2_unb2c_ring/src/vhdl/lofar2_unb2c_ring.vhd index ef5867134bb4f7251ee7b4fb0a0ae828cac3bdb4..11260553a0c6d9c0f8c4ebf6b505072db71ab067 100644 --- a/applications/lofar2/designs/lofar2_unb2c_ring/src/vhdl/lofar2_unb2c_ring.vhd +++ b/applications/lofar2/designs/lofar2_unb2c_ring/src/vhdl/lofar2_unb2c_ring.vhd @@ -716,6 +716,7 @@ BEGIN ring_info => ring_info ); + -- Use full c_byte_w range of ID for gn_index and ring_info.O_rn gn_index <= TO_UINT(ID); this_rn <= TO_UVEC(gn_index - TO_UINT(ring_info.O_rn), c_byte_w) WHEN rising_edge(dp_clk); -- Using register to ease timing closure. diff --git a/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/lofar2_unb2c_sdp_station.vhd b/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/lofar2_unb2c_sdp_station.vhd index b4b4601ea9be41cef8a0cf7262862d7bc412fe5f..1ef4c3b4ab9444431c638f49ef5a2d7289010c23 100644 --- a/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/lofar2_unb2c_sdp_station.vhd +++ b/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/lofar2_unb2c_sdp_station.vhd @@ -763,8 +763,9 @@ BEGIN ram_st_xsq_cipo => ram_st_xsq_cipo ); + -- Use gn_id = ID MOD 32, so map ID to 0:31 range (c_sdp_W_gn_id = 5) + gn_id <= ID(c_sdp_W_gn_id-1 DOWNTO 0); - gn_id <= ID(c_sdp_W_gn_id-1 DOWNTO 0); ----------------------------------------------------------------------------- -- sdp nodes ----------------------------------------------------------------------------- diff --git a/applications/lofar2/images/images.txt b/applications/lofar2/images/images.txt index 79e917ae9ba02a24367b4f7471b01c730ded940e..d2f076bcc1a9defbd99df056e7026bbee5e8494f 100644 --- a/applications/lofar2/images/images.txt +++ b/applications/lofar2/images/images.txt @@ -12,6 +12,6 @@ unb2b_minimal-rce6b96eed | 2021-08-26 | P. Donker lofar2_unb2b_sdp_station_full-r9ff51058a | 2022-01-12 | R vd Walle | Old Lofar2 SDP station full design for UniBoard2b without ring. lofar2_unb2b_sdp_station_full-r2c3958e1f | 2022-04-29 | R vd Walle | Lofar2 SDP station full design for UniBoard2b. lofar2_unb2b_sdp_station_full_wg-r70b28ffc3 | 2022-06-15 | R vd Walle | Do not use, has beamlet/subband weight bug, delete when rd3d2b75e1 is OK. -lofar2_unb2b_sdp_station_full_wg-rd3d2b75e1 | 2022-07-14 | R vd Walle | Lofar2 SDP station design without ADC inputs, only WG. Uses dp_clk + dp_pps instead of rx_clk + rx_sysref. +lofar2_unb2b_sdp_station_full_wg-rd3d2b75e1 | 2022-08-16 | R vd Walle | Lofar2 SDP station design without ADC inputs, only WG. Uses dp_clk + dp_pps instead of rx_clk + rx_sysref. lofar2_unb2c_sdp_station_full-r70484fd08 | 2022-04-29 | R vd Walle | Do not use, has beamlet/subband weight bug, delete when rc2b0cb728 is OK. -lofar2_unb2c_sdp_station_full-rc2b0cb728 | 2022-07-14 | R vd Walle | Lofar2 SDP station full design for UniBoard2c. +lofar2_unb2c_sdp_station_full-rc2b0cb728 | 2022-08-16 | R vd Walle | Lofar2 SDP station full design for UniBoard2c. diff --git a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd index b1972f74aa3ad94953a3dcdb1587725903982b5c..e94ce343eca19537f3c2d7b261c7f9e187b5471d 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd @@ -132,11 +132,14 @@ ARCHITECTURE str OF node_sdp_beamformer IS SIGNAL scope_bf_out_sosi_arr : t_dp_sosi_integer_arr(c_sdp_N_pol_bf-1 DOWNTO 0); SIGNAL beamlet_scale : STD_LOGIC_VECTOR(c_sdp_W_beamlet_scale-1 DOWNTO 0); - SIGNAL rn_index : NATURAL RANGE 0 TO c_sdp_N_pn_max-1 := 0; + SIGNAL rn_index : NATURAL RANGE 0 TO c_sdp_N_pn_max-1 := 0; + SIGNAL ref_sync : STD_LOGIC; BEGIN - rn_index <= TO_UINT(SUB_UVEC(gn_id, ring_info.O_rn)) WHEN rising_edge(dp_clk); -- Using register to ease timing closure. + -- Use register to ease timing closure. + rn_index <= TO_UINT(SUB_UVEC(gn_id, ring_info.O_rn)) WHEN rising_edge(dp_clk); + ref_sync <= in_sosi_arr(0).sync WHEN rising_edge(dp_clk); --------------------------------------------------------------- -- Beamlet Subband Select @@ -297,7 +300,7 @@ BEGIN -- Streaming clock domain dp_rst => dp_rst, dp_clk => dp_clk, - ref_sync => mon_bf_udp_sosi.sync, + ref_sync => ref_sync, in_sosi_arr(0) => mon_bf_udp_sosi ); 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 d7467674f8b7a78d568da9ea681077fe0a62cde1..56b2b2c35ba01da8bc9f60d83683b7eed9e28b71 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd @@ -485,8 +485,8 @@ BEGIN sdp_info => sdp_info, weighted_subbands_flag => '1', -- because XSub uses in_sosi_arr = fsub_sosi_arr, so weighted subbands - nof_crosslets => nof_crosslets, - crosslets_info_rec => prev_crosslets_info_rec + nof_crosslets => nof_crosslets, -- from MM + prev_crosslets_info_rec => prev_crosslets_info_rec ); END str; diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_crosslets_subband_select.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_crosslets_subband_select.vhd index 2e9acfb004d6e586a1e032f21cabccc6994b5646..3873d035fc0fd618afe26587fcd3a27ea9f8de0b 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_crosslets_subband_select.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_crosslets_subband_select.vhd @@ -33,9 +33,12 @@ -- The prev_crosslets_info identifies the crosslets that were calculated -- during the previous out_sosi.sync interval, so the XST for those crosslets -- are then pending to be offloaded. --- * The new_interval is active before the first out_sosi.sync and inactive --- before the next out_sosi.sync, so it can be used to know when a new --- sequence of out_sosi.sync intervals starts. +-- * The new_interval is active during the first in_sosi_arr.sync interval. The +-- out_sosi.sync is active one sop period after the in_sosi_arr.sync. Hence +-- the new_interval is active about one block before the first out_sosi.sync +-- and inactive about one block before the next out_sosi.sync, so +-- new_interval can be used to know when a new sequence of out_sosi.sync +-- intervals starts. -- Remark: -- . See L5 SDPFW Design Document: Subband Correlator -- Link: https://support.astron.nl/confluence/pages/viewpage.action?spaceKey=L2M&title=L5+SDPFW+Design+Document%3A+Subband+Correlator diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_info.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_info.vhd index 1e8aa33621e61ec599f4068f2e75a1e0be9bc327..9a81eccb020d86a917b979b79c36702519bd4a86 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_info.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_info.vhd @@ -52,7 +52,6 @@ ENTITY sdp_info IS reg_miso : OUT t_mem_miso; -- inputs from other blocks - gn_index : IN NATURAL; f_adc : IN STD_LOGIC; fsub_type : IN STD_LOGIC; diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd index 1f392030b12ec07086cbf4db03e3f304d44545a5..8435ef4163ca221cb5376635be90f1760e96bfe9 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd @@ -94,7 +94,7 @@ PACKAGE sdp_pkg is CONSTANT c_sdp_W_crosslet : NATURAL := 16; CONSTANT c_sdp_W_beamlet_sum : NATURAL := 18; CONSTANT c_sdp_W_beamlet : NATURAL := 8; - CONSTANT c_sdp_W_gn_id : NATURAL := 5; + CONSTANT c_sdp_W_gn_id : NATURAL := 5; -- Use gn_id = ID MOD 32, so map ID to 0:31 range (2**5 = 32) CONSTANT c_sdp_W_statistic : NATURAL := 64; CONSTANT c_sdp_W_statistic_sz : NATURAL := 2; -- = c_sdp_W_statistic / c_word_w CONSTANT c_sdp_W_sub_weight : NATURAL := 16; -- = w in s(w, p), s = signed @@ -189,8 +189,9 @@ PACKAGE sdp_pkg is -- https://git.astron.nl/desp/hdl/-/blob/master/boards/uniboard2b/libraries/unb2b_board/src/vhdl/ctrl_unb2b_board.vhd -- Can use same offload time for all statistics, because 1GbE mux will combine them + -- see https://support.astron.nl/confluence/display/L2M/L3+SDP+Testing+Notebook%3A+Statistics+offload --CONSTANT c_sdp_offload_time : NATURAL := 13000; -- from wave window 62855nS / 5nS = 12571 cycles. - CONSTANT c_sdp_offload_time : NATURAL := 600000; -- see L2SDP-452 + CONSTANT c_sdp_offload_time : NATURAL := 600000; -- 600000 * 5 ns = 3 ms, so gn 31 starts after 93 ms -- packet lengths, see ICD SC-SDP CONSTANT c_sdp_nof_bytes_per_statistic : NATURAL := 8; -- c_sdp_W_statistic_sz * c_word_sz = 2 * 4 = 8 diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd index 7455fcaf62c5127110fd4ff974b0c1d6a6187b3d..9260356e07c5ae4dc0ae0cb67188bfc601653dc5 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd @@ -65,9 +65,9 @@ ENTITY sdp_station IS dp_clk : IN STD_LOGIC; -- ID - gn_id : IN STD_LOGIC_VECTOR(c_sdp_W_gn_id-1 DOWNTO 0); - this_bck_id : IN STD_LOGIC_VECTOR(6-1 DOWNTO 0); - this_chip_id : IN STD_LOGIC_VECTOR(2-1 DOWNTO 0); + gn_id : IN STD_LOGIC_VECTOR(c_sdp_W_gn_id-1 DOWNTO 0); -- used for udp src port + this_bck_id : IN STD_LOGIC_VECTOR(6-1 DOWNTO 0); -- used for src mac / ip + this_chip_id : IN STD_LOGIC_VECTOR(2-1 DOWNTO 0); -- used for src mac / ip -- Transceiver clocks SA_CLK : IN STD_LOGIC := '0'; -- Clock 10GbE front (qsfp) and ring lines @@ -421,7 +421,7 @@ ARCHITECTURE str OF sdp_station IS CONSTANT c_ring_1_if_offset : NATURAL := 2; -- RING_1 signals are indexed at c_nof_if * I + 2. CONSTANT c_ring_nof_mac : NATURAL := 12; -- Using 9 out of 12 (this is NOT optimized away during synthesis), must match one of the MAC IP variations, e.g. 1, 3, 4, 12, 24, 48 - SIGNAL gn_index : NATURAL := 0; + SIGNAL gn_index : NATURAL := 0; -- range 0:31 (c_sdp_W_gn_id = 5) SIGNAL this_rn : STD_LOGIC_VECTOR(c_byte_w-1 DOWNTO 0); SIGNAL sdp_info : t_sdp_info := c_sdp_info_rst; @@ -559,10 +559,11 @@ ARCHITECTURE str OF sdp_station IS BEGIN + gn_index <= TO_UINT(gn_id); + ----------------------------------------------------------------------------- -- SDP Info register ----------------------------------------------------------------------------- - gn_index <= TO_UINT(gn_id); -- derive MAC, IP and UDP Port cep_eth_src_mac <= c_sdp_cep_eth_src_mac_47_16 & RESIZE_UVEC(this_bck_id, c_byte_w) & RESIZE_UVEC(this_chip_id, c_byte_w); -- Simply use chip_id since we only use 1 of the 6*4 = 24 10GbE port. cep_ip_src_addr <= c_sdp_cep_ip_src_addr_31_16 & RESIZE_UVEC(this_bck_id, c_byte_w) & INCR_UVEC(RESIZE_UVEC(this_chip_id, c_byte_w), 1); -- +1 to avoid IP = *.*.*.0 @@ -587,7 +588,6 @@ BEGIN reg_miso => reg_sdp_info_cipo, -- inputs from other blocks - gn_index => gn_index, f_adc => c_f_adc, fsub_type => c_fsub_type, @@ -599,6 +599,9 @@ BEGIN -- Ring info ----------------------------------------------------------------------------- u_ring_info : ENTITY ring_lib.ring_info + GENERIC MAP ( + g_ring_info_O_rn_w => c_sdp_W_gn_id -- wrap O_rn to gn_index range + ) PORT MAP ( mm_rst => mm_rst, mm_clk => mm_clk, diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd index b9815e98407b87317b19f63ed24da1f6c0b7fc98..40a078a306a943b1d30d856ab94704e7f9c16331 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd @@ -84,13 +84,13 @@ -- BST (Xh, Xl), (Yh, Yl), 2 keep X, Y parts order -- XST (Rh, Rl), (Ih, Il), 2 keep Re, Im parts order -- --- . g_P_sq and nof_used_P_sq +-- . g_P_sq and p.nof_used_P_sq -- The g_P_sq defines the number of correlator cells that is available in -- the SDPFW. Use generic to support P_sq = 1 for one node and P_sq = -- c_sdp_P_sq for multiple nodes (with ring). --- The nof_used_P_sq is the number of correlator cells that is actually +-- The p.nof_used_P_sq is the number of correlator cells that is actually -- used and that will output XST packets. Unused correlator cells yield --- zero data that should not be output. The nof_used_P_sq is the smallest +-- zero data that should not be output. The p.nof_used_P_sq is the smallest -- of g_P_sq and ring_info.N_rn/2 + 1. In this way the XST offload can work -- with g_P_sq = 1 when N_rn > 1 and also in a ring with N_rn < N_pn when -- g_P_sq = 9. @@ -158,8 +158,8 @@ ENTITY sdp_statistics_offload IS sdp_info : IN t_sdp_info; weighted_subbands_flag : IN STD_LOGIC := '0'; - nof_crosslets : IN STD_LOGIC_VECTOR(c_sdp_nof_crosslets_reg_w-1 DOWNTO 0) := (OTHERS => '0'); - crosslets_info_rec : IN t_sdp_crosslets_info := c_sdp_crosslets_info_rst + nof_crosslets : IN STD_LOGIC_VECTOR(c_sdp_nof_crosslets_reg_w-1 DOWNTO 0) := (OTHERS => '0'); -- from MM + prev_crosslets_info_rec : IN t_sdp_crosslets_info := c_sdp_crosslets_info_rst ); END sdp_statistics_offload; @@ -185,43 +185,57 @@ ARCHITECTURE str OF sdp_statistics_offload IS CONSTANT c_mm_nof_data : NATURAL := func_sdp_get_stat_from_mm_nof_data(g_statistics_type); CONSTANT c_mm_ram_size : NATURAL := c_mm_nof_data * c_mm_data_size * c_nof_packets_max; - -- offload control + -- Parameters that are fixed per node + TYPE t_parameters IS RECORD + gn_index : NATURAL; -- index of this global node + pn_index : NATURAL; -- index of this node in antenna band + rn_index : NATURAL; -- index of this ring node + local_si_offset : NATURAL; -- index of first signal input on this node + remote_rn : NATURAL; -- index of remote ring node + remote_gn : NATURAL; -- index of remote global node + remote_pn : NATURAL; -- index of remote node in antenna band + remote_si_offset : NATURAL; -- index of first signal input on remote node + nof_cycles_dly : NATURAL; -- trigger_offload delay for this node + offset_rn : NATURAL; -- = ring_info.O_rn, GN index of first ring node + nof_rn : NATURAL; -- = ring_info.N_rn, number of GN in the ring + nof_used_P_sq : NATURAL; -- number of used correlator cells <= g_P_sq (is number of available correlator cells) + END RECORD; + + -- Input capture per sync interval + TYPE t_input IS RECORD + nof_crosslets : NATURAL RANGE 0 TO c_sdp_N_crosslets_max; -- nof_crosslets from MM + nof_packets : NATURAL; -- nof XST offload packets per integration interval dependend on nof_crosslets + bsn_at_sync : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0); + integration_interval : NATURAL; + crosslets_info_rec : t_sdp_crosslets_info; + sop_cnt : NATURAL; + payload_err : STD_LOGIC; + END RECORD; + + -- Offload control TYPE t_reg IS RECORD packet_count : NATURAL RANGE 0 TO c_nof_packets_max; start_address : NATURAL RANGE 0 TO c_mm_ram_size; start_pulse : STD_LOGIC; start_sync : STD_LOGIC; dp_header_info : STD_LOGIC_VECTOR(1023 DOWNTO 0); - payload_err : STD_LOGIC; - in_sop_cnt : NATURAL; - integration_interval : NATURAL; interleave_count : NATURAL RANGE 0 TO c_sdp_Q_fft; interleave_address : NATURAL RANGE 0 TO c_mm_ram_size; crosslet_count : NATURAL RANGE 0 TO c_sdp_N_crosslets_max; instance_count : NATURAL RANGE 0 TO c_sdp_P_sq; instance_address : NATURAL RANGE 0 TO c_mm_ram_size; - nof_crosslets : NATURAL RANGE 0 TO c_sdp_N_crosslets_max; - crosslets_info_rec : t_sdp_crosslets_info; END RECORD; - CONSTANT c_reg_rst : t_reg := (0, 0, '0', '0', (OTHERS => '0'), '0', 0, 0, 0, 0, 0, 0, 0, 0, c_sdp_crosslets_info_rst); - - SIGNAL r : t_reg; - SIGNAL nxt_r : t_reg; - - SIGNAL gn_index_reg : NATURAL; -- index of this global node - SIGNAL pn_index : NATURAL; -- index of this node in antenna band - SIGNAL rn_index : NATURAL; -- index of this ring node - SIGNAL local_si_offset : NATURAL; -- index of first signal input on this node - SIGNAL remote_rn : NATURAL; -- index of remote ring node - SIGNAL remote_gn : NATURAL; -- index of remote global node - SIGNAL remote_pn : NATURAL; -- index of remote node in antenna band - SIGNAL remote_si_offset : NATURAL; -- index of first signal input on remote node - SIGNAL nof_cycles_dly : NATURAL; -- trigger_offload delay for this node - SIGNAL offset_rn : NATURAL; -- = ring_info.O_rn, GN index of first ring node - SIGNAL nof_rn : NATURAL; -- = ring_info.N_rn, number of GN in the ring - SIGNAL nof_used_P_sq : NATURAL; -- number of used correlator cells <= g_P_sq (is number of available correlator cells) - SIGNAL nof_packets : NATURAL; -- nof XST offload packets per integration interval + CONSTANT c_reg_rst : t_reg := (0, 0, '0', '0', (OTHERS => '0'), 0, 0, 0, 0, 0); + + SIGNAL p : t_parameters; + + SIGNAL reg_input : t_input; + SIGNAL prev_input : t_input; + SIGNAL hdr_input : t_input; + + SIGNAL r : t_reg; + SIGNAL nxt_r : t_reg; SIGNAL data_id_rec : t_sdp_stat_data_id; SIGNAL data_id_slv : STD_LOGIC_VECTOR(31 DOWNTO 0) := (OTHERS => '0'); @@ -239,7 +253,6 @@ ARCHITECTURE str OF sdp_statistics_offload IS SIGNAL udp_sosi : t_dp_sosi; - SIGNAL bsn_at_sync : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0) := (OTHERS => '0'); SIGNAL dp_header_info : STD_LOGIC_VECTOR(1023 DOWNTO 0):= (OTHERS => '0'); SIGNAL r_dp_header_sop : STD_LOGIC; SIGNAL r_dp_header_rec : t_sdp_stat_header; @@ -258,8 +271,6 @@ ARCHITECTURE str OF sdp_statistics_offload IS BEGIN - bsn_at_sync <= RESIZE_UVEC(in_sosi.bsn, c_dp_stream_bsn_w) WHEN rising_edge(dp_clk) AND in_sosi.sync = '1'; - ------------------------------------------------------------------------------- -- Assemble offload header info, for data path fields that are selected by: -- c_sdp_stat_hdr_field_sel = "1"&"101"&"111011111001"&"0100"&"0100"&"000000010"&"1000000"&"0" @@ -300,17 +311,17 @@ BEGIN dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id" )) <= sdp_info.nyquist_zone_index; dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc" )) <= SLV(sdp_info.f_adc); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type" )) <= SLV(sdp_info.fsub_type); - dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error" )) <= SLV(r.payload_err); + dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error" )) <= SLV(hdr_input.payload_err); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag" )) <= SLV(sdp_info.beam_repositioning_flag); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_weighted_subbands_flag" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_weighted_subbands_flag" )) <= SLV(weighted_subbands_flag); - dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id" )) <= TO_UVEC(gn_index, 5); - dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_integration_interval" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_integration_interval" )) <= TO_UVEC(r.integration_interval, 24); + dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id" )) <= TO_UVEC(p.gn_index, 5); + dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_integration_interval" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_integration_interval" )) <= TO_UVEC(hdr_input.integration_interval, 24); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_data_id" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_data_id" )) <= data_id_slv; dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs" )) <= TO_UVEC(c_nof_signal_inputs, 8); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_bytes_per_statistic" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_bytes_per_statistic" )) <= TO_UVEC(c_sdp_nof_bytes_per_statistic, 8); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet" )) <= TO_UVEC(c_nof_statistics_per_packet, 16); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_block_period" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_block_period" )) <= sdp_info.block_period; - dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "dp_bsn" )) <= bsn_at_sync; + dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "dp_bsn" )) <= hdr_input.bsn_at_sync; p_reg : PROCESS(dp_rst, dp_clk) BEGIN @@ -321,81 +332,105 @@ BEGIN END IF; END PROCESS; - -- Derive and pipeline dynamic parameters - p_parameters : PROCESS(dp_clk) + -- Derive and pipeline dynamic parameters that depend on node id and MM, but are otherwise fixed + -- . gn_index and ring_info.O_rn are in 0:31 range defined by c_sdp_W_gn_id = 5 + -- . O_rn is first GN index in ring, so O_rn <= gn_index + -- . c_sdp_offload_time = 600000 * 5 ns = 3 ms, so for max gn_index = 31 the + -- offload starts after 93 ms, to just fit within XST T_int min is 100 ms. + p_reg_parameters : PROCESS(dp_clk) BEGIN IF rising_edge(dp_clk) THEN - gn_index_reg <= gn_index; - pn_index <= func_sdp_gn_index_to_pn_index(gn_index_reg); - offset_rn <= TO_UINT(ring_info.O_rn); - rn_index <= gn_index_reg - offset_rn; - local_si_offset <= pn_index * c_sdp_S_pn; - nof_cycles_dly <= gn_index_reg * g_offload_time; - nof_rn <= TO_UINT(ring_info.N_rn); - nof_used_P_sq <= smallest(nof_rn / 2 + 1, g_P_sq); - nof_packets <= func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, nof_used_P_sq, r.nof_crosslets); - remote_rn <= func_ring_nof_hops_to_source_rn(r.instance_count, rn_index, nof_rn, g_crosslets_direction); - remote_gn <= offset_rn + remote_rn; - remote_pn <= func_sdp_gn_index_to_pn_index(remote_gn); - remote_si_offset <= remote_pn * c_sdp_S_pn; + p.gn_index <= gn_index; + p.pn_index <= func_sdp_gn_index_to_pn_index(p.gn_index); + p.offset_rn <= TO_UINT(ring_info.O_rn); + p.rn_index <= p.gn_index - p.offset_rn; + p.local_si_offset <= p.pn_index * c_sdp_S_pn; + p.nof_cycles_dly <= p.gn_index * g_offload_time; + p.nof_rn <= TO_UINT(ring_info.N_rn); + p.nof_used_P_sq <= smallest(p.nof_rn / 2 + 1, g_P_sq); + p.remote_rn <= func_ring_nof_hops_to_source_rn(r.instance_count, p.rn_index, p.nof_rn, g_crosslets_direction); + p.remote_gn <= p.offset_rn + p.remote_rn; + p.remote_pn <= func_sdp_gn_index_to_pn_index(p.remote_gn); + p.remote_si_offset <= p.remote_pn * c_sdp_S_pn; + END IF; + END PROCESS; + + -- Determine info from previous sync interval in which the statistics were + -- measured. + -- . hold nof_crosslets from MM (and related nof_packets) per sync interval + -- . capture bsn_at_sync at start of a sync interval + -- . count number of sop that occur in a sync interval, to determine the + -- actual number of sop for the previous integration_interval + -- . track whether payload errors occur in a sync interval, to determine + -- the payload_err flag for the previous integration_interval + p_input_capture : PROCESS(dp_clk) + BEGIN + IF rising_edge(dp_clk) THEN + -- Capture input + IF in_sosi.sync = '1' THEN + reg_input.nof_crosslets <= TO_UINT(nof_crosslets); + reg_input.bsn_at_sync <= in_sosi.bsn; + reg_input.integration_interval <= reg_input.sop_cnt + 1; -- size = index + 1 + reg_input.crosslets_info_rec <= prev_crosslets_info_rec; + reg_input.sop_cnt <= 0; + reg_input.payload_err <= '0'; + ELSIF in_sosi.sop = '1' THEN + reg_input.sop_cnt <= reg_input.sop_cnt + 1; + ELSIF in_sosi.eop = '1' THEN + reg_input.payload_err <= reg_input.payload_err OR in_sosi.err(0); + END IF; + reg_input.nof_packets <= func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, p.nof_used_P_sq, reg_input.nof_crosslets); + + -- Determine previous sync interval info + -- . just register when the value suits any sync interval + prev_input.nof_crosslets <= reg_input.nof_crosslets; + prev_input.nof_packets <= reg_input.nof_packets; + -- . register at sync to get value for previous sync interval + IF in_sosi.sync = '1' THEN + prev_input.bsn_at_sync <= reg_input.bsn_at_sync; + END IF; + -- . just register when the value already is for the previous sync interval + prev_input.integration_interval <= reg_input.integration_interval; + prev_input.crosslets_info_rec <= reg_input.crosslets_info_rec; + prev_input.sop_cnt <= reg_input.sop_cnt; + prev_input.payload_err <= reg_input.payload_err; + + -- Hold previous sync interval info at trigger_offload, so that + -- hdr_input can be used in p_control_packet_offload and + -- dp_header_info. + IF trigger_offload = '1' THEN + hdr_input <= prev_input; + END IF; END IF; END PROCESS; -- Assign application header data_id for different statistic types, use -- GENERATE to keep unused fields at 0 (all fields are NATURAL, so default to 0). gen_data_id_sst : IF g_statistics_type = "SST" GENERATE - data_id_rec.sst_signal_input_index <= r.packet_count + local_si_offset; + data_id_rec.sst_signal_input_index <= r.packet_count + p.local_si_offset; END GENERATE; gen_data_id_bst : IF g_statistics_type = "BST" GENERATE data_id_rec.bst_beamlet_index <= c_beamlet_id; END GENERATE; gen_data_id_xst : IF g_statistics_type = "XST" GENERATE - data_id_rec.xst_subband_index <= func_sdp_modulo_N_sub(r.crosslets_info_rec.offset_arr(r.crosslet_count)); - data_id_rec.xst_signal_input_A_index <= local_si_offset; - data_id_rec.xst_signal_input_B_index <= remote_si_offset; + data_id_rec.xst_subband_index <= func_sdp_modulo_N_sub(hdr_input.crosslets_info_rec.offset_arr(r.crosslet_count)); + data_id_rec.xst_signal_input_A_index <= p.local_si_offset; + data_id_rec.xst_signal_input_B_index <= p.remote_si_offset; END GENERATE; data_id_slv <= func_sdp_map_stat_data_id(g_statistics_type, data_id_rec); - p_control_packet_offload : PROCESS(r, in_sosi, nof_crosslets, crosslets_info_rec, trigger_offload, dp_sop, dp_header_info, nof_packets) -- in order of appearance + -- sensitivity list in order of appearance + p_control_packet_offload : PROCESS(r, trigger_offload, dp_sop, dp_header_info, reg_input) VARIABLE v : t_reg; VARIABLE v_index : NATURAL; BEGIN v := r; v.start_pulse := '0'; v.start_sync := '0'; - - -- Count number of sop in a sync interval and get payload errors and keep them till next sync. - IF in_sosi.sync = '1' THEN - v.integration_interval := r.in_sop_cnt + 1; -- count = index + 1 - v.in_sop_cnt := 0; - v.payload_err := '0'; - ELSE - IF in_sosi.eop = '1' THEN - v.payload_err := r.payload_err OR in_sosi.err(0); - END IF; - - IF in_sosi.sop = '1' THEN - v.in_sop_cnt := r.in_sop_cnt + 1; - END IF; - END IF; - - -- For XST offload capture nof_crosslets and crosslets_info_rec at in_sosi.sync, - -- to make sure they do not change during packets offload. - -- . The sdp_crosslets_subband_select.vhd in [2] takes care that - -- nof_crosslets and crosslets_info_rec are valid at the xsel_sosi.sync. The - -- mmp_dp_bsn_align_v2 in [2] then aligns the local xsel_sosi with the - -- remote data and passes on the sync. After some latency the sync - -- arrives at the sdp_statistics_offload. This latency is very short - -- compared to the sync period, so the nof_crosslets and crosslets_info_rec - -- are still valid at the in_sosi.sync. - IF in_sosi.sync = '1' THEN - v.nof_crosslets := TO_UINT(nof_crosslets); - v.crosslets_info_rec := crosslets_info_rec; - END IF; - -- The trigger_offload occurs nof_cycles_dly after the in_sosi.sync and the - -- offload will have finished before the next in_sosi.sync, because + -- The trigger_offload occurs p.nof_cycles_dly after the in_sosi.sync and + -- the offload will have finished before the next in_sosi.sync, because -- c_sdp_offload_time is such that all offload will finish within 100 ms -- and the integration interval (= sync interval) is 1 s for SST and BST -- and minimal 0.1s (= c_sdp_xst_nof_clk_per_sync_min) for XST. @@ -428,7 +463,7 @@ BEGIN v.dp_header_info := dp_header_info; -- Start next packets offload. - IF r.packet_count < nof_packets - 1 THEN + IF r.packet_count < reg_input.nof_packets - 1 THEN IF g_statistics_type = "SST" THEN -- step step step step step step -- start_address : 0, 2, 2048, 2050, 4096, 4098, 6144, 6146, 8192, 8194, 10240, 10242 @@ -448,7 +483,8 @@ BEGIN ELSIF g_statistics_type = "XST" THEN -- start_address: - -- nof_crosslets: 1, 2, 3, 4, 5, 6, 7 + -- reg_input.nof_crosslets: + -- 1, 2, 3, 4, 5, 6, 7 -- X_sq instance: -- 0 0, 576, 1152, 1728, 2304, 2880, 3456 -- 1 4096, 4672, 5248, 5824, 6400, 6976, 7552 @@ -461,7 +497,7 @@ BEGIN -- 8 32768, 33344, 33920, 34496, 35072, 35648, 36224 v.start_address := r.start_address + c_sdp_X_sq * c_nof_complex * c_sdp_W_statistic_sz; -- continue with next packet in this instance v.crosslet_count := r.crosslet_count + 1; - IF r.crosslet_count = TO_UINT(nof_crosslets) - 1 THEN + IF r.crosslet_count = reg_input.nof_crosslets - 1 THEN v.start_address := r.instance_address + 2**c_sdp_ram_st_xsq_addr_w; -- jump to first packet in next instance v.crosslet_count := 0; v.instance_count := r.instance_count + 1; @@ -498,7 +534,7 @@ BEGIN reg_enable_mosi => reg_enable_mosi, reg_enable_miso => reg_enable_miso, - delay => nof_cycles_dly, + delay => p.nof_cycles_dly, trigger => in_trigger, trigger_en => trigger_en, trigger_dly => trigger_offload @@ -522,7 +558,7 @@ BEGIN mm_clk => mm_clk, start_pulse => r.start_pulse, sync_in => r.start_sync, - bsn_at_sync => bsn_at_sync, + bsn_at_sync => hdr_input.bsn_at_sync, start_address => r.start_address, mm_mosi => master_mosi, mm_miso => master_miso, @@ -551,7 +587,7 @@ BEGIN src_out => dp_offload_snk_in ); - -- The bsn_at_sync is passed on via r.dp_header_info so that + -- The hdr_input.bsn_at_sync is passed on via r.dp_header_info so that -- u_dp_offload_tx_v3 can put it in the udp_sosi header. -- The dp_offload_snk_in.bsn that is passed on by u_dp_block_from_mm_dc is -- in fact not used, but useful to see in udp_sosi.bsn in the Wave Window. diff --git a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_info.vhd b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_info.vhd index 552b5ee8f28d839ba42a1d43be788724fcc18cb0..d775dbcc8e7f00878b786be46003d6e5674fc46a 100644 --- a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_info.vhd +++ b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_info.vhd @@ -72,7 +72,6 @@ ARCHITECTURE tb OF tb_sdp_info IS SIGNAL reg_miso : t_mem_miso; -- signals used to change settings of sdp_info - SIGNAL gn_index : NATURAL := 15; SIGNAL f_adc : STD_LOGIC := '0'; SIGNAL fsub_type : STD_LOGIC := '0'; @@ -225,7 +224,6 @@ BEGIN reg_mosi => reg_mosi, reg_miso => reg_miso, - gn_index => gn_index, f_adc => f_adc, fsub_type => fsub_type, diff --git a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd index 5298580a65d5c2f3be3ffb750a21a60821eb7e3f..279c2d3243826eeab089e3414dfea26ad4453aab 100644 --- a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd +++ b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd @@ -191,6 +191,7 @@ ARCHITECTURE tb OF tb_sdp_statistics_offload IS SIGNAL rx_sdp_stat_header : t_sdp_stat_header; SIGNAL exp_sdp_stat_header : t_sdp_stat_header; + SIGNAL cur_dp_bsn : NATURAL; SIGNAL exp_dp_bsn : NATURAL; SIGNAL exp_sst_signal_input : NATURAL; SIGNAL exp_bst_beamlet_index : NATURAL; @@ -357,7 +358,8 @@ BEGIN -- verify it at rx_offload_sosi.eop. -- For all statistics - exp_dp_bsn <= c_bsn_init + 1 + rx_sync_cnt * c_nof_block_per_sync; + cur_dp_bsn <= c_bsn_init + 1 + rx_sync_cnt * c_nof_block_per_sync; -- in current sync interval + exp_dp_bsn <= cur_dp_bsn WHEN rising_edge(dp_clk) AND rx_offload_sosi.sync = '1'; -- previous sync interval -- SST exp_sst_signal_input <= rx_packet_cnt + c_sdp_S_pn * gn_index; -- BST @@ -667,7 +669,7 @@ BEGIN weighted_subbands_flag => weighted_subbands_flag, nof_crosslets => c_mm_nof_crosslets, - crosslets_info_rec => in_crosslets_info_rec + prev_crosslets_info_rec => in_crosslets_info_rec ); -- Verify crosslets_info functions diff --git a/libraries/base/common/hdllib.cfg b/libraries/base/common/hdllib.cfg index e42e9b956044f6124596fd2c96dcdba0b073840d..81f4422437440dab0b384a21efbc319818696ad5 100644 --- a/libraries/base/common/hdllib.cfg +++ b/libraries/base/common/hdllib.cfg @@ -191,6 +191,7 @@ test_bench_files = tb/vhdl/tb_common_transpose_symbol.vhd tb/vhdl/tb_common_zip.vhd tb/vhdl/tb_common_variable_delay.vhd + tb/vhdl/tb_common_gcd.vhd tb/vhdl/tb_requantize.vhd tb/vhdl/tb_resize.vhd tb/vhdl/tb_round.vhd @@ -224,6 +225,7 @@ regression_test_vhdl = tb/vhdl/tb_common_shiftreg.vhd tb/vhdl/tb_common_transpose_symbol.vhd tb/vhdl/tb_common_variable_delay.vhd + tb/vhdl/tb_common_gcd.vhd tb/vhdl/tb_tb_resize.vhd tb/vhdl/tb_tb_round.vhd tb/vhdl/tb_requantize.vhd diff --git a/libraries/base/common/src/vhdl/common_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd index ef84bffe9d09ed520781e7108c6212dddc026d18..dd8e2b4c9e2b3864e09fddd1ca1780bee457813b 100644 --- a/libraries/base/common/src/vhdl/common_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_pkg.vhd @@ -205,6 +205,7 @@ PACKAGE common_pkg IS FUNCTION ceil_div( n : UNSIGNED; d: NATURAL) RETURN UNSIGNED; FUNCTION ceil_value( n : UNSIGNED; d: NATURAL) RETURN UNSIGNED; FUNCTION floor_value(n : UNSIGNED; d: NATURAL) RETURN UNSIGNED; + FUNCTION gcd(a, b : NATURAL) RETURN NATURAL; -- greatest common divider FUNCTION slv(n: IN STD_LOGIC) RETURN STD_LOGIC_VECTOR; -- standard logic to 1 element standard logic vector FUNCTION sl( n: IN STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- 1 element standard logic vector to standard logic @@ -719,6 +720,15 @@ PACKAGE BODY common_pkg IS RETURN p(w-1 DOWNTO 0); -- return same width as n END; + FUNCTION gcd(a, b : NATURAL) RETURN NATURAL IS -- greatest common divider + BEGIN + IF b = 0 THEN + RETURN a; + ELSE + RETURN gcd(b, a MOD b); + END IF; + END; + FUNCTION slv(n: IN STD_LOGIC) RETURN STD_LOGIC_VECTOR IS VARIABLE r : STD_LOGIC_VECTOR(0 DOWNTO 0); BEGIN diff --git a/libraries/base/common/tb/vhdl/tb_common_gcd.vhd b/libraries/base/common/tb/vhdl/tb_common_gcd.vhd new file mode 100644 index 0000000000000000000000000000000000000000..2083b0ee588d86468c8a848d55ea37d97e7bed7f --- /dev/null +++ b/libraries/base/common/tb/vhdl/tb_common_gcd.vhd @@ -0,0 +1,50 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2009 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +-- Author: E. Kooistra 2022 +-- Purpose: Test bench for gcd() in common_pkg.vhd +-- Usage: +-- > run -a + +LIBRARY IEEE; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; +USE work.common_pkg.ALL; + +ENTITY tb_common_gcd IS +END tb_common_gcd; + +ARCHITECTURE tb OF tb_common_gcd IS + +BEGIN + + ASSERT gcd( 0, 10) = 10 REPORT "Wrong gcd( 0, 10)" SEVERITY ERROR; + ASSERT gcd( 1, 1) = 1 REPORT "Wrong gcd( 1, 1)" SEVERITY ERROR; + ASSERT gcd(10, 1) = 1 REPORT "Wrong gcd(10, 1)" SEVERITY ERROR; + ASSERT gcd(10, 3) = 1 REPORT "Wrong gcd(10, 3)" SEVERITY ERROR; + ASSERT gcd(10, 4) = 2 REPORT "Wrong gcd(10, 4)" SEVERITY ERROR; + ASSERT gcd(10, 5) = 5 REPORT "Wrong gcd(10, 5)" SEVERITY ERROR; + ASSERT gcd(16, 4) = 4 REPORT "Wrong gcd(16, 4)" SEVERITY ERROR; + ASSERT gcd(15, 5) = 5 REPORT "Wrong gcd(15, 5)" SEVERITY ERROR; + ASSERT gcd(17, 17) = 17 REPORT "Wrong gcd(17, 17)" SEVERITY ERROR; + ASSERT gcd(17, 4) = 1 REPORT "Wrong gcd(17, 4)" SEVERITY ERROR; + +END tb; diff --git a/libraries/base/dp/src/vhdl/dp_bsn_source_reg_v2.vhd b/libraries/base/dp/src/vhdl/dp_bsn_source_reg_v2.vhd index 94535504404868246c757a796885e8c6f53738db..ead48b48a7d79a60e916b27ef1e746237bca17de 100644 --- a/libraries/base/dp/src/vhdl/dp_bsn_source_reg_v2.vhd +++ b/libraries/base/dp/src/vhdl/dp_bsn_source_reg_v2.vhd @@ -67,8 +67,7 @@ ENTITY dp_bsn_source_reg_v2 IS st_nof_clk_per_sync : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); -- nof block per sync st_bsn_init : OUT STD_LOGIC_VECTOR; -- wr init BSN st_current_bsn : IN STD_LOGIC_VECTOR; -- rd current BSN - st_bsn_time_offset : OUT STD_LOGIC_VECTOR; - st_current_bsn_time_offset : IN STD_LOGIC_VECTOR + st_bsn_time_offset : OUT STD_LOGIC_VECTOR ); END dp_bsn_source_reg_v2; @@ -92,16 +91,15 @@ ARCHITECTURE rtl OF dp_bsn_source_reg_v2 IS SIGNAL st_on_ctrl : STD_LOGIC_VECTOR(1 DOWNTO 0); -- = st_on_pps & st_on SIGNAL mm_on_status : STD_LOGIC; - SIGNAL mm_nof_clk_per_sync : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL mm_nof_clk_per_sync : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); SIGNAL mm_bsn_init : STD_LOGIC_VECTOR(c_longword_w-1 DOWNTO 0); SIGNAL mm_bsn_init_wr : STD_LOGIC; SIGNAL mm_current_bsn : STD_LOGIC_VECTOR(c_longword_w-1 DOWNTO 0) := (OTHERS=>'0'); SIGNAL mm_current_bsn_hi : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := (OTHERS=>'0'); - SIGNAL mm_bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0'); - SIGNAL mm_bsn_time_offset_wr : STD_LOGIC; - SIGNAL mm_current_bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0'); + SIGNAL mm_bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0'); + SIGNAL mm_bsn_time_offset_wr : STD_LOGIC; -- Registers in st_clk domain @@ -134,7 +132,7 @@ BEGIN sla_out.rdval <= '0'; -- Access event defaults - mm_bsn_init_wr <= '0'; + mm_bsn_init_wr <= '0'; mm_bsn_time_offset_wr <= '0'; -- Write access: set register value @@ -142,22 +140,22 @@ BEGIN CASE TO_UINT(sla_in.address(c_mm_reg.adr_w-1 DOWNTO 0)) IS -- Write Block Sync WHEN 0 => - mm_on <= sla_in.wrdata(0); - mm_on_pps <= sla_in.wrdata(1); + mm_on <= sla_in.wrdata(0); + mm_on_pps <= sla_in.wrdata(1); WHEN 1 => - mm_nof_clk_per_sync <= sla_in.wrdata(31 DOWNTO 0); + mm_nof_clk_per_sync <= sla_in.wrdata(31 DOWNTO 0); -- Write init BSN WHEN 2 => - mm_bsn_init(31 DOWNTO 0) <= sla_in.wrdata(31 DOWNTO 0); + mm_bsn_init(31 DOWNTO 0) <= sla_in.wrdata(31 DOWNTO 0); WHEN 3 => - mm_bsn_init(63 DOWNTO 32) <= sla_in.wrdata(31 DOWNTO 0); - mm_bsn_init_wr <= '1'; + mm_bsn_init(63 DOWNTO 32) <= sla_in.wrdata(31 DOWNTO 0); + mm_bsn_init_wr <= '1'; -- write bsn_time_offset WHEN 4 => - mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0) <= sla_in.wrdata(c_bsn_time_offset_w-1 DOWNTO 0); - mm_bsn_time_offset_wr <= '1'; + mm_bsn_time_offset <= sla_in.wrdata(c_bsn_time_offset_w-1 DOWNTO 0); + mm_bsn_time_offset_wr <= '1'; WHEN OTHERS => NULL; -- not used MM addresses END CASE; @@ -179,11 +177,11 @@ BEGIN sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn(31 DOWNTO 0); mm_current_bsn_hi <= mm_current_bsn(63 DOWNTO 32); -- first read low part and preserve high part WHEN 3 => - sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn_hi; + sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn_hi; -- then read preserved high part -- Read current bsn_time_offset WHEN 4 => - sla_out.rddata(c_bsn_time_offset_w-1 DOWNTO 0) <= mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0); + sla_out.rddata <= RESIZE_UVEC(mm_bsn_time_offset, c_word_w); WHEN OTHERS => NULL; -- not used MM addresses END CASE; @@ -213,7 +211,7 @@ BEGIN st_on_pps <= mm_on_pps; st_nof_clk_per_sync <= mm_nof_clk_per_sync; st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0); - st_bsn_time_offset <= mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0); + st_bsn_time_offset <= mm_bsn_time_offset; p_st_clk : PROCESS(st_rst, st_clk) BEGIN @@ -222,16 +220,16 @@ BEGIN st_bsn_time_offset <= TO_UVEC(0, c_bsn_time_offset_w); ELSIF rising_edge(st_clk) THEN IF mm_bsn_init_wr='1' THEN - st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0); -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word + -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word + st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0); END IF; IF mm_bsn_time_offset_wr='1' THEN - st_bsn_time_offset <= mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0); -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word + st_bsn_time_offset <= mm_bsn_time_offset; END IF; END IF; END PROCESS; - mm_current_bsn(c_bsn_w-1 DOWNTO 0) <= st_current_bsn; -- MM user may read current_bsn twice to avoid small chance that the high part of the double word changed (i.e. incremented) - mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0) <= st_current_bsn_time_offset; + mm_current_bsn(c_bsn_w-1 DOWNTO 0) <= st_current_bsn; END GENERATE; -- no_cross gen_cross : IF g_cross_clock_domain = TRUE GENERATE @@ -311,8 +309,8 @@ BEGIN PORT MAP ( in_rst => mm_rst, in_clk => mm_clk, - in_new => mm_bsn_time_offset_wr, -- use wr of mm_bsn_time_offset high part for in_new to ensure proper transfer of double word - in_dat => mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0), + in_new => mm_bsn_time_offset_wr, + in_dat => mm_bsn_time_offset, in_done => OPEN, -- pulses when no more pending in_new out_rst => st_rst, out_clk => st_clk, @@ -320,20 +318,6 @@ BEGIN out_new => OPEN ); - -- write occurs with sufficient margin before it is used, still use common_reg_cross_domain nonetheless - u_current_bsn_offset : ENTITY common_lib.common_reg_cross_domain - PORT MAP ( - in_rst => st_rst, - in_clk => st_clk, - in_new => '1', - in_dat => st_current_bsn_time_offset, - in_done => OPEN, -- pulses when no more pending in_new - out_rst => mm_rst, - out_clk => mm_clk, - out_dat => mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0), - out_new => OPEN - ); - END GENERATE; -- gen_cross END rtl; diff --git a/libraries/base/dp/src/vhdl/dp_bsn_source_v2.vhd b/libraries/base/dp/src/vhdl/dp_bsn_source_v2.vhd index 107073c4340d4e0f7ea3aa02911c7454dcac0e03..a2f1756cc82d92e2ca8ee5ba16e20e88321a42d0 100644 --- a/libraries/base/dp/src/vhdl/dp_bsn_source_v2.vhd +++ b/libraries/base/dp/src/vhdl/dp_bsn_source_v2.vhd @@ -19,29 +19,40 @@ -- ------------------------------------------------------------------------------- --- Purpose : +-- Author : P.Donker okt. 2020, added bsn_time_offset +-- E. Kooistra aug 2022, corected s_bsn_time_offset, clarified +-- sync_size_cnt, removed redundant current_bsn_time_offset +-- and s_init, simplified implementation of dp_on_status. +-- +-- Purpose : -- Start a periodic block sync interval and maintain a block sequence --- number +-- number according to the PPS and BSN grid defined in [1]. -- Description: -- When dp_on is low then all outputs are low. When dp_on is high, the -- output sync starts pulsing after bsn_time_offset (in clk cycles) with -- a period of g_block_size number of clk cycles and the output valid, -- sop and eop will be active. --- Alternatively, one can assert dp_on while dp_on_pps is high to --- start the data path on the next PPS. --- The dp_on is asynchronous. The dp_bsn_source_v2 takes care that --- src_out.valid starts with a src_out.sop and that src_out.valid can --- only go low after a src_out.eop, to ensure that src_out only produces --- complete sop-eop blocks that enter the subsequent processing. --- The bs_start is active at the first src_out.sop after dp_on went high. +-- The dp_on is asynchronous. Alternatively, one can assert dp_on while +-- dp_on_pps is high to start the data path on the next PPS. +-- The src_out.sync always happens at the src_out.sop. +-- If nof_clk_per_sync / g_block_size is an integer than all src_out.sync +-- intervals will have nof_clk_per_sync clk cycles, else nof_clk_per_sync +-- is the average number of clock cycles between src_out.sync and then the +-- number of blocks per sync intervals will vary between c_nof_block_hi +-- and c_nof_block_lo. +-- The dp_bsn_source_v2 takes care that src_out.valid starts with a +-- src_out.sync and src_out.sop and that src_out.valid can only go low +-- after a src_out.eop, to ensure that src_out only produces complete +-- sop-eop blocks that enter the subsequent processing. +-- The bs_restart is active at the first src_out.sop after dp_on went high. -- Remarks: -- . Starting the data path is only possible from the dp_off state, so one -- has to disable (dp_on='0') the data path before restarting it. -- . Effectively dp_on_status = src_out.valid, because when the BSN source -- is on, then src_out.valid = '1' at every clk cycle. -- --- author : P.Donker okt. 2020, added bsn_time_offset --- +-- References: +-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station LIBRARY IEEE, common_lib; USE IEEE.STD_LOGIC_1164.ALL; @@ -51,7 +62,7 @@ USE work.dp_stream_pkg.ALL; ENTITY dp_bsn_source_v2 IS GENERIC ( - g_block_size : NATURAL := 256; + g_block_size : NATURAL := 256; -- >= 3, see state machine g_nof_clk_per_sync : NATURAL := 200 * 10**6; g_bsn_w : NATURAL := 48; g_bsn_time_offset_w : NATURAL := 10 @@ -70,7 +81,6 @@ ENTITY dp_bsn_source_v2 IS nof_clk_per_sync : IN STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := TO_UVEC(g_nof_clk_per_sync, c_word_w); bsn_init : IN STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); bsn_time_offset : IN STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0'); - current_bsn_time_offset : OUT STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); -- output for monitoring purpose in test bench. src_out : OUT t_dp_sosi -- only uses sync, bsn[], valid, sop and eop ); @@ -82,21 +92,10 @@ ARCHITECTURE rtl OF dp_bsn_source_v2 IS CONSTANT c_block_size_cnt_w : NATURAL := ceil_log2(g_block_size); CONSTANT c_block_cnt_zero : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS => '0'); - -- The state machine starts synchronously via s_bsn_time_offset, s_dp_on_sop to s_dp_on, or it starts - -- directly to s_dp_on. When in dp_on it loops between s_dp_on and s_dp_on_eop for every block. - -- If the BSN source is switched off, then after completing the block s_dp_on_eop goes back to - -- s_dp_off. - -- - -- Sketch of the state machine: - -- - -- s_init --> s_dp_off --> s_bsn_time_offset -- ------------ (loop) <--- - -- \ \ \ / \ - -- \ ---------------------------> s_dp_on_sop --> s_dp_on --> s_dp_on_eop -- - -- \ / - -- ----------------------------------------------------------------- (off) <--- - + CONSTANT c_nof_block_lo : NATURAL := g_nof_clk_per_sync / g_block_size; + CONSTANT c_nof_block_hi : NATURAL := ceil_div(g_nof_clk_per_sync, g_block_size); - TYPE t_state_enum IS (s_init, s_dp_off, s_bsn_time_offset, s_dp_on_sop, s_dp_on, s_dp_on_eop); + TYPE t_state_enum IS (s_dp_off, s_bsn_time_offset, s_dp_on_sop, s_dp_on, s_dp_on_eop); SIGNAL state : t_state_enum; SIGNAL nxt_state : t_state_enum; @@ -108,144 +107,152 @@ ARCHITECTURE rtl OF dp_bsn_source_v2 IS SIGNAL i_src_out : t_dp_sosi := c_dp_sosi_init; SIGNAL nxt_src_out : t_dp_sosi; - SIGNAL i_dp_on_status : STD_LOGIC; - SIGNAL nxt_dp_on_status : STD_LOGIC; SIGNAL nxt_bs_restart : STD_LOGIC; SIGNAL nxt_bsn_time_offset_cnt : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); SIGNAL bsn_time_offset_cnt : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); - SIGNAL i_current_bsn_time_offset : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); - SIGNAL nxt_current_bsn_time_offset : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); - - SIGNAL nxt_clk_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - SIGNAL clk_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - SIGNAL nxt_sync : STD_LOGIC; - SIGNAL sync : STD_LOGIC; + SIGNAL nxt_sync_size_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL sync_size_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL nxt_sync : STD_LOGIC; + SIGNAL sync : STD_LOGIC; BEGIN src_out <= i_src_out; - dp_on_status <= i_dp_on_status; - current_bsn_time_offset <= i_current_bsn_time_offset; + dp_on_status <= i_src_out.valid; - p_state : PROCESS(state, i_src_out, block_size_cnt, clk_cnt, sync, i_dp_on_status, bsn_time_offset_cnt, bsn_time_offset, nof_clk_per_sync, bsn_init, dp_on, dp_on_pps, pps, prev_state) + p_state : PROCESS(sync, sync_size_cnt, nof_clk_per_sync, + state, i_src_out, block_size_cnt, bsn_time_offset_cnt, + bsn_init, dp_on, dp_on_pps, pps, bsn_time_offset, prev_state) BEGIN + -- Maintain sync_size_cnt for nof_clk_per_sync + -- . nof_clk_per_sync is the number of clk per pps interval and the + -- average number of clk per src_out.sync interval, due to that the + -- src_out.sync has to occur at the src_out.sop of a block. + -- . The sync_size_cnt is started in s_dp_off at the pps + -- . The pps interval is nof_clk_per_sync, so when the sync_size_cnt + -- wraps, then the sync is used to ensure that src_out.sync will + -- pulse at the src_out.sop of the next block. + nxt_sync <= sync; + nxt_sync_size_cnt <= INCR_UVEC(sync_size_cnt, 1); + IF UNSIGNED(sync_size_cnt) = UNSIGNED(nof_clk_per_sync) - 1 THEN + nxt_sync <= '1'; -- will set src_out.sync on next src_out.sop + nxt_sync_size_cnt <= (OTHERS=>'0'); + END IF; + + -- State machine for src_out nxt_state <= state; - nxt_src_out <= i_src_out; + nxt_src_out <= i_src_out; -- hold src_out.bsn nxt_src_out.sync <= '0'; nxt_src_out.valid <= '0'; nxt_src_out.sop <= '0'; nxt_src_out.eop <= '0'; nxt_block_size_cnt <= block_size_cnt; - nxt_clk_cnt <= INCR_UVEC(clk_cnt, 1); - nxt_sync <= sync; - nxt_dp_on_status <= i_dp_on_status; - nxt_bs_restart <= '0'; nxt_bsn_time_offset_cnt <= bsn_time_offset_cnt; - nxt_current_bsn_time_offset <= bsn_time_offset; - - - IF UNSIGNED(clk_cnt) = UNSIGNED(nof_clk_per_sync) - 1 THEN - nxt_clk_cnt <= (OTHERS=>'0'); - nxt_sync <= '1'; -- will set src_out.sync on next src_out.sop - END IF; CASE state IS - - WHEN s_init => - nxt_state <= s_dp_off; - WHEN s_dp_off => - nxt_dp_on_status <= '0'; nxt_src_out.bsn <= RESIZE_DP_BSN(bsn_init); + nxt_bsn_time_offset_cnt <= (OTHERS=>'0'); nxt_sync <= '0'; - nxt_clk_cnt <= (OTHERS=>'0'); - IF dp_on = '1' THEN + nxt_sync_size_cnt <= (OTHERS=>'0'); + nxt_block_size_cnt <= (OTHERS=>'0'); + IF dp_on = '1' THEN + nxt_sync <= '1'; -- ensure issue sync at first sync interval IF dp_on_pps = '1' THEN - nxt_sync <= '1'; -- ensure issue sync at first sync interval for start at PPS. + -- start at pps IF pps = '1' THEN - nxt_bsn_time_offset_cnt <= (OTHERS=>'0'); nxt_state <= s_bsn_time_offset; - END IF; - ELSE - nxt_state <= s_dp_on_sop; + END IF; + ELSE + -- start immediately + nxt_state <= s_dp_on_sop; END IF; END IF; WHEN s_bsn_time_offset => IF UNSIGNED(bsn_time_offset_cnt) = UNSIGNED(bsn_time_offset) THEN + -- The bsn_time_offset can be 0, so IF UNSIGNED(bsn_time_offset)-1 + -- can not be used to account for on clk latency of state + -- s_bsn_time_offset. Therefore do not count sync_size_cnt during + -- latency of state s_bsn_time_offset. + nxt_sync_size_cnt <= sync_size_cnt; nxt_state <= s_dp_on_sop; ELSE nxt_bsn_time_offset_cnt <= INCR_UVEC(bsn_time_offset_cnt, 1); END IF; + -- using separate states s_dp_on_sop and s_dp_on_eop instead of only + -- s_dp_on state and block_size_cnt, cause that g_block_size must be + -- >= 3, but that is fine. WHEN s_dp_on_sop => - nxt_dp_on_status <= '1'; - nxt_src_out.sop <= '1'; + -- Start of block + nxt_src_out.sop <= '1'; nxt_src_out.valid <= '1'; - nxt_state <= s_dp_on; + nxt_state <= s_dp_on; + -- block_size_cnt = 0 at src_out.sop nxt_block_size_cnt <= (OTHERS=>'0'); + -- after first block, increment bsn per block IF prev_state = s_dp_on_eop THEN nxt_src_out.bsn <= INCR_DP_BSN(i_src_out.bsn, 1, g_bsn_w); END IF; - IF sync = '1' THEN + -- check for pending sync + IF sync = '1' THEN nxt_src_out.sync <= '1'; nxt_sync <= '0'; END IF; - IF i_dp_on_status = '0' THEN -- transition from 0 to 1 is a (re)start - nxt_bs_restart <= '1'; -- bs_restart indicates a restart as a pulse on the sop (and sync if dp_on_pps is used). - END IF; WHEN s_dp_on => - nxt_src_out.valid <= '1'; + -- During block + nxt_src_out.valid <= '1'; + -- block_size_cnt increments to determine end of block nxt_block_size_cnt <= INCR_UVEC(block_size_cnt, 1); - IF UNSIGNED(block_size_cnt) = g_block_size -3 THEN - nxt_state <= s_dp_on_eop; + IF UNSIGNED(block_size_cnt) >= g_block_size - 3 THEN + nxt_state <= s_dp_on_eop; END IF; WHEN s_dp_on_eop => - nxt_src_out.eop <= '1'; - nxt_src_out.valid <= '1'; - nxt_state <= s_dp_on_sop; - nxt_block_size_cnt <= INCR_UVEC(block_size_cnt, 1); + -- End of block + nxt_src_out.eop <= '1'; + nxt_src_out.valid <= '1'; + nxt_state <= s_dp_on_sop; + -- block_size_cnt is dont care at at src_out.eop + -- accept dp_off after eop, to avoid fractional blocks IF dp_on = '0' THEN nxt_state <= s_dp_off; END IF; - WHEN OTHERS => -- s_init + WHEN OTHERS => -- reover from undefined state nxt_state <= s_dp_off; - END CASE; END PROCESS; + -- src_out.valid transition from 0 to 1 is a bs_restart, use nxt_bs_restart + -- to have bs_restart at first src_out.sync and src_out.sop. + nxt_bs_restart <= nxt_src_out.valid AND NOT i_src_out.valid; p_clk : PROCESS(rst, clk) BEGIN IF rst='1' THEN - prev_state <= s_init; - state <= s_init; - i_src_out <= c_dp_sosi_rst; - clk_cnt <= (OTHERS=>'0'); - sync <= '0'; - block_size_cnt <= (OTHERS=>'0'); - i_dp_on_status <= '0'; - bs_restart <= '0'; + prev_state <= s_dp_off; + state <= s_dp_off; + i_src_out <= c_dp_sosi_rst; + sync_size_cnt <= (OTHERS=>'0'); + sync <= '0'; + block_size_cnt <= (OTHERS=>'0'); + bs_restart <= '0'; bsn_time_offset_cnt <= (OTHERS=>'0'); ELSIF rising_edge(clk) THEN - prev_state <= state; - state <= nxt_state; - i_src_out <= nxt_src_out; - clk_cnt <= nxt_clk_cnt; - sync <= nxt_sync; - block_size_cnt <= nxt_block_size_cnt; - i_dp_on_status <= nxt_dp_on_status; - bs_restart <= nxt_bs_restart; + prev_state <= state; + state <= nxt_state; + i_src_out <= nxt_src_out; + sync_size_cnt <= nxt_sync_size_cnt; + sync <= nxt_sync; + block_size_cnt <= nxt_block_size_cnt; + bs_restart <= nxt_bs_restart; bsn_time_offset_cnt <= nxt_bsn_time_offset_cnt; - i_current_bsn_time_offset <= nxt_current_bsn_time_offset; END IF; END PROCESS; END rtl; - - diff --git a/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd b/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd index 62e5956eca69267ecf4434ab5fc0570419f82d3a..4ec4b826ef01ff0027fd13a3896710ecaf69dc2e 100644 --- a/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd +++ b/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd @@ -18,7 +18,8 @@ -- -- Author: Eric Kooistra, 30 July 2021 -- Purpose : --- Create programmable sync interval for input stream. +-- Create programmable sync interval for input stream, according to the +-- PPS and BSN grid defined in [1]. -- Description: -- * ctrl_start_bsn: -- The output sync interval starts at an in_sosi.bsn that is programmable via @@ -105,6 +106,9 @@ -- The differences are that: -- . dp_bsn_sync_scheduler requires in_sosi.sync and copies the other in_sosi -- ctrl, info and data, whereas generates bs_sosi.ctrl. +-- +-- References: +-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station LIBRARY IEEE, common_lib; USE IEEE.STD_LOGIC_1164.ALL; diff --git a/libraries/base/dp/src/vhdl/mms_dp_bsn_source_v2.vhd b/libraries/base/dp/src/vhdl/mms_dp_bsn_source_v2.vhd index bc82cda1f4970574aa8da25eee1ecfd57bec1d35..c20fc561b114fd9b1a0e952636baa56d7c5cd62c 100644 --- a/libraries/base/dp/src/vhdl/mms_dp_bsn_source_v2.vhd +++ b/libraries/base/dp/src/vhdl/mms_dp_bsn_source_v2.vhd @@ -70,7 +70,6 @@ ARCHITECTURE str OF mms_dp_bsn_source_v2 IS SIGNAL dp_on_status : STD_LOGIC; SIGNAL capture_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); - SIGNAL current_bsn_time_offset : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); BEGIN @@ -99,8 +98,7 @@ BEGIN st_nof_clk_per_sync => nof_clk_per_sync, st_bsn_init => bsn_init, st_current_bsn => capture_bsn, - st_bsn_time_offset => bsn_time_offset, - st_current_bsn_time_offset => current_bsn_time_offset + st_bsn_time_offset => bsn_time_offset ); u_bsn_source : ENTITY work.dp_bsn_source_v2 @@ -121,13 +119,10 @@ BEGIN bsn_init => bsn_init, nof_clk_per_sync => nof_clk_per_sync, bsn_time_offset => bsn_time_offset, - current_bsn_time_offset => current_bsn_time_offset, -- Streaming src_out => i_bs_sosi ); - --capture_bsn <= i_bs_sosi.bsn; -- capture current BSN - --capture_bsn <= i_bs_sosi.bsn WHEN rising_edge(dp_clk) AND dp_pps='1'; -- capture BSN at external PPS capture_bsn <= i_bs_sosi.bsn WHEN rising_edge(dp_clk) AND i_bs_sosi.sync='1'; -- capture BSN at internal sync END str; diff --git a/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd b/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd index 7f15f845fc365470ac223203310aa98097df13eb..c6ace124e0d2abd4500634a85ca50b75da766e2a 100644 --- a/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd +++ b/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------- -- --- Copyright (C) 2011 +-- Copyright (C) 2020 -- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> -- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands -- @@ -19,22 +19,28 @@ -- ------------------------------------------------------------------------------- --- Author: P. Donker --- Verify if eop and sop come in pairs and if sync is at sop and at expected_sync puls. --- The tb is using a SSN (second sample number) and BSN (block sample number) generator as reference --- for the test, it uses g_pps_interval and g_block_size for generator timing settings. --- Start/Stop BSN source tests: --- 1) test 1x asynchronously (dp_on_pps='0') without automatic check, check visualy in wave window. --- 2) test 3x synchronously (dp_on_pps='1') with automatic check. +-- Author: P. Donker, E. Kooistra +-- Purpose: Tb to verify that the BS source can be started in any PPS interval +-- to create the BSN grid and sync interval as defined in Fig. 3.1 in +-- [1]. +-- Decsription: +-- * Start/Stop BSN source tests: +-- 1) test once asynchronously (dp_on_pps='0') without automatic check, check +-- visualy in wave window. +-- 2) test c_nof_repeat synchronously (dp_on_pps='1') with automatic check. +-- . Verify if bs_sosi.eop and bs_sosi.sop come in pairs +-- . Verify that bs_sosi.sync is at bs_sosi.sop +-- . Verify that bs_sosi has fixed latency with respect to ref_grid +-- +-- References: +-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station +-- [2] https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+source+with+offset -- --- [doc] = https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+source+with+offset - -- Usage: --- > as 10 +-- > as 8, e.g. view bs_sosi, exp_grid and unexpected_bs_sync -- > run -all -- . sop, eop are verified automatically --- . sync and bsn are verified automatically --- and then manually verify on/off in Wave window +-- . sync and bsn are verified automatically using the ref_grid LIBRARY IEEE, common_lib, dp_lib; USE IEEE.STD_LOGIC_1164.ALL; @@ -46,193 +52,219 @@ USE dp_lib.tb_dp_pkg.ALL; ENTITY tb_dp_bsn_source_v2 IS GENERIC ( - g_nof_pps : NATURAL := 20; - g_pps_interval : NATURAL := 230; - g_block_size : NATURAL := 32 + g_pps_interval : NATURAL := 16; --101; + g_block_size : NATURAL := 5 --23 ); END tb_dp_bsn_source_v2; ARCHITECTURE tb OF tb_dp_bsn_source_v2 IS - CONSTANT c_clk_period : TIME := 10 ns; - CONSTANT c_bsn_w : NATURAL := 31; - CONSTANT c_dut_latency : NATURAL := 2; + -- The nof block per sync interval will be the same after every + -- c_min_nof_pps_interval. The c_gcd is the greatest common divider of + -- g_pps_interval and g_block_size, so g_block_size / c_gcd yields an + -- integer. When g_pps_interval and g_block_size are relative prime, + -- then c_gcd = 1, and then it takes g_block_size nof g_pps_interval + -- for the pattern of c_nof_block_per_sync_lo and c_nof_block_per_sync_hi + -- to repeat. If c_gcd = g_block_size, then c_nof_block_per_sync_lo = + -- c_nof_block_per_sync_hi, so then the nof block per sync interval is + -- the same in every pps interval. + CONSTANT c_gcd : NATURAL := gcd(g_pps_interval, g_block_size); + CONSTANT c_min_nof_pps_interval : NATURAL := g_block_size / c_gcd; + + CONSTANT c_nof_block_per_sync_lo : NATURAL := g_pps_interval / g_block_size; + CONSTANT c_nof_block_per_sync_hi : NATURAL := ceil_div(g_pps_interval, g_block_size); + + -- choose c_nof_pps and c_nof_repeat > c_min_nof_pps_interval, because the + -- fractional sync pattern will repeat every c_min_nof_pps_interval number + -- of g_pps_intervals. + CONSTANT c_factor : NATURAL := 3; + CONSTANT c_nof_pps : NATURAL := c_min_nof_pps_interval * c_factor; + CONSTANT c_nof_repeat : NATURAL := c_min_nof_pps_interval * c_factor; + + CONSTANT c_clk_period : TIME := 10 ns; + CONSTANT c_bsn_w : NATURAL := 31; + CONSTANT c_bsn_time_offset_w : NATURAL := ceil_log2(g_block_size); + + -- Minimum latency between sync and PPS, due to logic in DUT + CONSTANT c_dut_latency : NATURAL := 3; -- The state name tells what kind of test is being done TYPE t_state_enum IS ( s_disable, - s_start, - s_pps_start + s_dp_on, + s_dp_on_pps ); - SIGNAL tb_state : t_state_enum; + -- Define the PPS (SSN) and BSN grid that both start at 0 according to Figure 3.1 in [1]: + TYPE t_time_grid IS RECORD + pps : STD_LOGIC; -- pulse per second, g_pps_interval clk per pps interval + ssn : NATURAL; -- seconds sequence number + bsn : NATURAL; -- block sequence number, g_block_size clk per block + sync : STD_LOGIC; -- active at sop when pps is active or was active + sop : STD_LOGIC; -- start of block + eop : STD_LOGIC; -- end of block + END RECORD; + + CONSTANT c_time_grid_rst : t_time_grid := ('0', 0, 0, '0', '0', '0'); + + -- Reference grid + SIGNAL ref_grid : t_time_grid := c_time_grid_rst; + SIGNAL ssn_eop : STD_LOGIC := '0'; + SIGNAL hold_pps : STD_LOGIC := '0'; + SIGNAL nxt_hold_pps : STD_LOGIC := '0'; + -- Tb SIGNAL tb_end : STD_LOGIC := '0'; SIGNAL rst : STD_LOGIC := '1'; SIGNAL clk : STD_LOGIC := '1'; + SIGNAL tb_state : t_state_enum := s_disable; -- DUT - SIGNAL dp_on : STD_LOGIC := '0'; - SIGNAL dp_on_pps : STD_LOGIC := '0'; - SIGNAL bsn_init : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); - - SIGNAL bs_sosi : t_dp_sosi; + SIGNAL dp_on : STD_LOGIC := '0'; + SIGNAL dp_on_pps : STD_LOGIC := '0'; + SIGNAL dp_on_status : STD_LOGIC; + SIGNAL bs_restart : STD_LOGIC; + SIGNAL bsn_init : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); + SIGNAL bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0'); + SIGNAL bs_sosi : t_dp_sosi; -- Verify - SIGNAL verify_sync : STD_LOGIC := '0'; - SIGNAL hold_bs_sop : STD_LOGIC; - - SIGNAL tb_bsn_cnt : INTEGER := 0; - - -- Define the PPS grid and the BSN grid that both start at 0 according to Figure 3.1 in [doc]: - SIGNAL SSN : NATURAL := 0; - SIGNAL BSN : NATURAL := 0; - - SIGNAL pps_sop : STD_LOGIC := '0'; - SIGNAL nxt_pps_sop : STD_LOGIC := '0'; - SIGNAL pps_eop : STD_LOGIC := '0'; - SIGNAL bsn_sop : STD_LOGIC := '0'; - SIGNAL nxt_bsn_sop : STD_LOGIC := '0'; - SIGNAL bsn_eop : STD_LOGIC := '0'; - SIGNAL expected_sync : STD_LOGIC := '0'; - SIGNAL expected_sync_dly : STD_LOGIC := '0'; - SIGNAL expected_bsn : NATURAL := 0; - SIGNAL expected_offset_bsn : NATURAL := 0; - SIGNAL dbg_nof_blk : NATURAL := 0; - SIGNAL dbg_accumulate : NATURAL := 0; - SIGNAL dbg_expected_bsn : NATURAL := 0; + SIGNAL exp_grid : t_time_grid; + SIGNAL unexpected_bs_sync : STD_LOGIC; + SIGNAL sl0 : STD_LOGIC := '0'; + SIGNAL verify_en : STD_LOGIC := '0'; + SIGNAL verify_sync : STD_LOGIC := '0'; + SIGNAL hold_bs_sop : STD_LOGIC; + SIGNAL prev_bs_valid : STD_LOGIC; + SIGNAL bs_starts_cnt : NATURAL := 0; + + SIGNAL dbg_c_nof_pps : NATURAL := c_nof_pps; + SIGNAL dbg_c_nof_repeat : NATURAL := c_nof_repeat; + + SIGNAL dbg_nof_blk : NATURAL; + SIGNAL dbg_accumulate : NATURAL; + SIGNAL dbg_expected_bsn : NATURAL; BEGIN - ----------------------------------------------------------------------------- - -- Stimuli - ----------------------------------------------------------------------------- rst <= '1', '0' AFTER c_clk_period*7; clk <= (NOT clk) OR tb_end AFTER c_clk_period/2; - - -- SSN/BSN generator - SSN <= SSN + 1 WHEN rising_edge(clk) AND pps_eop='1'; -- Seconds Sequence Number - BSN <= BSN + 1 WHEN rising_edge(clk) AND bsn_eop='1'; -- Block Sequence Number - - proc_common_gen_pulse(1, g_pps_interval, '1', rst, clk, nxt_pps_sop); -- make PPS grid with pps_sop at start of interval - pps_sop <= nxt_pps_sop AFTER c_clk_period; - pps_eop <= pps_sop'DELAYED((g_pps_interval-1)*c_clk_period); -- make PPS grid with pps_eop at end of intervals - - proc_common_gen_pulse(1, g_block_size, '1', rst, clk, nxt_bsn_sop); -- make BSN grid with bsn_sop at start of interval - bsn_sop <= nxt_bsn_sop AFTER c_clk_period; - bsn_eop <= bsn_sop'DELAYED((g_block_size-1)*c_clk_period); -- make BSN grid with bsn_eop at end of interval - - -- Define the expected sync that occurs when pps_sop = bsn sop, or else at the first bsn_sop after the pps_sop, see Figure 3.1 in [doc]. - p_expected_sync : PROCESS + ----------------------------------------------------------------------------- + -- Generate reference time grid + ----------------------------------------------------------------------------- + proc_common_gen_pulse(1, g_pps_interval, '1', sl0, clk, ref_grid.pps); + proc_common_gen_pulse(1, g_block_size, '1', sl0, clk, ref_grid.sop); + ref_grid.eop <= ref_grid.sop'DELAYED((g_block_size - 1) * c_clk_period); + ssn_eop <= ref_grid.pps'DELAYED((g_pps_interval - 1) * c_clk_period); + ref_grid.ssn <= ref_grid.ssn + 1 WHEN rising_edge(clk) AND ssn_eop = '1'; + ref_grid.bsn <= ref_grid.bsn + 1 WHEN rising_edge(clk) AND ref_grid.eop = '1'; + + -- Issue sync at start of block + p_ref_grid_sync : PROCESS(ref_grid, hold_pps) BEGIN - WAIT UNTIL rising_edge(clk); - expected_sync <= '0'; - proc_common_wait_until_high(clk, pps_sop); - IF bsn_sop = '1' THEN - expected_sync <= '1'; - expected_bsn <= BSN+1; - expected_offset_bsn <= 0; - ELSE - proc_common_wait_until_high(clk, bsn_sop); - expected_sync <= '1'; - expected_bsn <= BSN+1; - expected_offset_bsn <= (BSN+1) * g_block_size - SSN * g_pps_interval; + ref_grid.sync <= '0'; + nxt_hold_pps <= hold_pps; + + IF ref_grid.pps = '1' THEN + IF ref_grid.sop = '1' THEN + ref_grid.sync <= '1'; -- immediately issue sync + ELSE + nxt_hold_pps <= '1'; -- wait until next block + END IF; + END IF; + + IF hold_pps = '1' THEN + IF ref_grid.sop = '1' THEN + ref_grid.sync <= '1'; -- issue pending sync + nxt_hold_pps <= '0'; + END IF; END IF; END PROCESS; - - expected_sync_dly <= expected_sync'DELAYED(c_dut_latency*c_clk_period); - -- MM control + hold_pps <= nxt_hold_pps WHEN rising_edge(clk); + + exp_grid <= ref_grid'DELAYED(c_dut_latency * c_clk_period); + + ----------------------------------------------------------------------------- + -- Stimuli + ----------------------------------------------------------------------------- p_mm : PROCESS - VARIABLE v_bsn_time_offset : NATURAL; + VARIABLE v_ssn : NATURAL; VARIABLE v_bsn_init : NATURAL; + VARIABLE v_bsn_time_offset : NATURAL; BEGIN - tb_end <= '0'; - tb_state <= s_disable; - --pps <= '0'; - - dp_on <= '0'; - dp_on_pps <= '0'; - -- Get synchronous to clk proc_common_wait_until_low(clk, rst); - proc_common_wait_some_cycles(clk, 500); + proc_common_wait_some_cycles(clk, 10); -- Start asynchronously by making dp_on high - proc_common_wait_until_high(clk, expected_sync_dly); - tb_state <= s_pps_start; + verify_en <= '0'; -- only verify visualy in wave window + tb_state <= s_dp_on; dp_on_pps <= '0'; - dp_on <= '1'; - verify_sync <= '0'; -- only verify visualy in wave window - proc_common_wait_some_cycles(clk, g_nof_pps*g_pps_interval); - verify_sync <= '0'; - -- Stop by making dp_on low + dp_on <= '1'; + proc_common_wait_some_cycles(clk, c_nof_pps*g_pps_interval); tb_state <= s_disable; dp_on <= '0'; dp_on_pps <= '0'; - - -- wait until one pps_interval before next begin of SSN generator (pps_sop = bsn_sop) - proc_common_wait_until_high(clk, pps_sop); - v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size; - WHILE v_bsn_time_offset > 0 LOOP - proc_common_wait_some_cycles(clk, g_pps_interval); - v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size; - END LOOP; + proc_common_wait_some_cycles(clk, 10); - -- Start synchronously by making dp_on high at pps - FOR i IN 0 TO 2 LOOP - -- Now start on PPS - proc_common_wait_until_high(clk, expected_sync); - v_bsn_time_offset := ((SSN + 1) * g_pps_interval) MOD g_block_size; - v_bsn_init := ((SSN + 1) * g_pps_interval) / g_block_size; - IF v_bsn_time_offset = 0 THEN - bsn_init <= TO_UVEC(v_bsn_init, c_bsn_w); - ELSE - bsn_init <= TO_UVEC(v_bsn_init+1, c_bsn_w); - END IF; - tb_state <= s_pps_start; + -- Start synchronously by making dp_on and dp_on_pps high + verify_en <= '1'; -- verify automatically in test bench + + FOR I IN 0 TO c_nof_repeat-1 LOOP + -- Wait some variable time between tests, to enforce testing different + -- bsn_time_offset values + proc_common_wait_some_cycles(clk, 20); + proc_common_wait_some_cycles(clk, I*g_pps_interval); + + -- Wait until in the beginning of PPS interval + proc_common_wait_until_hi_lo(clk, ref_grid.pps); + proc_common_wait_some_cycles(clk, c_dut_latency); + + -- Determine bsn_init and bsn_time_offset for BSN source start + -- . bsn_init = BSN at sync + -- . bsn_time_offset = number of clk that sync occurs after PPS + v_ssn := ref_grid.ssn + 1; -- +1 to prepare start in next PPS interval + v_bsn_init := ceil_div(v_SSN * g_pps_interval, g_block_size); -- Equation 3.6 in [1] + v_bsn_time_offset := v_bsn_init * g_block_size - v_SSN * g_pps_interval; -- Equation 3.7 in [1] + bsn_init <= TO_UVEC(v_bsn_init, c_bsn_w); -- + bsn_time_offset <= TO_UVEC(v_bsn_time_offset, c_bsn_time_offset_w); + -- Start synchronously by making dp_on and dp_on_pps high + tb_state <= s_dp_on_pps; dp_on_pps <= '1'; - dp_on <= '1'; - verify_sync <= '1'; -- verify automatically in test bench - proc_common_wait_some_cycles(clk, g_nof_pps*g_pps_interval); - verify_sync <= '0'; - -- Stop by making dp_on low + dp_on <= '1'; + proc_common_wait_some_cycles(clk, c_nof_pps*g_pps_interval); tb_state <= s_disable; dp_on <= '0'; dp_on_pps <= '0'; - - -- wait until one pps_interval before next begin of SSN generator (pps_sop = bsn_sop) - proc_common_wait_until_high(clk, pps_sop); - v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size; - WHILE v_bsn_time_offset > 0 LOOP - proc_common_wait_some_cycles(clk, g_pps_interval); - v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size; - END LOOP; - END LOOP; - tb_end <= '1'; + proc_common_wait_some_cycles(clk, 10); + ASSERT bs_starts_cnt = 1 + c_nof_repeat REPORT "Wrong number of BSN source starts." SEVERITY ERROR; + + tb_end <= '1'; WAIT; END PROCESS; ----------------------------------------------------------------------------- -- Verification + -- . Some aspects of bs_sosi are verified multiple times in different ways, + -- this overlap is fine, because the tb and DUT are rather complicated, so + -- using different approaches also helpt to verify the tb itself. ----------------------------------------------------------------------------- + verify_sync <= verify_en AND bs_sosi.valid; + proc_dp_verify_sop_and_eop(clk, bs_sosi.valid, bs_sosi.sop, bs_sosi.eop, hold_bs_sop); -- Verify that sop and eop come in pairs - proc_dp_verify_sync(clk, verify_sync, bs_sosi.sync, bs_sosi.sop, expected_sync_dly); -- Verify sync at sop and at expected_sync - - -- Verify sync at sop and at expected_sync again: - -- . now using the proc_dp_verify_sync() variant for dp_bsn_source_v2 that - -- can verify fractional sync periods. - -- . the proc_dp_verify_sync() v2 variant was made later, so in fact - -- this tb_dp_bsn_source_v2 verifies this new v2 procedure. - proc_dp_verify_sync(TO_UINT(bsn_init), + --proc_dp_verify_sync(clk, verify_sync, bs_sosi.sync, exp_grid.sop, exp_grid.sync); -- Verify sync at sop and at expected_sync + + -- Verify sync at sop and at expected_sync + proc_dp_verify_sync(0, -- start bsn of PPS grid and BSN grid is 0, see [1] g_pps_interval, g_block_size, clk, - verify_sync, + verify_en, bs_sosi.sync, bs_sosi.sop, bs_sosi.bsn, @@ -240,6 +272,38 @@ BEGIN dbg_accumulate, dbg_expected_bsn); + -- Verify bs_sosi by comparing with exp_grid, this again verifies bs_sosi.sync, sop and bsn + p_verify_bs_sosi_grid : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + unexpected_bs_sync <= '0'; + IF verify_en = '1' AND bs_sosi.valid = '1' THEN + ASSERT TO_UINT(bs_sosi.bsn) = exp_grid.bsn REPORT "Wrong bs_sosi.bsn /= exp_grid.bsn" SEVERITY ERROR; + ASSERT bs_sosi.sync = exp_grid.sync REPORT "Wrong bs_sosi.sync /= exp_grid.sync" SEVERITY ERROR; + ASSERT bs_sosi.sop = exp_grid.sop REPORT "Wrong bs_sosi.sop /= exp_grid.sop" SEVERITY ERROR; + ASSERT bs_sosi.eop = exp_grid.eop REPORT "Wrong bs_sosi.eop /= exp_grid.eop" SEVERITY ERROR; + -- Mark error in Wave window + IF bs_sosi.sync = '1' AND bs_sosi.sync /= exp_grid.sync THEN + unexpected_bs_sync <= '1'; + END IF; + END IF; + END IF; + END PROCESS; + + -- Verify that bs_sosi.valid = '1' did happen after dp_on by verifying bs_start + prev_bs_valid <= bs_sosi.valid WHEN rising_edge(clk); + bs_starts_cnt <= bs_starts_cnt + 1 WHEN rising_edge(clk) AND bs_sosi.valid = '1' AND prev_bs_valid = '0'; + + p_verify_bs_restart : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + IF bs_restart = '1' THEN + ASSERT bs_sosi.sync = '1' REPORT "Unexpected bs_start while bs_sosi.sync /= 1" SEVERITY ERROR; + ASSERT prev_bs_valid = '0' REPORT "Unexpected bs_start while prev_bs_valid /= 0" SEVERITY ERROR; + END IF; + END IF; + END PROCESS; + ----------------------------------------------------------------------------- -- DUT: dp_bsn_source_v2 ----------------------------------------------------------------------------- @@ -248,18 +312,25 @@ BEGIN GENERIC MAP ( g_block_size => g_block_size, g_nof_clk_per_sync => g_pps_interval, - g_bsn_w => c_bsn_w + g_bsn_w => c_bsn_w, + g_bsn_time_offset_w => c_bsn_time_offset_w ) PORT MAP ( - rst => rst, - clk => clk, - pps => pps_sop, + rst => rst, + clk => clk, + pps => ref_grid.pps, -- MM control - dp_on => dp_on, - dp_on_pps => dp_on_pps, - bsn_init => bsn_init, + dp_on => dp_on, + dp_on_pps => dp_on_pps, + + dp_on_status => dp_on_status, -- = src_out.valid + bs_restart => bs_restart, -- = src_out.sop for first sop after dp_on went high + + bsn_init => bsn_init, + bsn_time_offset => bsn_time_offset, + -- Streaming - src_out => bs_sosi + src_out => bs_sosi ); END tb; diff --git a/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd b/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd index 930588f18e0c8b0114dd064c864f1449a19ea81a..8d301b21d186c7a2f6c9d51367c7afd3ff28b445 100644 --- a/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd +++ b/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd @@ -63,6 +63,10 @@ -- temporarily changing the ASSERT condition -- b Initialy used LOOP in p_stimuli to repeat test. Later used list of -- c_nof_test_intervals and tb_state to try different stimuli. +-- +-- References: +-- [1] https://support.astron.nl/confluence/pages/viewpage.action?spaceKey=L2M&title=L2+STAT+Decision%3A+Timing+in+Station +-- LIBRARY IEEE, common_lib, dp_lib; USE IEEE.STD_LOGIC_1164.ALL; @@ -516,17 +520,18 @@ BEGIN ----------------------------------------------------------------------------- verify_sync <= NOT recover_from_in_lost; + -- Verify that sync is on PPS and BSN grid as defined in [1]. proc_dp_verify_sync(TO_UINT(ctrl_start_bsn), - ctrl_interval_size, - g_block_size, - clk, - verify_sync, - out_sosi.sync, - out_sosi.sop, - out_sosi.bsn, - dbg_nof_blk, - dbg_accumulate, - dbg_expected_bsn); + ctrl_interval_size, + g_block_size, + clk, + verify_sync, + out_sosi.sync, + out_sosi.sop, + out_sosi.bsn, + dbg_nof_blk, + dbg_accumulate, + dbg_expected_bsn); dbg_out_sosi_sync <= out_sosi.sync; dbg_out_sosi_sop <= out_sosi.sop; diff --git a/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd index b893581245a4262daf58758174bb4fd8ba9558fd..63a5385422a3adbef497d38fbdd597664134d926 100644 --- a/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd +++ b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd @@ -25,7 +25,7 @@ USE IEEE.std_logic_1164.ALL; USE work.tb_dp_pkg.ALL; --- > as 2 +-- > as 4 -- > run -all --> OK ENTITY tb_tb_dp_bsn_source_v2 IS @@ -33,30 +33,44 @@ END tb_tb_dp_bsn_source_v2; ARCHITECTURE tb OF tb_tb_dp_bsn_source_v2 IS - SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' - CONSTANT c_nof_pps : NATURAL := 50; -- choose > g_block_size, because the fractional sync pattern will repeat - -- within g_block_size number of g_pps_interval's + SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' BEGIN -- from tb_dp_bsn_source_v2.vhd -- - -- g_nof_pps : NATURAL := 20; -- g_pps_interval : NATURAL := 240 - -- g_block_size : NATURAL := 32 + -- g_block_size : NATURAL := 32 + + -- test integer case + u_20_10 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (20, 10); -- 20 // 10 = 2, 20 MOD 10 = 0, 20/10 = 2 block/sync + u_22_11 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (22, 11); -- 22 // 11 = 2, 22 MOD 11 = 0, 22/11 = 2 block/sync + u_39_13 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (39, 13); -- 39 // 13 = 3, 39 MOD 13 = 0, 39/13 = 3 block/sync + + -- test smallest nof block per sync + u_10_10 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (10, 10); -- 1 block/sync + u_5_5 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (5, 5); -- 1 block/sync + + -- test smallest g_block_size case + u_3_3 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (3, 3); -- 3 // 3 = 1, 3 MOD 3 = 0, 3/3 = 1 block/sync + u_6_3 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (6, 3); -- 6 // 3 = 2, 6 MOD 3 = 0, 6/3 = 2 block/sync + u_7_3 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (7, 3); -- 7 // 3 = 2, 7 MOD 3 = 1, 7/3 = 2.33 block/sync - -- test different clk_per_sync - u_230_32_div_7_mod_6 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 230, 32); -- 230 / 32 = 7, 230 MOD 32 = 6 - u_240_32_div_7_mod_16 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 240, 32); -- 240 / 32 = 7, 240 MOD 32 = 16 - u_248_32_div_7_mod_24 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 248, 32); -- 248 / 32 = 7, 248 MOD 32 = 24 + -- test lofar case with 0.5 fraction in average nof block/sync + u_20_8 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (20, 8); -- 20 // 8 = 2, 20 MOD 8 = 4, 20/8 = 2.5 block/sync - -- test different block_size's - u_240_27_div_8_mod_24 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 240, 27); -- 240 / 27 = 8, 240 MOD 27 = 24 - u_240_30_div_8_mod_0 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 240, 30); -- 240 / 30 = 8, 240 MOD 30 = 0 + -- test fractional (corner) cases + u_18_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (18, 9); -- 18 MOD 9 = 0 + u_17_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (17, 9); -- 17 MOD 9 = 8 = g_block_size - 1 + u_19_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (19, 9); -- 19 MOD 9 = 1 + u_20_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (20, 9); -- 20 MOD 9 = 2 + u_25_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (25, 9); -- 25 MOD 9 = 7 + u_26_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (26, 9); -- 26 MOD 9 = 8 = g_block_size - 1 + u_27_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (27, 9); -- 27 MOD 9 = 0 -- test some prime values - u_101_17_div_5_mod_16 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 101, 17); -- 101 / 17 = 5, 101 MOD 17 = 16 - u_101_23_div_4_mod_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 101, 23); -- 101 / 23 = 4, 101 MOD 23 = 9 + u_17_3 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (17, 3); -- 17 // 3 = 5, 17 MOD 3 = 2, 17/3 = 5.66 block/sync + u_101_17 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (101, 17); -- 101 // 17 = 5, 101 MOD 17 = 16, 101/17 = 5.9411 block/sync END tb; diff --git a/libraries/base/ring/src/vhdl/ring_info.vhd b/libraries/base/ring/src/vhdl/ring_info.vhd index 47e8cdb9fdfbe4e9ea069b85f3067817bf23a863..e1fbd16e32ca86c5a07df79654a30f34578ea79e 100644 --- a/libraries/base/ring/src/vhdl/ring_info.vhd +++ b/libraries/base/ring/src/vhdl/ring_info.vhd @@ -38,6 +38,10 @@ USE common_lib.common_field_pkg.ALL; USE work.ring_pkg.ALL; ENTITY ring_info IS + GENERIC ( + -- Actual ring_info.O_rn field width can be less than a byte + g_ring_info_O_rn_w : NATURAL := c_byte_w + ); PORT ( -- Clocks and reset mm_rst : IN STD_LOGIC; -- reset synchronous with mm_clk @@ -58,6 +62,8 @@ ARCHITECTURE str OF ring_info IS SIGNAL mm_fields_out : STD_LOGIC_VECTOR(field_slv_out_len(c_ring_info_field_arr)-1 DOWNTO 0); + SIGNAL O_rn : STD_LOGIC_VECTOR(c_byte_w-1 DOWNTO 0); + BEGIN u_mm_fields: ENTITY mm_lib.mm_fields @@ -81,10 +87,14 @@ BEGIN ); -- get "RW" fields from mm_fields - ring_info.O_rn <= mm_fields_out(field_hi(c_ring_info_field_arr, "O_rn") DOWNTO field_lo(c_ring_info_field_arr, "O_rn")); - ring_info.N_rn <= mm_fields_out(field_hi(c_ring_info_field_arr, "N_rn") DOWNTO field_lo(c_ring_info_field_arr, "N_rn")); + O_rn <= mm_fields_out(field_hi(c_ring_info_field_arr, "O_rn") DOWNTO field_lo(c_ring_info_field_arr, "O_rn")); + + ring_info.O_rn <= RESIZE_UVEC(O_rn(g_ring_info_O_rn_w-1 DOWNTO 0), c_byte_w); + ring_info.N_rn <= mm_fields_out(field_hi(c_ring_info_field_arr, "N_rn") DOWNTO field_lo(c_ring_info_field_arr, "N_rn")); + ring_info.use_cable_to_previous_rn <= sl(mm_fields_out(field_hi(c_ring_info_field_arr, "use_cable_to_previous_rn") DOWNTO field_lo(c_ring_info_field_arr, "use_cable_to_previous_rn"))); ring_info.use_cable_to_next_rn <= sl(mm_fields_out(field_hi(c_ring_info_field_arr, "use_cable_to_next_rn") DOWNTO field_lo(c_ring_info_field_arr, "use_cable_to_next_rn"))); + END str;