-- -------------------------------------------------------------------------- -- -- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- -- -------------------------------------------------------------------------- -- -- trashernet_phy_cdc.vhd : Dual clock wrapper for the trashernet_phy -- Implements CDC between the PHY and DATA clock domains. -- -------------------------------------------------------------------------- -- -- Author : Markus Koch -- Contributors : None -- License : Mozilla Public License (MPL) Version 2 -- -------------------------------------------------------------------------- -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.trashernet_pkg.all; entity trashernet_phy_cdc is generic( F_CLK : in integer; -- Clock frequency of design IF F_CLK_PHY : in integer -- PHY clock (expected to be faster than F_CLK) ); port( -- Global clk : in std_logic; -- Clock for internal interface phy_clk : in std_logic; -- Clock for PHY (rx_p, tx_p) rst : in std_logic; -- Asynchronous reset -- PHY application interface phy_out : out phy_out_t; -- PHY application IF (out) phy_in : in phy_in_t; -- PHY application IF (in) -- Ethernet physical signals rx_p : in std_logic; tx_p : out std_logic; tx_n : out std_logic ); end entity trashernet_phy_cdc; architecture rtl of trashernet_phy_cdc is -- PHY signals signal phy_rst : std_logic; signal phy_phy_out : phy_out_t; signal phy_phy_in : phy_in_t; -- Helper signals signal rx_data_valid_i : std_logic; begin assert F_CLK_PHY > 2 * F_CLK report "F_CLK_PHY must be at least 2x F_CLK" severity failure; -- ------------------------------------------------------------------------- -- Drives: PHY clock domain -- ------------------------------------------------------------------------- rstsync : process(phy_clk, rst) is begin if rst then phy_rst <= '1'; elsif rising_edge(phy_clk) then phy_rst <= '0'; end if; end process rstsync; trashernet_phy_inst : entity work.trashernet_phy generic map( F_CLK => F_CLK_PHY ) port map( clk => phy_clk, rst => phy_rst, phy_out => phy_phy_out, phy_in => phy_phy_in, rx_p => rx_p, tx_p => tx_p, tx_n => tx_n ); synchronizer_txen_inst : entity work.synchronizer generic map( SIZE => 5 ) port map( clk => phy_clk, rst => phy_rst, data_in => phy_in.tx_data_en, data_out => phy_phy_in.tx_data_en ); phy_phy_in.tx_data <= phy_in.tx_data; -- When tx_data_en is through the synchronizer, this should be stable (and in the other direction, it should only change when we don't read it anyways) -- ------------------------------------------------------------------------- -- Drives: System clock domain -- ------------------------------------------------------------------------- cdc_strobe_rxdv_inst : entity work.cdc_strobe port map( a_clk => phy_clk, a_rst => phy_rst, a_in => phy_phy_out.rx_data_valid, b_clk => clk, b_rst => rst, b_out => rx_data_valid_i ); rxdvff : process(clk, rst) is begin if rst then phy_out.rx_data_valid <= '0'; elsif rising_edge(clk) then phy_out.rx_data_valid <= rx_data_valid_i; phy_out.rx_data <= phy_phy_out.rx_data; -- Data should be stable after the time it takes the _valid signal to go through the synchronizer end if; end process rxdvff; cdc_strobe_rxer_inst : entity work.cdc_strobe port map( a_clk => phy_clk, a_rst => phy_rst, a_in => phy_phy_out.rx_error, b_clk => clk, b_rst => rst, b_out => phy_out.rx_error ); synchronizer_rxa_inst : entity work.synchronizer port map( clk => clk, rst => rst, data_in => phy_phy_out.rx_active, data_out => phy_out.rx_active ); cdc_strobe_txack_inst : entity work.cdc_strobe port map( a_clk => phy_clk, a_rst => phy_rst, a_in => phy_phy_out.tx_data_ack, b_clk => clk, b_rst => rst, b_out => phy_out.tx_data_ack ); synchronizer_crs_inst : entity work.synchronizer port map( clk => clk, rst => rst, data_in => phy_phy_out.carrier_detect, data_out => phy_out.carrier_detect ); synchronizer_txa_inst : entity work.synchronizer port map( clk => clk, rst => rst, data_in => phy_phy_out.tx_active, data_out => phy_out.tx_active ); end architecture rtl;