-
-
Save lschuermann/ab5c34c9f48d00a93f0174fcbe5e62e0 to your computer and use it in GitHub Desktop.
NetFPGA SUME 10GBit/s SFP+ Ethernet Transceiver Configuration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright (c) 2021 Leon Schuermann <leon@is.currently.online> | |
# SPDX-License-Identifier: BSD-2-Clause | |
class NetFPGASUMEEthernetPhyInst(Module, AutoCSR, AutoDoc): | |
def __init__(self, sfps, sfp_mgt_refclk, sfp_clk_rst, i2c_mux, clk_125mhz): | |
# This module is specifically configured to instantiate four | |
# transceivers, so the four board SFPs are required. | |
assert set(sfps.keys()) == {1, 2, 3, 4}, \ | |
"This module expects to be passed the onboard SFPs 1, 2, 3 and 4" | |
# Make the passed clock signal accessible as a local clock domain | |
self.clock_domains.cd_eth_phy_125mhz = ClockDomain() | |
self.comb += [ | |
self.cd_eth_phy_125mhz.clk.eq(clk_125mhz.clk), | |
self.cd_eth_phy_125mhz.rst.eq(clk_125mhz.rst), | |
] | |
# Instatiate the I2C core to enable proper clockig on the | |
# Si5324 clock generator. | |
# Outward-facing I2C pins | |
i2c_scl_ts = TSTriple() | |
self.specials += i2c_scl_ts.get_tristate(i2c_mux.scl) | |
i2c_sda_ts = TSTriple() | |
self.specials += i2c_sda_ts.get_tristate(i2c_mux.sda) | |
# Wire up the 125MHz management clock to the I2C mux reset | |
self.comb += [ | |
i2c_mux.mux_reset.eq(self.cd_eth_phy_125mhz.rst) | |
] | |
# Signals connected between the Si5324 and I2C master core | |
si5324_i2c_sig = Record([ | |
("cmd_address", 7), | |
("cmd_start", 1), | |
("cmd_read", 1), | |
("cmd_write", 1), | |
("cmd_write_multiple", 1), | |
("cmd_stop", 1), | |
("cmd_valid", 1), | |
("cmd_ready", 1), | |
("data", 8), | |
("data_valid", 1), | |
("data_ready", 1), | |
("data_last", 1), | |
]) | |
# Control signals: whether the Si5324 core is still busy... | |
si5324_busy = Signal() | |
# ...and a start condition | |
si5324_start = Signal() | |
# Instatiate the Si5324 configuration core | |
self.specials += Instance( | |
"si5324_i2c_init", | |
name = "si5324_i2c_init_inst", | |
i_clk = self.cd_eth_phy_125mhz.clk, | |
i_rst = self.cd_eth_phy_125mhz.rst, | |
# Interface to I2C master core | |
o_cmd_address = si5324_i2c_sig.cmd_address, | |
o_cmd_start = si5324_i2c_sig.cmd_start, | |
o_cmd_read = si5324_i2c_sig.cmd_read, | |
o_cmd_write = si5324_i2c_sig.cmd_write, | |
o_cmd_write_multiple = si5324_i2c_sig.cmd_write_multiple, | |
o_cmd_stop = si5324_i2c_sig.cmd_stop, | |
o_cmd_valid = si5324_i2c_sig.cmd_valid, | |
i_cmd_ready = si5324_i2c_sig.cmd_ready, | |
o_data_out = si5324_i2c_sig.data, | |
o_data_out_valid = si5324_i2c_sig.data_valid, | |
i_data_out_ready = si5324_i2c_sig.data_ready, | |
o_data_out_last = si5324_i2c_sig.data_last, | |
# Si5324 core status and control signals | |
o_busy = si5324_busy, | |
i_start = si5324_start, | |
) | |
# Instantiate the I2C master core | |
self.specials += Instance( | |
"i2c_master", | |
name = "si5324_i2c_master_inst", | |
i_clk = self.cd_eth_phy_125mhz.clk, | |
i_rst = self.cd_eth_phy_125mhz.rst, | |
# Interface to Si5324 configuration core | |
i_cmd_address = si5324_i2c_sig.cmd_address, | |
i_cmd_start = si5324_i2c_sig.cmd_start, | |
i_cmd_read = si5324_i2c_sig.cmd_read, | |
i_cmd_write = si5324_i2c_sig.cmd_write, | |
i_cmd_write_multiple = si5324_i2c_sig.cmd_write_multiple, | |
i_cmd_stop = si5324_i2c_sig.cmd_stop, | |
i_cmd_valid = si5324_i2c_sig.cmd_valid, | |
o_cmd_ready = si5324_i2c_sig.cmd_ready, | |
i_data_in = si5324_i2c_sig.data, | |
i_data_in_valid = si5324_i2c_sig.data_valid, | |
o_data_in_ready = si5324_i2c_sig.data_ready, | |
i_data_in_last = si5324_i2c_sig.data_last, | |
o_data_out = Signal(8), | |
o_data_out_valid = Signal(), | |
i_data_out_ready = Constant(1), | |
o_data_out_last = Signal(), | |
# Connection to the I2C tristate signals | |
i_scl_i = i2c_scl_ts.i, | |
o_scl_o = i2c_scl_ts.o, | |
o_scl_t = i2c_scl_ts.oe, | |
i_sda_i = i2c_sda_ts.i, | |
o_sda_o = i2c_sda_ts.o, | |
o_sda_t = i2c_sda_ts.oe, | |
# I2C master core status and control signals | |
o_busy = Signal(), | |
o_bus_control = Signal(), | |
o_bus_active = Signal(), | |
o_missed_ack = Signal(), | |
i_prescale = Constant(312, bits_sign=16), | |
i_stop_on_idle = Constant(1), | |
) | |
# On a system reset, wait for a couple of cyles (~10ms) before | |
# attempting to configure the Si5324 clock chip. | |
si5324_start_delay = Signal(21) | |
self.sync.eth_phy_125mhz += [ | |
If(~si5324_start_delay[20], | |
si5324_start_delay.eq(si5324_start_delay + 1), | |
) | |
] | |
self.comb += [ | |
si5324_start.eq(si5324_start_delay[20]), | |
] | |
# The Ethernet core must be held in reset until the clocking | |
# is properly configured. Thus it must use this reset signal, | |
# synchronous to the 156.25MHz refclk (core clock). | |
sfp_coreclk = Signal() | |
sfp_reset = Signal() | |
self.specials += Instance( | |
"sync_reset", | |
name = "sync_reset_sfp_inst", | |
p_N = 4, | |
i_clk = sfp_coreclk, | |
i_rst = self.cd_eth_phy_125mhz.rst | si5324_busy, | |
o_out = sfp_reset, | |
) | |
# Drive the SFP XGMII clock using the sfp_coreclk signal and | |
# synchronize the reset to the resetdone signal provided by | |
# the clock. This will cause the SFP clocks to properly be | |
# held in reset until the core is ready. | |
sfp_resetdone = Signal() | |
# Here represented as signals, will get converted into a | |
# proper ClockDomain per SFP below. We don't really want these | |
# to be represented in the same ClockDomain as it may well be | |
# different between SFPs on other boards. | |
sfp_xgmii_clk = Signal() | |
sfp_xgmii_rst = Signal() | |
self.comb += [ | |
sfp_xgmii_clk.eq(sfp_coreclk), | |
] | |
self.specials += Instance( | |
"sync_reset", | |
name = "sync_reset_156mhz_inst", | |
p_N = 4, | |
i_clk = sfp_xgmii_clk, | |
i_rst = ~sfp_resetdone, | |
o_out = sfp_xgmii_rst, | |
) | |
# ---------------------------------------------------------------------- | |
# PHY (Gigabit Transceiver -> XGMII) instantiation | |
xgmii_layout = [ | |
("tx_data", 64), | |
("tx_ctl", 8), | |
("rx_data", 64), | |
("rx_ctl", 8), | |
] | |
# Dictionary for XGMII interfaces driven by the instantiated | |
# PHYs | |
self.xgmii_interfaces = {} | |
for sfp_index in sfps.keys(): | |
# Basic XGMII interface definition consisting of a | |
# clocking and bus data signals | |
tx_clk = ClockDomain(name="cd_xgmii_sfp_{}_tx".format(sfp_index)) | |
rx_clk = ClockDomain(name="cd_xgmii_sfp_{}_rx".format(sfp_index)) | |
self.clock_domains += [tx_clk, rx_clk] | |
self.xgmii_interfaces[sfp_index] = xgmii = { | |
"tx_clk": tx_clk, | |
"rx_clk": rx_clk, | |
"bus": Record(xgmii_layout), | |
} | |
self.comb += [ | |
xgmii["tx_clk"].clk.eq(sfp_xgmii_clk), | |
xgmii["tx_clk"].rst.eq(sfp_xgmii_rst), | |
xgmii["rx_clk"].clk.eq(sfp_xgmii_clk), | |
xgmii["rx_clk"].rst.eq(sfp_xgmii_rst), | |
] | |
# Don't disable the SFPs | |
for sfp in sfps.values(): | |
self.comb += [ | |
sfp.tx_disable.eq(0), | |
] | |
sfp_qplloutclk = Signal() | |
sfp_qplloutrefclk = Signal() | |
sfp_qplllock = Signal() | |
sfp_txusrclk = Signal() | |
sfp_txusrclk2 = Signal() | |
sfp_areset_datapathclk = Signal() | |
sfp_gttxreset = Signal() | |
sfp_gtrxreset = Signal() | |
sfp_txuserrdy = Signal() | |
sfp_reset_counter_done = Signal() | |
# Global transceiver configuration vector | |
sfp_config_vector = Signal(536) | |
# Instantiate the ten_gig_eth_pcs_pma core for the first SFP, | |
# which is configured slightly differently than the other | |
# two. In particular, it features the PLL which the other | |
# transceivers in the quad will use as well. | |
sfp_1_status_vector = Signal(448) | |
sfp_1_core_status = Signal(8) | |
self.specials += Instance( | |
"ten_gig_eth_pcs_pma_0", | |
name = "sfp_1_pcs_pma_inst", | |
i_dclk = self.cd_eth_phy_125mhz.clk, | |
o_rxrecclk_out = Signal(), | |
i_refclk_p = sfp_mgt_refclk.p, | |
i_refclk_n = sfp_mgt_refclk.n, | |
i_sim_speedup_control = Constant(0), | |
o_coreclk_out = sfp_coreclk, | |
o_qplloutclk_out = sfp_qplloutclk, | |
o_qplloutrefclk_out = sfp_qplloutrefclk, | |
o_qplllock_out = sfp_qplllock, | |
o_txusrclk_out = sfp_txusrclk, | |
o_txusrclk2_out = sfp_txusrclk2, | |
o_areset_datapathclk_out = sfp_areset_datapathclk, | |
o_gttxreset_out = sfp_gttxreset, | |
o_gtrxreset_out = sfp_gtrxreset, | |
o_txuserrdy_out = sfp_txuserrdy, | |
o_reset_counter_done_out = sfp_reset_counter_done, | |
i_reset = sfp_reset, | |
i_xgmii_txd = self.xgmii_interfaces[1]["bus"].tx_data, | |
i_xgmii_txc = self.xgmii_interfaces[1]["bus"].tx_ctl, | |
o_xgmii_rxd = self.xgmii_interfaces[1]["bus"].rx_data, | |
o_xgmii_rxc = self.xgmii_interfaces[1]["bus"].rx_ctl, | |
o_txp = sfps[1].tx_p, | |
o_txn = sfps[1].tx_n, | |
i_rxp = sfps[1].rx_p, | |
i_rxn = sfps[1].rx_n, | |
i_configuration_vector = sfp_config_vector, | |
o_status_vector = sfp_1_status_vector, | |
o_core_status = sfp_1_core_status, | |
o_resetdone_out = sfp_resetdone, | |
i_signal_detect = Constant(1), | |
i_tx_fault = Constant(0), | |
o_drp_req = Signal(), | |
i_drp_gnt = Constant(1), | |
o_drp_den_o = Signal(), | |
o_drp_dwe_o = Signal(), | |
o_drp_daddr_o = Signal(16), | |
o_drp_di_o = Signal(16), | |
o_drp_drdy_o = Signal(), | |
o_drp_drpdo_o = Signal(16), | |
i_drp_den_i = Constant(0), | |
i_drp_dwe_i = Constant(0), | |
i_drp_daddr_i = Constant(0, bits_sign=16), | |
i_drp_di_i = Constant(0, bits_sign=16), | |
i_drp_drdy_i = Constant(0), | |
i_drp_drpdo_i = Constant(0, bits_sign=16), | |
i_pma_pmd_type = Constant(0, bits_sign=3), | |
o_tx_disable = Signal(), | |
) | |
for i, sfp in [(i, sfp) for i, sfp in sfps.items() if i > 1]: | |
sfp_status_vector = Signal(448) | |
sfp_core_status = Signal(8) | |
self.specials += Instance( | |
"ten_gig_eth_pcs_pma_1", | |
name = "sfp_{}_pcs_pma_inst".format(i), | |
# 125MHz transceiver core clock | |
i_dclk = self.cd_eth_phy_125mhz.clk, | |
# Clocking and reset signals provided by the primary | |
# transceiver | |
i_coreclk = sfp_coreclk, | |
i_txusrclk = sfp_txusrclk, | |
i_txusrclk2 = sfp_txusrclk2, | |
i_areset = sfp_reset, | |
i_areset_coreclk = sfp_areset_datapathclk, | |
i_gttxreset = sfp_gttxreset, | |
i_gtrxreset = sfp_gtrxreset, | |
i_txuserrdy = sfp_txuserrdy, | |
i_qplllock = sfp_qplllock, | |
i_qplloutclk = sfp_qplloutclk, | |
i_qplloutrefclk = sfp_qplloutrefclk, | |
i_reset_counter_done = sfp_reset_counter_done, | |
# Internal XGMII interface to the transceiver, | |
# synchronous to the sfp_xgmii_clk (equal to | |
# sfp_coreclk with a reset signal sfp_xgmii_rst | |
# synchronized to the core reset) | |
# | |
# This signal should be associated with a seperate | |
# clock domain (such that there is one clock domain | |
# per transceiver, even if they may hold the same | |
# signal). | |
i_xgmii_txd = self.xgmii_interfaces[i]["bus"].tx_data, | |
i_xgmii_txc = self.xgmii_interfaces[i]["bus"].tx_ctl, | |
o_xgmii_rxd = self.xgmii_interfaces[i]["bus"].rx_data, | |
o_xgmii_rxc = self.xgmii_interfaces[i]["bus"].rx_ctl, | |
# TX and RX differential pairs | |
o_txp = sfps[i].tx_p, | |
o_txn = sfps[i].tx_n, | |
i_rxp = sfps[i].rx_p, | |
i_rxn = sfps[i].rx_n, | |
# Core configuration and status signals | |
i_configuration_vector = sfp_config_vector, | |
o_status_vector = sfp_status_vector, | |
o_core_status = sfp_core_status, | |
# Generic control, status and clock signals | |
i_sim_speedup_control = Constant(0), | |
o_rxrecclk_out = Signal(), | |
o_txoutclk = Signal(), | |
o_tx_resetdone = Signal(), | |
o_rx_resetdone = Signal(), | |
i_signal_detect = Constant(1), | |
i_tx_fault = Constant(0), | |
o_drp_req = Signal(), | |
i_drp_gnt = Constant(1), | |
o_drp_den_o = Signal(), | |
o_drp_dwe_o = Signal(), | |
o_drp_daddr_o = Signal(16), | |
o_drp_di_o = Signal(16), | |
o_drp_drdy_o = Signal(), | |
o_drp_drpdo_o = Signal(16), | |
i_drp_den_i = Constant(0), | |
i_drp_dwe_i = Constant(0), | |
i_drp_daddr_i = Constant(0, bits_sign=16), | |
i_drp_di_i = Constant(0, bits_sign=16), | |
i_drp_drdy_i = Constant(0), | |
i_drp_drpdo_i = Constant(0, bits_sign=16), | |
i_pma_pmd_type = Constant(0, bits_sign=3), | |
o_tx_disable = Signal(), | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Taken from https://github.com/alexforencich/verilog-ethernet revision 6b18e56cb1 | |
# Original path: verilog-ethernet/example/NetFPGA_SUME/fpga/ip/ten_gig_eth_pcs_pma_0.tcl | |
# | |
# verilog-ethernet license: | |
# Copyright (c) 2014-2018 Alex Forencich | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy | |
# of this software and associated documentation files (the "Software"), to deal | |
# in the Software without restriction, including without limitation the rights | |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the Software is | |
# furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in | |
# all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY | |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
# THE SOFTWARE. | |
create_ip -name ten_gig_eth_pcs_pma -vendor xilinx.com -library ip -module_name ten_gig_eth_pcs_pma_0 | |
set_property -dict [list \ | |
CONFIG.MDIO_Management {false} \ | |
CONFIG.base_kr {BASE-R} \ | |
CONFIG.SupportLevel {1} \ | |
CONFIG.DClkRate {125} \ | |
] [get_ips ten_gig_eth_pcs_pma_0] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Taken from https://github.com/alexforencich/verilog-ethernet revision 6b18e56cb1 | |
# Original path: verilog-ethernet/example/NetFPGA_SUME/fpga/ip/ten_gig_eth_pcs_pma_1.tcl | |
# | |
# verilog-ethernet license: | |
# Copyright (c) 2014-2018 Alex Forencich | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy | |
# of this software and associated documentation files (the "Software"), to deal | |
# in the Software without restriction, including without limitation the rights | |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the Software is | |
# furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in | |
# all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY | |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
# THE SOFTWARE. | |
create_ip -name ten_gig_eth_pcs_pma -vendor xilinx.com -library ip -module_name ten_gig_eth_pcs_pma_1 | |
set_property -dict [list \ | |
CONFIG.MDIO_Management {false} \ | |
CONFIG.base_kr {BASE-R} \ | |
CONFIG.SupportLevel {0} \ | |
CONFIG.DClkRate {125} \ | |
] [get_ips ten_gig_eth_pcs_pma_1] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment