Hi,
I am trying to design an ethernet controller for my Genesys 2. From specifications (RGMII, Realtek RTL8211E-VL PHY) or other projects (special thanks to @zygot and his Ethernet PHY Test Tool project), I have successfully implemented the Tx transmitter part: I can now see the different packets with WireShark. However, I am now facing a problem in the Rx receiver part. After analyzing the signals, I've noticed that the issue seems to come from the signals that are received from the PHY. Below is a screenshot of a received packet observed from an ILA: the sampling clock is at 500MHz (so the waveform accuracy must be put into perspective).
The original packet starts with: ff ff ff ff ff ff a4 b1 c1 31 d8 05 08 00 45 00 ...
But as early as the preamble/SFD, it seems that a shift exists with the first 4 bits received on a falling edge. So my frame becomes 5X 55 55 55 55 55 55 fd ff ... instead of the expected 55 55 55 55 55 55 55 5d ff... And it stays like that until the end of the frame, and for all the received frames.
Here are the corresponding Verilog and constraints parts inspired by the previously mentioned sources:
// Verilog top file
input logic i_eth_mii_rx_clk; // Input Rx Clock
input logic i_eth_mii_rx_ctl; // Input Rx CTL
input logic [3:0] i_eth_mii_rx_data; // Input Rx Data
logic w_eth_mii_rx_clk; // Internal Rx Clock
logic w_eth_mii_rx_rst; // Internal Rx Reset
logic [1:0] w_eth_mii_rx_ctl; // Internal Rx CTL
logic w_eth_mii_rx_en; // Internal Rx Enable
logic w_eth_mii_rx_err; // Internal Rx Error
logic [7:0] w_eth_mii_rx_data; // Internal Rx Data
...
assign w_eth_mii_rx_clk = i_eth_mii_rx_clk;
...
IDDR #(
.DDR_CLK_EDGE ( "SAME_EDGE_PIPELINED" ),
.INIT_Q1 ( 1'b0 ),
.INIT_Q2 ( 1'b0 ),
.SRTYPE ( "SYNC" )
) m_IDDR_eth_mii_rx_en (
.Q1 ( w_eth_mii_rx_ctl[0] ),
.Q2 ( w_eth_mii_rx_ctl[1] ),
.C ( w_eth_mii_rx_clk ),
.CE ( 1'b1 ),
.D ( i_eth_mii_rx_ctl ),
.R ( w_eth_mii_rx_rst ),
.S ( 1'b0 )
);
assign w_eth_mii_rx_en = w_eth_mii_rx_ctl[0];
assign w_eth_mii_rx_err = w_eth_mii_rx_ctl[1] ^ w_eth_mii_rx_ctl[0];
genvar et;
generate
for (et = 0; et < 4; et = et + 1) begin
IDDR #(
.DDR_CLK_EDGE ( "SAME_EDGE_PIPELINED" ),
.INIT_Q1 ( 1'b0 ),
.INIT_Q2 ( 1'b0 ),
.SRTYPE ( "SYNC" )
) m_IDDR_eth_mii_rx_data (
.Q1 ( w_eth_mii_rx_data[et + 0] ),
.Q2 ( w_eth_mii_rx_data[et + 4] ),
.C ( w_eth_mii_rx_clk ),
.CE ( 1'b1 ),
.D ( i_eth_mii_rx_data[et] ),
.R ( w_eth_mii_rx_rst ),
.S ( 1'b0 )
);
end
endgenerate
# Top constraints file
...
## Ethernet PHY
set_property -dict { PACKAGE_PIN AK16 IOSTANDARD LVCMOS18 } [get_ports { i_eth_int_b }];
set_property -dict { PACKAGE_PIN AK15 IOSTANDARD LVCMOS18 } [get_ports { i_eth_pme_b }];
set_property -dict { PACKAGE_PIN AH24 IOSTANDARD LVCMOS33 } [get_ports { o_eth_phy_rstn }];
set_property -dict { PACKAGE_PIN AF12 IOSTANDARD LVCMOS15 } [get_ports { o_eth_mii_mdc }];
set_property -dict { PACKAGE_PIN AG12 IOSTANDARD LVCMOS15 } [get_ports { t_eth_mii_mdio }];
set_property -dict { PACKAGE_PIN AE10 IOSTANDARD LVCMOS15 } [get_ports { o_eth_mii_tx_clk }];
set_property -dict { PACKAGE_PIN AK14 IOSTANDARD LVCMOS15 } [get_ports { o_eth_mii_tx_ctl }];
set_property -dict { PACKAGE_PIN AJ12 IOSTANDARD LVCMOS15 } [get_ports { o_eth_mii_tx_data[0] }];
set_property -dict { PACKAGE_PIN AK11 IOSTANDARD LVCMOS15 } [get_ports { o_eth_mii_tx_data[1] }];
set_property -dict { PACKAGE_PIN AJ11 IOSTANDARD LVCMOS15 } [get_ports { o_eth_mii_tx_data[2] }];
set_property -dict { PACKAGE_PIN AK10 IOSTANDARD LVCMOS15 } [get_ports { o_eth_mii_tx_data[3] }];
set_property -dict { PACKAGE_PIN AG10 IOSTANDARD LVCMOS15 } [get_ports { i_eth_mii_rx_clk }];
set_property -dict { PACKAGE_PIN AH11 IOSTANDARD LVCMOS15 } [get_ports { i_eth_mii_rx_ctl }];
set_property -dict { PACKAGE_PIN AJ14 IOSTANDARD LVCMOS15 } [get_ports { i_eth_mii_rx_data[0] }];
set_property -dict { PACKAGE_PIN AH14 IOSTANDARD LVCMOS15 } [get_ports { i_eth_mii_rx_data[1] }];
set_property -dict { PACKAGE_PIN AK13 IOSTANDARD LVCMOS15 } [get_ports { i_eth_mii_rx_data[2] }];
set_property -dict { PACKAGE_PIN AJ13 IOSTANDARD LVCMOS15 } [get_ports { i_eth_mii_rx_data[3] }];
## Clocks
create_clock -add -name CLK_ETH_MII_RX -period 8.00 -waveform {0.0 4.0} [get_ports {i_eth_mii_rx_clk}];
create_generated_clock -name CLK_25MHZ [get_pins m_MMCME2_BASE/CLKOUT1]
create_generated_clock -name CLK_50MHZ [get_pins m_MMCME2_BASE/CLKOUT3] # Design Cock
create_generated_clock -name CLK_125MHZ [get_pins m_MMCME2_BASE/CLKOUT4] # Tx Clock
create_generated_clock -name CLK_125MHZ_P90 [get_pins m_MMCME2_BASE/CLKOUT5] # Tx Clock with 90 Phase Shift
create_generated_clock -name CLK_500MHZ [get_pins m_MMCME2_BASE/CLKOUT6] # ILA Clock
set_clock_groups -asynchronous -group {CLK_50MHZ CLK_ETH_MII_RX CLK_125MHZ}
After several checks, I seem to have used the same blocks (IDDR) and constraints as in the examples I've seen. However, what I'm observing doesn't seem to be consistent with what I expected. Any idea what I might have missed as a problem or constraint? I'm pretty sure it must be an obvious problem, but impossible for me to identify it ...
Thanks for your help!