--==============================================================================
--! @file rx_word_aligner.vhd
--==============================================================================
--! Standard library
library ieee;
--! Standard packages
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--! Specific packages
-------------------------------------------------------------------------------
-- --
-- CERN, EP-ESE-BE, HPTD
-- --
-------------------------------------------------------------------------------
--
-- unit name: Word aligner for HPTD tests (rx_word_aligner)
--
--! @brief FSM for word alignment used for HPTD tests
--! - Not recommended to be used for final applications but instead to use the corresponding protocol word aligner (i.e. TTC-PON, GBT-FPGA, LpGBT-FPGA)
--!
--! @author Eduardo Brandao de Souza Mendes - eduardo.brandao.de.souza.mendes@cern.ch
--! @date 22\05\2018
--! @version 1.0
--! @details
--!
--! Dependencies:\n
--!
--!
--! References:\n
--! \n
--!
--!
--! Modified by:\n
--! Author: Eduardo Brandao de Souza Mendes
-------------------------------------------------------------------------------
--! \n\nLast changes:\n
--! 22\05\2018 - EBSM - Created\n
--!
-------------------------------------------------------------------------------
--! @todo - \n
--! \n
--
-------------------------------------------------------------------------------
--==============================================================================
--! Entity declaration for rx_word_aligner
--==============================================================================
entity rx_word_aligner is
generic(
g_HDR_GOOD_TO_LOCK : integer := 64; --! number of consecutive good headers to lock
g_HDR_BAD_TO_UNLOCK : integer := 4; --! number of consecutive bad headers to unlock
g_DATA_WORD_WIDTH : integer := 32; --! rx data word width
g_SKIP_PULSE_DURATION : integer := 2; --! duration of the skip pulse (for K7, it is one clock cycle, for KU it is 2)
g_WAIT_BETWEEN_SKIP : integer := 32 --! minimum number of clock cycles (rxusrclk2) to wait between rx_slide pulses
);
port (
clk_rxusr_i : in std_logic; --! rxusrclk2
reset_i : in std_logic; --! active high sync. reset
enable_i : in std_logic; --! control scheduling
header_i : in std_logic; --! input header
rx_locked_o : out std_logic; --! rx locked was achieved
rx_slide_o : out std_logic; --! rx slide used for alignment
clk_sys_i : in std_logic; --! clock system input (free-running)
rx_reset_o : out std_logic --! reset transceiver to avoid odd bitslip number
);
end rx_word_aligner;
--==============================================================================
-- architecture declaration
--==============================================================================
architecture rtl of rx_word_aligner is
--! Function declaration
function fcn_reduce_or(arg: std_logic_vector) return std_logic is
variable result: std_logic;
begin
result := '0';
for i in arg'range loop
result := result or arg(i);
end loop;
return result;
end;
function fcn_log2( input:integer ) return integer is
variable temp,log:integer;
begin
temp:=input;
log:=0;
while (temp /= 0) loop
temp:=temp/2;
log:=log+1;
end loop;
return log;
end function fcn_log2;
--! Constant declaration
constant c_MAX_SKIP_WAIT : integer := g_SKIP_PULSE_DURATION+g_WAIT_BETWEEN_SKIP;
--! Signal declaration
-- FSM HDR-framing
-- principle:
-- HUNT : received a correct HDR -> GOING_SYNC
-- received a wrong HDR -> SKIP CYCLE
-- SKIP CYCLE : slips a bit and then -> SKIP_CYCLE_WAIT
-- SKIP_CYCLE_WAIT : waits the slip to be finished and then -> HUNT
-- GOING_SYNC : received a consecutive number of correct HDR and bit slip is even -> SYNC
-- received a consecutive number of correct HDR and bit slip is odd -> RESET_RX
-- received a wrong HDR -> HUNT
-- RESET_RX : basically waits forever (until the reset takes effect)
-- SYNC : received a wrong HDR -> GOING_HUNT
-- GOING_HUNT : received a consecutive number of wrong HDR -> HUNT
-- received a correct HDR -> SYNC
type t_FRAMING_FSM_STATE is (HUNT, SKIP_CYCLE, SKIP_CYCLE_WAIT, GOING_SYNC, SYNC, RESET_RX, GOING_HUNT);
signal framing_state : t_FRAMING_FSM_STATE;
signal correct_hdr_count : integer range 0 to (g_HDR_GOOD_TO_LOCK + 1);
signal wrong_hdr_count : integer range 0 to (g_HDR_BAD_TO_UNLOCK + 1);
signal skip_cycle_pipe : std_logic_vector(g_SKIP_PULSE_DURATION-1 downto 0);
signal skip_cycle_s : std_logic;
signal skip_cycle_cntr : integer range 0 to g_DATA_WORD_WIDTH-1;
signal skip_wait_cntr : integer range 0 to c_MAX_SKIP_WAIT;
signal reset_rx_latch : std_logic;
signal reset_rx_sys_meta : std_logic;
signal reset_rx_sys_r : std_logic;
signal reset_rx_sys_r2 : std_logic;
signal reset_rx_pipe : std_logic_vector(4 downto 0);
begin
--============================================================================
-- Process p_hdr_framing_fsm
--! FSM for HDR-framing locking procedure
--! read: enable_i, header_i, correct_hdr_count, wrong_hdr_count\n
--! write: -\n
--! r/w: framing_state \n
--============================================================================
p_hdr_framing_fsm : process(clk_rxusr_i, reset_i) -- asynchronous
begin
if(reset_i = '1') then
framing_state <= HUNT;
elsif(clk_rxusr_i'event and clk_rxusr_i='1') then
case framing_state is
when HUNT =>
if(enable_i='1') then
if(header_i='1') then
framing_state <= GOING_SYNC;
else
framing_state <= SKIP_CYCLE;
end if;
end if;
when SKIP_CYCLE =>
framing_state <= SKIP_CYCLE_WAIT;
when SKIP_CYCLE_WAIT =>
if(skip_wait_cntr >= g_WAIT_BETWEEN_SKIP) then
framing_state <= HUNT;
end if;
when GOING_SYNC =>
if(enable_i='1') then
if(correct_hdr_count >= g_HDR_GOOD_TO_LOCK) then
if( to_unsigned(skip_cycle_cntr,fcn_log2(g_DATA_WORD_WIDTH))(0) = '0' ) then
framing_state <= SYNC;
else
framing_state <= RESET_RX;
end if;
elsif(header_i='0') then
framing_state <= HUNT;
end if;
end if;
when SYNC =>
if(enable_i='1') then
if(header_i='0') then
framing_state <= GOING_HUNT;
end if;
end if;
when RESET_RX =>
framing_state <= RESET_RX;
when GOING_HUNT =>
if(enable_i='1') then
if(wrong_hdr_count >= g_HDR_BAD_TO_UNLOCK) then
framing_state <= HUNT;
elsif(header_i='1') then
framing_state <= SYNC;
end if;
end if;
when others => framing_state <= HUNT;
end case;
end if;
end process p_hdr_framing_fsm;
--============================================================================
-- Process p_hdr_framing_fsm_aux
--! counters for consecutive correct/wrong HDR frame
--! read: clk_i, reset_i, nibble_sent_i, framing_state\n
--! write: - \n
--! r/w: correct_hdr_count, wrong_hdr_count \n
--============================================================================
p_hdr_framing_fsm_aux : process(clk_rxusr_i)
begin
if(clk_rxusr_i'event and clk_rxusr_i='1') then
if(reset_i='1') then
correct_hdr_count <= 0;
wrong_hdr_count <= 0;
elsif(enable_i='1') then
if(framing_state=GOING_HUNT) then
if(header_i='0') then
wrong_hdr_count <= wrong_hdr_count + 1;
end if;
else
wrong_hdr_count <= 0;
end if;
if(framing_state=GOING_SYNC) then
if(header_i='1') then
correct_hdr_count <= correct_hdr_count + 1;
end if;
else
correct_hdr_count <= 0;
end if;
end if;
end if;
end process p_hdr_framing_fsm_aux;
--============================================================================
-- Process p_skip_cycle
--! skip cycle related functions (counters, pulse generation)
--! read: framing_state\n
--! write: - \n
--! r/w: skip_cycle_cntr, skip_wait_cntr, skip_cycle_pipe, skip_cycle_s \n
--============================================================================
p_skip_cycle : process(clk_rxusr_i)
begin
if(clk_rxusr_i'event and clk_rxusr_i='1') then
-- skip_cycle (rx_slide) generation
if(framing_state = SKIP_CYCLE) then
skip_cycle_pipe(0) <= '1';
else
skip_cycle_pipe(0) <= '0';
end if;
skip_cycle_pipe(skip_cycle_pipe'left downto 1) <= skip_cycle_pipe(skip_cycle_pipe'left - 1 downto 0);
skip_cycle_s <= fcn_reduce_or(skip_cycle_pipe);
-- skip_cycle counter to keep track of even/odd bitslip
-- the author of the code is aware that a single bit here is needed but in future it might be interesting to know how many skips were issued
if(reset_i='1') then
skip_cycle_cntr <= 0;
elsif(framing_state = SKIP_CYCLE) then
if(skip_cycle_cntr < g_DATA_WORD_WIDTH-1) then
skip_cycle_cntr <= skip_cycle_cntr + 1;
else
skip_cycle_cntr <= 0;
end if;
end if;
-- wait between skip pulses counter
if(framing_state = SKIP_CYCLE_WAIT) then
if(skip_wait_cntr < c_MAX_SKIP_WAIT) then
skip_wait_cntr <= skip_wait_cntr + 1;
end if;
else
skip_wait_cntr <= 0;
end if;
end if;
end process p_skip_cycle;
rx_slide_o <= skip_cycle_s;
-- create the reset latched whose rising edge will be used in the clk sys domain
p_reset_rx : process(clk_rxusr_i)
begin
if(clk_rxusr_i'event and clk_rxusr_i='1') then
if(framing_state = RESET_RX) then
reset_rx_latch <= '1';
else
reset_rx_latch <= '0';
end if;
end if;
end process p_reset_rx;
-- reset is issued with the clk_sys_i (free-running) to ensure stability
p_reset_sync : process(clk_sys_i)
begin
if(clk_sys_i'event and clk_sys_i='1') then
reset_rx_sys_meta <= reset_rx_latch;
reset_rx_sys_r <= reset_rx_sys_meta;
reset_rx_sys_r2 <= reset_rx_sys_r;
if(reset_rx_sys_r = '1' and reset_rx_sys_r2 = '0') then
reset_rx_pipe(0) <= '1';
else
reset_rx_pipe(0) <= '0';
end if;
reset_rx_pipe(reset_rx_pipe'left downto 1) <= reset_rx_pipe(reset_rx_pipe'left-1 downto 0);
rx_reset_o <= fcn_reduce_or(reset_rx_pipe);
end if;
end process p_reset_sync;
-- rx locked condition
p_rx_locked : process(clk_rxusr_i, reset_i)
begin
if(reset_i='1') then
rx_locked_o <= '0';
elsif(clk_rxusr_i'event and clk_rxusr_i='1') then
if(framing_state = SYNC or framing_state = GOING_HUNT) then
rx_locked_o <= '1';
else
rx_locked_o <= '0';
end if;
end if;
end process p_rx_locked;
end architecture rtl;
--==============================================================================
-- architecture end
--==============================================================================