Compare commits

..

5 Commits

Author SHA1 Message Date
7379df7b74 phy: Improve clock accuracy at 0.5 dividers at the cost of some jitter 2024-07-15 13:34:54 +02:00
21a0d0e69a phy_cdc: Allow unlimited clock ratios
Actually, assuming that each clock domain is fast enough for 10 MBit/s,
there is no problem running crazy ratios.
2024-07-06 18:05:00 +02:00
943febcb99 phy_cdc: Fix synchronization behavior
For low ratios of the two clock domains, the slow clock domain would
receive incorrect data as the fast (PHY) clock domain's data output
would have already updated with the first bit of the next byte by
the time the slow (system) clock domain read it.

This fix registers the data in the source clock domain as opposed
to the destination clock domain. This is allowed because the valid
strobe is synchronized in a way to guarantee that data has been
valid for a while when it becomes asserted.

Additionally, `phy_out.rx_data` and `phy_out.rx_data_valid` are no
longer registered in the destination (system) clock domain as this
now introduces unnecessary latency. Also, this was broken the way
it was anyway as `rx_active` and `rx_error` would've also had to be
registered to keep all timings in sync.
2024-07-06 17:58:11 +02:00
da7e329939 des: Fix compatibility with Yosys 2024-06-22 11:19:02 +02:00
4bce111d69 doc: Add synthesized size for PHY-only design 2024-02-04 09:34:36 +01:00
5 changed files with 41 additions and 23 deletions

View File

@ -28,6 +28,14 @@ Notes:
* The RX pull up/downs are probably not the best idea, and fixable in a better way. But for now, this shall work -- and suffice! * The RX pull up/downs are probably not the best idea, and fixable in a better way. But for now, this shall work -- and suffice!
* On the TX side, I'm grossly overloading the drivers. A line driver would probably be a good idea. Or you can just parallel multiple outputs ;D * On the TX side, I'm grossly overloading the drivers. A line driver would probably be a good idea. Or you can just parallel multiple outputs ;D
## Stats
Sythesized for a Lattice LFXP2-5E FPGA using LSE / Diamond v3.12:
| Configuration | Slices | LUT4 | Registers |
| ---------------- | ------ | ---- | --------- |
| `trashernet_phy` | 115 | 188 | 91 |
## License ## License
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.

View File

@ -141,10 +141,7 @@ begin
end if; end if;
when TXD => when TXD =>
if (not mac_in.tx_mac_data_en or not mac_out.tx_active) then state <= IDLE when (not mac_in.tx_mac_data_en or not mac_out.tx_active);
state <= IDLE;
sel <= SEL_ETH_I; -- To avoid arbitration errors, always select the highest priority one by default
end if;
end case; end case;
end if; end if;
end process arb; end process arb;

View File

@ -105,10 +105,7 @@ begin
end loop; end loop;
when TXD => when TXD =>
if not ipv4_in.tx_en then state <= IDLE when (not ipv4_in.tx_en);
state <= IDLE;
tx_sel <= ipv4_protocol_in'left; -- To avoid arbitration errors, always select the highest priority one by default
end if;
end case; end case;
end if; end if;
end process arb; end process arb;

View File

@ -253,6 +253,7 @@ begin
begin begin
if rst then if rst then
phy_out.rx_data_valid <= '0'; phy_out.rx_data_valid <= '0';
phy_out.rx_data <= (others => '0'); -- Needed for yosys to compile
elsif rising_edge(clk) then elsif rising_edge(clk) then
phy_out.rx_data_valid <= '0'; phy_out.rx_data_valid <= '0';
@ -296,8 +297,11 @@ begin
-- ------------------------------------------------------------------------- -- -------------------------------------------------------------------------
transmitter : block transmitter : block
constant TX_STB_CNT_MAX : integer := integer(round(real(F_CLK) / real((F_ETH * 2)))) - 1; constant TX_STB_CNT_IDEAL : real := real(F_CLK) / real((F_ETH * 2));
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX; constant TX_STB_SKIP_ERROR : real := abs (round(TX_STB_CNT_IDEAL) - TX_STB_CNT_IDEAL);
constant TX_STB_CNT_MAX : integer := integer(round(TX_STB_CNT_IDEAL + 0.25)) - 1; -- Round up starting for error > 0.25
constant TX_STB_SKIP_SECOND : boolean := TX_STB_SKIP_ERROR >= 0.25; -- Skip one clock cycle every other symbol to hit clock rate in between when the divider is close to x.5
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
type tx_state_t is (IDLE, NLP, TX, IPG); type tx_state_t is (IDLE, NLP, TX, IPG);
signal tx_state : tx_state_t; signal tx_state : tx_state_t;
@ -367,6 +371,9 @@ begin
if tx_stb_cnt = 0 then if tx_stb_cnt = 0 then
tx_stb_cnt <= TX_STB_CNT_MAX; tx_stb_cnt <= TX_STB_CNT_MAX;
if TX_STB_SKIP_SECOND and bit_stage = '1' then
tx_stb_cnt <= TX_STB_CNT_MAX - 1;
end if;
else else
tx_stb_cnt <= tx_stb_cnt - 1; tx_stb_cnt <= tx_stb_cnt - 1;
end if; end if;

View File

@ -44,14 +44,14 @@ architecture rtl of trashernet_phy_cdc is
signal phy_phy_in : phy_in_t; signal phy_phy_in : phy_in_t;
-- Helper signals -- Helper signals
signal rx_data_valid_i : std_logic; signal rx_data_i : byte;
begin 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 -- Drives: PHY clock domain
-- ------------------------------------------------------------------------- -- -------------------------------------------------------------------------
-- Reset synchronizer for PHY
rstsync : process(phy_clk, rst) is rstsync : process(phy_clk, rst) is
begin begin
if rst then if rst then
@ -61,6 +61,7 @@ begin
end if; end if;
end process rstsync; end process rstsync;
-- Operate Trashernet in PHY clock domain
trashernet_phy_inst : entity work.trashernet_phy trashernet_phy_inst : entity work.trashernet_phy
generic map( generic map(
F_CLK => F_CLK_PHY F_CLK => F_CLK_PHY
@ -75,6 +76,22 @@ begin
tx_n => tx_n 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 synchronizer_txen_inst : entity work.synchronizer
generic map( generic map(
SIZE => 5 SIZE => 5
@ -98,18 +115,10 @@ begin
a_in => phy_phy_out.rx_data_valid, a_in => phy_phy_out.rx_data_valid,
b_clk => clk, b_clk => clk,
b_rst => rst, b_rst => rst,
b_out => rx_data_valid_i b_out => phy_out.rx_data_valid
); );
rxdvff : process(clk, rst) is 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
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 cdc_strobe_rxer_inst : entity work.cdc_strobe
port map( port map(