~ruther/jesd204b-vhdl

9ae96ce8b1bc7ad1c02d6f15128ac336037203c1 — František Boháček 2 years ago a176991
feat: add multipoint link support
A src/jesd204b_multipoint_link_rx.vhd => src/jesd204b_multipoint_link_rx.vhd +117 -0
@@ 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;

M src/jesd204b_pkg.vhd => src/jesd204b_pkg.vhd +5 -0
@@ 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;

M src/transport/transport_pkg.vhd => src/transport/transport_pkg.vhd +1 -0
@@ 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;

A testbench/jesd204b_multipoint_data_tb.vhd => testbench/jesd204b_multipoint_data_tb.vhd +191 -0
@@ 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;

Do not follow this link