example/ZCU102: Add example design for ZCU102

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2025-02-23 14:12:34 -08:00
parent 87b696b2aa
commit d2f6a94318
15 changed files with 2315 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
# Taxi Example Design for ZCU102
## Introduction
This example design targets the Xilinx ZCU102 FPGA board.
The design places looped-back MACs on the SFP+ ports as well as a looped-back UART on on the USB UART connection.
* USB UART
* Looped-back UART
* QSFP28
* Looped-back 10GBASE-R MACs via GTH transceivers
## Board details
* FPGA: xczu9eg-ffvb1156-2-e
* 10GBASE-R PHY: Soft PCS with GTH transceivers
## 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 UART, use any serial terminal software like minicom, screen, etc. The looped-back UART will echo typed text back without modification.
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,172 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# XDC constraints for the Xilinx ZCU102 board
# part: xczu9eg-ffvb1156-2-e
# General configuration
set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
# System clocks
# 125 MHz
set_property -dict {LOC G21 IOSTANDARD LVDS_25} [get_ports clk_125mhz_p]
set_property -dict {LOC F21 IOSTANDARD LVDS_25} [get_ports clk_125mhz_n]
create_clock -period 8.000 -name clk_125mhz [get_ports clk_125mhz_p]
# LEDs
set_property -dict {LOC AG14 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {led[0]}]
set_property -dict {LOC AF13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {led[1]}]
set_property -dict {LOC AE13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {led[2]}]
set_property -dict {LOC AJ14 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {led[3]}]
set_property -dict {LOC AJ15 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {led[4]}]
set_property -dict {LOC AH13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {led[5]}]
set_property -dict {LOC AH14 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {led[6]}]
set_property -dict {LOC AL12 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {led[7]}]
set_false_path -to [get_ports {led[*]}]
set_output_delay 0 [get_ports {led[*]}]
# Reset button
set_property -dict {LOC AM13 IOSTANDARD LVCMOS33} [get_ports reset]
set_false_path -from [get_ports {reset}]
set_input_delay 0 [get_ports {reset}]
# Push buttons
set_property -dict {LOC AG15 IOSTANDARD LVCMOS33} [get_ports btnu]
set_property -dict {LOC AF15 IOSTANDARD LVCMOS33} [get_ports btnl]
set_property -dict {LOC AE15 IOSTANDARD LVCMOS33} [get_ports btnd]
set_property -dict {LOC AE14 IOSTANDARD LVCMOS33} [get_ports btnr]
set_property -dict {LOC AG13 IOSTANDARD LVCMOS33} [get_ports btnc]
set_false_path -from [get_ports {btnu btnl btnd btnr btnc}]
set_input_delay 0 [get_ports {btnu btnl btnd btnr btnc}]
# DIP switches
set_property -dict {LOC AN14 IOSTANDARD LVCMOS33} [get_ports {sw[0]}]
set_property -dict {LOC AP14 IOSTANDARD LVCMOS33} [get_ports {sw[1]}]
set_property -dict {LOC AM14 IOSTANDARD LVCMOS33} [get_ports {sw[2]}]
set_property -dict {LOC AN13 IOSTANDARD LVCMOS33} [get_ports {sw[3]}]
set_property -dict {LOC AN12 IOSTANDARD LVCMOS33} [get_ports {sw[4]}]
set_property -dict {LOC AP12 IOSTANDARD LVCMOS33} [get_ports {sw[5]}]
set_property -dict {LOC AL13 IOSTANDARD LVCMOS33} [get_ports {sw[6]}]
set_property -dict {LOC AK13 IOSTANDARD LVCMOS33} [get_ports {sw[7]}]
set_false_path -from [get_ports {sw[*]}]
set_input_delay 0 [get_ports {sw[*]}]
# UART
set_property -dict {LOC F13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports uart_txd]
set_property -dict {LOC E13 IOSTANDARD LVCMOS33} [get_ports uart_rxd]
set_property -dict {LOC D12 IOSTANDARD LVCMOS33} [get_ports uart_rts]
set_property -dict {LOC E12 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports uart_cts]
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 interfaces
#set_property -dict {LOC J10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports i2c0_scl]
#set_property -dict {LOC J11 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports i2c0_sda]
#set_property -dict {LOC K20 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports i2c1_scl]
#set_property -dict {LOC L20 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports i2c1_sda]
#set_false_path -to [get_ports {i2c1_sda i2c1_scl}]
#set_output_delay 0 [get_ports {i2c1_sda i2c1_scl}]
#set_false_path -from [get_ports {i2c1_sda i2c1_scl}]
#set_input_delay 0 [get_ports {i2c1_sda i2c1_scl}]
# SFP+ Interface
set_property -dict {LOC D2 } [get_ports {sfp_rx_p[0]}] ;# MGTHRXP0_230 GTHE4_CHANNEL_X1Y12 / GTHE4_COMMON_X1Y3
set_property -dict {LOC D1 } [get_ports {sfp_rx_n[0]}] ;# MGTHRXN0_230 GTHE4_CHANNEL_X1Y12 / GTHE4_COMMON_X1Y3
set_property -dict {LOC E4 } [get_ports {sfp_tx_p[0]}] ;# MGTHTXP0_230 GTHE4_CHANNEL_X1Y12 / GTHE4_COMMON_X1Y3
set_property -dict {LOC E3 } [get_ports {sfp_tx_n[0]}] ;# MGTHTXN0_230 GTHE4_CHANNEL_X1Y12 / GTHE4_COMMON_X1Y3
set_property -dict {LOC C4 } [get_ports {sfp_rx_p[1]}] ;# MGTHRXP1_230 GTHE4_CHANNEL_X1Y13 / GTHE4_COMMON_X1Y3
set_property -dict {LOC C3 } [get_ports {sfp_rx_n[1]}] ;# MGTHRXN1_230 GTHE4_CHANNEL_X1Y13 / GTHE4_COMMON_X1Y3
set_property -dict {LOC D6 } [get_ports {sfp_tx_p[1]}] ;# MGTHTXP1_230 GTHE4_CHANNEL_X1Y13 / GTHE4_COMMON_X1Y3
set_property -dict {LOC D5 } [get_ports {sfp_tx_n[1]}] ;# MGTHTXN1_230 GTHE4_CHANNEL_X1Y13 / GTHE4_COMMON_X1Y3
set_property -dict {LOC B2 } [get_ports {sfp_rx_p[2]}] ;# MGTHRXP2_230 GTHE4_CHANNEL_X1Y14 / GTHE4_COMMON_X1Y3
set_property -dict {LOC B1 } [get_ports {sfp_rx_n[2]}] ;# MGTHRXN2_230 GTHE4_CHANNEL_X1Y14 / GTHE4_COMMON_X1Y3
set_property -dict {LOC B6 } [get_ports {sfp_tx_p[2]}] ;# MGTHTXP2_230 GTHE4_CHANNEL_X1Y14 / GTHE4_COMMON_X1Y3
set_property -dict {LOC B5 } [get_ports {sfp_tx_n[2]}] ;# MGTHTXN2_230 GTHE4_CHANNEL_X1Y14 / GTHE4_COMMON_X1Y3
set_property -dict {LOC A4 } [get_ports {sfp_rx_p[3]}] ;# MGTHRXP3_230 GTHE4_CHANNEL_X1Y15 / GTHE4_COMMON_X1Y3
set_property -dict {LOC A3 } [get_ports {sfp_rx_n[3]}] ;# MGTHRXN3_230 GTHE4_CHANNEL_X1Y15 / GTHE4_COMMON_X1Y3
set_property -dict {LOC A8 } [get_ports {sfp_tx_p[3]}] ;# MGTHTXP3_230 GTHE4_CHANNEL_X1Y15 / GTHE4_COMMON_X1Y3
set_property -dict {LOC A7 } [get_ports {sfp_tx_n[3]}] ;# MGTHTXN3_230 GTHE4_CHANNEL_X1Y15 / GTHE4_COMMON_X1Y3
set_property -dict {LOC C8 } [get_ports {sfp_mgt_refclk_0_p}] ;# MGTREFCLK0P_230 from U56 SI570 via U51 SI53340
set_property -dict {LOC C7 } [get_ports {sfp_mgt_refclk_0_n}] ;# MGTREFCLK0N_230 from U56 SI570 via U51 SI53340
#set_property -dict {LOC B10 } [get_ports {sfp_mgt_refclk_1_p}] ;# MGTREFCLK1P_230 from U20 CKOUT2 SI5328
#set_property -dict {LOC B9 } [get_ports {sfp_mgt_refclk_1_n}] ;# MGTREFCLK1N_230 from U20 CKOUT2 SI5328
#set_property -dict {LOC R10 IOSTANDARD LVDS} [get_ports {sfp_recclk_p}] ;# to U20 CKIN1 SI5328
#set_property -dict {LOC R9 IOSTANDARD LVDS} [get_ports {sfp_recclk_n}] ;# to U20 CKIN1 SI5328
set_property -dict {LOC A12 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {sfp_tx_disable_b[0]}]
set_property -dict {LOC A13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {sfp_tx_disable_b[1]}]
set_property -dict {LOC B13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {sfp_tx_disable_b[2]}]
set_property -dict {LOC C13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 8} [get_ports {sfp_tx_disable_b[3]}]
# 156.25 MHz MGT reference clock
create_clock -period 6.400 -name sfp_mgt_refclk_0 [get_ports {sfp_mgt_refclk_0_p}]
set_false_path -to [get_ports {sfp_tx_disable_b[*]}]
set_output_delay 0 [get_ports {sfp_tx_disable_b[*]}]
# DDR4
# 1x MT40A256M16GE-075E
#set_property -dict {LOC AM8 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[0]}]
#set_property -dict {LOC AM9 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[1]}]
#set_property -dict {LOC AP8 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[2]}]
#set_property -dict {LOC AN8 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[3]}]
#set_property -dict {LOC AK10 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[4]}]
#set_property -dict {LOC AJ10 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[5]}]
#set_property -dict {LOC AP9 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[6]}]
#set_property -dict {LOC AN9 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[7]}]
#set_property -dict {LOC AP10 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[8]}]
#set_property -dict {LOC AP11 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[9]}]
#set_property -dict {LOC AM10 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[10]}]
#set_property -dict {LOC AL10 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[11]}]
#set_property -dict {LOC AM11 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[12]}]
#set_property -dict {LOC AL11 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[13]}]
#set_property -dict {LOC AJ7 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[14]}]
#set_property -dict {LOC AL5 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[15]}]
#set_property -dict {LOC AJ9 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_adr[16]}]
#set_property -dict {LOC AK12 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_ba[0]}]
#set_property -dict {LOC AJ12 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_ba[1]}]
#set_property -dict {LOC AK7 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_bg[0]}]
#set_property -dict {LOC AN7 IOSTANDARD DIFF_SSTL12_DCI} [get_ports {ddr4_ck_t}]
#set_property -dict {LOC AP7 IOSTANDARD DIFF_SSTL12_DCI} [get_ports {ddr4_ck_c}]
#set_property -dict {LOC AM3 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_cke}]
#set_property -dict {LOC AP2 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_cs_n}]
#set_property -dict {LOC AK8 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_act_n}]
#set_property -dict {LOC AK9 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_odt}]
#set_property -dict {LOC AP1 IOSTANDARD SSTL12_DCI } [get_ports {ddr4_par}]
#set_property -dict {LOC AH9 IOSTANDARD LVCMOS12 } [get_ports {ddr4_reset_n}]
#set_property -dict {LOC AK4 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[0]}] ;# U2.G2 DQL0
#set_property -dict {LOC AK5 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[1]}] ;# U2.F7 DQL1
#set_property -dict {LOC AN4 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[2]}] ;# U2.H3 DQL2
#set_property -dict {LOC AM4 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[3]}] ;# U2.H7 DQL3
#set_property -dict {LOC AP4 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[4]}] ;# U2.H2 DQL4
#set_property -dict {LOC AP5 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[5]}] ;# U2.H8 DQL5
#set_property -dict {LOC AM5 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[6]}] ;# U2.J3 DQL6
#set_property -dict {LOC AM6 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[7]}] ;# U2.J7 DQL7
#set_property -dict {LOC AK2 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[8]}] ;# U2.A3 DQU0
#set_property -dict {LOC AK3 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[9]}] ;# U2.B8 DQU1
#set_property -dict {LOC AL1 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[10]}] ;# U2.C3 DQU2
#set_property -dict {LOC AK1 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[11]}] ;# U2.C7 DQU3
#set_property -dict {LOC AN1 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[12]}] ;# U2.C2 DQU4
#set_property -dict {LOC AM1 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[13]}] ;# U2.C8 DQU5
#set_property -dict {LOC AP3 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[14]}] ;# U2.D3 DQU6
#set_property -dict {LOC AN3 IOSTANDARD POD12_DCI } [get_ports {ddr4_dq[15]}] ;# U2.D7 DQU7
#set_property -dict {LOC AN6 IOSTANDARD DIFF_POD12_DCI } [get_ports {ddr4_dqs_t[0]}] ;# U2.G3 DQSL_T
#set_property -dict {LOC AP6 IOSTANDARD DIFF_POD12_DCI } [get_ports {ddr4_dqs_c[0]}] ;# U2.F3 DQSL_C
#set_property -dict {LOC AL3 IOSTANDARD DIFF_POD12_DCI } [get_ports {ddr4_dqs_t[1]}] ;# U2.B7 DQSU_T
#set_property -dict {LOC AL2 IOSTANDARD DIFF_POD12_DCI } [get_ports {ddr4_dqs_c[1]}] ;# U2.A7 DQSU_C
#set_property -dict {LOC AL6 IOSTANDARD POD12_DCI } [get_ports {ddr4_dm_dbi_n[0]}] ;# U2.E7 DML_B/DBIL_B
#set_property -dict {LOC AN2 IOSTANDARD POD12_DCI } [get_ports {ddr4_dm_dbi_n[1]}] ;# U2.E2 DMU_B/DBIU_B

View File

@@ -0,0 +1,48 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# FPGA settings
FPGA_PART = xczu9eg-ffvb1156-2-e
FPGA_TOP = fpga
FPGA_ARCH = zynquplus
# Files for synthesis
SYN_FILES = ../rtl/fpga.sv
SYN_FILES += ../rtl/fpga_core.sv
SYN_FILES += ../lib/taxi/rtl/eth/us/taxi_eth_mac_25g_us.f
SYN_FILES += ../lib/taxi/rtl/lss/taxi_uart.f
SYN_FILES += ../lib/taxi/rtl/axis/taxi_axis_async_fifo.f
SYN_FILES += ../lib/taxi/rtl/sync/taxi_sync_reset.sv
SYN_FILES += ../lib/taxi/rtl/sync/taxi_sync_signal.sv
SYN_FILES += ../lib/taxi/rtl/io/taxi_debounce_switch.sv
# XDC files
XDC_FILES = ../fpga.xdc
XDC_FILES += ../lib/taxi/syn/vivado/taxi_eth_mac_fifo.tcl
XDC_FILES += ../lib/taxi/syn/vivado/taxi_axis_async_fifo.tcl
XDC_FILES += ../lib/taxi/syn/vivado/taxi_sync_reset.tcl
# IP
IP_TCL_FILES = ../lib/taxi/rtl/eth/us/taxi_eth_mac_25g_us_gth_10g_156.tcl
# Configuration
CONFIG_TCL_FILES = ./config.tcl
include ../common/vivado.mk
program: $(FPGA_TOP).bit
echo "open_hw_manager" > 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 {$(FPGA_TOP).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,21 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set params [dict create]
# SFP+ rate
# 0 for 1G, 1 for 10G
dict set params SFP_RATE "1"
# apply parameters to top-level
set param_list {}
dict for {name value} $params {
lappend param_list $name=$value
}
set_property generic $param_list [get_filesets sources_1]

View File

@@ -0,0 +1,48 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# FPGA settings
FPGA_PART = xczu9eg-ffvb1156-2-e
FPGA_TOP = fpga
FPGA_ARCH = zynquplus
# Files for synthesis
SYN_FILES = ../rtl/fpga.sv
SYN_FILES += ../rtl/fpga_core.sv
SYN_FILES += ../lib/taxi/rtl/eth/taxi_eth_mac_1g_fifo.f
SYN_FILES += ../lib/taxi/rtl/lss/taxi_uart.f
SYN_FILES += ../lib/taxi/rtl/sync/taxi_sync_reset.sv
SYN_FILES += ../lib/taxi/rtl/sync/taxi_sync_signal.sv
SYN_FILES += ../lib/taxi/rtl/io/taxi_debounce_switch.sv
# XDC files
XDC_FILES = ../fpga.xdc
XDC_FILES += ../lib/taxi/syn/vivado/taxi_eth_mac_fifo.tcl
XDC_FILES += ../lib/taxi/syn/vivado/taxi_axis_async_fifo.tcl
XDC_FILES += ../lib/taxi/syn/vivado/taxi_sync_reset.tcl
# IP
IP_TCL_FILES = ../ip/basex_pcs_pma_0.tcl
IP_TCL_FILES += ../ip/basex_pcs_pma_1.tcl
# Configuration
CONFIG_TCL_FILES = ./config.tcl
include ../common/vivado.mk
program: $(FPGA_TOP).bit
echo "open_hw_manager" > 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 {$(FPGA_TOP).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,21 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set params [dict create]
# SFP+ rate
# 0 for 1G, 1 for 10G
dict set params SFP_RATE "0"
# apply parameters to top-level
set param_list {}
dict for {name value} $params {
lappend param_list $name=$value
}
set_property generic $param_list [get_filesets sources_1]

View File

@@ -0,0 +1,21 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
create_ip -name gig_ethernet_pcs_pma -vendor xilinx.com -library ip -module_name basex_pcs_pma_0
set_property -dict [list \
CONFIG.Standard {1000BASEX} \
CONFIG.Physical_Interface {Transceiver} \
CONFIG.Management_Interface {false} \
CONFIG.Auto_Negotiation {false} \
CONFIG.TransceiverControl {false} \
CONFIG.RefClkRate {156.25} \
CONFIG.DrpClkRate {62.5} \
CONFIG.SupportLevel {Include_Shared_Logic_in_Core} \
CONFIG.GT_Location {X0Y10} \
] [get_ips basex_pcs_pma_0]

View File

@@ -0,0 +1,21 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
create_ip -name gig_ethernet_pcs_pma -vendor xilinx.com -library ip -module_name basex_pcs_pma_1
set_property -dict [list \
CONFIG.Standard {1000BASEX} \
CONFIG.Physical_Interface {Transceiver} \
CONFIG.Management_Interface {false} \
CONFIG.Auto_Negotiation {false} \
CONFIG.TransceiverControl {false} \
CONFIG.RefClkRate {156.25} \
CONFIG.DrpClkRate {62.5} \
CONFIG.SupportLevel {Include_Shared_Logic_in_Example_Design} \
CONFIG.GT_Location {X0Y9} \
] [get_ips basex_pcs_pma_1]

View File

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

View File

@@ -0,0 +1,683 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2020-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 = "zynquplus",
// SFP rate selection (0 for 1G, 1 for 10G)
parameter logic SFP_RATE = 1'b1
)
(
/*
* Clock: 125MHz LVDS
* Reset: Push button, active low
*/
input wire logic clk_125mhz_p,
input wire logic clk_125mhz_n,
input wire logic reset,
/*
* GPIO
*/
input wire logic btnu,
input wire logic btnl,
input wire logic btnd,
input wire logic btnr,
input wire logic btnc,
input wire logic [7:0] sw,
output wire logic [7: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,
/*
* Ethernet: SFP+
*/
input wire logic [3:0] sfp_rx_p,
input wire logic [3:0] sfp_rx_n,
output wire logic [3:0] sfp_tx_p,
output wire logic [3:0] sfp_tx_n,
input wire logic sfp_mgt_refclk_0_p,
input wire logic sfp_mgt_refclk_0_n,
output wire logic [3:0] sfp_tx_disable_b
);
wire clk_125mhz_ibufg;
wire clk_125mhz_bufg;
// Internal 125 MHz clock
wire clk_125mhz_mmcm_out;
wire clk_125mhz_int;
wire rst_125mhz_int;
// Internal 62.5 MHz clock
wire clk_62mhz_mmcm_out;
wire clk_62mhz_int;
wire mmcm_rst = reset;
wire mmcm_locked;
wire mmcm_clkfb;
IBUFGDS #(
.DIFF_TERM("FALSE"),
.IBUF_LOW_PWR("FALSE")
)
clk_125mhz_ibufg_inst (
.O (clk_125mhz_ibufg),
.I (clk_125mhz_p),
.IB (clk_125mhz_n)
);
BUFG
clk_125mhz_bufg_in_inst (
.I(clk_125mhz_ibufg),
.O(clk_125mhz_bufg)
);
// MMCM instance
MMCME4_BASE #(
// 125 MHz input
.CLKIN1_PERIOD(8.0),
.REF_JITTER1(0.010),
// 125 MHz input / 1 = 125 MHz PFD (range 10 MHz to 500 MHz)
.DIVCLK_DIVIDE(1),
// 125 MHz PFD * 10 = 1250 MHz VCO (range 800 MHz to 1600 MHz)
.CLKFBOUT_MULT_F(10),
.CLKFBOUT_PHASE(0),
// 1250 MHz / 10 = 125 MHz, 0 degrees
.CLKOUT0_DIVIDE_F(10),
.CLKOUT0_DUTY_CYCLE(0.5),
.CLKOUT0_PHASE(0),
// 1250 MHz / 20 = 62.5 MHz, 0 degrees
.CLKOUT1_DIVIDE(20),
.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 (
// 125 MHz input
.CLKIN1(clk_125mhz_bufg),
// direct clkfb feeback
.CLKFBIN(mmcm_clkfb),
.CLKFBOUT(mmcm_clkfb),
.CLKFBOUTB(),
// 125 MHz, 0 degrees
.CLKOUT0(clk_125mhz_mmcm_out),
.CLKOUT0B(),
// 62.5 MHz, 0 degrees
.CLKOUT1(clk_62mhz_mmcm_out),
.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_125mhz_bufg_inst (
.I(clk_125mhz_mmcm_out),
.O(clk_125mhz_int)
);
BUFG
clk_62mhz_bufg_inst (
.I(clk_62mhz_mmcm_out),
.O(clk_62mhz_int)
);
taxi_sync_reset #(
.N(4)
)
sync_reset_125mhz_inst (
.clk(clk_125mhz_int),
.rst(~mmcm_locked),
.out(rst_125mhz_int)
);
// GPIO
wire btnu_int;
wire btnl_int;
wire btnd_int;
wire btnr_int;
wire btnc_int;
wire [7:0] sw_int;
taxi_debounce_switch #(
.WIDTH(5+8),
.N(4),
.RATE(125000)
)
debounce_switch_inst (
.clk(clk_125mhz_int),
.rst(rst_125mhz_int),
.in({btnu,
btnl,
btnd,
btnr,
btnc,
sw}),
.out({btnu_int,
btnl_int,
btnd_int,
btnr_int,
btnc_int,
sw_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;
// SFP+
wire [3:0] sfp_tx_p_int;
wire [3:0] sfp_tx_n_int;
wire sfp0_gmii_clk_int;
wire sfp0_gmii_rst_int;
wire sfp0_gmii_clk_en_int = 1'b1;
wire [7:0] sfp0_gmii_txd_int;
wire sfp0_gmii_tx_en_int;
wire sfp0_gmii_tx_er_int;
wire [7:0] sfp0_gmii_rxd_int;
wire sfp0_gmii_rx_dv_int;
wire sfp0_gmii_rx_er_int;
wire [15:0] sfp0_status_vect;
wire sfp1_gmii_clk_int;
wire sfp1_gmii_rst_int;
wire sfp1_gmii_clk_en_int = 1'b1;
wire [7:0] sfp1_gmii_txd_int;
wire sfp1_gmii_tx_en_int;
wire sfp1_gmii_tx_er_int;
wire [7:0] sfp1_gmii_rxd_int;
wire sfp1_gmii_rx_dv_int;
wire sfp1_gmii_rx_er_int;
wire [15:0] sfp1_status_vect;
wire sfp2_gmii_clk_int;
wire sfp2_gmii_rst_int;
wire sfp2_gmii_clk_en_int = 1'b1;
wire [7:0] sfp2_gmii_txd_int;
wire sfp2_gmii_tx_en_int;
wire sfp2_gmii_tx_er_int;
wire [7:0] sfp2_gmii_rxd_int;
wire sfp2_gmii_rx_dv_int;
wire sfp2_gmii_rx_er_int;
wire [15:0] sfp2_status_vect;
wire sfp3_gmii_clk_int;
wire sfp3_gmii_rst_int;
wire sfp3_gmii_clk_en_int = 1'b1;
wire [7:0] sfp3_gmii_txd_int;
wire sfp3_gmii_tx_en_int;
wire sfp3_gmii_tx_er_int;
wire [7:0] sfp3_gmii_rxd_int;
wire sfp3_gmii_rx_dv_int;
wire sfp3_gmii_rx_er_int;
wire [15:0] sfp3_status_vect;
if (SFP_RATE == 0) begin : sfp_phy
// 1000BASE-X
wire sfp0_gmii_gtrefclk;
wire sfp0_gmii_txuserclk;
wire sfp0_gmii_txuserclk2;
wire sfp0_gmii_rxuserclk;
wire sfp0_gmii_rxuserclk2;
wire sfp0_gmii_resetdone;
wire sfp0_gmii_pmareset;
wire sfp0_gmii_mmcm_locked;
assign sfp0_gmii_clk_int = sfp0_gmii_txuserclk2;
taxi_sync_reset #(
.N(4)
)
sync_reset_sfp0_inst (
.clk(sfp0_gmii_clk_int),
.rst(rst_125mhz_int || !sfp0_gmii_resetdone),
.out(sfp0_gmii_rst_int)
);
wire sfp0_status_link_status = sfp0_status_vect[0];
wire sfp0_status_link_synchronization = sfp0_status_vect[1];
wire sfp0_status_rudi_c = sfp0_status_vect[2];
wire sfp0_status_rudi_i = sfp0_status_vect[3];
wire sfp0_status_rudi_invalid = sfp0_status_vect[4];
wire sfp0_status_rxdisperr = sfp0_status_vect[5];
wire sfp0_status_rxnotintable = sfp0_status_vect[6];
wire sfp0_status_phy_link_status = sfp0_status_vect[7];
wire [1:0] sfp0_status_remote_fault_encdg = sfp0_status_vect[9:8];
wire [1:0] sfp0_status_speed = sfp0_status_vect[11:10];
wire sfp0_status_duplex = sfp0_status_vect[12];
wire sfp0_status_remote_fault = sfp0_status_vect[13];
wire [1:0] sfp0_status_pause = sfp0_status_vect[15:14];
wire [4:0] sfp0_config_vect;
assign sfp0_config_vect[4] = 1'b0; // autonegotiation enable
assign sfp0_config_vect[3] = 1'b0; // isolate
assign sfp0_config_vect[2] = 1'b0; // power down
assign sfp0_config_vect[1] = 1'b0; // loopback enable
assign sfp0_config_vect[0] = 1'b0; // unidirectional enable
basex_pcs_pma_0
sfp0_pcspma (
.gtrefclk_p(sfp_mgt_refclk_0_p),
.gtrefclk_n(sfp_mgt_refclk_0_n),
.gtrefclk_out(sfp0_gmii_gtrefclk),
.txn(sfp_tx_n[0]),
.txp(sfp_tx_p[0]),
.rxn(sfp_rx_n[0]),
.rxp(sfp_rx_p[0]),
.independent_clock_bufg(clk_62mhz_int),
.userclk_out(sfp0_gmii_txuserclk),
.userclk2_out(sfp0_gmii_txuserclk2),
.rxuserclk_out(sfp0_gmii_rxuserclk),
.rxuserclk2_out(sfp0_gmii_rxuserclk2),
.gtpowergood(),
.resetdone(sfp0_gmii_resetdone),
.pma_reset_out(sfp0_gmii_pmareset),
.mmcm_locked_out(sfp0_gmii_mmcm_locked),
.gmii_txd(sfp0_gmii_txd_int),
.gmii_tx_en(sfp0_gmii_tx_en_int),
.gmii_tx_er(sfp0_gmii_tx_er_int),
.gmii_rxd(sfp0_gmii_rxd_int),
.gmii_rx_dv(sfp0_gmii_rx_dv_int),
.gmii_rx_er(sfp0_gmii_rx_er_int),
.gmii_isolate(),
.configuration_vector(sfp0_config_vect),
.status_vector(sfp0_status_vect),
.reset(rst_125mhz_int),
.signal_detect(1'b1)
);
wire sfp1_gmii_txuserclk2 = sfp0_gmii_txuserclk2;
wire sfp1_gmii_resetdone;
assign sfp1_gmii_clk_int = sfp1_gmii_txuserclk2;
taxi_sync_reset #(
.N(4)
)
sync_reset_sfp1_inst (
.clk(sfp1_gmii_clk_int),
.rst(rst_125mhz_int || !sfp1_gmii_resetdone),
.out(sfp1_gmii_rst_int)
);
wire sfp1_status_link_status = sfp1_status_vect[0];
wire sfp1_status_link_synchronization = sfp1_status_vect[1];
wire sfp1_status_rudi_c = sfp1_status_vect[2];
wire sfp1_status_rudi_i = sfp1_status_vect[3];
wire sfp1_status_rudi_invalid = sfp1_status_vect[4];
wire sfp1_status_rxdisperr = sfp1_status_vect[5];
wire sfp1_status_rxnotintable = sfp1_status_vect[6];
wire sfp1_status_phy_link_status = sfp1_status_vect[7];
wire [1:0] sfp1_status_remote_fault_encdg = sfp1_status_vect[9:8];
wire [1:0] sfp1_status_speed = sfp1_status_vect[11:10];
wire sfp1_status_duplex = sfp1_status_vect[12];
wire sfp1_status_remote_fault = sfp1_status_vect[13];
wire [1:0] sfp1_status_pause = sfp1_status_vect[15:14];
wire [4:0] sfp1_config_vect;
assign sfp1_config_vect[4] = 1'b0; // autonegotiation enable
assign sfp1_config_vect[3] = 1'b0; // isolate
assign sfp1_config_vect[2] = 1'b0; // power down
assign sfp1_config_vect[1] = 1'b0; // loopback enable
assign sfp1_config_vect[0] = 1'b0; // unidirectional enable
basex_pcs_pma_1
sfp1_pcspma (
.gtrefclk(sfp0_gmii_gtrefclk),
.txn(sfp_tx_n[1]),
.txp(sfp_tx_p[1]),
.rxn(sfp_rx_n[1]),
.rxp(sfp_rx_p[1]),
.independent_clock_bufg(clk_62mhz_int),
.txoutclk(),
.gtpowergood(),
.rxoutclk(),
.resetdone(sfp1_gmii_resetdone),
.cplllock(),
.mmcm_reset(),
.userclk(sfp0_gmii_txuserclk),
.userclk2(sfp0_gmii_txuserclk2),
.pma_reset(sfp0_gmii_pmareset),
.mmcm_locked(sfp0_gmii_mmcm_locked),
.rxuserclk(sfp0_gmii_txuserclk),
.rxuserclk2(sfp0_gmii_txuserclk2),
.gmii_txd(sfp1_gmii_txd_int),
.gmii_tx_en(sfp1_gmii_tx_en_int),
.gmii_tx_er(sfp1_gmii_tx_er_int),
.gmii_rxd(sfp1_gmii_rxd_int),
.gmii_rx_dv(sfp1_gmii_rx_dv_int),
.gmii_rx_er(sfp1_gmii_rx_er_int),
.gmii_isolate(),
.configuration_vector(sfp1_config_vect),
.status_vector(sfp1_status_vect),
.reset(rst_125mhz_int),
.signal_detect(1'b1)
);
wire sfp2_gmii_txuserclk2 = sfp0_gmii_txuserclk2;
wire sfp2_gmii_resetdone;
assign sfp2_gmii_clk_int = sfp2_gmii_txuserclk2;
taxi_sync_reset #(
.N(4)
)
sync_reset_sfp2_inst (
.clk(sfp2_gmii_clk_int),
.rst(rst_125mhz_int || !sfp2_gmii_resetdone),
.out(sfp2_gmii_rst_int)
);
wire sfp2_status_link_status = sfp2_status_vect[0];
wire sfp2_status_link_synchronization = sfp2_status_vect[1];
wire sfp2_status_rudi_c = sfp2_status_vect[2];
wire sfp2_status_rudi_i = sfp2_status_vect[3];
wire sfp2_status_rudi_invalid = sfp2_status_vect[4];
wire sfp2_status_rxdisperr = sfp2_status_vect[5];
wire sfp2_status_rxnotintable = sfp2_status_vect[6];
wire sfp2_status_phy_link_status = sfp2_status_vect[7];
wire [1:0] sfp2_status_remote_fault_encdg = sfp2_status_vect[9:8];
wire [1:0] sfp2_status_speed = sfp2_status_vect[11:10];
wire sfp2_status_duplex = sfp2_status_vect[12];
wire sfp2_status_remote_fault = sfp2_status_vect[13];
wire [1:0] sfp2_status_pause = sfp2_status_vect[15:14];
wire [4:0] sfp2_config_vect;
assign sfp2_config_vect[4] = 1'b0; // autonegotiation enable
assign sfp2_config_vect[3] = 1'b0; // isolate
assign sfp2_config_vect[2] = 1'b0; // power down
assign sfp2_config_vect[1] = 1'b0; // loopback enable
assign sfp2_config_vect[0] = 1'b0; // unidirectional enable
basex_pcs_pma_1
sfp2_pcspma (
.gtrefclk(sfp0_gmii_gtrefclk),
.txn(sfp_tx_n[2]),
.txp(sfp_tx_p[2]),
.rxn(sfp_rx_n[2]),
.rxp(sfp_rx_p[2]),
.independent_clock_bufg(clk_62mhz_int),
.txoutclk(),
.gtpowergood(),
.rxoutclk(),
.resetdone(sfp2_gmii_resetdone),
.cplllock(),
.mmcm_reset(),
.userclk(sfp0_gmii_txuserclk),
.userclk2(sfp0_gmii_txuserclk2),
.pma_reset(sfp0_gmii_pmareset),
.mmcm_locked(sfp0_gmii_mmcm_locked),
.rxuserclk(sfp0_gmii_txuserclk),
.rxuserclk2(sfp0_gmii_txuserclk2),
.gmii_txd(sfp2_gmii_txd_int),
.gmii_tx_en(sfp2_gmii_tx_en_int),
.gmii_tx_er(sfp2_gmii_tx_er_int),
.gmii_rxd(sfp2_gmii_rxd_int),
.gmii_rx_dv(sfp2_gmii_rx_dv_int),
.gmii_rx_er(sfp2_gmii_rx_er_int),
.gmii_isolate(),
.configuration_vector(sfp2_config_vect),
.status_vector(sfp2_status_vect),
.reset(rst_125mhz_int),
.signal_detect(1'b1)
);
wire sfp3_gmii_txuserclk2 = sfp0_gmii_txuserclk2;
wire sfp3_gmii_resetdone;
assign sfp3_gmii_clk_int = sfp3_gmii_txuserclk2;
taxi_sync_reset #(
.N(4)
)
sync_reset_sfp3_inst (
.clk(sfp3_gmii_clk_int),
.rst(rst_125mhz_int || !sfp3_gmii_resetdone),
.out(sfp3_gmii_rst_int)
);
wire sfp3_status_link_status = sfp3_status_vect[0];
wire sfp3_status_link_synchronization = sfp3_status_vect[1];
wire sfp3_status_rudi_c = sfp3_status_vect[2];
wire sfp3_status_rudi_i = sfp3_status_vect[3];
wire sfp3_status_rudi_invalid = sfp3_status_vect[4];
wire sfp3_status_rxdisperr = sfp3_status_vect[5];
wire sfp3_status_rxnotintable = sfp3_status_vect[6];
wire sfp3_status_phy_link_status = sfp3_status_vect[7];
wire [1:0] sfp3_status_remote_fault_encdg = sfp3_status_vect[9:8];
wire [1:0] sfp3_status_speed = sfp3_status_vect[11:10];
wire sfp3_status_duplex = sfp3_status_vect[12];
wire sfp3_status_remote_fault = sfp3_status_vect[13];
wire [1:0] sfp3_status_pause = sfp3_status_vect[15:14];
wire [4:0] sfp3_config_vect;
assign sfp3_config_vect[4] = 1'b0; // autonegotiation enable
assign sfp3_config_vect[3] = 1'b0; // isolate
assign sfp3_config_vect[2] = 1'b0; // power down
assign sfp3_config_vect[1] = 1'b0; // loopback enable
assign sfp3_config_vect[0] = 1'b0; // unidirectional enable
basex_pcs_pma_1
sfp3_pcspma (
.gtrefclk(sfp0_gmii_gtrefclk),
.txn(sfp_tx_n[3]),
.txp(sfp_tx_p[3]),
.rxn(sfp_rx_n[3]),
.rxp(sfp_rx_p[3]),
.independent_clock_bufg(clk_62mhz_int),
.txoutclk(),
.gtpowergood(),
.rxoutclk(),
.resetdone(sfp3_gmii_resetdone),
.cplllock(),
.mmcm_reset(),
.userclk(sfp0_gmii_txuserclk),
.userclk2(sfp0_gmii_txuserclk2),
.pma_reset(sfp0_gmii_pmareset),
.mmcm_locked(sfp0_gmii_mmcm_locked),
.rxuserclk(sfp0_gmii_txuserclk),
.rxuserclk2(sfp0_gmii_txuserclk2),
.gmii_txd(sfp3_gmii_txd_int),
.gmii_tx_en(sfp3_gmii_tx_en_int),
.gmii_tx_er(sfp3_gmii_tx_er_int),
.gmii_rxd(sfp3_gmii_rxd_int),
.gmii_rx_dv(sfp3_gmii_rx_dv_int),
.gmii_rx_er(sfp3_gmii_rx_er_int),
.gmii_isolate(),
.configuration_vector(sfp3_config_vect),
.status_vector(sfp3_status_vect),
.reset(rst_125mhz_int),
.signal_detect(1'b1)
);
end else begin
// 10GBASE-R
assign sfp_tx_p = sfp_tx_p_int;
assign sfp_tx_n = sfp_tx_n_int;
end
// SGMII interface debug:
// SW12:1 (sw[3]) off for payload byte, on for status vector
// SW12:2 (sw[2]) off for SFP0, on for SFP1
// SW12:4 (sw[0]) off for LSB of status vector, on for MSB
wire [15:0] sel_sv = sw[2] ? sfp1_status_vect : sfp0_status_vect;
assign led = sw[3] ? (sw[0] ? sel_sv[15:8] : sel_sv[7:0]) : led_int;
fpga_core #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.SFP_RATE(SFP_RATE)
)
core_inst (
/*
* Clock: 125MHz
* Synchronous reset
*/
.clk_125mhz(clk_125mhz_int),
.rst_125mhz(rst_125mhz_int),
/*
* GPIO
*/
.btnu(btnu_int),
.btnl(btnl_int),
.btnd(btnd_int),
.btnr(btnr_int),
.btnc(btnc_int),
.sw(sw_int),
.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),
/*
* Ethernet: SFP+
*/
.sfp_rx_p(sfp_rx_p),
.sfp_rx_n(sfp_rx_n),
.sfp_tx_p(sfp_tx_p_int),
.sfp_tx_n(sfp_tx_n_int),
.sfp_mgt_refclk_0_p(sfp_mgt_refclk_0_p),
.sfp_mgt_refclk_0_n(sfp_mgt_refclk_0_n),
.sfp0_gmii_clk(sfp0_gmii_clk_int),
.sfp0_gmii_rst(sfp0_gmii_rst_int),
.sfp0_gmii_clk_en(sfp0_gmii_clk_en_int),
.sfp0_gmii_rxd(sfp0_gmii_rxd_int),
.sfp0_gmii_rx_dv(sfp0_gmii_rx_dv_int),
.sfp0_gmii_rx_er(sfp0_gmii_rx_er_int),
.sfp0_gmii_txd(sfp0_gmii_txd_int),
.sfp0_gmii_tx_en(sfp0_gmii_tx_en_int),
.sfp0_gmii_tx_er(sfp0_gmii_tx_er_int),
.sfp1_gmii_clk(sfp1_gmii_clk_int),
.sfp1_gmii_rst(sfp1_gmii_rst_int),
.sfp1_gmii_clk_en(sfp1_gmii_clk_en_int),
.sfp1_gmii_rxd(sfp1_gmii_rxd_int),
.sfp1_gmii_rx_dv(sfp1_gmii_rx_dv_int),
.sfp1_gmii_rx_er(sfp1_gmii_rx_er_int),
.sfp1_gmii_txd(sfp1_gmii_txd_int),
.sfp1_gmii_tx_en(sfp1_gmii_tx_en_int),
.sfp1_gmii_tx_er(sfp1_gmii_tx_er_int),
.sfp2_gmii_clk(sfp2_gmii_clk_int),
.sfp2_gmii_rst(sfp2_gmii_rst_int),
.sfp2_gmii_clk_en(sfp2_gmii_clk_en_int),
.sfp2_gmii_rxd(sfp2_gmii_rxd_int),
.sfp2_gmii_rx_dv(sfp2_gmii_rx_dv_int),
.sfp2_gmii_rx_er(sfp2_gmii_rx_er_int),
.sfp2_gmii_txd(sfp2_gmii_txd_int),
.sfp2_gmii_tx_en(sfp2_gmii_tx_en_int),
.sfp2_gmii_tx_er(sfp2_gmii_tx_er_int),
.sfp3_gmii_clk(sfp3_gmii_clk_int),
.sfp3_gmii_rst(sfp3_gmii_rst_int),
.sfp3_gmii_clk_en(sfp3_gmii_clk_en_int),
.sfp3_gmii_rxd(sfp3_gmii_rxd_int),
.sfp3_gmii_rx_dv(sfp3_gmii_rx_dv_int),
.sfp3_gmii_rx_er(sfp3_gmii_rx_er_int),
.sfp3_gmii_txd(sfp3_gmii_txd_int),
.sfp3_gmii_tx_en(sfp3_gmii_tx_en_int),
.sfp3_gmii_tx_er(sfp3_gmii_tx_er_int),
.sfp_tx_disable_b(sfp_tx_disable_b)
);
endmodule
`resetall

View File

@@ -0,0 +1,721 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2020-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 = "zynquplus",
// SFP rate selection (0 for 1G, 1 for 10G)
parameter logic SFP_RATE = 1'b1
)
(
/*
* Clock: 125MHz
* Synchronous reset
*/
input wire logic clk_125mhz,
input wire logic rst_125mhz,
/*
* GPIO
*/
input wire logic btnu,
input wire logic btnl,
input wire logic btnd,
input wire logic btnr,
input wire logic btnc,
input wire logic [7:0] sw,
output wire logic [7: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,
/*
* Ethernet: SFP+
*/
input wire logic [3:0] sfp_rx_p,
input wire logic [3:0] sfp_rx_n,
output wire logic [3:0] sfp_tx_p,
output wire logic [3:0] sfp_tx_n,
input wire logic sfp_mgt_refclk_0_p,
input wire logic sfp_mgt_refclk_0_n,
input wire logic sfp0_gmii_clk,
input wire logic sfp0_gmii_rst,
input wire logic sfp0_gmii_clk_en,
input wire logic [7:0] sfp0_gmii_rxd,
input wire logic sfp0_gmii_rx_dv,
input wire logic sfp0_gmii_rx_er,
output wire logic [7:0] sfp0_gmii_txd,
output wire logic sfp0_gmii_tx_en,
output wire logic sfp0_gmii_tx_er,
input wire logic sfp1_gmii_clk,
input wire logic sfp1_gmii_rst,
input wire logic sfp1_gmii_clk_en,
input wire logic [7:0] sfp1_gmii_rxd,
input wire logic sfp1_gmii_rx_dv,
input wire logic sfp1_gmii_rx_er,
output wire logic [7:0] sfp1_gmii_txd,
output wire logic sfp1_gmii_tx_en,
output wire logic sfp1_gmii_tx_er,
input wire logic sfp2_gmii_clk,
input wire logic sfp2_gmii_rst,
input wire logic sfp2_gmii_clk_en,
input wire logic [7:0] sfp2_gmii_rxd,
input wire logic sfp2_gmii_rx_dv,
input wire logic sfp2_gmii_rx_er,
output wire logic [7:0] sfp2_gmii_txd,
output wire logic sfp2_gmii_tx_en,
output wire logic sfp2_gmii_tx_er,
input wire logic sfp3_gmii_clk,
input wire logic sfp3_gmii_rst,
input wire logic sfp3_gmii_clk_en,
input wire logic [7:0] sfp3_gmii_rxd,
input wire logic sfp3_gmii_rx_dv,
input wire logic sfp3_gmii_rx_er,
output wire logic [7:0] sfp3_gmii_txd,
output wire logic sfp3_gmii_tx_en,
output wire logic sfp3_gmii_tx_er,
output wire logic [3:0] sfp_tx_disable_b
);
assign led = sw;
// UART
assign uart_cts = 0;
taxi_axis_if #(.DATA_W(8)) axis_uart();
taxi_uart
uut (
.clk(clk_125mhz),
.rst(rst_125mhz),
/*
* AXI4-Stream input (sink)
*/
.s_axis_tx(axis_uart),
/*
* AXI4-Stream output (source)
*/
.m_axis_rx(axis_uart),
/*
* UART interface
*/
.rxd(uart_rxd),
.txd(uart_txd),
/*
* Status
*/
.tx_busy(),
.rx_busy(),
.rx_overrun_error(),
.rx_frame_error(),
/*
* Configuration
*/
.prescale(16'(125000000/115200/8))
);
// SFP+
assign sfp_tx_disable_b = '1;
if (SFP_RATE == 0) begin : sfp_mac
taxi_axis_if #(.DATA_W(8), .ID_W(8)) axis_sfp0_eth();
taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp0_tx_cpl();
taxi_axis_if #(.DATA_W(8), .ID_W(8)) axis_sfp1_eth();
taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp1_tx_cpl();
taxi_axis_if #(.DATA_W(8), .ID_W(8)) axis_sfp2_eth();
taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp2_tx_cpl();
taxi_axis_if #(.DATA_W(8), .ID_W(8)) axis_sfp3_eth();
taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp3_tx_cpl();
taxi_eth_mac_1g_fifo #(
.PADDING_EN(1),
.MIN_FRAME_LEN(64),
.TX_FIFO_DEPTH(16384),
.TX_FRAME_FIFO(1),
.RX_FIFO_DEPTH(16384),
.RX_FRAME_FIFO(1)
)
sfp0_eth_mac_inst (
.rx_clk(sfp0_gmii_clk),
.rx_rst(sfp0_gmii_rst),
.tx_clk(sfp0_gmii_clk),
.tx_rst(sfp0_gmii_rst),
.logic_clk(clk_125mhz),
.logic_rst(rst_125mhz),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_sfp0_eth),
.m_axis_tx_cpl(axis_sfp0_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_sfp0_eth),
/*
* GMII interface
*/
.gmii_rxd(sfp0_gmii_rxd),
.gmii_rx_dv(sfp0_gmii_rx_dv),
.gmii_rx_er(sfp0_gmii_rx_er),
.gmii_txd(sfp0_gmii_txd),
.gmii_tx_en(sfp0_gmii_tx_en),
.gmii_tx_er(sfp0_gmii_tx_er),
/*
* Control
*/
.rx_clk_enable(sfp0_gmii_clk_en),
.tx_clk_enable(sfp0_gmii_clk_en),
.rx_mii_select(1'b0),
.tx_mii_select(1'b0),
/*
* Status
*/
.tx_error_underflow(),
.tx_fifo_overflow(),
.tx_fifo_bad_frame(),
.tx_fifo_good_frame(),
.rx_error_bad_frame(),
.rx_error_bad_fcs(),
.rx_fifo_overflow(),
.rx_fifo_bad_frame(),
.rx_fifo_good_frame(),
/*
* Configuration
*/
.cfg_ifg(8'd12),
.cfg_tx_enable(1'b1),
.cfg_rx_enable(1'b1)
);
taxi_eth_mac_1g_fifo #(
.PADDING_EN(1),
.MIN_FRAME_LEN(64),
.TX_FIFO_DEPTH(16384),
.TX_FRAME_FIFO(1),
.RX_FIFO_DEPTH(16384),
.RX_FRAME_FIFO(1)
)
sfp1_eth_mac_inst (
.rx_clk(sfp1_gmii_clk),
.rx_rst(sfp1_gmii_rst),
.tx_clk(sfp1_gmii_clk),
.tx_rst(sfp1_gmii_rst),
.logic_clk(clk_125mhz),
.logic_rst(rst_125mhz),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_sfp1_eth),
.m_axis_tx_cpl(axis_sfp1_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_sfp1_eth),
/*
* GMII interface
*/
.gmii_rxd(sfp1_gmii_rxd),
.gmii_rx_dv(sfp1_gmii_rx_dv),
.gmii_rx_er(sfp1_gmii_rx_er),
.gmii_txd(sfp1_gmii_txd),
.gmii_tx_en(sfp1_gmii_tx_en),
.gmii_tx_er(sfp1_gmii_tx_er),
/*
* Control
*/
.rx_clk_enable(sfp1_gmii_clk_en),
.tx_clk_enable(sfp1_gmii_clk_en),
.rx_mii_select(1'b0),
.tx_mii_select(1'b0),
/*
* Status
*/
.tx_error_underflow(),
.tx_fifo_overflow(),
.tx_fifo_bad_frame(),
.tx_fifo_good_frame(),
.rx_error_bad_frame(),
.rx_error_bad_fcs(),
.rx_fifo_overflow(),
.rx_fifo_bad_frame(),
.rx_fifo_good_frame(),
/*
* Configuration
*/
.cfg_ifg(8'd12),
.cfg_tx_enable(1'b1),
.cfg_rx_enable(1'b1)
);
taxi_eth_mac_1g_fifo #(
.PADDING_EN(1),
.MIN_FRAME_LEN(64),
.TX_FIFO_DEPTH(16384),
.TX_FRAME_FIFO(1),
.RX_FIFO_DEPTH(16384),
.RX_FRAME_FIFO(1)
)
sfp2_eth_mac_inst (
.rx_clk(sfp2_gmii_clk),
.rx_rst(sfp2_gmii_rst),
.tx_clk(sfp2_gmii_clk),
.tx_rst(sfp2_gmii_rst),
.logic_clk(clk_125mhz),
.logic_rst(rst_125mhz),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_sfp2_eth),
.m_axis_tx_cpl(axis_sfp2_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_sfp2_eth),
/*
* GMII interface
*/
.gmii_rxd(sfp2_gmii_rxd),
.gmii_rx_dv(sfp2_gmii_rx_dv),
.gmii_rx_er(sfp2_gmii_rx_er),
.gmii_txd(sfp2_gmii_txd),
.gmii_tx_en(sfp2_gmii_tx_en),
.gmii_tx_er(sfp2_gmii_tx_er),
/*
* Control
*/
.rx_clk_enable(sfp2_gmii_clk_en),
.tx_clk_enable(sfp2_gmii_clk_en),
.rx_mii_select(1'b0),
.tx_mii_select(1'b0),
/*
* Status
*/
.tx_error_underflow(),
.tx_fifo_overflow(),
.tx_fifo_bad_frame(),
.tx_fifo_good_frame(),
.rx_error_bad_frame(),
.rx_error_bad_fcs(),
.rx_fifo_overflow(),
.rx_fifo_bad_frame(),
.rx_fifo_good_frame(),
/*
* Configuration
*/
.cfg_ifg(8'd12),
.cfg_tx_enable(1'b1),
.cfg_rx_enable(1'b1)
);
taxi_eth_mac_1g_fifo #(
.PADDING_EN(1),
.MIN_FRAME_LEN(64),
.TX_FIFO_DEPTH(16384),
.TX_FRAME_FIFO(1),
.RX_FIFO_DEPTH(16384),
.RX_FRAME_FIFO(1)
)
sfp3_eth_mac_inst (
.rx_clk(sfp3_gmii_clk),
.rx_rst(sfp3_gmii_rst),
.tx_clk(sfp3_gmii_clk),
.tx_rst(sfp3_gmii_rst),
.logic_clk(clk_125mhz),
.logic_rst(rst_125mhz),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_sfp3_eth),
.m_axis_tx_cpl(axis_sfp3_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_sfp3_eth),
/*
* GMII interface
*/
.gmii_rxd(sfp3_gmii_rxd),
.gmii_rx_dv(sfp3_gmii_rx_dv),
.gmii_rx_er(sfp3_gmii_rx_er),
.gmii_txd(sfp3_gmii_txd),
.gmii_tx_en(sfp3_gmii_tx_en),
.gmii_tx_er(sfp3_gmii_tx_er),
/*
* Control
*/
.rx_clk_enable(sfp3_gmii_clk_en),
.tx_clk_enable(sfp3_gmii_clk_en),
.rx_mii_select(1'b0),
.tx_mii_select(1'b0),
/*
* Status
*/
.tx_error_underflow(),
.tx_fifo_overflow(),
.tx_fifo_bad_frame(),
.tx_fifo_good_frame(),
.rx_error_bad_frame(),
.rx_error_bad_fcs(),
.rx_fifo_overflow(),
.rx_fifo_bad_frame(),
.rx_fifo_good_frame(),
/*
* Configuration
*/
.cfg_ifg(8'd12),
.cfg_tx_enable(1'b1),
.cfg_rx_enable(1'b1)
);
end else begin : sfp_mac
wire [3:0] sfp_tx_clk;
wire [3:0] sfp_tx_rst;
wire [3:0] sfp_rx_clk;
wire [3:0] sfp_rx_rst;
wire [3:0] sfp_rx_status;
wire sfp_gtpowergood;
wire sfp_mgt_refclk_0;
wire sfp_mgt_refclk_0_int;
wire sfp_mgt_refclk_0_bufg;
wire sfp_rst;
taxi_axis_if #(.DATA_W(64), .ID_W(8)) axis_sfp_tx[3:0]();
taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp_tx_cpl[3:0]();
taxi_axis_if #(.DATA_W(64), .ID_W(8)) axis_sfp_rx[3:0]();
if (SIM) begin
assign sfp_gtpowergood = 1'b1;
assign sfp_mgt_refclk_0 = sfp_mgt_refclk_0_p;
assign sfp_mgt_refclk_0_int = sfp_mgt_refclk_0_p;
assign sfp_mgt_refclk_0_bufg = sfp_mgt_refclk_0_int;
end else begin
IBUFDS_GTE4 ibufds_gte4_sfp_mgt_refclk_0_inst (
.I (sfp_mgt_refclk_0_p),
.IB (sfp_mgt_refclk_0_n),
.CEB (1'b0),
.O (sfp_mgt_refclk_0),
.ODIV2 (sfp_mgt_refclk_0_int)
);
BUFG_GT bufg_gt_sfp_mgt_refclk_0_inst (
.CE (sfp_gtpowergood),
.CEMASK (1'b1),
.CLR (1'b0),
.CLRMASK (1'b1),
.DIV (3'd0),
.I (sfp_mgt_refclk_0_int),
.O (sfp_mgt_refclk_0_bufg)
);
end
taxi_sync_reset #(
.N(4)
)
sfp_sync_reset_inst (
.clk(sfp_mgt_refclk_0_bufg),
.rst(rst_125mhz),
.out(sfp_rst)
);
taxi_eth_mac_25g_us #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.CNT(4),
// GT type
.GT_TYPE("GTH"),
// PHY parameters
.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)
)
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_0),
.xcvr_qpll0lock_out(),
.xcvr_qpll0clk_out(),
.xcvr_qpll0refclk_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('0),
.rx_rst_out(sfp_rx_rst),
.tx_clk(sfp_tx_clk),
.tx_rst_in('0),
.tx_rst_out(sfp_tx_rst),
.ptp_sample_clk('0),
/*
* 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('0),
.tx_ptp_ts_step('0),
.rx_ptp_ts('0),
.rx_ptp_ts_step('0),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req('0),
.tx_lfc_resend('0),
.rx_lfc_en('0),
.rx_lfc_req(),
.rx_lfc_ack('0),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req('0),
.tx_pfc_resend('0),
.rx_pfc_en('0),
.rx_pfc_req(),
.rx_pfc_ack('0),
/*
* Pause interface
*/
.tx_lfc_pause_en('0),
.tx_pause_req('0),
.tx_pause_ack(),
/*
* Status
*/
.tx_start_packet(),
.tx_error_underflow(),
.rx_start_packet(),
.rx_error_count(),
.rx_error_bad_frame(),
.rx_error_bad_fcs(),
.rx_bad_block(),
.rx_sequence_error(),
.rx_block_lock(),
.rx_high_ber(),
.rx_status(sfp_rx_status),
.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_ifg('{4{8'd12}}),
.cfg_tx_enable('1),
.cfg_rx_enable('1),
.cfg_tx_prbs31_enable('0),
.cfg_rx_prbs31_enable('0),
.cfg_mcf_rx_eth_dst_mcast('{4{48'h01_80_C2_00_00_01}}),
.cfg_mcf_rx_check_eth_dst_mcast('1),
.cfg_mcf_rx_eth_dst_ucast('{4{48'd0}}),
.cfg_mcf_rx_check_eth_dst_ucast('0),
.cfg_mcf_rx_eth_src('{4{48'd0}}),
.cfg_mcf_rx_check_eth_src('0),
.cfg_mcf_rx_eth_type('{4{16'h8808}}),
.cfg_mcf_rx_opcode_lfc('{4{16'h0001}}),
.cfg_mcf_rx_check_opcode_lfc('1),
.cfg_mcf_rx_opcode_pfc('{4{16'h0101}}),
.cfg_mcf_rx_check_opcode_pfc('1),
.cfg_mcf_rx_forward('0),
.cfg_mcf_rx_enable('0),
.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('0),
.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('0),
.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('0),
.cfg_rx_pfc_opcode('{4{16'h0101}}),
.cfg_rx_pfc_en('0)
);
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
end
endmodule
`resetall

View File

@@ -0,0 +1,54 @@
# 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
DUT = fpga_core
COCOTB_TEST_MODULES = test_$(DUT)
COCOTB_TOPLEVEL = $(DUT)
MODULE = $(COCOTB_TEST_MODULES)
TOPLEVEL = $(COCOTB_TOPLEVEL)
VERILOG_SOURCES += ../../rtl/$(DUT).sv
VERILOG_SOURCES += ../../lib/taxi/rtl/eth/taxi_eth_mac_1g_fifo.f
VERILOG_SOURCES += ../../lib/taxi/rtl/eth/us/taxi_eth_mac_25g_us.f
VERILOG_SOURCES += ../../lib/taxi/rtl/lss/taxi_uart.f
VERILOG_SOURCES += ../../lib/taxi/rtl/sync/taxi_sync_reset.sv
VERILOG_SOURCES += ../../lib/taxi/rtl/sync/taxi_sync_signal.sv
VERILOG_SOURCES += ../../lib/taxi/rtl/io/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 := "\"zynquplus\""
export PARAM_SFP_RATE := "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/tb/eth/baser.py

View File

@@ -0,0 +1,314 @@
#!/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 GmiiFrame, GmiiSource, GmiiSink
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, speed=1000e6):
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 = []
if dut.SFP_RATE.value == 0:
cocotb.start_soon(Clock(dut.sfp0_gmii_clk, 8, units="ns").start())
cocotb.start_soon(Clock(dut.sfp1_gmii_clk, 8, units="ns").start())
cocotb.start_soon(Clock(dut.sfp2_gmii_clk, 8, units="ns").start())
cocotb.start_soon(Clock(dut.sfp3_gmii_clk, 8, units="ns").start())
self.sfp_sources.append(GmiiSource(dut.sfp0_gmii_rxd, dut.sfp0_gmii_rx_er, dut.sfp0_gmii_rx_dv,
dut.sfp0_gmii_clk, dut.sfp0_gmii_rst, dut.sfp0_gmii_clk_en))
self.sfp_sinks.append(GmiiSink(dut.sfp0_gmii_txd, dut.sfp0_gmii_tx_er, dut.sfp0_gmii_tx_en,
dut.sfp0_gmii_clk, dut.sfp0_gmii_rst, dut.sfp0_gmii_clk_en))
self.sfp_sources.append(GmiiSource(dut.sfp1_gmii_rxd, dut.sfp1_gmii_rx_er, dut.sfp1_gmii_rx_dv,
dut.sfp1_gmii_clk, dut.sfp1_gmii_rst, dut.sfp1_gmii_clk_en))
self.sfp_sinks.append(GmiiSink(dut.sfp1_gmii_txd, dut.sfp1_gmii_tx_er, dut.sfp1_gmii_tx_en,
dut.sfp1_gmii_clk, dut.sfp1_gmii_rst, dut.sfp1_gmii_clk_en))
self.sfp_sources.append(GmiiSource(dut.sfp2_gmii_rxd, dut.sfp2_gmii_rx_er, dut.sfp2_gmii_rx_dv,
dut.sfp2_gmii_clk, dut.sfp2_gmii_rst, dut.sfp2_gmii_clk_en))
self.sfp_sinks.append(GmiiSink(dut.sfp2_gmii_txd, dut.sfp2_gmii_tx_er, dut.sfp2_gmii_tx_en,
dut.sfp2_gmii_clk, dut.sfp2_gmii_rst, dut.sfp2_gmii_clk_en))
self.sfp_sources.append(GmiiSource(dut.sfp3_gmii_rxd, dut.sfp3_gmii_rx_er, dut.sfp3_gmii_rx_dv,
dut.sfp3_gmii_clk, dut.sfp3_gmii_rst, dut.sfp3_gmii_clk_en))
self.sfp_sinks.append(GmiiSink(dut.sfp3_gmii_txd, dut.sfp3_gmii_tx_er, dut.sfp3_gmii_tx_en,
dut.sfp3_gmii_clk, dut.sfp3_gmii_rst, dut.sfp3_gmii_clk_en))
else:
cocotb.start_soon(Clock(dut.sfp_mgt_refclk_0_p, 6.4, units="ns").start())
for ch in dut.sfp_mac.sfp_mac_inst.ch:
cocotb.start_soon(Clock(ch.ch_inst.tx_clk, 6.4, units="ns").start())
cocotb.start_soon(Clock(ch.ch_inst.rx_clk, 6.4, units="ns").start())
self.sfp_sources.append(BaseRSerdesSource(ch.ch_inst.serdes_rx_data, ch.ch_inst.serdes_rx_hdr, ch.ch_inst.rx_clk, slip=ch.ch_inst.serdes_rx_bitslip, reverse=True))
self.sfp_sinks.append(BaseRSerdesSink(ch.ch_inst.serdes_tx_data, ch.ch_inst.serdes_tx_hdr, ch.ch_inst.tx_clk, reverse=True))
self.uart_source = UartSource(dut.uart_rxd, baud=115200, bits=8, stop_bits=1)
self.uart_sink = UartSink(dut.uart_txd, baud=115200, bits=8, stop_bits=1)
dut.btnu.setimmediatevalue(0)
dut.btnl.setimmediatevalue(0)
dut.btnd.setimmediatevalue(0)
dut.btnr.setimmediatevalue(0)
dut.btnc.setimmediatevalue(0)
dut.sw.setimmediatevalue(0)
dut.uart_rts.setimmediatevalue(0)
async def init(self):
self.dut.rst_125mhz.setimmediatevalue(0)
self.dut.sfp0_gmii_rst.setimmediatevalue(0)
self.dut.sfp1_gmii_rst.setimmediatevalue(0)
self.dut.sfp2_gmii_rst.setimmediatevalue(0)
self.dut.sfp3_gmii_rst.setimmediatevalue(0)
for k in range(10):
await RisingEdge(self.dut.clk_125mhz)
self.dut.rst_125mhz.value = 1
self.dut.sfp0_gmii_rst.value = 1
self.dut.sfp1_gmii_rst.value = 1
self.dut.sfp2_gmii_rst.value = 1
self.dut.sfp3_gmii_rst.value = 1
for k in range(10):
await RisingEdge(self.dut.clk_125mhz)
self.dut.rst_125mhz.value = 0
self.dut.sfp0_gmii_rst.value = 0
self.dut.sfp1_gmii_rst.value = 0
self.dut.sfp2_gmii_rst.value = 0
self.dut.sfp3_gmii_rst.value = 0
for k in range(10):
await RisingEdge(self.dut.clk_125mhz)
async def uart_test(tb, source, sink):
tb.log.info("Test UART")
tx_data = b"FPGA Ninja"
tb.log.info("UART TX: %s", tx_data)
await source.write(tx_data)
rx_data = bytearray()
while len(rx_data) < len(tx_data):
rx_data.extend(await sink.read())
tb.log.info("UART RX: %s", rx_data)
tb.log.info("UART test done")
async def mac_test(tb, source, sink):
tb.log.info("Test MAC")
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(GmiiFrame.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()
assert rx_frame.error is None
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(GmiiFrame.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()
assert rx_frame.error is None
tb.log.info("MAC test done")
async def mac_test_25g(tb, source, sink):
tb.log.info("Test MAC")
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 = []
tb.log.info("Start UART test")
tests.append(cocotb.start_soon(uart_test(tb, tb.uart_source, tb.uart_sink)))
for k in range(len(tb.sfp_sources)):
if dut.SFP_RATE.value == 0:
tb.log.info("Start SFP %d 1G MAC loopback test", k)
tests.append(cocotb.start_soon(mac_test(tb, tb.sfp_sources[k], tb.sfp_sinks[k])))
else:
tb.log.info("Start SFP %d 10G MAC loopback test", k)
tests.append(cocotb.start_soon(mac_test_25g(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(rtl_dir, '..', 'lib'))
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(lib_dir, "taxi", "rtl", "eth", "taxi_eth_mac_1g_fifo.f"),
os.path.join(lib_dir, "taxi", "rtl", "eth", "us", "taxi_eth_mac_25g_us.f"),
os.path.join(lib_dir, "taxi", "rtl", "lss", "taxi_uart.f"),
os.path.join(lib_dir, "taxi", "rtl", "sync", "taxi_sync_reset.sv"),
os.path.join(lib_dir, "taxi", "rtl", "sync", "taxi_sync_signal.sv"),
os.path.join(lib_dir, "taxi", "rtl", "io", "taxi_debounce_switch.sv"),
]
verilog_sources = process_f_files(verilog_sources)
parameters = {}
parameters['SIM'] = "1'b1"
parameters['VENDOR'] = "\"XILINX\""
parameters['FAMILY'] = "\"zynquplus\""
parameters['SFP_RATE'] = f"1'b{sfp_rate}"
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,
)