Actually, assuming that each clock domain is fast enough for 10 MBit/s, there is no problem running crazy ratios.
168 lines
4.8 KiB
VHDL
168 lines
4.8 KiB
VHDL
-- -------------------------------------------------------------------------- --
|
|
-- 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 <markus@notsyncing.net>
|
|
-- 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_i : byte;
|
|
|
|
begin
|
|
-- -------------------------------------------------------------------------
|
|
-- Drives: PHY clock domain
|
|
-- -------------------------------------------------------------------------
|
|
|
|
-- Reset synchronizer for PHY
|
|
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;
|
|
|
|
-- Operate Trashernet in PHY clock domain
|
|
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
|
|
);
|
|
|
|
-- Latch data in PHY clock domain when valid is strobed
|
|
-- If the other clock domain is slower than the time it takes for the strobe to synchronize,
|
|
-- `phy_phy_out.rx_data` will already have shifted in the next bit and no longer be valid.
|
|
-- Therefore, we need to latch it here.
|
|
rxdff : process(phy_clk, rst) is
|
|
begin
|
|
if rst then
|
|
rx_data_i <= (others => '0');
|
|
|
|
elsif rising_edge(phy_clk) then
|
|
if phy_phy_out.rx_data_valid then
|
|
rx_data_i <= phy_phy_out.rx_data;
|
|
end if;
|
|
end if;
|
|
end process rxdff;
|
|
|
|
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 => phy_out.rx_data_valid
|
|
);
|
|
|
|
phy_out.rx_data <= rx_data_i; -- No need to synchronize in new clock domain as latched data has been stable for a while thanks to the delay in the _valid synchronizer
|
|
|
|
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;
|