diff --git a/libraries/base/dp/src/vhdl/dp_offload_tx.vhd b/libraries/base/dp/src/vhdl/dp_offload_tx.vhd index 2736f3458f992f1545db8d92fd5a95d2ab534f8b..29aec4185391e3710a620573c685fda000ec2972 100644 --- a/libraries/base/dp/src/vhdl/dp_offload_tx.vhd +++ b/libraries/base/dp/src/vhdl/dp_offload_tx.vhd @@ -20,18 +20,20 @@ ------------------------------------------------------------------------------- -- Purpose: --- . Development version that will eventually replace dp_offload_tx. +-- . Reduce data rate and/or packet rate and add a user-defined header -- Description: --- . From each incoming streams, dp_offload_tx allows the user to control: --- . The number of words per incoming frame that is selected --- . The numer of blocks of these selected words that are put into one new frame --- . The above control settings can be done dynaically via the MM interface. If the MM --- interface is not used, the default settings passed via g_hdr_field_arr are used. --- . NOTE: dp_frame_rd DOES NOT SUPPORT 1 WORD FRAMES due to its separate s_sof and --- s_eof states. +-- . From each incoming streams, dp_offload_tx allows the user to set: +-- . The number of words per incoming frame that is selected (word 0..g_nof_words_per_block-1) +-- . The numer of blocks of these selected words that are put into one new frame (g_nof_blocks_per_packet) +-- . The header contents can be controlled dynamically by data path or MM control (selected by g_hdr_field_sel) -- Remarks: --- . Note that an output (fill) FIFO may be needed if the packets have gaps in them! --- The Ethernet cores require this so an EOP-triggered FIFO may added to them. +-- . You can use the internal post split FIFO (enabled by default) if: +-- . Your input data rate does not have sufficient gaps to insert the header, and/or +-- . Your sink requires the output frames to be uninterrupted +-- FIXME If the input stream is 100% valid (no gaps), don't connect snk_out_arr to the source because +-- dp_split introduces (non-functional) non-ready cycles between EOP and SOP, causing FIFO +-- overflow upstream. dp_plit should not do that, or better, something simpler such as dp_tail_remove +-- should be used instead. LIBRARY IEEE, common_lib, work, mm_lib; USE IEEE.STD_LOGIC_1164.ALL; @@ -46,13 +48,11 @@ ENTITY dp_offload_tx IS g_nof_streams : NATURAL; g_data_w : NATURAL; g_use_complex : BOOLEAN; -- TRUE uses re(0..g_data_w/2 -1) & im(0..g_data_w/2-1) as input instead of data(0..g_data_w-1). - g_max_nof_words_per_block : NATURAL; - g_def_nof_words_per_block : NATURAL; - g_max_nof_blocks_per_packet : NATURAL; - g_def_nof_blocks_per_packet : NATURAL; - g_hdr_field_arr : t_common_field_arr; - g_hdr_field_sel : STD_LOGIC_VECTOR; - g_use_post_split_fifo : BOOLEAN := FALSE + g_nof_words_per_block : NATURAL; + g_nof_blocks_per_packet : NATURAL; + g_hdr_field_arr : t_common_field_arr; -- User defined header fields + g_hdr_field_sel : STD_LOGIC_VECTOR; -- For each header field, select the source: 0=data path, 1=MM controlled + g_use_post_split_fifo : BOOLEAN := TRUE ); PORT ( mm_rst : IN STD_LOGIC; @@ -61,9 +61,6 @@ ENTITY dp_offload_tx IS dp_rst : IN STD_LOGIC; dp_clk : IN STD_LOGIC; - reg_mosi : IN t_mem_mosi := c_mem_mosi_rst; - reg_miso : OUT t_mem_miso; - reg_hdr_dat_mosi : IN t_mem_mosi := c_mem_mosi_rst; reg_hdr_dat_miso : OUT t_mem_miso; @@ -80,61 +77,54 @@ END dp_offload_tx; ARCHITECTURE str OF dp_offload_tx IS - CONSTANT c_dp_split_val_latency : NATURAL := 1; - CONSTANT c_dp_packet_merge_val_latency : NATURAL := 2; - CONSTANT c_nof_header_words : NATURAL := field_slv_len(g_hdr_field_arr) / g_data_w; - CONSTANT c_dp_field_blk_snk_data_w : NATURAL := field_slv_out_len(field_arr_set_mode(g_hdr_field_arr , "RW")); - CONSTANT c_dp_field_blk_src_data_w : NATURAL := g_data_w; - - SIGNAL dp_split_snk_in_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL dp_split_snk_out_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); - - SIGNAL dp_split_src_out_2arr : t_dp_sosi_2arr_2(g_nof_streams-1 DOWNTO 0); - SIGNAL dp_split_src_in_2arr : t_dp_siso_2arr_2(g_nof_streams-1 DOWNTO 0); - - SIGNAL dp_packet_merge_snk_in_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL dp_packet_merge_snk_out_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); - - SIGNAL dp_packet_merge_src_out_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL dp_packet_merge_src_in_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); + CONSTANT c_dp_split_val_latency : NATURAL := 1; + CONSTANT c_dp_packet_merge_val_latency : NATURAL := 2; + CONSTANT c_nof_header_words : NATURAL := field_nof_words(g_hdr_field_arr, g_data_w); + CONSTANT c_nof_merged_pkt_words : NATURAL := g_nof_words_per_block*g_nof_blocks_per_packet; + CONSTANT c_dp_field_blk_snk_data_w : NATURAL := field_slv_out_len(field_arr_set_mode(g_hdr_field_arr , "RW")); + CONSTANT c_dp_field_blk_src_data_w : NATURAL := g_data_w; - SIGNAL dp_field_blk_snk_in_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL dp_field_blk_snk_out_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); + -- Internal FIFO fill level: one merged packet (to provide an uninterrupted output packet) + CONSTANT c_dp_fifo_fill : NATURAL := c_nof_merged_pkt_words; + -- Total internat FIFO size: one merged packet + one header (we need to be able to hold the data path + -- during header insertion) + margin + CONSTANT c_dp_fifo_size : NATURAL := c_dp_fifo_fill+c_nof_header_words+10; - SIGNAL dp_concat_snk_in_2arr : t_dp_sosi_2arr_2(g_nof_streams-1 DOWNTO 0); - SIGNAL dp_concat_snk_out_2arr : t_dp_siso_2arr_2(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_split_snk_in_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_split_hdr_fields_snk_in_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); - -- MM control - CONSTANT c_nof_ctrl_fields : NATURAL := 2; - CONSTANT c_ctrl_field_arr : t_common_field_arr(c_nof_ctrl_fields-1 DOWNTO 0) := ( ( field_name_pad("nof_words_per_block" ), "RW", ceil_log2(g_max_nof_words_per_block+1 ), field_default(g_def_nof_words_per_block) ), - ( field_name_pad("nof_blocks_per_packet"), "RW", ceil_log2(g_max_nof_blocks_per_packet+1), field_default(g_def_nof_blocks_per_packet) )); + SIGNAL dp_split_snk_out_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); + + SIGNAL dp_split_src_out_2arr : t_dp_sosi_2arr_2(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_split_hdr_fields_src_out_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); - TYPE t_ctrl_fields_out_arr IS ARRAY(g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(field_slv_out_len(c_ctrl_field_arr)-1 DOWNTO 0); - SIGNAL ctrl_fields_out_arr : t_ctrl_fields_out_arr; + SIGNAL dp_split_src_in_2arr : t_dp_siso_2arr_2(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_split_hdr_fields_src_in_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); - TYPE t_nof_words_per_block_arr IS ARRAY(g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(ceil_log2(g_max_nof_words_per_block+1)-1 DOWNTO 0); - SIGNAL nof_words_per_block_arr : t_nof_words_per_block_arr; + SIGNAL dp_packet_merge_snk_in_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_packet_merge_snk_out_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); - TYPE t_nof_blocks_per_packet_arr IS ARRAY(g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(ceil_log2(g_max_nof_blocks_per_packet+1)-1 DOWNTO 0); - SIGNAL nof_blocks_per_packet_arr : t_nof_blocks_per_packet_arr; + SIGNAL dp_packet_merge_src_out_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_packet_merge_hdr_fields_src_out_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL reg_mosi_arr : t_mem_mosi_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL reg_miso_arr : t_mem_miso_arr(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_packet_merge_src_in_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_packet_merge_hdr_fields_src_in_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL reg_hdr_dat_mosi_arr : t_mem_mosi_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL reg_hdr_dat_miso_arr : t_mem_miso_arr(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_field_blk_snk_in_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_field_blk_snk_out_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL dp_pipeline_hdr_fields_snk_in_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL dp_pipeline_hdr_fields_snk_out_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_concat_snk_in_2arr : t_dp_sosi_2arr_2(g_nof_streams-1 DOWNTO 0); + SIGNAL dp_concat_snk_out_2arr : t_dp_siso_2arr_2(g_nof_streams-1 DOWNTO 0); - SIGNAL dp_pipeline_hdr_fields_src_out_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); - SIGNAL dp_pipeline_hdr_fields_src_in_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); + SIGNAL reg_hdr_dat_mosi_arr : t_mem_mosi_arr(g_nof_streams-1 DOWNTO 0); + SIGNAL reg_hdr_dat_miso_arr : t_mem_miso_arr(g_nof_streams-1 DOWNTO 0); BEGIN --------------------------------------------------------------------------------------- - -- Input: use complex or data field + -- Wire inputs to dp_split inputs --------------------------------------------------------------------------------------- + -- use complex or data field gen_wires_complex : IF g_use_complex = TRUE GENERATE p_wires_complex : PROCESS(snk_in_arr) BEGIN @@ -144,13 +134,18 @@ BEGIN END LOOP; END PROCESS; END GENERATE; - gen_wires_data : IF g_use_complex = FALSE GENERATE dp_split_snk_in_arr <= snk_in_arr; END GENERATE; + + -- hdr_fields_in_arr(i) is considered valid at snk_in_arr(i).sop + gen_dp_pipeline_hdr_fields_in_arr : FOR i IN 0 TO g_nof_streams-1 GENERATE + dp_split_hdr_fields_snk_in_arr(i).data(field_slv_len(g_hdr_field_arr)-1 DOWNTO 0) <= hdr_fields_in_arr(i)(field_slv_len(g_hdr_field_arr)-1 DOWNTO 0); + dp_split_hdr_fields_snk_in_arr(i).valid <= snk_in_arr(i).sop; + END GENERATE; --------------------------------------------------------------------------------------- - -- Split nof_words_per_block + -- Split nof_words_per_block (forward only the first nof_words_per_block of each block) --------------------------------------------------------------------------------------- snk_out_arr <= dp_split_snk_out_arr; @@ -159,14 +154,12 @@ BEGIN GENERIC MAP ( g_data_w => g_data_w, g_symbol_w => g_data_w, - g_nof_symbols => g_max_nof_words_per_block + g_nof_symbols => g_nof_words_per_block ) PORT MAP ( rst => dp_rst, clk => dp_clk, - nof_symbols => nof_words_per_block_arr(i), - snk_out => dp_split_snk_out_arr(i), snk_in => dp_split_snk_in_arr(i), @@ -178,32 +171,22 @@ BEGIN END GENERATE; - --------------------------------------------------------------------------------------- - -- Use a post-split FIFO when the source has no flow control and its inter-block gap - -- is shorter than required for the header to be inserted. - --------------------------------------------------------------------------------------- - gen_dp_fifo_sc : FOR i IN 0 TO sel_a_b(g_use_post_split_fifo, g_nof_streams-1, -1) GENERATE - u_dp_fifo_sc : ENTITY work.dp_fifo_sc - GENERIC MAP ( - g_data_w => g_data_w, - g_fifo_size => g_max_nof_words_per_block - ) - PORT MAP ( - rst => dp_rst, - clk => dp_clk, - - snk_in => dp_split_src_out_2arr(i)(1), - snk_out => dp_split_src_in_2arr(i)(1), - - src_out => dp_packet_merge_snk_in_arr(i), - src_in => dp_packet_merge_snk_out_arr(i) - ); - END GENERATE; + -- Introduce the same delay (as dp_plit) on the corresponding header fields + u_dp_pipeline_arr_dp_split : ENTITY work.dp_pipeline_arr + GENERIC MAP ( + g_nof_streams => g_nof_streams, + g_pipeline => c_dp_split_val_latency + ) + PORT MAP ( + rst => dp_rst, + clk => dp_clk, - no_dp_fifo_sc : FOR i IN 0 TO sel_a_b(g_use_post_split_fifo, -1, g_nof_streams-1) GENERATE - dp_packet_merge_snk_in_arr(i) <= dp_split_src_out_2arr(i)(1); - dp_split_src_in_2arr(i)(1) <= dp_packet_merge_snk_out_arr(i); - END GENERATE; + snk_in_arr => dp_split_hdr_fields_snk_in_arr, + snk_out_arr => OPEN, --Flow control is already taken care of by dp_split + + src_out_arr => dp_split_hdr_fields_src_out_arr, + src_in_arr => dp_split_hdr_fields_src_in_arr + ); --------------------------------------------------------------------------------------- -- Merge nof_blocks_per_packet @@ -211,64 +194,93 @@ BEGIN gen_dp_packet_merge : FOR i IN 0 TO g_nof_streams-1 GENERATE u_dp_packet_merge : ENTITY work.dp_packet_merge GENERIC MAP ( - g_nof_pkt => g_max_nof_blocks_per_packet + g_nof_pkt => g_nof_blocks_per_packet ) PORT MAP ( rst => dp_rst, clk => dp_clk, - - nof_pkt => nof_blocks_per_packet_arr(i), - snk_out => dp_packet_merge_snk_out_arr(i), - snk_in => dp_packet_merge_snk_in_arr(i), + snk_out => dp_split_src_in_2arr(i)(1), + snk_in => dp_split_src_out_2arr(i)(1), src_in => dp_packet_merge_src_in_arr(i), src_out => dp_packet_merge_src_out_arr(i) ); + END GENERATE; - dp_concat_snk_in_2arr(i)(0) <= dp_packet_merge_src_out_arr(i); - dp_packet_merge_src_in_arr(i) <= dp_concat_snk_out_2arr(i)(0); + -- Introduce the same delay (as dp_packet_merge) on the corresponding header fields + u_dp_pipeline_arr_dp_packet_merge : ENTITY work.dp_pipeline_arr + GENERIC MAP ( + g_nof_streams => g_nof_streams, + g_pipeline => c_dp_packet_merge_val_latency + ) + PORT MAP ( + rst => dp_rst, + clk => dp_clk, - END GENERATE; + snk_in_arr => dp_split_hdr_fields_src_out_arr, + snk_out_arr => dp_split_hdr_fields_src_in_arr, - --------------------------------------------------------------------------------------- - -- Pipeline the header SLV so it lines up with dp_packet_merge_src_out_arr(i).sop - --------------------------------------------------------------------------------------- - gen_dp_pipeline_hdr_fields_in_arr : FOR i IN 0 TO g_nof_streams-1 GENERATE + src_out_arr => dp_packet_merge_hdr_fields_src_out_arr, + src_in_arr => dp_packet_merge_hdr_fields_src_in_arr + ); - -- FIXME - Put this DP bus on the entity instead of hdr_fields_in_arr. - dp_pipeline_hdr_fields_snk_in_arr(i).data(field_slv_len(g_hdr_field_arr)-1 DOWNTO 0) <= hdr_fields_in_arr(i)(field_slv_len(g_hdr_field_arr)-1 DOWNTO 0); - dp_pipeline_hdr_fields_snk_in_arr(i).valid <= snk_in_arr(i).sop; - dp_pipeline_hdr_fields_snk_in_arr(i).sync <= snk_in_arr(i).sync; - dp_pipeline_hdr_fields_snk_in_arr(i).bsn <= snk_in_arr(i).bsn; + -- dp_packet_merge_hdr_fields_src_out_arr contains a valid header for each block that was merged + -- into one packet. We want only the first valid header per merged block to be forwarded to + -- dp_field_blk. + -- . From here on, we won't need any more pipelining for the header as we now have one header + -- in sync with each merged packet. + -- . dp_field_blk has flow control and dp_concat will hold the header until the payload + -- also arrives via the fill fifo. + p_wire_valid : PROCESS(dp_packet_merge_src_out_arr, dp_packet_merge_hdr_fields_src_out_arr) + BEGIN + FOR i IN 0 TO g_nof_streams-1 LOOP + dp_field_blk_snk_in_arr(i) <= dp_packet_merge_hdr_fields_src_out_arr(i); + dp_field_blk_snk_in_arr(i).valid <= dp_packet_merge_src_out_arr(i).sop; + END LOOP; + END PROCESS; + + dp_packet_merge_hdr_fields_src_in_arr <= dp_field_blk_snk_out_arr; - u_dp_pipeline : ENTITY work.dp_pipeline + --------------------------------------------------------------------------------------- + -- Use this FIFO when the gaps in the input stream are not large enoung for the header + -- to be insterted. + -- . This FIFO buffers the merged packets; the corresponding headers go around it. + -- . This FIFO sits behind dp_split so data rate here is reduced (if + -- g_nof_words_per_block < block length) + -- . Use a FILL fifo variant here so the output frames can be read out as fast the sink + -- (most likely an Ethernet core) would like + --------------------------------------------------------------------------------------- + gen_dp_fifo_fill : FOR i IN 0 TO sel_a_b(g_use_post_split_fifo, g_nof_streams, 0)-1 GENERATE + u_dp_fifo_fill : ENTITY work.dp_fifo_fill GENERIC MAP ( - g_pipeline => c_dp_split_val_latency + c_dp_packet_merge_val_latency - ) + g_data_w => g_data_w, + g_fifo_fill => c_dp_fifo_fill, + g_fifo_size => c_dp_fifo_size + ) PORT MAP ( - rst => dp_rst, - clk => dp_clk, - - snk_in => dp_pipeline_hdr_fields_snk_in_arr(i), - snk_out => OPEN, --dp_pipeline_hdr_fields_snk_out_arr(i), -- snk_in is only valid at the SOP so we won't need this - - src_out => dp_pipeline_hdr_fields_src_out_arr(i), - src_in => dp_pipeline_hdr_fields_src_in_arr(i) + rst => dp_rst, + clk => dp_clk, + + snk_in => dp_packet_merge_src_out_arr(i), + snk_out => dp_packet_merge_src_in_arr(i), + + src_out => dp_concat_snk_in_2arr(i)(0), + src_in => dp_concat_snk_out_2arr(i)(0) ); END GENERATE; - + + no_dp_fifo_fill : FOR i IN 0 TO sel_a_b(g_use_post_split_fifo, 0, g_nof_streams)-1 GENERATE + dp_concat_snk_in_2arr(i)(0) <= dp_packet_merge_src_out_arr(i); + dp_packet_merge_src_in_arr(i) <= dp_concat_snk_out_2arr(i)(0); + END GENERATE; + --------------------------------------------------------------------------------------- -- Create header block & concatenate header to offload stream. - -- . hdr_fields_in_arr(i) is clocked in at snk_in_arr(i).sop after which it can be - -- read out by dp_concat when it is ready for it. --------------------------------------------------------------------------------------- gen_dp_field_blk : FOR i IN 0 TO g_nof_streams-1 GENERATE - -- Flow control for the header source. Should not be needed. - dp_pipeline_hdr_fields_src_in_arr(i) <= dp_field_blk_snk_out_arr(i); - dp_field_blk_snk_in_arr(i) <= dp_pipeline_hdr_fields_src_out_arr(i); - + -- Create multi-cycle header block from single-cycle wide header SLV u_dp_field_blk : ENTITY work.dp_field_blk GENERIC MAP ( g_field_arr => field_arr_set_mode(g_hdr_field_arr , "RW"), @@ -283,7 +295,7 @@ BEGIN mm_rst => mm_rst, mm_clk => mm_clk, - snk_in => dp_field_blk_snk_in_arr(i), -- Valid for only one cycle. + snk_in => dp_field_blk_snk_in_arr(i), snk_out => dp_field_blk_snk_out_arr(i), src_in => dp_concat_snk_out_2arr(i)(1), @@ -315,41 +327,6 @@ BEGIN --------------------------------------------------------------------------------------- -- MM control & monitoring --------------------------------------------------------------------------------------- - gen_mm_fields: FOR i IN 0 TO g_nof_streams-1 GENERATE - u_mm_fields: ENTITY mm_lib.mm_fields - GENERIC MAP( - g_field_arr => c_ctrl_field_arr - ) - PORT MAP ( - mm_clk => mm_clk, - mm_rst => mm_rst, - - mm_mosi => reg_mosi_arr(i), - mm_miso => reg_miso_arr(i), - - slv_clk => dp_clk, - slv_rst => dp_rst, - - slv_out => ctrl_fields_out_arr(i) - ); - - nof_words_per_block_arr(i) <= ctrl_fields_out_arr(i)(field_hi(c_ctrl_field_arr, "nof_words_per_block" ) DOWNTO field_lo(c_ctrl_field_arr, "nof_words_per_block" )); - nof_blocks_per_packet_arr(i) <= ctrl_fields_out_arr(i)(field_hi(c_ctrl_field_arr, "nof_blocks_per_packet") DOWNTO field_lo(c_ctrl_field_arr, "nof_blocks_per_packet")); - - END GENERATE; - - u_common_mem_mux_ctrl : ENTITY common_lib.common_mem_mux - GENERIC MAP ( - g_nof_mosi => g_nof_streams, - g_mult_addr_w => ceil_log2(field_nof_words(c_ctrl_field_arr, c_word_w)) - ) - PORT MAP ( - mosi => reg_mosi, - miso => reg_miso, - mosi_arr => reg_mosi_arr, - miso_arr => reg_miso_arr - ); - u_common_mem_mux_hdr_dat : ENTITY common_lib.common_mem_mux GENERIC MAP ( g_nof_mosi => g_nof_streams,