From 9ae96ce8b1bc7ad1c02d6f15128ac336037203c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= <fandabohacek@gmail.com> Date: Sat, 1 Apr 2023 20:52:00 +0200 Subject: [PATCH] feat: add multipoint link support --- src/jesd204b_multipoint_link_rx.vhd | 117 +++++++++++++ src/jesd204b_pkg.vhd | 5 + src/transport/transport_pkg.vhd | 1 + testbench/jesd204b_multipoint_data_tb.vhd | 191 ++++++++++++++++++++++ 4 files changed, 314 insertions(+) create mode 100644 src/jesd204b_multipoint_link_rx.vhd create mode 100644 testbench/jesd204b_multipoint_data_tb.vhd diff --git a/src/jesd204b_multipoint_link_rx.vhd b/src/jesd204b_multipoint_link_rx.vhd new file mode 100644 index 0000000..ae597c0 --- /dev/null +++ b/src/jesd204b_multipoint_link_rx.vhd @@ -0,0 +1,117 @@ +library ieee; +use ieee.std_logic_1164.all; +use work.jesd204b_pkg.all; +use work.data_link_pkg.all; +use work.transport_pkg.all; + +entity jesd204b_multipoint_link_rx is + + generic ( + K_character : std_logic_vector(7 downto 0) := "10111100"; -- Sync character + R_character : std_logic_vector(7 downto 0) := "00011100"; -- ILAS first + -- frame character + A_character : std_logic_vector(7 downto 0) := "01111100"; -- Multiframe + -- alignment character + Q_character : std_logic_vector(7 downto 0) := "10011100"; -- ILAS 2nd + -- frame 2nd character + LINKS : integer; -- Count of links + LANES : integer; -- Total nubmer of lanes + CONVERTERS : integer; -- Total number of converters + CONFIG : link_config_array(0 to LINKS - 1); + ERROR_CONFIG : error_handling_config := (2, 0, 5, 5, 5)); + + port ( + ci_char_clk : in std_logic; + ci_frame_clk : in std_logic; + ci_reset : in std_logic; + ci_request_sync : in std_logic; + co_nsynced : out std_logic; + co_error : out std_logic; + di_transceiver_data : in lane_input_array(0 to LANES - 1); + do_samples : out simple_samples_array(0 to LINKS - 1)(0 to CONFIG(0).M - 1, 0 to CONFIG(0).CS - 1); + co_frame_state : out frame_state_array(0 to LINKS - 1); + co_correct_data : out std_logic); + +end entity jesd204b_multipoint_link_rx; + +architecture a1 of jesd204b_multipoint_link_rx is + constant all_ones : std_logic_vector(LINKS - 1 downto 0) := (others => '1'); + constant all_zeros : std_logic_vector(LINKS - 1 downto 0) := (others => '0'); + + signal links_correct_data : std_logic_vector(LINKS - 1 downto 0); + signal links_error : std_logic_vector(LINKS - 1 downto 0); + signal links_nsynced : std_logic_vector(LINKS - 1 downto 0); + + -- purpose: Count lanes before link with index link_index + function sumCummulativeLanes ( + link_index : integer) + return integer is + variable lanes_count : integer := 0; + begin -- function sumCummulativeLanes + if link_index /= 0 then + for i in 0 to link_index -1 loop + lanes_count := lanes_count + CONFIG(i).L; + end loop; -- i + end if; + + return lanes_count; + end function sumCummulativeLanes; + + -- purpose: Count converters before link with index link_index + function sumCummulativeConverters ( + link_index : integer) + return integer is + variable converters_count : integer := 0; + begin -- function sumCummulativeConverters + if link_index /= 0 then + for i in 0 to link_index -1 loop + converters_count := converters_count + CONFIG(i).M; + end loop; -- i + end if; + + return converters_count; + end function sumCummulativeConverters; +begin -- architecture a1 + co_nsynced <= '0' when links_nsynced = all_zeros else '1'; + co_error <= '0' when links_error = all_zeros else '1'; + co_correct_data <= '1' when links_correct_data = all_ones else '0'; + + links_rx: for i in 0 to LINKS - 1 generate + link: entity work.jesd204b_link_rx + generic map ( + K_character => K_character, + R_character => R_character, + A_character => A_character, + Q_character => Q_character, + ERROR_CONFIG => ERROR_CONFIG, + ADJCNT => CONFIG(i).ADJCNT, + BID => CONFIG(i).BID, + DID => CONFIG(i).DID, + HD => CONFIG(i).HD, + JESDV => CONFIG(i).JESDV, + PHADJ => CONFIG(i).PHADJ, + SUBCLASSV => CONFIG(i).SUBCLASSV, + K => CONFIG(i).K, + CS => CONFIG(i).CS, + M => CONFIG(i).M, + S => CONFIG(i).S, + L => CONFIG(i).L, + F => CONFIG(i).F, + CF => CONFIG(i).CF, + N => CONFIG(i).N, + Nn => CONFIG(i).Nn, + ADJDIR => CONFIG(i).ADJDIR) + port map ( + ci_char_clk => ci_char_clk, + ci_frame_clk => ci_frame_clk, + ci_reset => ci_reset, + ci_request_sync => ci_request_sync, + co_nsynced => links_nsynced(i), + co_error => links_error(i), + di_transceiver_data => di_transceiver_data(sumCummulativeLanes(i) to sumCummulativeLanes(i) + CONFIG(i).L - 1), + do_samples => do_samples(i), -- do_samples(sumCummulativeConverters(i) to sumCummulativeConverters(i) + CONFIG(i).M - 1), + co_frame_state => co_frame_state(i), + co_correct_data => links_correct_data(i)); + end generate links_rx; + +end architecture a1; diff --git a/src/jesd204b_pkg.vhd b/src/jesd204b_pkg.vhd index 5ff7773..983ac89 100644 --- a/src/jesd204b_pkg.vhd +++ b/src/jesd204b_pkg.vhd @@ -1,5 +1,7 @@ library ieee; use ieee.std_logic_1164.all; +use work.transport_pkg.all; +use work.data_link_pkg.all; -- Package for jesd204b types package jesd204b_pkg is @@ -7,4 +9,7 @@ package jesd204b_pkg is -- array input data from lanes type lane_input_array is array (natural range <>) of std_logic_vector(9 downto 0); + -- array for link configs used in multipoint link + type link_config_array is array (natural range <>) of link_config; + end package jesd204b_pkg; diff --git a/src/transport/transport_pkg.vhd b/src/transport/transport_pkg.vhd index eb4d345..4df3e3e 100644 --- a/src/transport/transport_pkg.vhd +++ b/src/transport/transport_pkg.vhd @@ -35,5 +35,6 @@ package transport_pkg is -- Array of samples in one frame by converter and by sample (used with oversampling) type samples_array is array (natural range <>, natural range <>) of sample; + type simple_samples_array is array (natural range <>) of samples_array; end package transport_pkg; diff --git a/testbench/jesd204b_multipoint_data_tb.vhd b/testbench/jesd204b_multipoint_data_tb.vhd new file mode 100644 index 0000000..fb7eea8 --- /dev/null +++ b/testbench/jesd204b_multipoint_data_tb.vhd @@ -0,0 +1,191 @@ +library ieee; +use ieee.std_logic_1164.all; +use work.testing_functions.all; +use work.jesd204b_pkg.all; +use work.data_link_pkg.all; +use work.transport_pkg.all; + +entity jesd204b_multipoint_rx_data_tb is +end entity jesd204b_multipoint_rx_data_tb; + +architecture a1 of jesd204b_multipoint_rx_data_tb is + constant LANES : integer := 2; + constant LINKS : integer := 2; + constant CONFIG : link_config_array(0 to LINKS - 1) := + ( + (0, '0', 0, 0, 2, 0, 2, '0', 1, 9, 1, 0, 1, 14, 16, '0', 1, '0', 0, "00000000", "00000000", "000000000", 0), + (0, '0', 0, 0, 2, 0, 2, '0', 1, 9, 1, 0, 1, 14, 16, '0', 1, '0', 0, "00000000", "00000000", "000000000", 0) + ); + + type octet_data is record + data : std_logic_vector(7 downto 0); + k : std_logic; + end record octet_data; + + type lane_data_array is array (natural range <>) of octet_data; + type test_vector is record + data : lane_data_array(0 to LANES-1); + end record test_vector; + + type test_vector_array is array (natural range <>) of test_vector; + + constant char_offset : integer := 2; + constant char_prepend : std_logic_vector(char_offset-1 downto 0) := "00"; + constant test_vectors : test_vector_array := + ( + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("10111100", '1'), ("10111100", '1'))), + (data => (("10111100", '1'), ("10111100", '1'))), + (data => (("10111100", '1'), ("10111100", '1'))), + (data => (("10111100", '1'), ("10111100", '1'))), + (data => (("10111100", '1'), ("10111100", '1'))), + (data => (("10111100", '1'), ("10111100", '1'))), + (data => (("10111100", '1'), ("10111100", '1'))), + (data => (("10111100", '1'), ("10111100", '1'))), + (data => (("10111100", '1'), ("10111100", '1'))), + (data => (("00011100", '1'), ("00011100", '1'))), -- 1st ILAS multiframe start + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("01111100", '1'), ("01111100", '1'))), -- 1st ILAS multiframe end + (data => (("00011100", '1'), ("00011100", '1'))), -- 2nd ILAS multiframe start + (data => (("10011100", '1'), ("10011100", '1'))), -- configuration start delimiter + (data => (("10101010", '0'), ("10101010", '0'))), -- DID + (data => (("01111110", '0'), ("01111110", '0'))), -- ADJCNT,BID + (data => (("01101010", '0'), ("01101010", '0'))), -- X,ADJDIR,PHADJ,LID + (data => (("11011110", '0'), ("11011110", '0'))), -- SCR,X,L + (data => (("11001100", '0'), ("11001100", '0'))), -- F + (data => (("01011111", '0'), ("01011111", '0'))), -- X, K + (data => (("00110011", '0'), ("00110011", '0'))), -- M + (data => (("10000011", '0'), ("10000011", '0'))), -- CS,X,N + (data => (("00111101", '0'), ("00111101", '0'))), -- SUBCLASSV,Nn + (data => (("00000000", '0'), ("00000000", '0'))), -- JESDV,S + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("01111100", '1'), ("01111100", '1'))), -- 2nd ILAS multiframe end + (data => (("00011100", '1'), ("00011100", '1'))), -- 3rd ILAS multiframe start + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("01111100", '1'), ("01111100", '1'))), -- 3rd ILAS multiframe end + (data => (("00011100", '1'), ("00011100", '1'))), -- 4th ILAS multiframe start + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("00000000", '0'), ("00000000", '0'))), + (data => (("01111100", '1'), ("01111100", '1'))), -- 4th ILAS multiframe end + (data => (("10101010", '0'), ("01010101", '0'))), -- data + (data => (("10101001", '0'), ("01010110", '0'))), + (data => (("11111111", '0'), ("00000000", '0'))), -- data + (data => (("11111110", '0'), ("00000001", '0'))), + (data => (("00000001", '0'), ("11111110", '0'))), -- data + (data => (("11111111", '0'), ("00000000", '0'))) + ); + + constant char_clk_period : time := 1 ns; -- The clock period + constant frame_clk_period : time := 1 ns * CONFIG(0).F; -- The clock period + + signal di_transceiver_data : lane_input_array(0 to LANES-1); + signal di_lane_data : lane_data_array(0 to LANES-1); + + signal char_clk : std_logic := '0'; -- The clock + signal frame_clk : std_logic := '0'; -- The clock + signal reset : std_logic := '0'; -- The reset + + signal test_vec_index : integer := 0; + + signal co_nsynced : std_logic; + signal co_error : std_logic; + signal do_samples : simple_samples_array (0 to 1) (0 to CONFIG(0).M - 1, 0 to CONFIG(0).S - 1) + (data(CONFIG(0).N - 1 downto 0), ctrl_bits(CONFIG(0).CS - 1 downto 0)); + signal co_correct_data : std_logic; + +begin -- architecture a1 + uut : entity work.jesd204b_multipoint_link_rx + generic map ( + LINKS => LINKS, + LANES => LANES, + CONVERTERS => 2, + CONFIG => CONFIG) + port map ( + ci_char_clk => char_clk, + ci_frame_clk => frame_clk, + ci_reset => reset, + ci_request_sync => '0', + di_transceiver_data => di_transceiver_data, + co_nsynced => co_nsynced, + co_error => co_error, + do_samples => do_samples, + co_correct_data => co_correct_data); + + encoders: for i in 0 to LANES-1 generate + encoder: entity work.an8b10b_encoder + port map ( + reset => reset, + clk => char_clk, + ena => '1', + KI => di_lane_data(i).k, + datain => di_lane_data(i).data, + dataout => di_transceiver_data(i)); + end generate encoders; + + char_clk <= not char_clk after char_clk_period/2; + frame_clk <= not frame_clk after frame_clk_period/2; + reset <= '1' after char_clk_period*2; + + test: process is + begin -- process test + wait for char_clk_period*2; + + for i in test_vectors'range loop + test_vec_index <= i; + di_lane_data <= test_vectors(i).data; + wait for char_clk_period; + end loop; -- i + + wait for 1000 ms; + end process test; +end architecture a1; -- 2.48.1