Skip to content

Instantly share code, notes, and snippets.

@lschuermann
Last active November 2, 2021 20:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lschuermann/ab5c34c9f48d00a93f0174fcbe5e62e0 to your computer and use it in GitHub Desktop.
Save lschuermann/ab5c34c9f48d00a93f0174fcbe5e62e0 to your computer and use it in GitHub Desktop.
NetFPGA SUME 10GBit/s SFP+ Ethernet Transceiver Configuration
# 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(),
)
# 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]
# 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