~ruther/jesd204b-vhdl

186db2051444bb8e30abcb2b90d6749be7d8ec2d — František Boháček 2 years ago 73aed1d
feat(link): add link controller
2 files changed, 383 insertions(+), 0 deletions(-)

A src/data_link/link_controller.vhd
A testbench/data_link/link_controller_tb.vhd
A src/data_link/link_controller.vhd => src/data_link/link_controller.vhd +108 -0
@@ 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;

A testbench/data_link/link_controller_tb.vhd => testbench/data_link/link_controller_tb.vhd +275 -0
@@ 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;

Do not follow this link