eth: Add Ethernet example design for Napatech NT20E3/NT40E3

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2026-04-03 17:23:54 -07:00
parent 32ed8d68a3
commit 4f0c8e74fa
11 changed files with 2081 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
# Taxi Example Design for NT20E3/NT40E3
## Introduction
This example design targets the Napatech NT20E3/NT40E3 FPGA board.
The design places looped-back MACs on the SFP+ cages.
* SFP+ cages
* Looped-back 10GBASE-R MACs via GTH transceivers
## Board details
* FPGA: XC7VX330T-2FFG1157
## Licensing
* Toolchain
* Vivado Enterprise (requires license)
* IP
* No licensed vendor IP or 3rd party IP
## How to build
Run `make` in the appropriate `fpga*` subdirectory to build the bitstream. Ensure that the Xilinx Vivado toolchain components are in PATH.
## How to test
Run `make program` to program the board with Vivado.
To test the looped-back MAC, it is recommended to use a network tester like the Viavi T-BERD 5800 that supports basic layer 2 tests with a loopback. Do not connect the looped-back MAC to a network as the reflected packets may cause problems.
## JTAG pinout
Napatech boards use a non-standard connector for JTAG. There are three debug connectors, and one of them carries the JTAG signals for the FPGA.
J18 J24
FPGA AVR
TDI 7 8 GND TDI 7 8 GND
TMS 5 6 HALT TMS 5 6
TDO 3 4 Vref TDO 3 4 Vref
TCK 1 2 GND TCK 1 2 GND
J20
GND 2 1
4 3
6 5
Note: J18.6 HALT must be driven low to access the JTAG chain. So, either tie to to ground, or connect it to the HALT signal on DLC9/DLC10 cables.

View File

@@ -0,0 +1,153 @@
# SPDX-License-Identifier: MIT
###################################################################
#
# Xilinx Vivado FPGA Makefile
#
# Copyright (c) 2016-2025 Alex Forencich
#
###################################################################
#
# Parameters:
# FPGA_TOP - Top module name
# FPGA_FAMILY - FPGA family (e.g. VirtexUltrascale)
# FPGA_DEVICE - FPGA device (e.g. xcvu095-ffva2104-2-e)
# SYN_FILES - list of source files
# INC_FILES - list of include files
# XDC_FILES - list of timing constraint files
# XCI_FILES - list of IP XCI files
# IP_TCL_FILES - list of IP TCL files (sourced during project creation)
# CONFIG_TCL_FILES - list of config TCL files (sourced before each build)
#
# Note: both SYN_FILES and INC_FILES support file list files. File list
# files are files with a .f extension that contain a list of additional
# files to include, one path relative to the .f file location per line.
# The .f files are processed recursively, and then the complete file list
# is de-duplicated, with later files in the list taking precedence.
#
# Example:
#
# FPGA_TOP = fpga
# FPGA_FAMILY = VirtexUltrascale
# FPGA_DEVICE = xcvu095-ffva2104-2-e
# SYN_FILES = rtl/fpga.v
# XDC_FILES = fpga.xdc
# XCI_FILES = ip/pcspma.xci
# include ../common/vivado.mk
#
###################################################################
# phony targets
.PHONY: fpga vivado tmpclean clean distclean
# prevent make from deleting intermediate files and reports
.PRECIOUS: %.xpr %.bit %.bin %.ltx %.xsa %.mcs %.prm
.SECONDARY:
CONFIG ?= config.mk
-include $(CONFIG)
FPGA_TOP ?= fpga
PROJECT ?= $(FPGA_TOP)
XDC_FILES ?= $(PROJECT).xdc
# handle file list files
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
SYN_FILES := $(call uniq_base,$(call process_f_files,$(SYN_FILES)))
INC_FILES := $(call uniq_base,$(call process_f_files,$(INC_FILES)))
###################################################################
# Main Targets
#
# all: build everything (fpga)
# fpga: build FPGA config
# vivado: open project in Vivado
# tmpclean: remove intermediate files
# clean: remove output files and project files
# distclean: remove archived output files
###################################################################
all: fpga
fpga: $(PROJECT).bit
vivado: $(PROJECT).xpr
vivado $(PROJECT).xpr
tmpclean::
-rm -rf *.log *.jou *.cache *.gen *.hbs *.hw *.ip_user_files *.runs *.xpr *.html *.xml *.sim *.srcs *.str .Xil defines.v
-rm -rf create_project.tcl update_config.tcl run_synth.tcl run_impl.tcl generate_bit.tcl
clean:: tmpclean
-rm -rf *.bit *.bin *.ltx *.xsa program.tcl generate_mcs.tcl *.mcs *.prm flash.tcl
-rm -rf *_utilization.rpt *_utilization_hierarchical.rpt
distclean:: clean
-rm -rf rev
###################################################################
# Target implementations
###################################################################
# Vivado project file
# create fresh project if Makefile or IP files have changed
create_project.tcl: Makefile $(XCI_FILES) $(IP_TCL_FILES)
rm -rf defines.v
touch defines.v
for x in $(DEFS); do echo '`define' $$x >> defines.v; done
echo "create_project -force -part $(FPGA_PART) $(PROJECT)" > $@
echo "add_files -fileset sources_1 defines.v $(SYN_FILES)" >> $@
echo "set_property top $(FPGA_TOP) [current_fileset]" >> $@
echo "add_files -fileset constrs_1 $(XDC_FILES)" >> $@
for x in $(XCI_FILES); do echo "import_ip $$x" >> $@; done
for x in $(IP_TCL_FILES); do echo "source $$x" >> $@; done
for x in $(CONFIG_TCL_FILES); do echo "source $$x" >> $@; done
# source config TCL scripts if any source file has changed
update_config.tcl: $(CONFIG_TCL_FILES) $(SYN_FILES) $(INC_FILES) $(XDC_FILES)
echo "open_project -quiet $(PROJECT).xpr" > $@
for x in $(CONFIG_TCL_FILES); do echo "source $$x" >> $@; done
$(PROJECT).xpr: create_project.tcl update_config.tcl
vivado -nojournal -nolog -mode batch $(foreach x,$?,-source $x)
# synthesis run
$(PROJECT).runs/synth_1/$(PROJECT).dcp: create_project.tcl update_config.tcl $(SYN_FILES) $(INC_FILES) $(XDC_FILES) | $(PROJECT).xpr
echo "open_project $(PROJECT).xpr" > run_synth.tcl
echo "reset_run synth_1" >> run_synth.tcl
echo "launch_runs -jobs 4 synth_1" >> run_synth.tcl
echo "wait_on_run synth_1" >> run_synth.tcl
vivado -nojournal -nolog -mode batch -source run_synth.tcl
# implementation run
$(PROJECT).runs/impl_1/$(PROJECT)_routed.dcp: $(PROJECT).runs/synth_1/$(PROJECT).dcp
echo "open_project $(PROJECT).xpr" > run_impl.tcl
echo "reset_run impl_1" >> run_impl.tcl
echo "launch_runs -jobs 4 impl_1" >> run_impl.tcl
echo "wait_on_run impl_1" >> run_impl.tcl
echo "open_run impl_1" >> run_impl.tcl
echo "report_utilization -file $(PROJECT)_utilization.rpt" >> run_impl.tcl
echo "report_utilization -hierarchical -file $(PROJECT)_utilization_hierarchical.rpt" >> run_impl.tcl
vivado -nojournal -nolog -mode batch -source run_impl.tcl
# output files (including potentially bit, bin, ltx, and xsa)
$(PROJECT).bit $(PROJECT).bin $(PROJECT).ltx $(PROJECT).xsa: $(PROJECT).runs/impl_1/$(PROJECT)_routed.dcp
echo "open_project $(PROJECT).xpr" > generate_bit.tcl
echo "open_run impl_1" >> generate_bit.tcl
echo "write_bitstream -force -bin_file $(PROJECT).runs/impl_1/$(PROJECT).bit" >> generate_bit.tcl
echo "write_debug_probes -force $(PROJECT).runs/impl_1/$(PROJECT).ltx" >> generate_bit.tcl
echo "write_hw_platform -fixed -force -include_bit $(PROJECT).xsa" >> generate_bit.tcl
vivado -nojournal -nolog -mode batch -source generate_bit.tcl
ln -f -s $(PROJECT).runs/impl_1/$(PROJECT).bit .
ln -f -s $(PROJECT).runs/impl_1/$(PROJECT).bin .
if [ -e $(PROJECT).runs/impl_1/$(PROJECT).ltx ]; then ln -f -s $(PROJECT).runs/impl_1/$(PROJECT).ltx .; fi
mkdir -p rev
COUNT=100; \
while [ -e rev/$(PROJECT)_rev$$COUNT.bit ]; \
do COUNT=$$((COUNT+1)); done; \
cp -pv $(PROJECT).runs/impl_1/$(PROJECT).bit rev/$(PROJECT)_rev$$COUNT.bit; \
cp -pv $(PROJECT).runs/impl_1/$(PROJECT).bin rev/$(PROJECT)_rev$$COUNT.bin; \
if [ -e $(PROJECT).runs/impl_1/$(PROJECT).ltx ]; then cp -pv $(PROJECT).runs/impl_1/$(PROJECT).ltx rev/$(PROJECT)_rev$$COUNT.ltx; fi; \
if [ -e $(PROJECT).xsa ]; then cp -pv $(PROJECT).xsa rev/$(PROJECT)_rev$$COUNT.xsa; fi

View File

@@ -0,0 +1,256 @@
# XDC constraints for the Napatech NT40E3
# part: xc7vx330tffg1157-2
# General configuration
set_property CFGBVS GND [current_design]
set_property CONFIG_VOLTAGE 1.8 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
# set_property BITSTREAM.CONFIG.CONFIGFALLBACK ENABLE [current_design]
set_property BITSTREAM.CONFIG.EXTMASTERCCLK_EN DIV-1 [current_design]
# set_property BITSTREAM.CONFIG.SPI_32BIT_ADDR YES [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
# set_property BITSTREAM.CONFIG.SPI_FALL_EDGE YES [current_design]
set_property BITSTREAM.CONFIG.UNUSEDPIN PULLUP [current_design]
# 80 MHz EMC clock
set_property -dict {LOC AP33 IOSTANDARD LVCMOS18} [get_ports clk_80mhz]
create_clock -period 12.5 -name clk_80mhz [get_ports clk_80mhz]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_80mhz_ibufg]
# 233.33 MHz DDR3 MIG clock
#set_property -dict {LOC J30 IOSTANDARD LVDS} [get_ports clk_ddr_233mhz_p]
#set_property -dict {LOC J31 IOSTANDARD LVDS} [get_ports clk_ddr_233mhz_n]
#create_clock -period 4.285 -name clk_ddr_233mhz [get_ports clk_ddr_233mhz_p]
# LEDs
set_property -dict {LOC AC12 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_led[0]}]
set_property -dict {LOC AE9 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_led[1]}]
set_property -dict {LOC AE8 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_led[2]}]
set_property -dict {LOC AC10 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_led[3]}]
set_property -dict {LOC AD30 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {led[0]}]
set_property -dict {LOC AD31 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {led[1]}]
set_property -dict {LOC AG30 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {led[2]}]
set_property -dict {LOC AH30 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {led[3]}]
set_property -dict {LOC AB27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {led_red}]
set_property -dict {LOC AB28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {led_green}]
set_property -dict {LOC AJ26 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {led_sync[0]}]
set_property -dict {LOC AP29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {led_sync[1]}]
set_false_path -to [get_ports {sfp_led[*] led[*] led_red led_green led_sync[*]}]
set_output_delay 0 [get_ports {sfp_led[*] led[*] led_red led_green led_sync[*]}]
# Time sync
#set_property -dict {LOC AB31 IOSTANDARD LVCMOS18} [get_ports {sync_ext_in}]
#set_property -dict {LOC AB32 IOSTANDARD LVCMOS18 SLEW FAST DRIVE 12} [get_ports {sync_ext_out}]
#set_property -dict {LOC AB30 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_ext_in_en}]
#set_property -dict {LOC AA30 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_ext_out_en[0]}]
#set_property -dict {LOC W32 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_ext_out_en[1]}]
#set_property -dict {LOC Y32 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_ext_term[0]}]
#set_property -dict {LOC AA31 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_ext_term[1]}]
#set_property -dict {LOC V30 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_ext_vsel[0]}]
#set_property -dict {LOC U30 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_ext_vsel[1]}]
#set_property -dict {LOC AK26 IOSTANDARD LVCMOS18} [get_ports {sync_int_1_in}]
#set_property -dict {LOC AK27 IOSTANDARD LVCMOS18 SLEW FAST DRIVE 12} [get_ports {sync_int_1_out}]
#set_property -dict {LOC AH24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_1_in_en}]
#set_property -dict {LOC AH25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_1_out_en[0]}]
#set_property -dict {LOC AJ25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_1_out_en[1]}]
#set_property -dict {LOC AL25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_1_term[0]}]
#set_property -dict {LOC AL26 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_1_term[1]}]
#set_property -dict {LOC AG25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_1_vsel[0]}]
#set_property -dict {LOC AJ27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_1_vsel[1]}]
#set_property -dict {LOC AM27 IOSTANDARD LVCMOS18} [get_ports {sync_int_2_in}]
#set_property -dict {LOC AN29 IOSTANDARD LVCMOS18 SLEW FAST DRIVE 12} [get_ports {sync_int_2_out}]
#set_property -dict {LOC AN28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_2_in_en}]
#set_property -dict {LOC AM28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_2_out_en[0]}]
#set_property -dict {LOC AP25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_2_out_en[1]}]
#set_property -dict {LOC AP26 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_2_term[0]}]
#set_property -dict {LOC AN27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_2_term[1]}]
#set_property -dict {LOC AN25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_2_vsel[0]}]
#set_property -dict {LOC AM25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sync_int_2_vsel[1]}]
#set_false_path -from [get_ports {sync_ext_in}]
#set_input_delay 0 [get_ports {sync_ext_in}]
#set_false_path -to [get_ports {sync_ext_out sync_ext_in_en sync_ext_out_en[*] sync_ext_term[*] sync_ext_vsel[*]}]
#set_output_delay 0 [get_ports {sync_ext_out sync_ext_in_en sync_ext_out_en[*] sync_ext_term[*] sync_ext_vsel[*]}]
#set_false_path -from [get_ports {sync_int_1_in}]
#set_input_delay 0 [get_ports {sync_int_1_in}]
#set_false_path -to [get_ports {sync_int_1_out sync_int_1_in_en sync_int_1_out_en[*] sync_int_1_term[*] sync_int_1_vsel[*]}]
#set_output_delay 0 [get_ports {sync_int_1_out sync_int_1_in_en sync_int_1_out_en[*] sync_int_1_term[*] sync_int_1_vsel[*]}]
#set_false_path -from [get_ports {sync_int_2_in}]
#set_input_delay 0 [get_ports {sync_int_2_in}]
#set_false_path -to [get_ports {sync_int_2_out sync_int_2_in_en sync_int_2_out_en[*] sync_int_2_term[*] sync_int_2_vsel[*]}]
#set_output_delay 0 [get_ports {sync_int_2_out sync_int_2_in_en sync_int_2_out_en[*] sync_int_2_term[*] sync_int_2_vsel[*]}]
# AVR BMC U2
#set_property -dict {LOC AC23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pc4] ;# U2.J7 PC4
#set_property -dict {LOC AD24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pc5] ;# U2.H7 PC5
#set_property -dict {LOC AC24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pc6] ;# U2.G7 PC6
#set_property -dict {LOC AD25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pc7] ;# U2.J8 PC7
#set_property -dict {LOC AC25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pd4] ;# U2.H9 PD4
#set_property -dict {LOC AE24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pd5] ;# U2.H8 PD5
#set_property -dict {LOC AE23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pd6] ;# U2.G9 PD6
#set_property -dict {LOC AF26 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pd7] ;# U2.G8 PD7
#set_property -dict {LOC AF29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pj0] ;# U2.C5 PJ0
#set_property -dict {LOC AC28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pj1] ;# U2.D5 PJ1
#set_property -dict {LOC AC29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pj2] ;# U2.E5 PJ2
#set_property -dict {LOC AE28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pj3] ;# U2.A4 PJ3
#set_property -dict {LOC AE29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pj4] ;# U2.B4 PJ4
#set_property -dict {LOC AD29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pj5] ;# U2.C4 PJ5
#set_property -dict {LOC AH28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pj6] ;# U2.D4 PJ6
#set_property -dict {LOC AD26 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pj7] ;# U2.E4 PJ7
#set_property -dict {LOC AH27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pdi_data] ;# U2.F4 PDI_DATA
#set_property -dict {LOC AH29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports avr_pdi_clk] ;# U2.F3 PDI_CLK
# Si5338 U18
set_property -dict {LOC AF33 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports si5338_i2c_scl] ;# U18.12 SCL
set_property -dict {LOC AF34 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports si5338_i2c_sda] ;# U18.19 SDA
set_property -dict {LOC AC27 IOSTANDARD LVCMOS18} [get_ports si5338_intr] ;# U18.8 INTR
set_false_path -to [get_ports {si5338_i2c_scl si5338_i2c_sda}]
set_output_delay 0 [get_ports {si5338_i2c_scl si5338_i2c_sda}]
set_false_path -from [get_ports {si5338_i2c_scl si5338_i2c_sda si5338_intr}]
set_input_delay 0 [get_ports {si5338_i2c_scl si5338_i2c_sda si5338_intr}]
# 10/100 PHY (DP83630)
#set_property -dict {LOC AJ16 IOSTANDARD LVCMOS18} [get_ports phy_tx_clk] ;# U5.1 TX_CLK via U53.4/13
#set_property -dict {LOC AN14 IOSTANDARD LVCMOS18} [get_ports phy_tx_en] ;# U5.2 TX_EN via U51.4/13
#set_property -dict {LOC AN18 IOSTANDARD LVCMOS18} [get_ports {phy_txd[0]}] ;# U5.3 TXD_0 via U50.4/13
#set_property -dict {LOC AL16 IOSTANDARD LVCMOS18} [get_ports {phy_txd[1]}] ;# U5.4 TXD_1 via U50.5/12
#set_property -dict {LOC AM16 IOSTANDARD LVCMOS18} [get_ports {phy_txd[2]}] ;# U5.5 TXD_2 via U50.6/11
#set_property -dict {LOC AP17 IOSTANDARD LVCMOS18} [get_ports {phy_txd[3]}] ;# U5.6 TXD_3 via U50.7/10
#set_property -dict {LOC AH15 IOSTANDARD LVCMOS18} [get_ports phy_rx_clk] ;# U5.38 RX_CLK via U55.5/12
#set_property -dict {LOC AK18 IOSTANDARD LVCMOS18} [get_ports phy_rx_dv] ;# U5.39 RX_DV via U55.6/11
#set_property -dict {LOC AL18 IOSTANDARD LVCMOS18} [get_ports phy_rx_er] ;# U5.41 RX_ER via U55.7/10
#set_property -dict {LOC AP14 IOSTANDARD LVCMOS18} [get_ports {phy_rxd[0]}] ;# U5.46 RXD_0 via U53.5/12
#set_property -dict {LOC AM17 IOSTANDARD LVCMOS18} [get_ports {phy_rxd[1]}] ;# U5.45 RXD_1 via U53.6/11
#set_property -dict {LOC AN17 IOSTANDARD LVCMOS18} [get_ports {phy_rxd[2]}] ;# U5.44 RXD_2 via U53.7/10
#set_property -dict {LOC AL15 IOSTANDARD LVCMOS18} [get_ports {phy_rxd[3]}] ;# U5.43 RXD_3 via U55.4/13
#set_property -dict {LOC AL14 IOSTANDARD LVCMOS18} [get_ports phy_crs] ;# U5.40 CRS/CRS_DV via U57.5/12
#set_property -dict {LOC AK14 IOSTANDARD LVCMOS18} [get_ports phy_col] ;# U5.42 COL via U57.4/13
#set_property -dict {LOC AF16 IOSTANDARD LVCMOS18} [get_ports phy_refclk] ;# U5.34 X1
#set_property -dict {LOC AC15 IOSTANDARD LVCMOS18} [get_ports phy_reset_n] ;# U5.29 RESET_N
#set_property -dict {LOC AD15 IOSTANDARD LVCMOS18} [get_ports phy_int_n] ;# U5.7 PWRDOWN/INTN
#set_property -dict {LOC AJ17 IOSTANDARD LVCMOS18} [get_ports phy_mdc] ;# U5.31 MDC
#set_property -dict {LOC AK17 IOSTANDARD LVCMOS18} [get_ports phy_mdio] ;# U5.30 MDIO
#set_property -dict {LOC AF13 IOSTANDARD LVCMOS18} [get_ports phy_gpio1] ;# U5.21 GPIO1
#set_property -dict {LOC AG13 IOSTANDARD LVCMOS18} [get_ports phy_gpio2] ;# U5.22 GPIO2
#set_property -dict {LOC AE17 IOSTANDARD LVCMOS18} [get_ports phy_gpio3] ;# U5.23 GPIO3
#set_property -dict {LOC AE16 IOSTANDARD LVCMOS18} [get_ports phy_gpio4] ;# U5.25 GPIO4
#set_property -dict {LOC AE14 IOSTANDARD LVCMOS18} [get_ports phy_gpio5] ;# U5.26 GPIO5/LED_ACT
#set_property -dict {LOC AF14 IOSTANDARD LVCMOS18} [get_ports phy_gpio6] ;# U5.27 GPIO6/LED_SPEED/FX_SD
#set_property -dict {LOC AC17 IOSTANDARD LVCMOS18} [get_ports phy_gpio7] ;# U5.28 GPIO7/LED_LINK
#set_property -dict {LOC AD17 IOSTANDARD LVCMOS18} [get_ports phy_gpio8] ;# U5.36 GPIO8
#set_property -dict {LOC AD14 IOSTANDARD LVCMOS18} [get_ports phy_gpio9] ;# U5.37 GPIO9
#set_property -dict {LOC AG17 IOSTANDARD LVCMOS18} [get_ports phy_gpio12] ;# U5.24 GPIO12/CLK_OUT via U57.7/10
#set_property -dict {LOC AJ14 IOSTANDARD LVCMOS18} [get_ports phy_isolate] ;# OE on U50, U51, U53, U55, U57
# SFP+ Interfaces (J1-J4)
set_property -dict {LOC B6 } [get_ports {sfp_rx_p[0]}] ;# MGTHRXP3_118 GTHE2_CHANNEL_X1Y39 / GTHE2_COMMON_X1Y9
set_property -dict {LOC B5 } [get_ports {sfp_rx_n[0]}] ;# MGTHRXN3_118 GTHE2_CHANNEL_X1Y39 / GTHE2_COMMON_X1Y9
set_property -dict {LOC A4 } [get_ports {sfp_tx_p[0]}] ;# MGTHTXP3_118 GTHE2_CHANNEL_X1Y39 / GTHE2_COMMON_X1Y9
set_property -dict {LOC A3 } [get_ports {sfp_tx_n[0]}] ;# MGTHTXN3_118 GTHE2_CHANNEL_X1Y39 / GTHE2_COMMON_X1Y9
set_property -dict {LOC D6 } [get_ports {sfp_rx_p[1]}] ;# MGTHRXP2_118 GTHE2_CHANNEL_X1Y38 / GTHE2_COMMON_X1Y9
set_property -dict {LOC D5 } [get_ports {sfp_rx_n[1]}] ;# MGTHRXN2_118 GTHE2_CHANNEL_X1Y38 / GTHE2_COMMON_X1Y9
set_property -dict {LOC B2 } [get_ports {sfp_tx_p[1]}] ;# MGTHTXP2_118 GTHE2_CHANNEL_X1Y38 / GTHE2_COMMON_X1Y9
set_property -dict {LOC B1 } [get_ports {sfp_tx_n[1]}] ;# MGTHTXN2_118 GTHE2_CHANNEL_X1Y38 / GTHE2_COMMON_X1Y9
set_property -dict {LOC H6 } [get_ports {sfp_mgt_refclk_p[0]}] ;# MGTREFCLK1P_118 from U20.10
set_property -dict {LOC H5 } [get_ports {sfp_mgt_refclk_n[0]}] ;# MGTREFCLK1N_118 from U20.9
set_property -dict {LOC W4 } [get_ports {sfp_rx_p[2]}] ;# MGTHRXP1_116 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC W3 } [get_ports {sfp_rx_n[2]}] ;# MGTHRXN1_116 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC V2 } [get_ports {sfp_tx_p[2]}] ;# MGTHTXP1_116 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC V1 } [get_ports {sfp_tx_n[2]}] ;# MGTHTXN1_116 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC AA4 } [get_ports {sfp_rx_p[3]}] ;# MGTHRXP0_116 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC AA3 } [get_ports {sfp_rx_n[3]}] ;# MGTHRXN0_116 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC Y2 } [get_ports {sfp_tx_p[3]}] ;# MGTHTXP0_116 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC Y1 } [get_ports {sfp_tx_n[3]}] ;# MGTHTXN0_116 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC T6 } [get_ports {sfp_mgt_refclk_p[1]}] ;# MGTREFCLK0P_116 from U20.20
set_property -dict {LOC T5 } [get_ports {sfp_mgt_refclk_n[1]}] ;# MGTREFCLK0N_116 from U20.21
set_property -dict {LOC AG12 IOSTANDARD LVCMOS18 PULLUP true} [get_ports {sfp_mod_abs[0]}]
set_property -dict {LOC AJ11 IOSTANDARD LVCMOS18 PULLUP true} [get_ports {sfp_mod_abs[1]}]
set_property -dict {LOC AK9 IOSTANDARD LVCMOS18 PULLUP true} [get_ports {sfp_mod_abs[2]}]
set_property -dict {LOC AN10 IOSTANDARD LVCMOS18 PULLUP true} [get_ports {sfp_mod_abs[3]}]
set_property -dict {LOC AF10 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[0][0]}]
set_property -dict {LOC AG10 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[0][1]}]
set_property -dict {LOC AJ12 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[1][0]}]
set_property -dict {LOC AK12 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[1][1]}]
set_property -dict {LOC AL11 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[2][0]}]
set_property -dict {LOC AM11 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[2][1]}]
set_property -dict {LOC AP12 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[3][0]}]
set_property -dict {LOC AP11 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[3][1]}]
set_property -dict {LOC AH10 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_tx_disable[0]}]
set_property -dict {LOC AK8 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_tx_disable[1]}]
set_property -dict {LOC AM12 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_tx_disable[2]}]
set_property -dict {LOC AM13 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_tx_disable[3]}]
# set_property -dict {LOC AF9 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_scl[0]}]
# set_property -dict {LOC AD9 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_sda[0]}]
# set_property -dict {LOC AD12 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_scl[1]}]
# set_property -dict {LOC AF8 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_sda[1]}]
# set_property -dict {LOC AF11 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_scl[2]}]
# set_property -dict {LOC AD11 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_sda[2]}]
# set_property -dict {LOC AG8 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_scl[3]}]
# set_property -dict {LOC AG11 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_sda[3]}]
# 156.25 MHz MGT reference clock
create_clock -period 6.4 -name {sfp_mgt_refclk_0} [get_ports {sfp_mgt_refclk_p[0]}]
create_clock -period 6.4 -name {sfp_mgt_refclk_1} [get_ports {sfp_mgt_refclk_p[1]}]
set_false_path -from [get_ports {sfp_mod_abs[*]}]
set_input_delay 0 [get_ports {sfp_mod_abs[*]}]
set_false_path -to [get_ports {sfp_rs[*][*]}]
set_output_delay 0 [get_ports {sfp_rs[*][*]}]
set_false_path -to [get_ports {get_ports sfp_tx_disable[*]}]
set_output_delay 0 [get_ports {get_ports sfp_tx_disable[*]}]
# set_false_path -to [get_ports {sfp_i2c_scl[*] sfp_i2c_sda[*]}]
# set_output_delay 0 [get_ports {sfp_i2c_scl[*] sfp_i2c_sda[*]}]
# set_false_path -from [get_ports {sfp_i2c_scl[*] sfp_i2c_sda[*]}]
# set_input_delay 0 [get_ports {sfp_i2c_scl[*] sfp_i2c_sda[*]}]
# PCIe Interface
#set_property -dict {LOC AC4 } [get_ports {pcie_rx_p[0]}] ;# MGTHRXP3_115 GTHE2_CHANNEL_X0Y11 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AC3 } [get_ports {pcie_rx_n[0]}] ;# MGTHRXN3_115 GTHE2_CHANNEL_X0Y11 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AB2 } [get_ports {pcie_tx_p[0]}] ;# MGTHTXP3_115 GTHE2_CHANNEL_X0Y11 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AB1 } [get_ports {pcie_tx_n[0]}] ;# MGTHTXN3_115 GTHE2_CHANNEL_X0Y11 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AE4 } [get_ports {pcie_rx_p[1]}] ;# MGTHRXP2_115 GTHE2_CHANNEL_X0Y10 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AE3 } [get_ports {pcie_rx_n[1]}] ;# MGTHRXN2_115 GTHE2_CHANNEL_X0Y10 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AD2 } [get_ports {pcie_tx_p[1]}] ;# MGTHTXP2_115 GTHE2_CHANNEL_X0Y10 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AD1 } [get_ports {pcie_tx_n[1]}] ;# MGTHTXN2_115 GTHE2_CHANNEL_X0Y10 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AF6 } [get_ports {pcie_rx_p[2]}] ;# MGTHRXP1_115 GTHE2_CHANNEL_X0Y9 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AF5 } [get_ports {pcie_rx_n[2]}] ;# MGTHRXN1_115 GTHE2_CHANNEL_X0Y9 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AF2 } [get_ports {pcie_tx_p[2]}] ;# MGTHTXP1_115 GTHE2_CHANNEL_X0Y9 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AF1 } [get_ports {pcie_tx_n[2]}] ;# MGTHTXN1_115 GTHE2_CHANNEL_X0Y9 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AG4 } [get_ports {pcie_rx_p[3]}] ;# MGTHRXP0_115 GTHE2_CHANNEL_X0Y8 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AG3 } [get_ports {pcie_rx_n[3]}] ;# MGTHRXN0_115 GTHE2_CHANNEL_X0Y8 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AH2 } [get_ports {pcie_tx_p[3]}] ;# MGTHTXP0_115 GTHE2_CHANNEL_X0Y8 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AH1 } [get_ports {pcie_tx_n[3]}] ;# MGTHTXN0_115 GTHE2_CHANNEL_X0Y8 / GTHE2_COMMON_X0Y2
#set_property -dict {LOC AJ4 } [get_ports {pcie_rx_p[4]}] ;# MGTHRXP3_114 GTHE2_CHANNEL_X0Y7 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AJ3 } [get_ports {pcie_rx_n[4]}] ;# MGTHRXN3_114 GTHE2_CHANNEL_X0Y7 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AK2 } [get_ports {pcie_tx_p[4]}] ;# MGTHTXP3_114 GTHE2_CHANNEL_X0Y7 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AK1 } [get_ports {pcie_tx_n[4]}] ;# MGTHTXN3_114 GTHE2_CHANNEL_X0Y7 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AL4 } [get_ports {pcie_rx_p[5]}] ;# MGTHRXP2_114 GTHE2_CHANNEL_X0Y6 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AL3 } [get_ports {pcie_rx_n[5]}] ;# MGTHRXN2_114 GTHE2_CHANNEL_X0Y6 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AM2 } [get_ports {pcie_tx_p[5]}] ;# MGTHTXP2_114 GTHE2_CHANNEL_X0Y6 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AM1 } [get_ports {pcie_tx_n[5]}] ;# MGTHTXN2_114 GTHE2_CHANNEL_X0Y6 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AM6 } [get_ports {pcie_rx_p[6]}] ;# MGTHRXP1_114 GTHE2_CHANNEL_X0Y5 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AM5 } [get_ports {pcie_rx_n[6]}] ;# MGTHRXN1_114 GTHE2_CHANNEL_X0Y5 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AN4 } [get_ports {pcie_tx_p[6]}] ;# MGTHTXP1_114 GTHE2_CHANNEL_X0Y5 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AN3 } [get_ports {pcie_tx_n[6]}] ;# MGTHTXN1_114 GTHE2_CHANNEL_X0Y5 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AP6 } [get_ports {pcie_rx_p[7]}] ;# MGTHRXP0_114 GTHE2_CHANNEL_X0Y4 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AP5 } [get_ports {pcie_rx_n[7]}] ;# MGTHRXN0_114 GTHE2_CHANNEL_X0Y4 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AP2 } [get_ports {pcie_tx_p[7]}] ;# MGTHTXP0_114 GTHE2_CHANNEL_X0Y4 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AP1 } [get_ports {pcie_tx_n[7]}] ;# MGTHTXN0_114 GTHE2_CHANNEL_X0Y4 / GTHE2_COMMON_X0Y1
#set_property -dict {LOC AK6 } [get_ports pcie_mgt_refclk_p] ;# MGTREFCLK1P_115 via U28
#set_property -dict {LOC AK5 } [get_ports pcie_mgt_refclk_n] ;# MGTREFCLK1N_115 via U28
#set_property -dict {LOC AK32 IOSTANDARD LVCMOS18 PULLUP true} [get_ports pcie_reset]
# 100 MHz MGT reference clock
#create_clock -period 10 -name pcie_mgt_refclk [get_ports pcie_mgt_refclk_p]
#set_false_path -from [get_ports {pcie_reset}]
#set_input_delay 0 [get_ports {pcie_reset}]

View File

@@ -0,0 +1,52 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# FPGA settings
FPGA_PART = xc7vx330tffg1157-2
FPGA_TOP = fpga
FPGA_ARCH = virtex7
RTL_DIR = ../rtl
LIB_DIR = ../lib
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
# Files for synthesis
SYN_FILES = $(RTL_DIR)/fpga.sv
SYN_FILES += $(RTL_DIR)/fpga_core.sv
SYN_FILES += $(RTL_DIR)/si5338_i2c_init.sv
SYN_FILES += $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_mac_25g_us.f
SYN_FILES += $(TAXI_SRC_DIR)/lss/rtl/taxi_i2c_master.sv
SYN_FILES += $(TAXI_SRC_DIR)/axis/rtl/taxi_axis_async_fifo.f
SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_reset.sv
SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
# XDC files
XDC_FILES = ../fpga.xdc
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_phy_10g_7_gt.tcl
XDC_FILES += $(TAXI_SRC_DIR)/axis/syn/vivado/taxi_axis_async_fifo.tcl
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_reset.tcl
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl
# IP
#IP_TCL_FILES = ../ip/sgmii_pcs_pma_0.tcl
# Configuration
#CONFIG_TCL_FILES = config.tcl
include ../common/vivado.mk
program: $(PROJECT).bit
echo "open_hw" > program.tcl
echo "connect_hw_server" >> program.tcl
echo "open_hw_target" >> program.tcl
echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl
echo "set_property PROGRAM.FILE {$(PROJECT).bit} [current_hw_device]" >> program.tcl
echo "program_hw_devices [current_hw_device]" >> program.tcl
echo "exit" >> program.tcl
vivado -nojournal -nolog -mode batch -source program.tcl

View File

@@ -0,0 +1 @@
../../../../../../

View File

@@ -0,0 +1,302 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2014-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* FPGA top-level module
*/
module fpga #
(
// simulation (set to avoid vendor primitives)
parameter logic SIM = 1'b0,
// vendor ("GENERIC", "XILINX", "ALTERA")
parameter string VENDOR = "XILINX",
// device family
parameter string FAMILY = "virtex7",
// 10G MAC configuration
parameter logic CFG_LOW_LATENCY = 1'b1,
parameter logic COMBINED_MAC_PCS = 1'b1
)
(
/*
* Clock: 80MHz
*/
input wire logic clk_80mhz,
/*
* GPIO
*/
output wire logic [3:0] sfp_led,
output wire logic [3:0] led,
output wire logic led_red,
output wire logic led_green,
output wire logic [1:0] led_sync,
/*
* I2C
*/
inout wire logic si5338_i2c_scl,
inout wire logic si5338_i2c_sda,
input wire logic si5338_intr,
/*
* Ethernet: SFP+
*/
input wire logic sfp_rx_p[4],
input wire logic sfp_rx_n[4],
output wire logic sfp_tx_p[4],
output wire logic sfp_tx_n[4],
input wire logic sfp_mgt_refclk_p[2],
input wire logic sfp_mgt_refclk_n[2],
input wire logic sfp_mod_abs[4],
output wire logic [1:0] sfp_rs[4],
output wire logic sfp_tx_disable[4]
);
// Clock and reset
wire clk_80mhz_ibufg;
// Internal 125 MHz clock
wire clk_125mhz_mmcm_out;
wire clk_125mhz_int;
wire rst_125mhz_int;
// Internal 156.25 MHz clock
wire clk_156mhz_int;
wire rst_156mhz_int;
wire mmcm_rst = 1'b0;
wire mmcm_locked;
wire mmcm_clkfb;
IBUFG
clk_80mhz_ibufg_inst (
.I(clk_80mhz),
.O(clk_80mhz_ibufg)
);
// MMCM instance
// 80 MHz in, 125 MHz out
// PFD range: 10 MHz to 500 MHz
// VCO range: 600 MHz to 1440 MHz
// M = 25, D = 2 sets Fvco = 1000 MHz
// Divide by 8 to get output frequency of 125 MHz
MMCME2_BASE #(
.BANDWIDTH("OPTIMIZED"),
.CLKOUT0_DIVIDE_F(8),
.CLKOUT0_DUTY_CYCLE(0.5),
.CLKOUT0_PHASE(0),
.CLKOUT1_DIVIDE(1),
.CLKOUT1_DUTY_CYCLE(0.5),
.CLKOUT1_PHASE(0),
.CLKOUT2_DIVIDE(1),
.CLKOUT2_DUTY_CYCLE(0.5),
.CLKOUT2_PHASE(0),
.CLKOUT3_DIVIDE(1),
.CLKOUT3_DUTY_CYCLE(0.5),
.CLKOUT3_PHASE(0),
.CLKOUT4_DIVIDE(1),
.CLKOUT4_DUTY_CYCLE(0.5),
.CLKOUT4_PHASE(0),
.CLKOUT5_DIVIDE(1),
.CLKOUT5_DUTY_CYCLE(0.5),
.CLKOUT5_PHASE(0),
.CLKOUT6_DIVIDE(1),
.CLKOUT6_DUTY_CYCLE(0.5),
.CLKOUT6_PHASE(0),
.CLKFBOUT_MULT_F(25),
.CLKFBOUT_PHASE(0),
.DIVCLK_DIVIDE(2),
.REF_JITTER1(0.010),
.CLKIN1_PERIOD(12.5),
.STARTUP_WAIT("FALSE"),
.CLKOUT4_CASCADE("FALSE")
)
clk_mmcm_inst (
.CLKIN1(clk_80mhz_ibufg),
.CLKFBIN(mmcm_clkfb),
.RST(mmcm_rst),
.PWRDWN(1'b0),
.CLKOUT0(clk_125mhz_mmcm_out),
.CLKOUT0B(),
.CLKOUT1(),
.CLKOUT1B(),
.CLKOUT2(),
.CLKOUT2B(),
.CLKOUT3(),
.CLKOUT3B(),
.CLKOUT4(),
.CLKOUT5(),
.CLKOUT6(),
.CLKFBOUT(mmcm_clkfb),
.CLKFBOUTB(),
.LOCKED(mmcm_locked)
);
BUFG
clk_bufg_inst (
.I(clk_125mhz_mmcm_out),
.O(clk_125mhz_int)
);
taxi_sync_reset #(
.N(4)
)
sync_reset_inst (
.clk(clk_125mhz_int),
.rst(~mmcm_locked),
.out(rst_125mhz_int)
);
// I2C
wire si5338_i2c_scl_i;
wire si5338_i2c_scl_o;
wire si5338_i2c_sda_i;
wire si5338_i2c_sda_o;
assign si5338_i2c_scl_i = si5338_i2c_scl;
assign si5338_i2c_scl = si5338_i2c_scl_o ? 1'bz : 1'b0;
assign si5338_i2c_sda_i = si5338_i2c_sda;
assign si5338_i2c_sda = si5338_i2c_sda_o ? 1'bz : 1'b0;
// wire i2c_init_scl_i = i2c_scl_i;
// wire i2c_init_scl_o;
// wire i2c_init_sda_i = i2c_sda_i;
// wire i2c_init_sda_o;
// wire i2c_int_scl_i = i2c_scl_i;
// wire i2c_int_scl_o;
// wire i2c_int_sda_i = i2c_sda_i;
// wire i2c_int_sda_o;
// assign si5338_i2c_scl_o = si5338_i2c_init_scl_o & si5338_i2c_int_scl_o;
// assign si5338_i2c_sda_o = si5338_i2c_init_sda_o & si5338_i2c_int_sda_o;
// Si5338 init
taxi_axis_if #(.DATA_W(12)) si5338_i2c_cmd();
taxi_axis_if #(.DATA_W(8)) si5338_i2c_tx();
taxi_axis_if #(.DATA_W(8)) si5338_i2c_rx();
assign si5338_i2c_rx.tready = 1'b1;
wire si5338_i2c_busy;
// assign si5338_rst = ~rst_125mhz_int;
taxi_i2c_master
si5338_i2c_master_inst (
.clk(clk_125mhz_int),
.rst(rst_125mhz_int),
/*
* Host interface
*/
.s_axis_cmd(si5338_i2c_cmd),
.s_axis_tx(si5338_i2c_tx),
.m_axis_rx(si5338_i2c_rx),
/*
* I2C interface
*/
.scl_i(si5338_i2c_scl_i),
.scl_o(si5338_i2c_scl_o),
.sda_i(si5338_i2c_sda_i),
.sda_o(si5338_i2c_sda_o),
/*
* Status
*/
.busy(),
.bus_control(),
.bus_active(),
.missed_ack(),
/*
* Configuration
*/
.prescale(SIM ? 32 : 312),
.stop_on_idle(1)
);
si5338_i2c_init #(
.SIM_SPEEDUP(SIM)
)
si5338_i2c_init_inst (
.clk(clk_125mhz_int),
.rst(rst_125mhz_int),
/*
* I2C master interface
*/
.m_axis_cmd(si5338_i2c_cmd),
.m_axis_tx(si5338_i2c_tx),
/*
* Status
*/
.busy(si5338_i2c_busy),
/*
* Configuration
*/
.start(1'b1)
);
fpga_core #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.CFG_LOW_LATENCY(CFG_LOW_LATENCY),
.COMBINED_MAC_PCS(COMBINED_MAC_PCS)
)
core_inst (
/*
* Clock: 125MHz
* Synchronous reset
*/
.clk_125mhz(clk_125mhz_int),
.rst_125mhz(rst_125mhz_int),
/*
* GPIO
*/
.sfp_led(sfp_led),
.led(led),
.led_red(led_red),
.led_green(led_green),
.led_sync(led_sync),
/*
* Ethernet: SFP+
*/
.sfp_rx_p(sfp_rx_p),
.sfp_rx_n(sfp_rx_n),
.sfp_tx_p(sfp_tx_p),
.sfp_tx_n(sfp_tx_n),
.sfp_mgt_refclk_p(sfp_mgt_refclk_p),
.sfp_mgt_refclk_n(sfp_mgt_refclk_n),
// .sma_mgt_refclk_p(sma_mgt_refclk_p),
// .sma_mgt_refclk_n(sma_mgt_refclk_n),
// .sfp_recclk_p(sfp_recclk_p),
// .sfp_recclk_n(sfp_recclk_n),
.sfp_mod_abs(sfp_mod_abs),
.sfp_rs(sfp_rs),
.sfp_tx_disable(sfp_tx_disable)
);
endmodule
`resetall

View File

@@ -0,0 +1,434 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2014-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* FPGA core logic
*/
module fpga_core #
(
// simulation (set to avoid vendor primitives)
parameter logic SIM = 1'b0,
// vendor ("GENERIC", "XILINX", "ALTERA")
parameter string VENDOR = "XILINX",
// device family
parameter string FAMILY = "virtex7",
// 10G MAC configuration
parameter logic CFG_LOW_LATENCY = 1'b1,
parameter logic COMBINED_MAC_PCS = 1'b1,
parameter MAC_DATA_W = 32
)
(
/*
* Clock: 125 MHz
* Synchronous reset
*/
input wire logic clk_125mhz,
input wire logic rst_125mhz,
/*
* GPIO
*/
output wire logic [3:0] sfp_led,
output wire logic [3:0] led,
output wire logic led_red,
output wire logic led_green,
output wire logic [1:0] led_sync,
/*
* Ethernet: SFP+
*/
input wire logic sfp_rx_p[4],
input wire logic sfp_rx_n[4],
output wire logic sfp_tx_p[4],
output wire logic sfp_tx_n[4],
input wire logic sfp_mgt_refclk_p[2],
input wire logic sfp_mgt_refclk_n[2],
input wire logic sfp_mod_abs[4],
output wire logic [1:0] sfp_rs[4],
input wire logic sfp_los[4],
output wire logic sfp_tx_disable[4],
input wire logic sfp_tx_fault[4]
);
// SFP+
assign sfp_tx_disable = '{4{1'b0}};
wire sfp_tx_clk[4];
wire sfp_tx_rst[4];
wire sfp_rx_clk[4];
wire sfp_rx_rst[4];
wire sfp_rx_status[4];
assign sfp_led[0] = sfp_rx_status[0];
assign sfp_led[1] = sfp_rx_status[1];
assign sfp_led[2] = sfp_rx_status[2];
assign sfp_led[3] = sfp_rx_status[3];
wire [1:0] sfp_gtpowergood;
taxi_axis_if #(.DATA_W(MAC_DATA_W), .ID_W(8), .USER_EN(1), .USER_W(1)) axis_sfp_tx[4]();
taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp_tx_cpl[4]();
taxi_axis_if #(.DATA_W(MAC_DATA_W), .ID_W(8), .USER_EN(1), .USER_W(1)) axis_sfp_rx[4]();
taxi_axis_if #(.DATA_W(16), .KEEP_W(1), .KEEP_EN(0), .LAST_EN(0), .USER_EN(1), .USER_W(1), .ID_EN(1), .ID_W(8)) axis_sfp_stat[2]();
wire sfp_mgt_refclk[2];
wire sfp_mgt_refclk_bufg[2];
wire sfp_rst[2];
for (genvar n = 0; n < 2; n = n + 1) begin : gty_clk
wire sfp_mgt_refclk_int;
if (SIM) begin
assign sfp_mgt_refclk[n] = sfp_mgt_refclk_p[n];
assign sfp_mgt_refclk_int = sfp_mgt_refclk_p[n];
assign sfp_mgt_refclk_bufg[n] = sfp_mgt_refclk_int;
end else begin
IBUFDS_GTE2 ibufds_gte2_sfp_mgt_refclk_inst (
.I (sfp_mgt_refclk_p[n]),
.IB (sfp_mgt_refclk_n[n]),
.CEB (1'b0),
.O (sfp_mgt_refclk[n]),
.ODIV2 (sfp_mgt_refclk_int)
);
BUFG bufg_sfp_mgt_refclk_inst (
.I (sfp_mgt_refclk_int),
.O (sfp_mgt_refclk_bufg[n])
);
end
taxi_sync_reset #(
.N(4)
)
sfp_sync_reset_inst (
.clk(sfp_mgt_refclk_bufg[n]),
.rst(rst_125mhz),
.out(sfp_rst[n])
);
end
for (genvar n = 0; n < 2; n = n + 1) begin : gt_quad
localparam CLK = n;
localparam CNT = 2;
taxi_apb_if #(
.ADDR_W(18),
.DATA_W(16)
)
gt_apb_ctrl();
taxi_eth_mac_25g_us #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.CNT(CNT),
// GT config
.CFG_LOW_LATENCY(CFG_LOW_LATENCY),
// GT type
.GT_TYPE("GTH"),
// GT parameters
.GT_TX_POLARITY(n == 0 ? 2'b01 : 2'b00),
.GT_RX_POLARITY('0),
// MAC/PHY config
.COMBINED_MAC_PCS(COMBINED_MAC_PCS),
.DATA_W(MAC_DATA_W),
.PADDING_EN(1'b1),
.DIC_EN(1'b1),
.MIN_FRAME_LEN(64),
.PTP_TS_EN(1'b0),
.PTP_TD_EN(1'b0),
.PTP_TS_FMT_TOD(1'b1),
.PTP_TS_W(96),
.PTP_TD_SDI_PIPELINE(2),
.PRBS31_EN(1'b0),
.TX_SERDES_PIPELINE(1),
.RX_SERDES_PIPELINE(1),
.COUNT_125US(125000/6.4),
.STAT_EN(1'b0)
)
mac_inst (
.xcvr_ctrl_clk(clk_125mhz),
.xcvr_ctrl_rst(sfp_rst[CLK]),
/*
* Transceiver control
*/
.s_apb_ctrl(gt_apb_ctrl),
/*
* Common
*/
.xcvr_gtpowergood_out(sfp_gtpowergood[n]),
.xcvr_gtrefclk00_in(sfp_mgt_refclk[CLK]),
.xcvr_qpll0pd_in(1'b0),
.xcvr_qpll0reset_in(1'b0),
.xcvr_qpll0pcierate_in(3'd0),
.xcvr_qpll0lock_out(),
.xcvr_qpll0clk_out(),
.xcvr_qpll0refclk_out(),
.xcvr_gtrefclk01_in(sfp_mgt_refclk[CLK]),
.xcvr_qpll1pd_in(1'b0),
.xcvr_qpll1reset_in(1'b0),
.xcvr_qpll1pcierate_in(3'd0),
.xcvr_qpll1lock_out(),
.xcvr_qpll1clk_out(),
.xcvr_qpll1refclk_out(),
/*
* Serial data
*/
.xcvr_txp(sfp_tx_p[n*CNT +: CNT]),
.xcvr_txn(sfp_tx_n[n*CNT +: CNT]),
.xcvr_rxp(sfp_rx_p[n*CNT +: CNT]),
.xcvr_rxn(sfp_rx_n[n*CNT +: CNT]),
/*
* MAC clocks
*/
.rx_clk(sfp_rx_clk[n*CNT +: CNT]),
.rx_rst_in('{CNT{1'b0}}),
.rx_rst_out(sfp_rx_rst[n*CNT +: CNT]),
.tx_clk(sfp_tx_clk[n*CNT +: CNT]),
.tx_rst_in('{CNT{1'b0}}),
.tx_rst_out(sfp_tx_rst[n*CNT +: CNT]),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_sfp_tx[n*CNT +: CNT]),
.m_axis_tx_cpl(axis_sfp_tx_cpl[n*CNT +: CNT]),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_sfp_rx[n*CNT +: CNT]),
/*
* PTP clock
*/
.ptp_clk(1'b0),
.ptp_rst(1'b0),
.ptp_sample_clk(1'b0),
.ptp_td_sdi(1'b0),
.tx_ptp_ts_in('{CNT{'0}}),
.tx_ptp_ts_out(),
.tx_ptp_ts_step_out(),
.tx_ptp_locked(),
.rx_ptp_ts_in('{CNT{'0}}),
.rx_ptp_ts_out(),
.rx_ptp_ts_step_out(),
.rx_ptp_locked(),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req('{CNT{1'b0}}),
.tx_lfc_resend('{CNT{1'b0}}),
.rx_lfc_en('{CNT{1'b0}}),
.rx_lfc_req(),
.rx_lfc_ack('{CNT{1'b0}}),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req('{CNT{'0}}),
.tx_pfc_resend('{CNT{1'b0}}),
.rx_pfc_en('{CNT{'0}}),
.rx_pfc_req(),
.rx_pfc_ack('{CNT{'0}}),
/*
* Pause interface
*/
.tx_lfc_pause_en('{CNT{1'b0}}),
.tx_pause_req('{CNT{1'b0}}),
.tx_pause_ack(),
/*
* Statistics
*/
.stat_clk(clk_125mhz),
.stat_rst(rst_125mhz),
.m_axis_stat(axis_sfp_stat[n]),
/*
* Status
*/
.tx_start_packet(),
.stat_tx_byte(),
.stat_tx_pkt_len(),
.stat_tx_pkt_ucast(),
.stat_tx_pkt_mcast(),
.stat_tx_pkt_bcast(),
.stat_tx_pkt_vlan(),
.stat_tx_pkt_good(),
.stat_tx_pkt_bad(),
.stat_tx_err_oversize(),
.stat_tx_err_user(),
.stat_tx_err_underflow(),
.rx_start_packet(),
.rx_error_count(),
.rx_block_lock(),
.rx_high_ber(),
.rx_status(sfp_rx_status[n*CNT +: CNT]),
.stat_rx_byte(),
.stat_rx_pkt_len(),
.stat_rx_pkt_fragment(),
.stat_rx_pkt_jabber(),
.stat_rx_pkt_ucast(),
.stat_rx_pkt_mcast(),
.stat_rx_pkt_bcast(),
.stat_rx_pkt_vlan(),
.stat_rx_pkt_good(),
.stat_rx_pkt_bad(),
.stat_rx_err_oversize(),
.stat_rx_err_bad_fcs(),
.stat_rx_err_bad_block(),
.stat_rx_err_framing(),
.stat_rx_err_preamble(),
.stat_rx_fifo_drop('{CNT{1'b0}}),
.stat_tx_mcf(),
.stat_rx_mcf(),
.stat_tx_lfc_pkt(),
.stat_tx_lfc_xon(),
.stat_tx_lfc_xoff(),
.stat_tx_lfc_paused(),
.stat_tx_pfc_pkt(),
.stat_tx_pfc_xon(),
.stat_tx_pfc_xoff(),
.stat_tx_pfc_paused(),
.stat_rx_lfc_pkt(),
.stat_rx_lfc_xon(),
.stat_rx_lfc_xoff(),
.stat_rx_lfc_paused(),
.stat_rx_pfc_pkt(),
.stat_rx_pfc_xon(),
.stat_rx_pfc_xoff(),
.stat_rx_pfc_paused(),
/*
* Configuration
*/
.cfg_tx_max_pkt_len('{CNT{16'd9218}}),
.cfg_tx_ifg('{CNT{8'd12}}),
.cfg_tx_enable('{CNT{1'b1}}),
.cfg_rx_max_pkt_len('{CNT{16'd9218}}),
.cfg_rx_enable('{CNT{1'b1}}),
.cfg_tx_prbs31_enable('{CNT{1'b0}}),
.cfg_rx_prbs31_enable('{CNT{1'b0}}),
.cfg_mcf_rx_eth_dst_mcast('{CNT{48'h01_80_C2_00_00_01}}),
.cfg_mcf_rx_check_eth_dst_mcast('{CNT{1'b1}}),
.cfg_mcf_rx_eth_dst_ucast('{CNT{48'd0}}),
.cfg_mcf_rx_check_eth_dst_ucast('{CNT{1'b0}}),
.cfg_mcf_rx_eth_src('{CNT{48'd0}}),
.cfg_mcf_rx_check_eth_src('{CNT{1'b0}}),
.cfg_mcf_rx_eth_type('{CNT{16'h8808}}),
.cfg_mcf_rx_opcode_lfc('{CNT{16'h0001}}),
.cfg_mcf_rx_check_opcode_lfc('{CNT{1'b1}}),
.cfg_mcf_rx_opcode_pfc('{CNT{16'h0101}}),
.cfg_mcf_rx_check_opcode_pfc('{CNT{1'b1}}),
.cfg_mcf_rx_forward('{CNT{1'b0}}),
.cfg_mcf_rx_enable('{CNT{1'b0}}),
.cfg_tx_lfc_eth_dst('{CNT{48'h01_80_C2_00_00_01}}),
.cfg_tx_lfc_eth_src('{CNT{48'h80_23_31_43_54_4C}}),
.cfg_tx_lfc_eth_type('{CNT{16'h8808}}),
.cfg_tx_lfc_opcode('{CNT{16'h0001}}),
.cfg_tx_lfc_en('{CNT{1'b0}}),
.cfg_tx_lfc_quanta('{CNT{16'hffff}}),
.cfg_tx_lfc_refresh('{CNT{16'h7fff}}),
.cfg_tx_pfc_eth_dst('{CNT{48'h01_80_C2_00_00_01}}),
.cfg_tx_pfc_eth_src('{CNT{48'h80_23_31_43_54_4C}}),
.cfg_tx_pfc_eth_type('{CNT{16'h8808}}),
.cfg_tx_pfc_opcode('{CNT{16'h0101}}),
.cfg_tx_pfc_en('{CNT{1'b0}}),
.cfg_tx_pfc_quanta('{CNT{'{8{16'hffff}}}}),
.cfg_tx_pfc_refresh('{CNT{'{8{16'h7fff}}}}),
.cfg_rx_lfc_opcode('{CNT{16'h0001}}),
.cfg_rx_lfc_en('{CNT{1'b0}}),
.cfg_rx_pfc_opcode('{CNT{16'h0101}}),
.cfg_rx_pfc_en('{CNT{1'b0}})
);
end
for (genvar n = 0; n < 4; n = n + 1) begin : sfp_ch
taxi_axis_async_fifo #(
.DEPTH(16384),
.RAM_PIPELINE(2),
.FRAME_FIFO(1),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(1),
.DROP_BAD_FRAME(1),
.DROP_WHEN_FULL(1)
)
ch_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(sfp_rx_clk[n]),
.s_rst(sfp_rx_rst[n]),
.s_axis(axis_sfp_rx[n]),
/*
* AXI4-Stream output (source)
*/
.m_clk(sfp_tx_clk[n]),
.m_rst(sfp_tx_rst[n]),
.m_axis(axis_sfp_tx[n]),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
end
endmodule
`resetall

View File

@@ -0,0 +1,548 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* I2C init
*/
module si5338_i2c_init #
(
parameter logic SIM_SPEEDUP = 1'b0
)
(
input wire logic clk,
input wire logic rst,
/*
* I2C master interface
*/
taxi_axis_if.src m_axis_cmd,
taxi_axis_if.src m_axis_tx,
/*
* Status
*/
output wire logic busy,
/*
* Configuration
*/
input wire logic start
);
/*
Generic module for I2C bus initialization. Good for use when multiple devices
on an I2C bus must be initialized on system start without intervention of a
general-purpose processor.
Copy this file and change init_data and INIT_DATA_LEN as needed.
This module can be used in two modes: simple device initialization, or multiple
device initialization. In multiple device mode, the same initialization sequence
can be performed on multiple different device addresses.
To use single device mode, only use the start write to address and write data commands.
The module will generate the I2C commands in sequential order. Terminate the list
with a 0 entry.
To use the multiple device mode, use the start data and start address block commands
to set up lists of initialization data and device addresses. The module enters
multiple device mode upon seeing a start data block command. The module stores the
offset of the start of the data block and then skips ahead until it reaches a start
address block command. The module will store the offset to the address block and
read the first address in the block. Then it will jump back to the data block
and execute it, substituting the stored address for each current address write
command. Upon reaching the start address block command, the module will read out the
next address and start again at the top of the data block. If the module encounters
a start data block command while looking for an address, then it will store a new data
offset and then look for a start address block command. Terminate the list with a 0
entry. Normal address commands will operate normally inside a data block.
Commands:
00 0000000 : halt
00 0000001 : exit multiple device mode
00 0000011 : start write to current address
00 0001000 : start address block
00 0001001 : start data block
00 001dddd : delay 2**(16+d) cycles
00 1000001 : send I2C stop
01 aaaaaaa : start write to address
1 dddddddd : write 8-bit data
Examples
write 0x11223344 to register 0x0004 on device at 0x50
01 1010000 start write to 0x50
1 00000000 write address 0x0004
1 00000100
1 00010001 write data 0x11223344
1 00100010
1 00110011
1 01000100
0 00000000 halt
write 0x11223344 to register 0x0004 on devices at 0x50, 0x51, 0x52, and 0x53
00 0001001 start data block
00 0000011 start write to current address
1 00000000 write address 0x0004
1 00000100
1 00010001 write data 0x11223344
1 00100010
1 00110011
1 01000100
00 0001000 start address block
01 1010000 address 0x50
01 1010001 address 0x51
01 1010010 address 0x52
01 1010011 address 0x53
00 0000001 exit multi-dev mode
00 0000000 halt
*/
// check configuration
if (m_axis_cmd.DATA_W < 12)
$fatal(0, "Command interface width must be at least 12 bits (instance %m)");
if (m_axis_tx.DATA_W != 8)
$fatal(0, "Data interface width must be 8 bits (instance %m)");
function [8:0] cmd_start(input [6:0] addr);
cmd_start = {2'b01, addr};
endfunction
function [8:0] cmd_wr(input [7:0] data);
cmd_wr = {1'b1, data};
endfunction
function [8:0] cmd_stop();
cmd_stop = {2'b00, 7'b1000001};
endfunction
function [8:0] cmd_delay(input [3:0] d);
cmd_delay = {2'b00, 3'b001, d};
endfunction
function [8:0] cmd_halt();
cmd_halt = 9'd0;
endfunction
function [8:0] blk_start_data();
blk_start_data = {2'b00, 7'b0001001};
endfunction
function [8:0] blk_start_addr();
blk_start_addr = {2'b00, 7'b0001000};
endfunction
function [8:0] cmd_start_cur();
cmd_start_cur = {2'b00, 7'b0000011};
endfunction
function [8:0] cmd_exit();
cmd_exit = {2'b00, 7'b0000001};
endfunction
// init_data ROM
localparam INIT_DATA_LEN = 20;
logic [8:0] init_data [INIT_DATA_LEN-1:0];
initial begin
// Initial delay
init_data[0] = cmd_delay(6); // delay 30 ms
// init Si5338 registers
init_data[1] = cmd_start(7'h70); // start write to 0x70 (Si5338)
init_data[2] = cmd_wr(8'd255); // register 255
init_data[3] = cmd_wr(8'd0); // Reg 255: select page 0
init_data[4] = cmd_start(7'h70); // start write to 0x70 (Si5338)
init_data[5] = cmd_wr(8'd31); // register 31
init_data[6] = cmd_wr(8'hC0); // Reg 31: power up CLK0
init_data[7] = cmd_wr(8'hC1); // Reg 32: power down CLK1
init_data[8] = cmd_wr(8'hC1); // Reg 33: power down CLK2
init_data[9] = cmd_wr(8'hC0); // Reg 34: power up CLK3
init_data[10] = cmd_wr(8'hAA); // Reg 35: VDDO 0-3 = 1.8V
init_data[11] = cmd_wr(8'h06); // Reg 36: CLK0 LVDS, no inversion
init_data[12] = cmd_wr(8'h06); // Reg 37: CLK1 LVDS, no inversion
init_data[13] = cmd_wr(8'h06); // Reg 38: CLK2 LVDS, no inversion
init_data[14] = cmd_wr(8'h06); // Reg 39: CLK3 LVDS, no inversion
init_data[15] = cmd_start(7'h70); // start write to 0x70 (Si5338)
init_data[16] = cmd_wr(8'd230); // register 230
init_data[17] = cmd_wr(8'h06); // Reg 230: enable CLK0 and CLK3
init_data[18] = cmd_delay(6); // delay 30 ms
init_data[19] = cmd_halt(); // stop
end
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_RUN = 3'd1,
STATE_TABLE_1 = 3'd2,
STATE_TABLE_2 = 3'd3,
STATE_TABLE_3 = 3'd4;
logic [2:0] state_reg = STATE_IDLE, state_next;
localparam AW = $clog2(INIT_DATA_LEN);
logic [8:0] init_data_reg = '0;
logic [AW-1:0] address_reg = '0, address_next;
logic [AW-1:0] address_ptr_reg = '0, address_ptr_next;
logic [AW-1:0] data_ptr_reg = '0, data_ptr_next;
logic [6:0] cur_address_reg = '0, cur_address_next;
logic [31:0] delay_counter_reg = '0, delay_counter_next;
logic [6:0] m_axis_cmd_address_reg = '0, m_axis_cmd_address_next;
logic m_axis_cmd_start_reg = 1'b0, m_axis_cmd_start_next;
logic m_axis_cmd_write_reg = 1'b0, m_axis_cmd_write_next;
logic m_axis_cmd_stop_reg = 1'b0, m_axis_cmd_stop_next;
logic m_axis_cmd_valid_reg = 1'b0, m_axis_cmd_valid_next;
logic [7:0] m_axis_tx_tdata_reg = '0, m_axis_tx_tdata_next;
logic m_axis_tx_tvalid_reg = 1'b0, m_axis_tx_tvalid_next;
logic start_flag_reg = 1'b0, start_flag_next;
logic busy_reg = 1'b0;
assign m_axis_cmd.tdata[6:0] = m_axis_cmd_address_reg;
assign m_axis_cmd.tdata[7] = m_axis_cmd_start_reg;
assign m_axis_cmd.tdata[8] = 1'b0; // read
assign m_axis_cmd.tdata[9] = m_axis_cmd_write_reg;
assign m_axis_cmd.tdata[10] = 1'b0; // write multi
assign m_axis_cmd.tdata[11] = m_axis_cmd_stop_reg;
assign m_axis_cmd.tvalid = m_axis_cmd_valid_reg;
assign m_axis_cmd.tlast = 1'b1;
assign m_axis_cmd.tid = '0;
assign m_axis_cmd.tdest = '0;
assign m_axis_cmd.tuser = '0;
assign m_axis_tx.tdata = m_axis_tx_tdata_reg;
assign m_axis_tx.tvalid = m_axis_tx_tvalid_reg;
assign m_axis_tx.tlast = 1'b1;
assign m_axis_tx.tid = '0;
assign m_axis_tx.tdest = '0;
assign m_axis_tx.tuser = '0;
assign busy = busy_reg;
always_comb begin
state_next = STATE_IDLE;
address_next = address_reg;
address_ptr_next = address_ptr_reg;
data_ptr_next = data_ptr_reg;
cur_address_next = cur_address_reg;
delay_counter_next = delay_counter_reg;
m_axis_cmd_address_next = m_axis_cmd_address_reg;
m_axis_cmd_start_next = m_axis_cmd_start_reg && !(m_axis_cmd.tvalid && m_axis_cmd.tready);
m_axis_cmd_write_next = m_axis_cmd_write_reg && !(m_axis_cmd.tvalid && m_axis_cmd.tready);
m_axis_cmd_stop_next = m_axis_cmd_stop_reg && !(m_axis_cmd.tvalid && m_axis_cmd.tready);
m_axis_cmd_valid_next = m_axis_cmd_valid_reg && !m_axis_cmd.tready;
m_axis_tx_tdata_next = m_axis_tx_tdata_reg;
m_axis_tx_tvalid_next = m_axis_tx_tvalid_reg && !m_axis_tx.tready;
start_flag_next = start_flag_reg;
if (m_axis_cmd.tvalid || m_axis_tx.tvalid) begin
// wait for output registers to clear
state_next = state_reg;
end else if (delay_counter_reg != 0) begin
// delay
delay_counter_next = delay_counter_reg - 1;
state_next = state_reg;
end else begin
case (state_reg)
STATE_IDLE: begin
// wait for start signal
if (!start_flag_reg && start) begin
address_next = '0;
start_flag_next = 1'b1;
state_next = STATE_RUN;
end else begin
state_next = STATE_IDLE;
end
end
STATE_RUN: begin
// process commands
if (init_data_reg[8] == 1'b1) begin
// write data
m_axis_cmd_write_next = 1'b1;
m_axis_cmd_stop_next = 1'b0;
m_axis_cmd_valid_next = 1'b1;
m_axis_tx_tdata_next = init_data_reg[7:0];
m_axis_tx_tvalid_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg[8:7] == 2'b01) begin
// write address
m_axis_cmd_address_next = init_data_reg[6:0];
m_axis_cmd_start_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg[8:4] == 5'b00001) begin
// delay
if (SIM_SPEEDUP) begin
delay_counter_next = 32'd1 << (init_data_reg[3:0]);
end else begin
delay_counter_next = 32'd1 << (init_data_reg[3:0]+16);
end
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg == 9'b001000001) begin
// send stop
m_axis_cmd_write_next = 1'b0;
m_axis_cmd_start_next = 1'b0;
m_axis_cmd_stop_next = 1'b1;
m_axis_cmd_valid_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg == 9'b000001001) begin
// data table start
data_ptr_next = address_reg + 1;
address_next = address_reg + 1;
state_next = STATE_TABLE_1;
end else if (init_data_reg == 9'd0) begin
// stop
m_axis_cmd_start_next = 1'b0;
m_axis_cmd_write_next = 1'b0;
m_axis_cmd_stop_next = 1'b1;
m_axis_cmd_valid_next = 1'b1;
state_next = STATE_IDLE;
end else begin
// invalid command, skip
address_next = address_reg + 1;
state_next = STATE_RUN;
end
end
STATE_TABLE_1: begin
// find address table start
if (init_data_reg == 9'b000001000) begin
// address table start
address_ptr_next = address_reg + 1;
address_next = address_reg + 1;
state_next = STATE_TABLE_2;
end else if (init_data_reg == 9'b000001001) begin
// data table start
data_ptr_next = address_reg + 1;
address_next = address_reg + 1;
state_next = STATE_TABLE_1;
end else if (init_data_reg == 1) begin
// exit mode
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg == 9'd0) begin
// stop
m_axis_cmd_start_next = 1'b0;
m_axis_cmd_write_next = 1'b0;
m_axis_cmd_stop_next = 1'b1;
m_axis_cmd_valid_next = 1'b1;
state_next = STATE_IDLE;
end else begin
// invalid command, skip
address_next = address_reg + 1;
state_next = STATE_TABLE_1;
end
end
STATE_TABLE_2: begin
// find next address
if (init_data_reg[8:7] == 2'b01) begin
// write address command
// store address and move to data table
cur_address_next = init_data_reg[6:0];
address_ptr_next = address_reg + 1;
address_next = data_ptr_reg;
state_next = STATE_TABLE_3;
end else if (init_data_reg == 9'b000001001) begin
// data table start
data_ptr_next = address_reg + 1;
address_next = address_reg + 1;
state_next = STATE_TABLE_1;
end else if (init_data_reg == 9'd1) begin
// exit mode
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg == 9'd0) begin
// stop
m_axis_cmd_start_next = 1'b0;
m_axis_cmd_write_next = 1'b0;
m_axis_cmd_stop_next = 1'b1;
m_axis_cmd_valid_next = 1'b1;
state_next = STATE_IDLE;
end else begin
// invalid command, skip
address_next = address_reg + 1;
state_next = STATE_TABLE_2;
end
end
STATE_TABLE_3: begin
// process data table with selected address
if (init_data_reg[8] == 1'b1) begin
// write data
m_axis_cmd_write_next = 1'b1;
m_axis_cmd_stop_next = 1'b0;
m_axis_cmd_valid_next = 1'b1;
m_axis_tx_tdata_next = init_data_reg[7:0];
m_axis_tx_tvalid_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end else if (init_data_reg[8:7] == 2'b01) begin
// write address
m_axis_cmd_address_next = init_data_reg[6:0];
m_axis_cmd_start_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end else if (init_data_reg == 9'b000000011) begin
// write current address
m_axis_cmd_address_next = cur_address_reg;
m_axis_cmd_start_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end else if (init_data_reg[8:4] == 5'b00001) begin
// delay
if (SIM_SPEEDUP) begin
delay_counter_next = 32'd1 << (init_data_reg[3:0]);
end else begin
delay_counter_next = 32'd1 << (init_data_reg[3:0]+16);
end
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end else if (init_data_reg == 9'b001000001) begin
// send stop
m_axis_cmd_write_next = 1'b0;
m_axis_cmd_start_next = 1'b0;
m_axis_cmd_stop_next = 1'b1;
m_axis_cmd_valid_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end else if (init_data_reg == 9'b000001001) begin
// data table start
data_ptr_next = address_reg + 1;
address_next = address_reg + 1;
state_next = STATE_TABLE_1;
end else if (init_data_reg == 9'b000001000) begin
// address table start
address_next = address_ptr_reg;
state_next = STATE_TABLE_2;
end else if (init_data_reg == 9'd1) begin
// exit mode
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg == 9'd0) begin
// stop
m_axis_cmd_start_next = 1'b0;
m_axis_cmd_write_next = 1'b0;
m_axis_cmd_stop_next = 1'b1;
m_axis_cmd_valid_next = 1'b1;
state_next = STATE_IDLE;
end else begin
// invalid command, skip
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end
end
default: begin
// invalid state
state_next = STATE_IDLE;
end
endcase
end
end
always_ff @(posedge clk) begin
state_reg <= state_next;
// read init_data ROM
init_data_reg <= init_data[address_next];
address_reg <= address_next;
address_ptr_reg <= address_ptr_next;
data_ptr_reg <= data_ptr_next;
cur_address_reg <= cur_address_next;
delay_counter_reg <= delay_counter_next;
m_axis_cmd_address_reg <= m_axis_cmd_address_next;
m_axis_cmd_start_reg <= m_axis_cmd_start_next;
m_axis_cmd_write_reg <= m_axis_cmd_write_next;
m_axis_cmd_stop_reg <= m_axis_cmd_stop_next;
m_axis_cmd_valid_reg <= m_axis_cmd_valid_next;
m_axis_tx_tdata_reg <= m_axis_tx_tdata_next;
m_axis_tx_tvalid_reg <= m_axis_tx_tvalid_next;
start_flag_reg <= start && start_flag_next;
busy_reg <= (state_reg != STATE_IDLE);
if (rst) begin
state_reg <= STATE_IDLE;
init_data_reg <= '0;
address_reg <= '0;
address_ptr_reg <= '0;
data_ptr_reg <= '0;
cur_address_reg <= '0;
delay_counter_reg <= '0;
m_axis_cmd_valid_reg <= 1'b0;
m_axis_tx_tvalid_reg <= 1'b0;
start_flag_reg <= 1'b0;
busy_reg <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,58 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2020-2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
TOPLEVEL_LANG = verilog
SIM ?= verilator
WAVES ?= 0
COCOTB_HDL_TIMEUNIT = 1ns
COCOTB_HDL_TIMEPRECISION = 1ps
RTL_DIR = ../../rtl
LIB_DIR = ../../lib
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
DUT = fpga_core
COCOTB_TEST_MODULES = test_$(DUT)
COCOTB_TOPLEVEL = $(DUT)
MODULE = $(COCOTB_TEST_MODULES)
TOPLEVEL = $(COCOTB_TOPLEVEL)
VERILOG_SOURCES += $(RTL_DIR)/$(DUT).sv
VERILOG_SOURCES += $(TAXI_SRC_DIR)/eth/rtl/taxi_eth_mac_1g_fifo.f
VERILOG_SOURCES += $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_mac_25g_us.f
VERILOG_SOURCES += $(TAXI_SRC_DIR)/lss/rtl/taxi_i2c_master.sv
VERILOG_SOURCES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_reset.sv
VERILOG_SOURCES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
# handle file list files
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
# module parameters
export PARAM_SIM := "1'b1"
export PARAM_VENDOR := "\"XILINX\""
export PARAM_FAMILY := "\"virtex7\""
export PARAM_CFG_LOW_LATENCY := "1'b1"
export PARAM_COMBINED_MAC_PCS := "1'b1"
ifeq ($(SIM), icarus)
PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
else ifeq ($(SIM), verilator)
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst
VERILATOR_TRACE = 1
endif
endif
include $(shell cocotb-config --makefiles)/Makefile.sim

View File

@@ -0,0 +1 @@
../../lib/taxi/src/eth/tb/baser.py

View File

@@ -0,0 +1,227 @@
#!/usr/bin/env python
# SPDX-License-Identifier: MIT
"""
Copyright (c) 2020-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
"""
import itertools
import logging
import os
import sys
import pytest
import cocotb_test.simulator
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge, Combine
from cocotbext.eth import XgmiiFrame
try:
from baser import BaseRSerdesSource, BaseRSerdesSink
except ImportError:
# attempt import from current directory
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))
try:
from baser import BaseRSerdesSource, BaseRSerdesSink
finally:
del sys.path[0]
class TB:
def __init__(self, dut):
self.dut = dut
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
cocotb.start_soon(Clock(dut.clk_125mhz, 8, units="ns").start())
self.sfp_sources = []
self.sfp_sinks = []
cocotb.start_soon(Clock(dut.sfp_mgt_refclk_p[0], 6.4, units="ns").start())
cocotb.start_soon(Clock(dut.sfp_mgt_refclk_p[1], 6.4, units="ns").start())
for ch in itertools.chain.from_iterable([inst.mac_inst.ch for inst in dut.gt_quad]):
gt_inst = ch.ch_inst.gt.gt_inst
if ch.ch_inst.CFG_LOW_LATENCY.value:
clk = 3.102
gbx_cfg = (66, [64, 65])
else:
clk = 3.2
gbx_cfg = None
cocotb.start_soon(Clock(gt_inst.tx_clk, clk, units="ns").start())
cocotb.start_soon(Clock(gt_inst.rx_clk, clk, units="ns").start())
self.sfp_sources.append(BaseRSerdesSource(
data=gt_inst.serdes_rx_data,
data_valid=gt_inst.serdes_rx_data_valid,
hdr=gt_inst.serdes_rx_hdr,
hdr_valid=gt_inst.serdes_rx_hdr_valid,
clock=gt_inst.rx_clk,
slip=gt_inst.serdes_rx_bitslip,
reverse=True,
gbx_cfg=gbx_cfg
))
self.sfp_sinks.append(BaseRSerdesSink(
data=gt_inst.serdes_tx_data,
data_valid=gt_inst.serdes_tx_data_valid,
hdr=gt_inst.serdes_tx_hdr,
hdr_valid=gt_inst.serdes_tx_hdr_valid,
gbx_sync=gt_inst.serdes_tx_gbx_sync,
clock=gt_inst.tx_clk,
reverse=True,
gbx_cfg=gbx_cfg
))
async def init(self):
self.dut.rst_125mhz.setimmediatevalue(0)
for k in range(10):
await RisingEdge(self.dut.clk_125mhz)
self.dut.rst_125mhz.value = 1
for k in range(10):
await RisingEdge(self.dut.clk_125mhz)
self.dut.rst_125mhz.value = 0
for k in range(10):
await RisingEdge(self.dut.clk_125mhz)
async def mac_test_10g(tb, source, sink):
tb.log.info("Test MAC")
tb.log.info("Wait for block lock")
for k in range(1200):
await RisingEdge(tb.dut.clk_125mhz)
tb.log.info("Multiple small packets")
count = 64
pkts = [bytearray([(x+k) % 256 for x in range(60)]) for k in range(count)]
for p in pkts:
await source.send(XgmiiFrame.from_payload(p))
for k in range(count):
rx_frame = await sink.recv()
tb.log.info("RX frame: %s", rx_frame)
assert rx_frame.get_payload() == pkts[k]
assert rx_frame.check_fcs()
tb.log.info("Multiple large packets")
count = 32
pkts = [bytearray([(x+k) % 256 for x in range(1514)]) for k in range(count)]
for p in pkts:
await source.send(XgmiiFrame.from_payload(p))
for k in range(count):
rx_frame = await sink.recv()
tb.log.info("RX frame: %s", rx_frame)
assert rx_frame.get_payload() == pkts[k]
assert rx_frame.check_fcs()
tb.log.info("MAC test done")
@cocotb.test()
async def run_test(dut):
tb = TB(dut)
await tb.init()
tests = []
for k in range(len(tb.sfp_sources)):
tb.log.info("Start SFP %d 10G MAC loopback test", k)
tests.append(cocotb.start_soon(mac_test_10g(tb, tb.sfp_sources[k], tb.sfp_sinks[k])))
await Combine(*tests)
await RisingEdge(dut.clk_125mhz)
await RisingEdge(dut.clk_125mhz)
# cocotb-test
tests_dir = os.path.abspath(os.path.dirname(__file__))
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
lib_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'lib'))
taxi_src_dir = os.path.abspath(os.path.join(lib_dir, 'taxi', 'src'))
def process_f_files(files):
lst = {}
for f in files:
if f[-2:].lower() == '.f':
with open(f, 'r') as fp:
l = fp.read().split()
for f in process_f_files([os.path.join(os.path.dirname(f), x) for x in l]):
lst[os.path.basename(f)] = f
else:
lst[os.path.basename(f)] = f
return list(lst.values())
@pytest.mark.parametrize("sfp_rate", [0, 1])
def test_fpga_core(request, sfp_rate):
dut = "fpga_core"
module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = dut
verilog_sources = [
os.path.join(rtl_dir, f"{dut}.sv"),
os.path.join(taxi_src_dir, "eth", "rtl", "taxi_eth_mac_1g_fifo.f"),
os.path.join(taxi_src_dir, "eth", "rtl", "us", "taxi_eth_mac_25g_us.f"),
os.path.join(taxi_src_dir, "lss", "rtl", "taxi_i2c_master.sv"),
os.path.join(taxi_src_dir, "sync", "rtl", "taxi_sync_reset.sv"),
os.path.join(taxi_src_dir, "sync", "rtl", "taxi_sync_signal.sv"),
]
verilog_sources = process_f_files(verilog_sources)
parameters = {}
parameters['SIM'] = "1'b1"
parameters['VENDOR'] = "\"XILINX\""
parameters['FAMILY'] = "\"virtex7\""
parameters['CFG_LOW_LATENCY'] = "1'b1"
parameters['COMBINED_MAC_PCS'] = "1'b1"
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
sim_build = os.path.join(tests_dir, "sim_build",
request.node.name.replace('[', '-').replace(']', ''))
cocotb_test.simulator.run(
simulator="verilator",
python_search=[tests_dir],
verilog_sources=verilog_sources,
toplevel=toplevel,
module=module,
parameters=parameters,
sim_build=sim_build,
extra_env=extra_env,
)