From 186db2051444bb8e30abcb2b90d6749be7d8ec2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= Date: Wed, 16 Nov 2022 19:00:36 +0100 Subject: [PATCH] feat(link): add link controller --- src/data_link/link_controller.vhd | 108 ++++++++ testbench/data_link/link_controller_tb.vhd | 275 +++++++++++++++++++++ 2 files changed, 383 insertions(+) create mode 100644 src/data_link/link_controller.vhd create mode 100644 testbench/data_link/link_controller_tb.vhd diff --git a/src/data_link/link_controller.vhd b/src/data_link/link_controller.vhd new file mode 100644 index 0000000..627320c --- /dev/null +++ b/src/data_link/link_controller.vhd @@ -0,0 +1,108 @@ +library ieee; +use ieee.std_logic_1164.all; +use work.data_link_pkg.all; + +-- What does it do? +-- Well, it manages the states (init, cgs, ilas, data) +-- It sends sync if needed (irecoverrable error detected). It holds? ILAS data. +-- It should get all the control outputs from lane_alignment, +-- frame_alignment, an8b10bdecoder, char_alignment. +-- It should get the current data from an8b10bdecoder + +entity link_controller is + generic ( + K_character : std_logic_vector(7 downto 0) := "10111100"); + port ( + ci_char_clk : in std_logic; + ci_reset : in std_logic; + di_char : in character_vector; + + do_config : out link_config; + + ci_F : in integer range 0 to 256; + ci_K : in integer range 0 to 32; + + ci_lane_alignment_error : in std_logic; + ci_lane_alignment_aligned : in std_logic; + ci_lane_alignment_ready : in std_logic; + + ci_frame_alignment_error : in std_logic; + ci_frame_alignment_aligned : in std_logic; + + ci_resync : in std_logic; + + co_synced : out std_logic; + co_state : out link_state; + co_uncorrectable_error : out std_logic; + co_error : out std_logic); +end entity link_controller; + +architecture a1 of link_controller is + constant SYNC_COUNT : integer := 4; + signal synced : std_logic := '0'; + + signal reg_state : link_state := INIT; + signal reg_k_counter : integer range 0 to 15 := 0; + + signal ilas_finished : std_logic := '0'; + signal ilas_error : std_logic := '0'; + signal ilas_wrong_chksum : std_logic := '0'; + signal ilas_unexpected_char : std_logic := '0'; +begin -- architecture a1 + ilas: entity work.ilas_parser + port map ( + ci_char_clk => ci_char_clk, + ci_reset => ci_reset, + ci_F => ci_F, + ci_K => ci_K, + ci_state => reg_state, + di_char => di_char, + do_config => do_config, + co_finished => ilas_finished, + co_error => ilas_error, + co_wrong_chksum => ilas_wrong_chksum, + co_unexpected_char => ilas_unexpected_char); + + set_state: process (ci_char_clk, ci_reset) is + begin -- process set_state + if ci_reset = '0' then -- asynchronous reset (active low) + reg_state <= INIT; + elsif ci_char_clk'event and ci_char_clk = '1' then -- rising clock edge + if ci_resync = '1' then + reg_state <= INIT; + reg_k_counter <= 0; + elsif reg_state = CGS then + if reg_k_counter < SYNC_COUNT then + if di_char.d8b = K_character and di_char.kout = '1' then + reg_k_counter <= reg_k_counter + 1; + else + reg_k_counter <= 0; + end if; + elsif di_char.d8b /= K_character or di_char.kout = '0' then + reg_state <= ILS; + end if; + elsif di_char.d8b = K_character and di_char.kout = '1' then + reg_state <= CGS; + reg_k_counter <= 0; + elsif reg_state = ILS then + if ilas_finished = '1' then + reg_state <= DATA; + elsif ilas_error = '1' then + reg_state <= INIT; + end if; + elsif reg_state = DATA then + -- uncorrectable error? resync. + end if; + end if; + end process set_state; + + co_synced <= synced; + synced <= '0' when reg_state = INIT or (reg_state = CGS and reg_k_counter < SYNC_COUNT) else '1'; + + co_state <= reg_state; + -- TODO: add ILAS errors, add CGS error in case sync does not happen for long + -- time + co_error <= ci_lane_alignment_error or ci_frame_alignment_error or di_char.missing_error or di_char.disparity_error; + co_uncorrectable_error <= ilas_error; + +end architecture a1; diff --git a/testbench/data_link/link_controller_tb.vhd b/testbench/data_link/link_controller_tb.vhd new file mode 100644 index 0000000..ea4c0a9 --- /dev/null +++ b/testbench/data_link/link_controller_tb.vhd @@ -0,0 +1,275 @@ +library ieee; +use ieee.std_logic_1164.all; +use work.testing_functions.all; +use work.data_link_pkg.all; + +entity link_controller_tb is +end entity link_controller_tb; + +architecture a1 of link_controller_tb is + type test_vector is record + di_char : character_vector; + ci_resync : std_logic; + ci_lane_alignment_error : std_logic; + ci_lane_alignment_aligned : std_logic; + ci_lane_alignment_ready : std_logic; + + ci_frame_alignment_error : std_logic; + ci_frame_alignment_aligned : std_logic; + + expected_synced : std_logic; + expected_state : link_state; + expected_uncorrectable_error : std_logic; + expected_error : std_logic; + expected_config_index : integer; + end record test_vector; + + type config_array is array (natural range<>) of link_config; + constant config_vectors : config_array := + ( + ( + DID => 170, + ADJCNT => 7, + BID => 14, + ADJDIR => '1', + PHADJ => '1', + LID => 10, + SCR => '1', + L => 31, + F => 205, + K => 32, + M => 52, + CS => 2, + N => 4, + SUBCLASSV => 1, + Nn => 30, + JESDV => 0, + S => 1, + HD => '0', + CF => 0, + RES1 => "11111111", + RES2 => "00000000", + X => "010010000", + CHKSUM => 48 + ), + ( + DID => 170, + ADJCNT => 7, + BID => 14, + ADJDIR => '1', + PHADJ => '1', + LID => 10, + SCR => '1', + L => 31, + F => 205, + K => 32, + M => 52, + CS => 2, + N => 4, + SUBCLASSV => 1, + Nn => 30, + JESDV => 0, + S => 1, + HD => '0', + CF => 0, + RES1 => "11111111", + RES2 => "11111111", + X => "010010000", + CHKSUM => 48 + ) + ); + + type test_vector_array is array (natural range<>) of test_vector; + constant test_vectors : test_vector_array := + ( + --kout der noter char resync ler lal lre fer fal expsyn expstexpuner exper expconf + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '1', CGS, '0', '0', -1), + (('1', '0', '0', "00011100"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), --R + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('1', '0', '0', "01111100"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), --A + (('1', '0', '0', "00011100"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), --R + (('0', '0', '0', "10011100"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "10101010"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "01111110"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "01101010"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "11011110"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "11001100"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "01011111"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00110011"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "10000011"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00111101"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "11111111"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('0', '0', '0', "00110000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), + (('1', '0', '0', "01111100"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', -1), --A + (('1', '0', '0', "00011100"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), --R + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('1', '0', '0', "01111100"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), --A + (('1', '0', '0', "00011100"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), --R + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), + (('1', '0', '0', "01111100"), '0', '0', '0', '0', '0', '0', '1', ILS, '0', '0', 0), --A + (('0', '0', '0', "01010101"), '0', '0', '0', '0', '0', '0', '1', DATA, '0', '0', 0), + (('0', '0', '0', "01010101"), '0', '0', '0', '0', '0', '0', '1', DATA, '0', '0', 0), + (('0', '0', '0', "01010101"), '0', '0', '0', '0', '0', '0', '1', DATA, '0', '0', 0), + (('0', '0', '0', "01010101"), '0', '0', '0', '0', '0', '0', '1', DATA, '0', '0', 0), + (('0', '0', '0', "01010101"), '0', '0', '0', '0', '0', '0', '1', DATA, '0', '0', 0), + (('0', '0', '1', "01010101"), '0', '0', '0', '0', '0', '0', '1', DATA, '0', '1', 0), + (('0', '0', '0', "01010101"), '0', '1', '0', '0', '0', '0', '1', DATA, '0', '1', 0), + (('0', '0', '0', "01010101"), '0', '1', '0', '0', '1', '0', '1', DATA, '0', '1', 0), + (('0', '0', '0', "01010101"), '0', '0', '0', '0', '1', '0', '1', DATA, '0', '1', 0), + (('0', '0', '0', "01010101"), '1', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1), + (('0', '0', '0', "01010101"), '1', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1), + (('0', '0', '0', "01010101"), '1', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '0', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '1', CGS, '0', '0', -1), + (('1', '0', '0', "10111100"), '0', '0', '0', '0', '0', '0', '1', CGS, '0', '0', -1), + (('0', '0', '0', "00000000"), '1', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1), + (('0', '0', '0', "00000000"), '0', '0', '0', '0', '0', '0', '0', INIT, '0', '0', -1) + ); + + constant clk_period : time := 1 ns; + + constant F : integer range 0 to 256 := 17; + constant K : integer range 0 to 32 := 1; + + signal clk : std_logic := '0'; + signal reset : std_logic := '0'; + + signal di_char : character_vector; + signal do_config : link_config; + + signal ci_resync : std_logic := '0'; + signal ci_lane_alignment_error : std_logic := '0'; + signal ci_lane_alignment_aligned : std_logic := '0'; + signal ci_lane_alignment_ready : std_logic := '0'; + signal ci_frame_alignment_error : std_logic := '0'; + signal ci_frame_alignment_aligned : std_logic := '0'; + + + signal co_finished : std_logic; + signal co_state : link_state; + signal co_synced : std_logic; + signal co_error : std_logic; + signal co_uncorrectable_error : std_logic; + + signal test_data_index : integer := 0; + +begin -- architecture a1 + uut : entity work.link_controller + port map ( + ci_char_clk => clk, + ci_reset => reset, + ci_F => F, + ci_K => K, + ci_resync => ci_resync, + ci_lane_alignment_aligned => ci_lane_alignment_aligned, + ci_lane_alignment_error => ci_lane_alignment_error, + ci_lane_alignment_ready => ci_lane_alignment_ready, + ci_frame_alignment_aligned => ci_frame_alignment_aligned, + ci_frame_alignment_error => ci_frame_alignment_error, + di_char => di_char, + do_config => do_config, + co_synced => co_synced, + co_state => co_state, + co_uncorrectable_error => co_uncorrectable_error, + co_error => co_error + ); + + clk <= not clk after clk_period/2; + reset <= '1' after clk_period*2; + + test: process is + variable test_vec : test_vector; + variable prev_test_vec : test_vector; + begin -- process test + wait for clk_period*2; + + for i in test_vectors'range loop + test_data_index <= i; + test_vec := test_vectors(i); + di_char <= test_vec.di_char; + ci_resync <= test_vec.ci_resync; + ci_lane_alignment_aligned <= test_vec.ci_lane_alignment_aligned; + ci_lane_alignment_error <= test_vec.ci_lane_alignment_error; + ci_lane_alignment_ready <= test_vec.ci_lane_alignment_ready; + ci_frame_alignment_aligned <= test_vec.ci_frame_alignment_aligned; + ci_frame_alignment_error <= test_vec.ci_frame_alignment_error; + + if i > 0 then + prev_test_vec := test_vectors(i - 1); + + if prev_test_vec.expected_config_index > -1 then + assert do_config = config_vectors(prev_test_vec.expected_config_index) report "The config does not match. Index: " & integer'image(i-1) severity error; + end if; + + assert co_state = prev_test_vec.expected_state report "The state does not match. Index: " & integer'image(i-1) severity error; + assert co_synced = prev_test_vec.expected_synced report "The synced does not match. Index: " & integer'image(i-1) severity error; + assert co_error = prev_test_vec.expected_error report "The error does not match. Index: " & integer'image(i-1) severity error; + assert co_uncorrectable_error = prev_test_vec.expected_uncorrectable_error report "The uncorrectable error does not match. Index: " & integer'image(i-1) severity error; + end if; + + wait for clk_period; + end loop; -- i + wait for 100 ms; + end process test; +end architecture a1; -- 2.48.1