eth: Add Ethernet example design for NetFPGA SUME

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2025-11-08 19:46:20 -08:00
parent 4dbfc4d388
commit 6054f76a17
12 changed files with 2222 additions and 0 deletions

View File

@@ -156,6 +156,7 @@ Example designs are provided for several different FPGA boards, showcasing many
* Cisco Nexus K3P-Q/ExaNIC X100 (Xilinx Kintex UltraScale+ XCKU3P)
* Alibaba AS02MC04 (Xilinx Kintex UltraScale+ XCKU3P)
* Digilent Arty A7 (Xilinx Artix 7 XC7A35T)
* Digilent NetFPGA SUME (Xilinx Virtex 7 XC7V690T)
* HiTech Global HTG-940 (Xilinx Virtex UltraScale+ XCVU9P/XCVU13P)
* HiTech Global HTG-9200 (Xilinx Virtex UltraScale+ XCVU9P/XCVU13P)
* HiTech Global HTG-ZRF8-R2 (Xilinx Zynq UltraScale+ RFSoC XCZU28DR/XCZU48DR)

View File

@@ -0,0 +1,34 @@
# Taxi Example Design for NetFPGA SUME
## Introduction
This example design targets the Digilent NetFPGA SUME FPGA board.
The design places looped-back MACs on the SFP+ cages, as well as XFCP on the USB UART for monitoring and control.
* USB UART
* XFCP (3 Mbaud)
* SFP+ cages
* Looped-back 10GBASE-R MACs via GTH transceivers
## Board details
* FPGA: XC7VX690T-3FFG1761
* USB UART: FTDI FT2232HQ
## 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.

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,185 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2014-2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# XDC constraints for the Xilinx VC709
# part: xc7vx690tffg1761-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.UNUSEDPIN Pullup [current_design]
# 200 MHz system clock
set_property -dict {LOC H19 IOSTANDARD LVDS} [get_ports clk_200mhz_p]
set_property -dict {LOC G18 IOSTANDARD LVDS} [get_ports clk_200mhz_n]
create_clock -period 5 -name clk_200mhz [get_ports clk_200mhz_p]
# 200 MHz QDRII A/B MIG clock
#set_property -dict {LOC AD32 IOSTANDARD LVDS} [get_ports clk_qdrii_200mhz_p]
#set_property -dict {LOC AD33 IOSTANDARD LVDS} [get_ports clk_qdrii_200mhz_n]
#create_clock -period 5 -name clk_qdrii_200mhz [get_ports clk_qdrii_200mhz_p]
# 200 MHz QDRII C MIG clock
#set_property -dict {LOC AU14 IOSTANDARD LVDS} [get_ports clk_qdriic_200mhz_p]
#set_property -dict {LOC AU13 IOSTANDARD LVDS} [get_ports clk_qdriic_200mhz_n]
#create_clock -period 5 -name clk_qdriic_200mhz [get_ports clk_qdriic_200mhz_p]
# 233.33 MHz DDR3 MIG clock
#set_property -dict {LOC E34 IOSTANDARD LVDS} [get_ports clk_ddr_233mhz_p]
#set_property -dict {LOC E35 IOSTANDARD LVDS} [get_ports clk_ddr_233mhz_n]
#create_clock -period 4.286 -name clk_ddr_233mhz [get_ports clk_ddr_233mhz_p]
# LEDs
set_property -dict {LOC G13 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_led[0][0]}]
set_property -dict {LOC L15 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_led[0][1]}]
set_property -dict {LOC AL22 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_led[1][0]}]
set_property -dict {LOC BA20 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_led[1][1]}]
set_property -dict {LOC AY18 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_led[2][0]}]
set_property -dict {LOC AY17 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_led[2][1]}]
set_property -dict {LOC P31 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_led[3][0]}]
set_property -dict {LOC K32 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_led[3][1]}]
set_property -dict {LOC AR22 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {led[0]}]
set_property -dict {LOC AR23 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {led[1]}]
set_false_path -to [get_ports {sfp_led[*][*] led[*]}]
set_output_delay 0 [get_ports {sfp_led[*][*] led[*]}]
# Push buttons
set_property -dict {LOC AR13 IOSTANDARD LVCMOS15} [get_ports {btn[0]}]
set_property -dict {LOC BB12 IOSTANDARD LVCMOS15} [get_ports {btn[1]}]
set_false_path -from [get_ports {btn[*]}]
set_input_delay 0 [get_ports {btn[*]}]
# UART (IC47 FT2232HQ)
set_property -dict {LOC BA19 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 8} [get_ports {uart_txd}] ;# IC47.38 RXD_I
set_property -dict {LOC AY19 IOSTANDARD LVCMOS15} [get_ports {uart_rxd}] ;# IC47.39 TXD_O
set_property -dict {LOC BB16 IOSTANDARD LVCMOS15} [get_ports {uart_rts}] ;# IC47.40 RTS_O_B
set_property -dict {LOC BA16 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 8} [get_ports {uart_cts}] ;# IC47.41 CTS_I_B
set_false_path -to [get_ports {uart_txd uart_cts}]
set_output_delay 0 [get_ports {uart_txd uart_cts}]
set_false_path -from [get_ports {uart_rxd uart_rts}]
set_input_delay 0 [get_ports {uart_rxd uart_rts}]
# I2C interface
set_property -dict {LOC AK24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports i2c_scl]
set_property -dict {LOC AK25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports i2c_sda]
set_property -dict {LOC AM39 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports i2c_mux_reset]
set_false_path -to [get_ports {i2c_sda i2c_scl i2c_mux_reset}]
set_output_delay 0 [get_ports {i2c_sda i2c_scl i2c_mux_reset}]
set_false_path -from [get_ports {i2c_sda i2c_scl}]
set_input_delay 0 [get_ports {i2c_sda i2c_scl}]
# SFP+ Interfaces
set_property -dict {LOC A6 } [get_ports {sfp_rx_p[0]}] ;# MGTHRXP3_119 GTHE2_CHANNEL_X1Y39 / GTHE2_COMMON_X1Y9
set_property -dict {LOC A5 } [get_ports {sfp_rx_n[0]}] ;# MGTHRXN3_119 GTHE2_CHANNEL_X1Y39 / GTHE2_COMMON_X1Y9
set_property -dict {LOC B4 } [get_ports {sfp_tx_p[0]}] ;# MGTHTXP3_119 GTHE2_CHANNEL_X1Y39 / GTHE2_COMMON_X1Y9
set_property -dict {LOC B3 } [get_ports {sfp_tx_n[0]}] ;# MGTHTXN3_119 GTHE2_CHANNEL_X1Y39 / GTHE2_COMMON_X1Y9
set_property -dict {LOC B8 } [get_ports {sfp_rx_p[1]}] ;# MGTHRXP2_119 GTHE2_CHANNEL_X1Y38 / GTHE2_COMMON_X1Y9
set_property -dict {LOC B7 } [get_ports {sfp_rx_n[1]}] ;# MGTHRXN2_119 GTHE2_CHANNEL_X1Y38 / GTHE2_COMMON_X1Y9
set_property -dict {LOC C2 } [get_ports {sfp_tx_p[1]}] ;# MGTHTXP2_119 GTHE2_CHANNEL_X1Y38 / GTHE2_COMMON_X1Y9
set_property -dict {LOC C1 } [get_ports {sfp_tx_n[1]}] ;# MGTHTXN2_119 GTHE2_CHANNEL_X1Y38 / GTHE2_COMMON_X1Y9
set_property -dict {LOC C6 } [get_ports {sfp_rx_p[2]}] ;# MGTHRXP1_119 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC C5 } [get_ports {sfp_rx_n[2]}] ;# MGTHRXN1_119 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC D4 } [get_ports {sfp_tx_p[2]}] ;# MGTHTXP1_119 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC D3 } [get_ports {sfp_tx_n[2]}] ;# MGTHTXN1_119 GTHE2_CHANNEL_X1Y37 / GTHE2_COMMON_X1Y9
set_property -dict {LOC D8 } [get_ports {sfp_rx_p[3]}] ;# MGTHRXP0_119 GTHE2_CHANNEL_X1Y36 / GTHE2_COMMON_X1Y9
set_property -dict {LOC D7 } [get_ports {sfp_rx_n[3]}] ;# MGTHRXN0_119 GTHE2_CHANNEL_X1Y36 / GTHE2_COMMON_X1Y9
set_property -dict {LOC E2 } [get_ports {sfp_tx_p[3]}] ;# MGTHTXP0_119 GTHE2_CHANNEL_X1Y36 / GTHE2_COMMON_X1Y9
set_property -dict {LOC E1 } [get_ports {sfp_tx_n[3]}] ;# MGTHTXN0_119 GTHE2_CHANNEL_X1Y36 / GTHE2_COMMON_X1Y9
set_property -dict {LOC E10 } [get_ports sfp_mgt_refclk_p] ;# MGTREFCLK0P_118 from IC20.28
set_property -dict {LOC E9 } [get_ports sfp_mgt_refclk_n] ;# MGTREFCLK0N_118 from IC20.29
#set_property -dict {LOC AW32 IOSTANDARD LVDS} [get_ports sfp_recclk_p] ;# to IC20.16
#set_property -dict {LOC AW33 IOSTANDARD LVDS} [get_ports sfp_recclk_n] ;# to IC20.17
set_property -dict {LOC BA29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports si5324_rst]
set_property -dict {LOC AM29 IOSTANDARD LVCMOS18 PULLUP true} [get_ports si5324_int]
set_property -dict {LOC N18 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_mod_detect[0]}]
set_property -dict {LOC L19 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_mod_detect[1]}]
set_property -dict {LOC J37 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_mod_detect[2]}]
set_property -dict {LOC H36 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_mod_detect[3]}]
set_property -dict {LOC N19 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[0][0]}]
set_property -dict {LOC P18 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[0][1]}]
set_property -dict {LOC P20 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[1][0]}]
set_property -dict {LOC N20 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[1][1]}]
set_property -dict {LOC F39 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[2][0]}]
set_property -dict {LOC G36 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[2][1]}]
set_property -dict {LOC H38 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[3][0]}]
set_property -dict {LOC G38 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_rs[3][1]}]
set_property -dict {LOC L17 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_los[0]}]
set_property -dict {LOC L20 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_los[1]}]
set_property -dict {LOC G37 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_los[2]}]
set_property -dict {LOC J36 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_los[3]}]
set_property -dict {LOC M18 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_tx_disable[0]}]
set_property -dict {LOC B31 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_tx_disable[1]}]
set_property -dict {LOC J38 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_tx_disable[2]}]
set_property -dict {LOC L21 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {sfp_tx_disable[3]}]
set_property -dict {LOC M19 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_tx_fault[0]}]
set_property -dict {LOC C26 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_tx_fault[1]}]
set_property -dict {LOC E39 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_tx_fault[2]}]
set_property -dict {LOC J26 IOSTANDARD LVCMOS15 PULLUP true} [get_ports {sfp_tx_fault[3]}]
# 156.25 MHz MGT reference clock
create_clock -period 6.4 -name sfp_mgt_refclk [get_ports sfp_mgt_refclk_p]
set_false_path -to [get_ports {si5324_rst}]
set_output_delay 0 [get_ports {si5324_rst}]
set_false_path -from [get_ports {si5324_int}]
set_input_delay 0 [get_ports {si5324_int}]
set_false_path -from [get_ports {sfp_mod_detect[*] sfp_los[*] sfp_tx_fault[*]}]
set_input_delay 0 [get_ports {sfp_mod_detect[*] sfp_los[*] sfp_tx_fault[*]}]
set_false_path -to [get_ports {sfp_rs[*][*] sfp_tx_disable[*]}]
set_output_delay 0 [get_ports {sfp_rs[*][*] sfp_tx_disable[*]}]
# PCIe Interface
#set_property -dict {LOC Y4 } [get_ports {pcie_rx_p[0]}] ;# MGTHTXP3_115 GTHE2_CHANNEL_X1Y23 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC Y3 } [get_ports {pcie_rx_n[0]}] ;# MGTHTXN3_115 GTHE2_CHANNEL_X1Y23 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC W2 } [get_ports {pcie_tx_p[0]}] ;# MGTHTXP3_115 GTHE2_CHANNEL_X1Y23 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC W1 } [get_ports {pcie_tx_n[0]}] ;# MGTHTXN3_115 GTHE2_CHANNEL_X1Y23 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AA6 } [get_ports {pcie_rx_p[1]}] ;# MGTHTXP2_115 GTHE2_CHANNEL_X1Y22 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AA5 } [get_ports {pcie_rx_n[1]}] ;# MGTHTXN2_115 GTHE2_CHANNEL_X1Y22 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AA2 } [get_ports {pcie_tx_p[1]}] ;# MGTHTXP2_115 GTHE2_CHANNEL_X1Y22 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AA1 } [get_ports {pcie_tx_n[1]}] ;# MGTHTXN2_115 GTHE2_CHANNEL_X1Y22 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AB4 } [get_ports {pcie_rx_p[2]}] ;# MGTHTXP1_115 GTHE2_CHANNEL_X1Y21 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AB3 } [get_ports {pcie_rx_n[2]}] ;# MGTHTXN1_115 GTHE2_CHANNEL_X1Y21 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AC2 } [get_ports {pcie_tx_p[2]}] ;# MGTHTXP1_115 GTHE2_CHANNEL_X1Y21 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AC1 } [get_ports {pcie_tx_n[2]}] ;# MGTHTXN1_115 GTHE2_CHANNEL_X1Y21 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AC6 } [get_ports {pcie_rx_p[3]}] ;# MGTHTXP0_115 GTHE2_CHANNEL_X1Y20 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AC5 } [get_ports {pcie_rx_n[3]}] ;# MGTHTXN0_115 GTHE2_CHANNEL_X1Y20 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AE2 } [get_ports {pcie_tx_p[3]}] ;# MGTHTXP0_115 GTHE2_CHANNEL_X1Y20 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AE1 } [get_ports {pcie_tx_n[3]}] ;# MGTHTXN0_115 GTHE2_CHANNEL_X1Y20 / GTHE2_COMMON_X1Y5
#set_property -dict {LOC AD4 } [get_ports {pcie_rx_p[4]}] ;# MGTHTXP3_114 GTHE2_CHANNEL_X1Y19 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AD3 } [get_ports {pcie_rx_n[4]}] ;# MGTHTXN3_114 GTHE2_CHANNEL_X1Y19 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AG2 } [get_ports {pcie_tx_p[4]}] ;# MGTHTXP3_114 GTHE2_CHANNEL_X1Y19 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AG1 } [get_ports {pcie_tx_n[4]}] ;# MGTHTXN3_114 GTHE2_CHANNEL_X1Y19 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AE6 } [get_ports {pcie_rx_p[5]}] ;# MGTHTXP2_114 GTHE2_CHANNEL_X1Y18 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AE5 } [get_ports {pcie_rx_n[5]}] ;# MGTHTXN2_114 GTHE2_CHANNEL_X1Y18 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AH4 } [get_ports {pcie_tx_p[5]}] ;# MGTHTXP2_114 GTHE2_CHANNEL_X1Y18 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AH3 } [get_ports {pcie_tx_n[5]}] ;# MGTHTXN2_114 GTHE2_CHANNEL_X1Y18 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AF4 } [get_ports {pcie_rx_p[6]}] ;# MGTHTXP1_114 GTHE2_CHANNEL_X1Y17 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AF3 } [get_ports {pcie_rx_n[6]}] ;# MGTHTXN1_114 GTHE2_CHANNEL_X1Y17 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AJ2 } [get_ports {pcie_tx_p[6]}] ;# MGTHTXP1_114 GTHE2_CHANNEL_X1Y17 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AJ1 } [get_ports {pcie_tx_n[6]}] ;# MGTHTXN1_114 GTHE2_CHANNEL_X1Y17 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AG6 } [get_ports {pcie_rx_p[7]}] ;# MGTHTXP0_114 GTHE2_CHANNEL_X1Y16 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AG5 } [get_ports {pcie_rx_n[7]}] ;# MGTHTXN0_114 GTHE2_CHANNEL_X1Y16 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AK4 } [get_ports {pcie_tx_p[7]}] ;# MGTHTXP0_114 GTHE2_CHANNEL_X1Y16 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AK3 } [get_ports {pcie_tx_n[7]}] ;# MGTHTXN0_114 GTHE2_CHANNEL_X1Y16 / GTHE2_COMMON_X1Y4
#set_property -dict {LOC AB8 } [get_ports pcie_mgt_refclk_p] ;# MGTREFCLK1P_115
#set_property -dict {LOC AB7 } [get_ports pcie_mgt_refclk_n] ;# MGTREFCLK1N_115
#set_property -dict {LOC AY35 IOSTANDARD LVCMOS18 PULLUP true} [get_ports pcie_reset_n]
# 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_n}]
#set_input_delay 0 [get_ports {pcie_reset_n}]

View File

@@ -0,0 +1,56 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# FPGA settings
FPGA_PART = xc7vx690tffg1761-3
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)/si5324_i2c_init.sv
SYN_FILES += $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_mac_25g_us.f
SYN_FILES += $(TAXI_SRC_DIR)/xfcp/rtl/taxi_xfcp_if_uart.f
SYN_FILES += $(TAXI_SRC_DIR)/xfcp/rtl/taxi_xfcp_switch.sv
SYN_FILES += $(TAXI_SRC_DIR)/xfcp/rtl/taxi_xfcp_mod_i2c_master.f
SYN_FILES += $(TAXI_SRC_DIR)/xfcp/rtl/taxi_xfcp_mod_stats.f
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
SYN_FILES += $(TAXI_SRC_DIR)/io/rtl/taxi_debounce_switch.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,379 @@
// 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: 200MHz LVDS
*/
input wire logic clk_200mhz_p,
input wire logic clk_200mhz_n,
/*
* GPIO
*/
input wire logic [1:0] btn,
output wire logic [1:0] sfp_led[4],
output wire logic [1:0] led,
/*
* UART: 115200 bps, 8N1
*/
input wire logic uart_rxd,
output wire logic uart_txd,
input wire logic uart_rts,
output wire logic uart_cts,
/*
* I2C
*/
inout wire logic i2c_scl,
inout wire logic i2c_sda,
output wire logic i2c_mux_reset,
/*
* 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,
input wire logic sfp_mgt_refclk_n,
// input wire logic sfp_recclk_p,
// input wire logic sfp_recclk_n,
output wire logic si5324_rst,
input wire logic si5324_int,
input wire logic sfp_mod_detect[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]
);
// Clock and reset
wire clk_200mhz_ibufg;
// Internal 125 MHz clock
wire clk_125mhz_mmcm_out;
wire clk_125mhz_int;
wire rst_125mhz_int;
wire mmcm_rst = 1'b0;
wire mmcm_locked;
wire mmcm_clkfb;
IBUFGDS
clk_200mhz_ibufgds_inst (
.I(clk_200mhz_p),
.IB(clk_200mhz_n),
.O(clk_200mhz_ibufg)
);
// MMCM instance
MMCME2_BASE #(
// 200 MHz input
.CLKIN1_PERIOD(5.0),
.REF_JITTER1(0.010),
// 200 MHz input / 1 = 200 MHz PFD (range 10 MHz to 500 MHz)
.DIVCLK_DIVIDE(1),
// 200 MHz PFD * 5 = 1000 MHz VCO (range 600 MHz to 1440 MHz)
.CLKFBOUT_MULT_F(5),
.CLKFBOUT_PHASE(0),
// 1000 MHz VCO / 8 = 125 MHz, 0 degrees
.CLKOUT0_DIVIDE_F(8),
.CLKOUT0_DUTY_CYCLE(0.5),
.CLKOUT0_PHASE(0),
// Not used
.CLKOUT1_DIVIDE(1),
.CLKOUT1_DUTY_CYCLE(0.5),
.CLKOUT1_PHASE(0),
// Not used
.CLKOUT2_DIVIDE(1),
.CLKOUT2_DUTY_CYCLE(0.5),
.CLKOUT2_PHASE(0),
// Not used
.CLKOUT3_DIVIDE(1),
.CLKOUT3_DUTY_CYCLE(0.5),
.CLKOUT3_PHASE(0),
// Not used
.CLKOUT4_DIVIDE(1),
.CLKOUT4_DUTY_CYCLE(0.5),
.CLKOUT4_PHASE(0),
.CLKOUT4_CASCADE("FALSE"),
// Not used
.CLKOUT5_DIVIDE(1),
.CLKOUT5_DUTY_CYCLE(0.5),
.CLKOUT5_PHASE(0),
// Not used
.CLKOUT6_DIVIDE(1),
.CLKOUT6_DUTY_CYCLE(0.5),
.CLKOUT6_PHASE(0),
// optimized bandwidth
.BANDWIDTH("OPTIMIZED"),
// don't wait for lock during startup
.STARTUP_WAIT("FALSE")
)
clk_mmcm_inst (
// 200 MHz input
.CLKIN1(clk_200mhz_ibufg),
// direct clkfb feeback
.CLKFBIN(mmcm_clkfb),
.CLKFBOUT(mmcm_clkfb),
.CLKFBOUTB(),
// 125 MHz, 0 degrees
.CLKOUT0(clk_125mhz_mmcm_out),
.CLKOUT0B(),
// Not used
.CLKOUT1(),
.CLKOUT1B(),
// Not used
.CLKOUT2(),
.CLKOUT2B(),
// Not used
.CLKOUT3(),
.CLKOUT3B(),
// Not used
.CLKOUT4(),
// Not used
.CLKOUT5(),
// Not used
.CLKOUT6(),
// reset input
.RST(mmcm_rst),
// don't power down
.PWRDWN(1'b0),
// locked output
.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)
);
// GPIO
wire [1:0] btn_int;
taxi_debounce_switch #(
.WIDTH(2),
.N(4),
.RATE(125000)
)
debounce_switch_inst (
.clk(clk_125mhz_int),
.rst(rst_125mhz_int),
.in({btn}),
.out({btn_int})
);
wire uart_rxd_int;
wire uart_rts_int;
taxi_sync_signal #(
.WIDTH(2),
.N(2)
)
sync_signal_inst (
.clk(clk_125mhz_int),
.in({uart_rxd, uart_rts}),
.out({uart_rxd_int, uart_rts_int})
);
wire [7:0] led_int;
// I2C
wire i2c_scl_i;
wire i2c_scl_o;
wire i2c_sda_i;
wire i2c_sda_o;
assign i2c_scl_i = i2c_scl;
assign i2c_scl = i2c_scl_o ? 1'bz : 1'b0;
assign i2c_sda_i = i2c_sda;
assign i2c_sda = 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 i2c_scl_o = i2c_init_scl_o & i2c_int_scl_o;
assign i2c_sda_o = i2c_init_sda_o & i2c_int_sda_o;
assign i2c_mux_reset = 1'b0;
// Si5324 init
taxi_axis_if #(.DATA_W(12)) si5324_i2c_cmd();
taxi_axis_if #(.DATA_W(8)) si5324_i2c_tx();
taxi_axis_if #(.DATA_W(8)) si5324_i2c_rx();
assign si5324_i2c_rx.tready = 1'b1;
wire si5324_i2c_busy;
assign si5324_rst = rst_125mhz_int;
taxi_i2c_master
si5324_i2c_master_inst (
.clk(clk_125mhz_int),
.rst(rst_125mhz_int),
/*
* Host interface
*/
.s_axis_cmd(si5324_i2c_cmd),
.s_axis_tx(si5324_i2c_tx),
.m_axis_rx(si5324_i2c_rx),
/*
* I2C interface
*/
.scl_i(i2c_init_scl_i),
.scl_o(i2c_init_scl_o),
.sda_i(i2c_init_sda_i),
.sda_o(i2c_init_sda_o),
/*
* Status
*/
.busy(),
.bus_control(),
.bus_active(),
.missed_ack(),
/*
* Configuration
*/
.prescale(SIM ? 32 : 312),
.stop_on_idle(1)
);
si5324_i2c_init #(
.SIM_SPEEDUP(SIM)
)
si5324_i2c_init_inst (
.clk(clk_125mhz_int),
.rst(rst_125mhz_int),
/*
* I2C master interface
*/
.m_axis_cmd(si5324_i2c_cmd),
.m_axis_tx(si5324_i2c_tx),
/*
* Status
*/
.busy(si5324_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
*/
.btn(btn_int),
.sfp_led(sfp_led),
.led(led_int),
/*
* UART: 115200 bps, 8N1
*/
.uart_rxd(uart_rxd_int),
.uart_txd(uart_txd),
.uart_rts(uart_rts_int),
.uart_cts(uart_cts),
/*
* I2C
*/
.i2c_scl_i(i2c_int_scl_i),
.i2c_scl_o(i2c_int_scl_o),
.i2c_sda_i(i2c_int_sda_i),
.i2c_sda_o(i2c_int_sda_o),
/*
* 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),
// .sfp_recclk_p(sfp_recclk_p),
// .sfp_recclk_n(sfp_recclk_n),
.sfp_mod_detect(sfp_mod_detect),
.sfp_rs(sfp_rs),
.sfp_los(sfp_los),
.sfp_tx_disable(sfp_tx_disable),
.sfp_tx_fault(sfp_tx_fault)
);
endmodule
`resetall

View File

@@ -0,0 +1,546 @@
// 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
*/
input wire logic [1:0] btn,
output wire logic [1:0] sfp_led[4],
output wire logic [1:0] led,
/*
* UART: 115200 bps, 8N1
*/
input wire logic uart_rxd,
output wire logic uart_txd,
input wire logic uart_rts,
output wire logic uart_cts,
/*
* I2C
*/
input wire logic i2c_scl_i,
output wire logic i2c_scl_o,
input wire logic i2c_sda_i,
output wire logic i2c_sda_o,
/*
* 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,
input wire logic sfp_mgt_refclk_n,
// input wire logic sfp_recclk_p,
// input wire logic sfp_recclk_n,
input wire logic sfp_mod_detect[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]
);
assign led = '0;
assign sfp_led = '{4{'0}};
// XFCP
assign uart_cts = 1'b0;
taxi_axis_if #(.DATA_W(8), .USER_EN(1), .USER_W(1)) xfcp_ds(), xfcp_us();
taxi_xfcp_if_uart #(
.TX_FIFO_DEPTH(512),
.RX_FIFO_DEPTH(512)
)
xfcp_if_uart_inst (
.clk(clk_125mhz),
.rst(rst_125mhz),
/*
* UART interface
*/
.uart_rxd(uart_rxd),
.uart_txd(uart_txd),
/*
* XFCP downstream interface
*/
.xfcp_dsp_ds(xfcp_ds),
.xfcp_dsp_us(xfcp_us),
/*
* Configuration
*/
.prescale(16'(125000000/3000000))
);
taxi_axis_if #(.DATA_W(8), .USER_EN(1), .USER_W(1)) xfcp_sw_ds[2](), xfcp_sw_us[2]();
taxi_xfcp_switch #(
.XFCP_ID_STR("NetFPGA SUME"),
.XFCP_EXT_ID(0),
.XFCP_EXT_ID_STR("Taxi example"),
.PORTS($size(xfcp_sw_us))
)
xfcp_sw_inst (
.clk(clk_125mhz),
.rst(rst_125mhz),
/*
* XFCP upstream port
*/
.xfcp_usp_ds(xfcp_ds),
.xfcp_usp_us(xfcp_us),
/*
* XFCP downstream ports
*/
.xfcp_dsp_ds(xfcp_sw_ds),
.xfcp_dsp_us(xfcp_sw_us)
);
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(10)) axis_stat();
taxi_xfcp_mod_stats #(
.XFCP_ID_STR("Statistics"),
.XFCP_EXT_ID(0),
.XFCP_EXT_ID_STR(""),
.STAT_COUNT_W(64),
.STAT_PIPELINE(2)
)
xfcp_stats_inst (
.clk(clk_125mhz),
.rst(rst_125mhz),
/*
* XFCP upstream port
*/
.xfcp_usp_ds(xfcp_sw_ds[0]),
.xfcp_usp_us(xfcp_sw_us[0]),
/*
* Statistics increment input
*/
.s_axis_stat(axis_stat)
);
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(10)) axis_eth_stat[1]();
taxi_axis_arb_mux #(
.S_COUNT($size(axis_eth_stat)),
.UPDATE_TID(1'b0),
.ARB_ROUND_ROBIN(1'b1),
.ARB_LSB_HIGH_PRIO(1'b0)
)
stat_mux_inst (
.clk(clk_125mhz),
.rst(rst_125mhz),
/*
* AXI4-Stream inputs (sink)
*/
.s_axis(axis_eth_stat),
/*
* AXI4-Stream output (source)
*/
.m_axis(axis_stat)
);
// I2C
taxi_xfcp_mod_i2c_master #(
.XFCP_EXT_ID_STR("I2C"),
.DEFAULT_PRESCALE(16'(125000000/200000/4))
)
xfcp_mod_i2c_inst (
.clk(clk_125mhz),
.rst(rst_125mhz),
/*
* XFCP upstream port
*/
.xfcp_usp_ds(xfcp_sw_ds[1]),
.xfcp_usp_us(xfcp_sw_us[1]),
/*
* I2C interface
*/
.i2c_scl_i(i2c_scl_i),
.i2c_scl_o(i2c_scl_o),
.i2c_sda_i(i2c_sda_i),
.i2c_sda_o(i2c_sda_o)
);
// 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];
wire sfp_gtpowergood;
wire sfp_mgt_refclk;
wire sfp_mgt_refclk_bufg;
wire sfp_rst;
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]();
if (SIM) begin
assign sfp_mgt_refclk = sfp_mgt_refclk_p;
assign sfp_mgt_refclk_bufg = sfp_mgt_refclk_p;
end else begin
IBUFDS_GTE2 ibufds_gte2_sfp_mgt_refclk_inst (
.I (sfp_mgt_refclk_p),
.IB (sfp_mgt_refclk_n),
.CEB (1'b0),
.O (sfp_mgt_refclk),
.ODIV2 ()
);
BUFG bufg_sfp_mgt_refclk_inst (
.I (sfp_mgt_refclk),
.O (sfp_mgt_refclk_bufg)
);
end
taxi_sync_reset #(
.N(4)
)
sfp_sync_reset_inst (
.clk(sfp_mgt_refclk_bufg),
.rst(rst_125mhz),
.out(sfp_rst)
);
taxi_eth_mac_25g_us #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.CNT(4),
// GT config
.CFG_LOW_LATENCY(CFG_LOW_LATENCY),
// GT type
.GT_TYPE("GTH"),
// GT parameters
.GT_TX_DIFFCTRL('{4{5'd8}}),
// 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_TS_FMT_TOD(1'b1),
.PTP_TS_W(96),
.PRBS31_EN(1'b0),
.TX_SERDES_PIPELINE(1),
.RX_SERDES_PIPELINE(1),
.COUNT_125US(125000/6.4),
.STAT_EN(1),
.STAT_TX_LEVEL(1),
.STAT_RX_LEVEL(1),
.STAT_ID_BASE(0),
.STAT_UPDATE_PERIOD(1024),
.STAT_STR_EN(1),
.STAT_PREFIX_STR('{"SFP0", "SFP1", "SFP2", "SFP3"})
)
sfp_mac_inst (
.xcvr_ctrl_clk(clk_125mhz),
.xcvr_ctrl_rst(sfp_rst),
/*
* Common
*/
.xcvr_gtpowergood_out(sfp_gtpowergood),
.xcvr_gtrefclk00_in(sfp_mgt_refclk),
.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),
.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),
.xcvr_txn(sfp_tx_n),
.xcvr_rxp(sfp_rx_p),
.xcvr_rxn(sfp_rx_n),
/*
* MAC clocks
*/
.rx_clk(sfp_rx_clk),
.rx_rst_in('{4{1'b0}}),
.rx_rst_out(sfp_rx_rst),
.tx_clk(sfp_tx_clk),
.tx_rst_in('{4{1'b0}}),
.tx_rst_out(sfp_tx_rst),
.ptp_sample_clk('{4{1'b0}}),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_sfp_tx),
.m_axis_tx_cpl(axis_sfp_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_sfp_rx),
/*
* PTP clock
*/
.tx_ptp_ts('{4{'0}}),
.tx_ptp_ts_step('{4{1'b0}}),
.rx_ptp_ts('{4{'0}}),
.rx_ptp_ts_step('{4{1'b0}}),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req('{4{1'b0}}),
.tx_lfc_resend('{4{1'b0}}),
.rx_lfc_en('{4{1'b0}}),
.rx_lfc_req(),
.rx_lfc_ack('{4{1'b0}}),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req('{4{'0}}),
.tx_pfc_resend('{4{1'b0}}),
.rx_pfc_en('{4{'0}}),
.rx_pfc_req(),
.rx_pfc_ack('{4{'0}}),
/*
* Pause interface
*/
.tx_lfc_pause_en('{4{1'b0}}),
.tx_pause_req('{4{1'b0}}),
.tx_pause_ack(),
/*
* Statistics
*/
.stat_clk(clk_125mhz),
.stat_rst(rst_125mhz),
.m_axis_stat(axis_eth_stat[0]),
/*
* 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),
.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('{4{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('{4{16'd9218}}),
.cfg_tx_ifg('{4{8'd12}}),
.cfg_tx_enable('{4{1'b1}}),
.cfg_rx_max_pkt_len('{4{16'd9218}}),
.cfg_rx_enable('{4{1'b1}}),
.cfg_tx_prbs31_enable('{4{1'b0}}),
.cfg_rx_prbs31_enable('{4{1'b0}}),
.cfg_mcf_rx_eth_dst_mcast('{4{48'h01_80_C2_00_00_01}}),
.cfg_mcf_rx_check_eth_dst_mcast('{4{1'b1}}),
.cfg_mcf_rx_eth_dst_ucast('{4{48'd0}}),
.cfg_mcf_rx_check_eth_dst_ucast('{4{1'b0}}),
.cfg_mcf_rx_eth_src('{4{48'd0}}),
.cfg_mcf_rx_check_eth_src('{4{1'b0}}),
.cfg_mcf_rx_eth_type('{4{16'h8808}}),
.cfg_mcf_rx_opcode_lfc('{4{16'h0001}}),
.cfg_mcf_rx_check_opcode_lfc('{4{1'b1}}),
.cfg_mcf_rx_opcode_pfc('{4{16'h0101}}),
.cfg_mcf_rx_check_opcode_pfc('{4{1'b1}}),
.cfg_mcf_rx_forward('{4{1'b0}}),
.cfg_mcf_rx_enable('{4{1'b0}}),
.cfg_tx_lfc_eth_dst('{4{48'h01_80_C2_00_00_01}}),
.cfg_tx_lfc_eth_src('{4{48'h80_23_31_43_54_4C}}),
.cfg_tx_lfc_eth_type('{4{16'h8808}}),
.cfg_tx_lfc_opcode('{4{16'h0001}}),
.cfg_tx_lfc_en('{4{1'b0}}),
.cfg_tx_lfc_quanta('{4{16'hffff}}),
.cfg_tx_lfc_refresh('{4{16'h7fff}}),
.cfg_tx_pfc_eth_dst('{4{48'h01_80_C2_00_00_01}}),
.cfg_tx_pfc_eth_src('{4{48'h80_23_31_43_54_4C}}),
.cfg_tx_pfc_eth_type('{4{16'h8808}}),
.cfg_tx_pfc_opcode('{4{16'h0101}}),
.cfg_tx_pfc_en('{4{1'b0}}),
.cfg_tx_pfc_quanta('{4{'{8{16'hffff}}}}),
.cfg_tx_pfc_refresh('{4{'{8{16'h7fff}}}}),
.cfg_rx_lfc_opcode('{4{16'h0001}}),
.cfg_rx_lfc_en('{4{1'b0}}),
.cfg_rx_pfc_opcode('{4{16'h0101}}),
.cfg_rx_pfc_en('{4{1'b0}})
);
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,567 @@
// 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 si5324_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 = 38;
logic [8:0] init_data [INIT_DATA_LEN-1:0];
initial begin
// Initial delay
init_data[0] = cmd_delay(6); // delay 30 ms
// Select Si5324
init_data[1] = cmd_start(7'h74);
init_data[2] = cmd_wr(8'h10);
init_data[3] = cmd_stop();
// init Si5324 registers
init_data[4] = cmd_start(7'h68); // start write to 0x68 (Si5324)
init_data[5] = cmd_wr(8'd0); // register 0
init_data[6] = cmd_wr(8'h54); // Reg 0: Free run, Clock off before ICAL, Bypass off (normal operation)
init_data[7] = cmd_wr(8'hE4); // Reg 1: CKIN2 second priority, CKIN1 first priority
init_data[8] = cmd_wr(8'h12); // Reg 2: BWSEL = 1
init_data[9] = cmd_wr(8'h15); // Reg 3: CKIN1 selected, Digital Hold off, Output clocks disabled during ICAL
init_data[10] = cmd_wr(8'h92); // Reg 4: Automatic Revertive, HIST_DEL = 0x12
init_data[11] = cmd_start(7'h68); // start write to 0x68 (Si5324)
init_data[12] = cmd_wr(8'd10); // register 10
init_data[13] = cmd_wr(8'h08); // Reg 10: CKOUT2 disabled, CKOUT1 enabled
init_data[14] = cmd_wr(8'h40); // Reg 11: CKIN2 enabled, CKIN1 enabled
init_data[15] = cmd_start(7'h68); // start write to 0x68 (Si5324)
init_data[16] = cmd_wr(8'd25); // register 25
init_data[17] = cmd_wr(8'hA0); // Reg 25: N1_HS = 9
init_data[18] = cmd_start(7'h68); // start write to 0x68 (Si5324)
init_data[19] = cmd_wr(8'd31); // register 31
init_data[20] = cmd_wr(8'h00); // Regs 31,32,33: NC1_LS = 4
init_data[21] = cmd_wr(8'h00);
init_data[22] = cmd_wr(8'h03);
init_data[23] = cmd_start(7'h68); // start write to 0x68 (Si5324)
init_data[24] = cmd_wr(8'd40); // register 40
init_data[25] = cmd_wr(8'hC2); // Regs 40,41,42: N2_HS = 10, N2_LS = 150000
init_data[26] = cmd_wr(8'h49);
init_data[27] = cmd_wr(8'hEF);
init_data[28] = cmd_wr(8'h00); // Regs 43,44,45: N31 = 30475
init_data[29] = cmd_wr(8'h77);
init_data[30] = cmd_wr(8'h0B);
init_data[31] = cmd_wr(8'h00); // Regs 46,47,48: N32 = 30475
init_data[32] = cmd_wr(8'h77);
init_data[33] = cmd_wr(8'h0B);
init_data[34] = cmd_start(7'h68); // start write to 0x68 (Si5324)
init_data[35] = cmd_wr(8'd136); // register 136
init_data[36] = cmd_wr(8'h40); // Reg 136: ICAL = 1
init_data[37] = 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,62 @@
# 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)/xfcp/rtl/taxi_xfcp_if_uart.f
VERILOG_SOURCES += $(TAXI_SRC_DIR)/xfcp/rtl/taxi_xfcp_switch.sv
VERILOG_SOURCES += $(TAXI_SRC_DIR)/xfcp/rtl/taxi_xfcp_mod_i2c_master.f
VERILOG_SOURCES += $(TAXI_SRC_DIR)/xfcp/rtl/taxi_xfcp_mod_stats.f
VERILOG_SOURCES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_reset.sv
VERILOG_SOURCES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
VERILOG_SOURCES += $(TAXI_SRC_DIR)/io/rtl/taxi_debounce_switch.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,237 @@
#!/usr/bin/env python
# SPDX-License-Identifier: MIT
"""
Copyright (c) 2020-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
"""
import logging
import os
import sys
import pytest
import cocotb_test.simulator
import cocotb
from cocotb.log import SimLog
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge, Combine
from cocotbext.eth import XgmiiFrame
from cocotbext.uart import UartSource, UartSink
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 = SimLog("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, 6.4, units="ns").start())
for ch in dut.sfp_mac_inst.ch:
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
))
self.uart_source = UartSource(dut.uart_rxd, baud=3000000, bits=8, stop_bits=1)
self.uart_sink = UartSink(dut.uart_txd, baud=3000000, bits=8, stop_bits=1)
dut.btn.setimmediatevalue(0)
dut.uart_rts.setimmediatevalue(0)
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, "xfcp", "rtl", "taxi_xfcp_if_uart.f"),
os.path.join(taxi_src_dir, "xfcp", "rtl", "taxi_xfcp_switch.sv"),
os.path.join(taxi_src_dir, "xfcp", "rtl", "taxi_xfcp_mod_i2c_master.f"),
os.path.join(taxi_src_dir, "xfcp", "rtl", "taxi_xfcp_mod_stats.f"),
os.path.join(taxi_src_dir, "sync", "rtl", "taxi_sync_reset.sv"),
os.path.join(taxi_src_dir, "sync", "rtl", "taxi_sync_signal.sv"),
os.path.join(taxi_src_dir, "io", "rtl", "taxi_debounce_switch.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,
)