eth: Add support for 10GBASE-R to KC705 example design

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2025-11-09 14:24:05 -08:00
parent 4e256cfe37
commit 3519abbee5
16 changed files with 2881 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
# Taxi Example Design for KC705
## Introduction
This example design targets the Xilinx KC705 FPGA board.
The design places looped-back MACs on both the BASE-T port and the SFP+ cage, as well as XFCP on the USB UART for monitoring and control.
Note that this design does not support SGMII as the Xilinx SGMII core requires the GTX QPLL, which is already used for 10GBASE-R on the same quad.
* USB UART
* XFCP (921600 baud)
* RJ-45 Ethernet port with Marvell 88E1111 PHY
* Looped-back MAC via GMII
* Looped-back MAC via RGMII
* SFP+ cage
* Looped-back 10GBASE-R MACs via GTX transceiver
## Board details
* FPGA: XC7K325T-2FFG900C
* USB UART: Silicon Labs CP2103
* 1000BASE-T PHY: Marvell 88E1111 via GMII or RGMII
## 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.
## Board configuration
Several jumpers must be configured to configure the PHY chip for the appropriate mode.
| Mode | J29 | J30 | J64 |
| -------- | ---- | ---- | ---- |
| GMII/MII | 1-2 | 1-2 | open |
| RGMII | 1-2 | open | 1-2 |
Also, note that version 1.0 of the KC705 has the SFP+ TX and RX connections polarity-inverted. Version 1.1 has this fixed. This setting is controlled via a top-level parameter setting that is configurable via config.tcl, so ensure to use a design matching the board revision.
## 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,12 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# Ethernet constraints
# BUFGMUX outputs (GMII)
set_clock_groups -physically_exclusive -group clk_mmcm_out -group phy_tx_clk

View File

@@ -0,0 +1,15 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# Ethernet constraints
# IDELAY from PHY chip (RGMII)
set_property IDELAY_VALUE 0 [get_cells {phy_if.phy_rx_ctl_idelay phy_if.phy_rxd_idelay_bit[*].idelay_inst}]
# MMCM phase (RGMII)
set_property CLKOUT1_PHASE 90 [get_cells clk_mmcm_inst]

View File

@@ -0,0 +1,210 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2014-2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# XDC constraints for the Xilinx KC705 board
# part: xc7k325tffg900-2
# General configuration
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 2.5 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
# System clocks
# 200 MHz system clock
set_property -dict {LOC AD12 IOSTANDARD LVDS} [get_ports clk_200mhz_p] ;# from SiT9102 U6.4
set_property -dict {LOC AD11 IOSTANDARD LVDS} [get_ports clk_200mhz_n] ;# from SiT9102 U6.5
create_clock -period 5.000 -name clk_200mhz [get_ports clk_200mhz_p]
# Si570 user clock (156.25 MHz default)
#set_property -dict {LOC K28 IOSTANDARD LVDS_25} [get_ports clk_user_p] ;# from Si570 U45.4
#set_property -dict {LOC K29 IOSTANDARD LVDS_25} [get_ports clk_user_n] ;# from Si570 U45.5
#create_clock -period 6.400 -name clk_user [get_ports clk_user_p]
# User SMA clock
#set_property -dict {LOC L25 IOSTANDARD LVDS_25} [get_ports clk_user_sma_p] ;# from J11
#set_property -dict {LOC K25 IOSTANDARD LVDS_25} [get_ports clk_user_sma_n] ;# from J12
#create_clock -period 10.000 -name clk_user_sma [get_ports clk_user_sma_p]
# LEDs
set_property -dict {LOC AB8 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {led[0]}] ;# to DS4
set_property -dict {LOC AA8 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {led[1]}] ;# to DS1
set_property -dict {LOC AC9 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {led[2]}] ;# to DS10
set_property -dict {LOC AB9 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {led[3]}] ;# to DS2
set_property -dict {LOC AE26 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[4]}] ;# to DS3
set_property -dict {LOC G19 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[5]}] ;# to DS25
set_property -dict {LOC E18 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[6]}] ;# to DS26
set_property -dict {LOC F16 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[7]}] ;# to DS27
set_false_path -to [get_ports {led[*]}]
set_output_delay 0 [get_ports {led[*]}]
# Reset button
set_property -dict {LOC AB7 IOSTANDARD LVCMOS15} [get_ports reset] ;# from SW7
set_false_path -from [get_ports {reset}]
set_input_delay 0 [get_ports {reset}]
# Push buttons
set_property -dict {LOC AA12 IOSTANDARD LVCMOS15} [get_ports btnu] ;# from SW2
set_property -dict {LOC AC6 IOSTANDARD LVCMOS15} [get_ports btnl] ;# from SW6
set_property -dict {LOC AB12 IOSTANDARD LVCMOS15} [get_ports btnd] ;# from SW4
set_property -dict {LOC AG5 IOSTANDARD LVCMOS15} [get_ports btnr] ;# from SW3
set_property -dict {LOC G12 IOSTANDARD LVCMOS25} [get_ports btnc] ;# from SW5
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 Y29 IOSTANDARD LVCMOS25} [get_ports {sw[0]}] ;# from SW4.4
set_property -dict {LOC W29 IOSTANDARD LVCMOS25} [get_ports {sw[1]}] ;# from SW4.3
set_property -dict {LOC AA28 IOSTANDARD LVCMOS25} [get_ports {sw[2]}] ;# from SW4.2
set_property -dict {LOC Y28 IOSTANDARD LVCMOS25} [get_ports {sw[3]}] ;# from SW4.1
set_false_path -from [get_ports {sw[*]}]
set_input_delay 0 [get_ports {sw[*]}]
# UART (U12 CP2103)
set_property -dict {LOC K24 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 8} [get_ports {uart_txd}] ;# U12.24 RXD_I
set_property -dict {LOC M19 IOSTANDARD LVCMOS25} [get_ports {uart_rxd}] ;# U12.25 TXD_O
set_property -dict {LOC L27 IOSTANDARD LVCMOS25} [get_ports {uart_rts}] ;# U12.23 RTS_O_B
set_property -dict {LOC K23 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 8} [get_ports {uart_cts}] ;# U12.22 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 K21 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 8} [get_ports i2c_scl]
set_property -dict {LOC L21 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 8} [get_ports i2c_sda]
set_property -dict {LOC P23 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 8} [get_ports i2c_mux_reset]
set_false_path -to [get_ports {i2c_sda i2c_scl}]
set_output_delay 0 [get_ports {i2c_sda i2c_scl}]
set_false_path -from [get_ports {i2c_sda i2c_scl}]
set_input_delay 0 [get_ports {i2c_sda i2c_scl}]
# Gigabit Ethernet GMII PHY
set_property -dict {LOC U27 IOSTANDARD LVCMOS25} [get_ports phy_rx_clk] ;# from U37.C1 RXCLK
set_property -dict {LOC U30 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[0]}] ;# from U37.B2 RXD0
set_property -dict {LOC U25 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[1]}] ;# from U37.D3 RXD1
set_property -dict {LOC T25 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[2]}] ;# from U37.C3 RXD2
set_property -dict {LOC U28 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[3]}] ;# from U37.B3 RXD3
set_property -dict {LOC R19 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[4]}] ;# from U37.C4 RXD4
set_property -dict {LOC T27 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[5]}] ;# from U37.A1 RXD5
set_property -dict {LOC T26 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[6]}] ;# from U37.A2 RXD6
set_property -dict {LOC T28 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[7]}] ;# from U37.C5 RXD7
set_property -dict {LOC R28 IOSTANDARD LVCMOS25} [get_ports phy_rx_dv] ;# from U37.B1 RXCTL_RXDV
set_property -dict {LOC V26 IOSTANDARD LVCMOS25} [get_ports phy_rx_er] ;# from U37.D4 RXER
set_property -dict {LOC K30 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_gtx_clk] ;# from U37.E2 TXC_GTXCLK
set_property -dict {LOC M28 IOSTANDARD LVCMOS25} [get_ports phy_tx_clk] ;# from U37.D1 TXCLK
set_property -dict {LOC N27 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[0]}] ;# from U37.F1 TXD0
set_property -dict {LOC N25 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[1]}] ;# from U37.G2 TXD1
set_property -dict {LOC M29 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[2]}] ;# from U37.G3 TXD2
set_property -dict {LOC L28 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[3]}] ;# from U37.H1 TXD3
set_property -dict {LOC J26 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[4]}] ;# from U37.H2 TXD4
set_property -dict {LOC K26 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[5]}] ;# from U37.H3 TXD5
set_property -dict {LOC L30 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[6]}] ;# from U37.J1 TXD6
set_property -dict {LOC J28 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[7]}] ;# from U37.J2 TXD7
set_property -dict {LOC M27 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_tx_en] ;# from U37.E1 TXCTL_TXEN
set_property -dict {LOC N29 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_tx_er] ;# from U37.F2 TXER
set_property -dict {LOC L20 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports phy_reset_n] ;# from U37.K3 RESET_B
set_property -dict {LOC N30 IOSTANDARD LVCMOS25} [get_ports phy_int_n] ;# from U37.L1 INT_B
#set_property -dict {LOC J21 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports phy_mdio] ;# from U37.M1 MDIO
#set_property -dict {LOC R23 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports phy_mdc] ;# from U37.L3 MDC
create_clock -period 40.000 -name phy_tx_clk [get_ports phy_tx_clk]
create_clock -period 8.000 -name phy_rx_clk [get_ports phy_rx_clk]
set_false_path -to [get_ports {phy_reset_n}]
set_output_delay 0 [get_ports {phy_reset_n}]
set_false_path -from [get_ports {phy_int_n}]
set_input_delay 0 [get_ports {phy_int_n}]
#set_false_path -to [get_ports {phy_mdio phy_mdc}]
#set_output_delay 0 [get_ports {phy_mdio phy_mdc}]
#set_false_path -from [get_ports {phy_mdio}]
#set_input_delay 0 [get_ports {phy_mdio}]
# GTX for Ethernet
set_property -dict {LOC G4 } [get_ports sfp_rx_p] ;# MGTXRXP2_117 GTXE2_CHANNEL_X0Y10 / GTXE2_COMMON_X0Y2 from P5.13
set_property -dict {LOC G3 } [get_ports sfp_rx_n] ;# MGTXRXN2_117 GTXE2_CHANNEL_X0Y10 / GTXE2_COMMON_X0Y2 from P5.12
set_property -dict {LOC H2 } [get_ports sfp_tx_p] ;# MGTXTXP2_117 GTXE2_CHANNEL_X0Y10 / GTXE2_COMMON_X0Y2 from P5.18
set_property -dict {LOC H1 } [get_ports sfp_tx_n] ;# MGTXTXN2_117 GTXE2_CHANNEL_X0Y10 / GTXE2_COMMON_X0Y2 from P5.19
#set_property -dict {LOC H6 } [get_ports phy_sgmii_rx_p] ;# MGTXRXP1_117 GTXE2_CHANNEL_X0Y9 / GTXE2_COMMON_X0Y2 from U37.A7 SOUT_P
#set_property -dict {LOC H5 } [get_ports phy_sgmii_rx_n] ;# MGTXRXN1_117 GTXE2_CHANNEL_X0Y9 / GTXE2_COMMON_X0Y2 from U37.A8 SOUT_N
#set_property -dict {LOC J4 } [get_ports phy_sgmii_tx_p] ;# MGTXTXP1_117 GTXE2_CHANNEL_X0Y9 / GTXE2_COMMON_X0Y2 from U37.A3 SIN_P
#set_property -dict {LOC J3 } [get_ports phy_sgmii_tx_n] ;# MGTXTXN1_117 GTXE2_CHANNEL_X0Y9 / GTXE2_COMMON_X0Y2 from U37.A4 SIN_N
#set_property -dict {LOC G8 } [get_ports sgmii_mgt_refclk_p] ;# MGTREFCLK0P_117 from U2.7
#set_property -dict {LOC G7 } [get_ports sgmii_mgt_refclk_n] ;# MGTREFCLK0N_117 from U2.6
set_property -dict {LOC L8 } [get_ports sfp_mgt_refclk_p] ;# MGTREFCLK0P_116 from Si5324 U70.28 CKOUT1_P
set_property -dict {LOC L7 } [get_ports sfp_mgt_refclk_n] ;# MGTREFCLK0N_116 from Si5324 U70.29 CKOUT1_N
#set_property -dict {LOC W27 IOSTANDARD LVDS} [get_ports sfp_recclk_p] ;# to Si5324 U70.16 CKIN1_P
#set_property -dict {LOC W28 IOSTANDARD LVDS} [get_ports sfp_recclk_n] ;# to Si5324 U70.17 CKIN1_N
set_property -dict {LOC AE20 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports si5324_rst]
set_property -dict {LOC AG24 IOSTANDARD LVCMOS25 PULLUP true} [get_ports si5324_int]
set_property -dict {LOC Y20 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {sfp_tx_disable_b}]
# 125 MHz MGT reference clock (SGMII, 1000BASE-X)
#create_clock -period 8.000 -name sgmii_mgt_refclk [get_ports sgmii_mgt_refclk_p]
# 156.25 MHz MGT reference clock (10GBASE-R)
create_clock -period 6.400 -name sgmii_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 -to [get_ports {sfp_tx_disable_b}]
set_output_delay 0 [get_ports {sfp_tx_disable_b}]
# PCIe Interface
#set_property -dict {LOC M6 } [get_ports {pcie_rx_p[0]}] ;# MGTHRXP3_225 GTXE3_CHANNEL_X0Y7 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC M5 } [get_ports {pcie_rx_n[0]}] ;# MGTHRXN3_225 GTXE3_CHANNEL_X0Y7 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC L4 } [get_ports {pcie_tx_p[0]}] ;# MGTHTXP3_225 GTXE3_CHANNEL_X0Y7 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC L3 } [get_ports {pcie_tx_n[0]}] ;# MGTHTXN3_225 GTXE3_CHANNEL_X0Y7 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC P6 } [get_ports {pcie_rx_p[1]}] ;# MGTHRXP2_225 GTXE3_CHANNEL_X0Y6 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC P5 } [get_ports {pcie_rx_n[1]}] ;# MGTHRXN2_225 GTXE3_CHANNEL_X0Y6 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC M2 } [get_ports {pcie_tx_p[1]}] ;# MGTHTXP2_225 GTXE3_CHANNEL_X0Y6 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC M1 } [get_ports {pcie_tx_n[1]}] ;# MGTHTXN2_225 GTXE3_CHANNEL_X0Y6 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC R4 } [get_ports {pcie_rx_p[2]}] ;# MGTHRXP1_225 GTXE3_CHANNEL_X0Y5 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC R3 } [get_ports {pcie_rx_n[2]}] ;# MGTHRXN1_225 GTXE3_CHANNEL_X0Y5 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC N4 } [get_ports {pcie_tx_p[2]}] ;# MGTHTXP1_225 GTXE3_CHANNEL_X0Y5 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC N3 } [get_ports {pcie_tx_n[2]}] ;# MGTHTXN1_225 GTXE3_CHANNEL_X0Y5 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC T6 } [get_ports {pcie_rx_p[3]}] ;# MGTHRXP0_225 GTXE3_CHANNEL_X0Y4 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC T5 } [get_ports {pcie_rx_n[3]}] ;# MGTHRXN0_225 GTXE3_CHANNEL_X0Y4 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC P2 } [get_ports {pcie_tx_p[3]}] ;# MGTHTXP0_225 GTXE3_CHANNEL_X0Y4 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC P1 } [get_ports {pcie_tx_n[3]}] ;# MGTHTXN0_225 GTXE3_CHANNEL_X0Y4 / GTXE3_COMMON_X0Y1
#set_property -dict {LOC V6 } [get_ports {pcie_rx_p[4]}] ;# MGTHRXP3_224 GTXE3_CHANNEL_X0Y3 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC V5 } [get_ports {pcie_rx_n[4]}] ;# MGTHRXN3_224 GTXE3_CHANNEL_X0Y3 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC T2 } [get_ports {pcie_tx_p[4]}] ;# MGTHTXP3_224 GTXE3_CHANNEL_X0Y3 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC T1 } [get_ports {pcie_tx_n[4]}] ;# MGTHTXN3_224 GTXE3_CHANNEL_X0Y3 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC W4 } [get_ports {pcie_rx_p[5]}] ;# MGTHRXP2_224 GTXE3_CHANNEL_X0Y2 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC W3 } [get_ports {pcie_rx_n[5]}] ;# MGTHRXN2_224 GTXE3_CHANNEL_X0Y2 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC U4 } [get_ports {pcie_tx_p[5]}] ;# MGTHTXP2_224 GTXE3_CHANNEL_X0Y2 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC U3 } [get_ports {pcie_tx_n[5]}] ;# MGTHTXN2_224 GTXE3_CHANNEL_X0Y2 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC Y6 } [get_ports {pcie_rx_p[6]}] ;# MGTHRXP1_224 GTXE3_CHANNEL_X0Y1 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC Y5 } [get_ports {pcie_rx_n[6]}] ;# MGTHRXN1_224 GTXE3_CHANNEL_X0Y1 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC V2 } [get_ports {pcie_tx_p[6]}] ;# MGTHTXP1_224 GTXE3_CHANNEL_X0Y1 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC V1 } [get_ports {pcie_tx_n[6]}] ;# MGTHTXN1_224 GTXE3_CHANNEL_X0Y1 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC AA4 } [get_ports {pcie_rx_p[7]}] ;# MGTHRXP0_224 GTXE3_CHANNEL_X0Y0 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC AA3 } [get_ports {pcie_rx_n[7]}] ;# MGTHRXN0_224 GTXE3_CHANNEL_X0Y0 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC Y2 } [get_ports {pcie_tx_p[7]}] ;# MGTHTXP0_224 GTXE3_CHANNEL_X0Y0 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC Y1 } [get_ports {pcie_tx_n[7]}] ;# MGTHTXN0_224 GTXE3_CHANNEL_X0Y0 / GTXE3_COMMON_X0Y0
#set_property -dict {LOC U8 } [get_ports pcie_mgt_refclk_p] ;# MGTREFCLK0P_225
#set_property -dict {LOC U7 } [get_ports pcie_mgt_refclk_n] ;# MGTREFCLK0N_225
#set_property -dict {LOC G25 IOSTANDARD LVCMOS25 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,58 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# FPGA settings
FPGA_PART = xc7k325tffg900-2
FPGA_TOP = fpga
FPGA_ARCH = kintex7
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)/eth/rtl/taxi_eth_mac_1g_gmii_fifo.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)/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 += ../eth_gmii.xdc
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_phy_10g_7_gt.tcl
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_mac_fifo.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_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 {$(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,30 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set params [dict create]
# Type of PHY on RJ-45 10/100/1000BASE-T port (GMII or RGMII)
dict set params BASET_PHY_TYPE "GMII"
# Invert polarity for SFP+ cage
# KC705 rev 1.0: diff pairs to SFP+ are polarity-swapped
dict set params SFP_INVERT "1"
# KC705 rev 1.1: diff pairs to SFP+ are correct
#dict set params SFP_INVERT "0"
# 10G MAC configuration
dict set params CFG_LOW_LATENCY "1"
dict set params COMBINED_MAC_PCS "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,58 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# FPGA settings
FPGA_PART = xc7k325tffg900-2
FPGA_TOP = fpga
FPGA_ARCH = kintex7
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/taxi_eth_mac_1g_fifo.f
SYN_FILES += $(TAXI_SRC_DIR)/eth/rtl/taxi_eth_mac_1g_rgmii_fifo.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)/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 += ../eth_rgmii.xdc
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_rgmii_phy_if.tcl
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_mac_fifo.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_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 {$(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,30 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set params [dict create]
# Type of PHY on RJ-45 10/100/1000BASE-T port (GMII or RGMII)
dict set params BASET_PHY_TYPE "RGMII"
# Invert polarity for SFP+ cage
# KC705 rev 1.0: diff pairs to SFP+ are polarity-swapped
dict set params SFP_INVERT "1"
# KC705 rev 1.1: diff pairs to SFP+ are correct
#dict set params SFP_INVERT "0"
# 10G MAC configuration
dict set params CFG_LOW_LATENCY "1"
dict set params COMBINED_MAC_PCS "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 @@
../../../../../../

View File

@@ -0,0 +1,556 @@
// 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 = "kintex7",
// Use 90 degree clock for RGMII transmit
parameter logic USE_CLK90 = 1'b1,
// BASE-T PHY type (GMII, RGMII)
parameter BASET_PHY_TYPE = "GMII",
// SFP rate selection (0 for 1G, 1 for 10G)
parameter logic SFP_RATE = 1'b1,
// Invert SFP data pins
parameter logic SFP_INVERT = 1'b1,
// 10G MAC configuration
parameter logic CFG_LOW_LATENCY = 1'b1,
parameter logic COMBINED_MAC_PCS = 1'b1
)
(
/*
* Clock: 200MHz
* Reset: Push button, active high
*/
input wire logic clk_200mhz_p,
input wire logic clk_200mhz_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 [3: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,
/*
* 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,
input wire logic sfp_rx_n,
output wire logic sfp_tx_p,
output wire logic sfp_tx_n,
input wire logic sfp_mgt_refclk_p,
input wire logic sfp_mgt_refclk_n,
output wire logic si5324_rst,
input wire logic si5324_int,
output wire logic sfp_tx_disable_b,
/*
* Ethernet: 1000BASE-T GMII or RGMII
*/
input wire logic phy_rx_clk,
input wire logic [7:0] phy_rxd,
input wire logic phy_rx_dv,
input wire logic phy_rx_er,
output wire logic phy_gtx_clk,
input wire logic phy_tx_clk,
output wire logic [7:0] phy_txd,
output wire logic phy_tx_en,
output wire logic phy_tx_er,
output wire logic phy_reset_n,
input wire logic phy_int_n
);
// Clock and reset
wire clk_200mhz_ibufg;
// Internal 125 MHz clock
wire clk_mmcm_out;
wire clk_int;
wire clk90_mmcm_out;
wire clk90_int;
wire rst_int;
wire clk_200mhz_mmcm_out;
wire clk_200mhz_int;
wire mmcm_rst = reset;
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),
// 1000 MHz VCO / 8 = 125 MHz, 90 degrees
.CLKOUT1_DIVIDE(8),
.CLKOUT1_DUTY_CYCLE(0.5),
.CLKOUT1_PHASE(90),
// 1000 MHz VCO / 5 = 200 MHz, 0 degrees
.CLKOUT2_DIVIDE(5),
.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_mmcm_out),
.CLKOUT0B(),
// 125 MHz, 90 degrees
.CLKOUT1(clk90_mmcm_out),
.CLKOUT1B(),
// 200 MHz, 0 degrees
.CLKOUT2(clk_200mhz_mmcm_out),
.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_mmcm_out),
.O(clk_int)
);
BUFG
clk90_bufg_inst (
.I(clk90_mmcm_out),
.O(clk90_int)
);
BUFG
clk_200mhz_bufg_inst (
.I(clk_200mhz_mmcm_out),
.O(clk_200mhz_int)
);
taxi_sync_reset #(
.N(4)
)
sync_reset_inst (
.clk(clk_int),
.rst(~mmcm_locked),
.out(rst_int)
);
// GPIO
wire btnu_int;
wire btnl_int;
wire btnd_int;
wire btnr_int;
wire btnc_int;
wire [3:0] sw_int;
taxi_debounce_switch #(
.WIDTH(9),
.N(4),
.RATE(125000)
)
debounce_switch_inst (
.clk(clk_int),
.rst(rst_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_int),
.in({uart_rxd, uart_rts}),
.out({uart_rxd_int, uart_rts_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;
// 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_int;
taxi_i2c_master
si5324_i2c_master_inst (
.clk(clk_int),
.rst(rst_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_int),
.rst(rst_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)
);
wire phy_rgmii_rx_clk_int;
wire [3:0] phy_rgmii_rxd_int;
wire phy_rgmii_rx_ctl_int;
wire phy_rgmii_tx_clk_int;
wire [3:0] phy_rgmii_txd_int;
wire phy_rgmii_tx_ctl_int;
wire phy_gmii_rx_clk_int;
wire [7:0] phy_gmii_rxd_int;
wire phy_gmii_rx_dv_int;
wire phy_gmii_rx_er_int;
wire phy_gmii_gtx_clk_int;
wire phy_gmii_tx_clk_int;
wire [7:0] phy_gmii_txd_int;
wire phy_gmii_tx_en_int;
wire phy_gmii_tx_er_int;
if (BASET_PHY_TYPE == "RGMII") begin : phy_if
assign phy_rgmii_rx_clk_int = phy_rx_clk;
// IODELAY elements for RGMII interface to PHY
IDELAYCTRL
idelayctrl_inst (
.REFCLK(clk_200mhz_int),
.RST(rst_int),
.RDY()
);
for (genvar n = 0; n < 4; n = n + 1) begin : phy_rxd_idelay_bit
IDELAYE2 #(
.IDELAY_TYPE("FIXED")
)
idelay_inst (
.IDATAIN(phy_rxd[n]),
.DATAOUT(phy_rgmii_rxd_int[n]),
.DATAIN(1'b0),
.C(1'b0),
.CE(1'b0),
.INC(1'b0),
.CINVCTRL(1'b0),
.CNTVALUEIN(5'd0),
.CNTVALUEOUT(),
.LD(1'b0),
.LDPIPEEN(1'b0),
.REGRST(1'b0)
);
end
IDELAYE2 #(
.IDELAY_TYPE("FIXED")
)
phy_rx_ctl_idelay (
.IDATAIN(phy_rx_dv),
.DATAOUT(phy_rgmii_rx_ctl_int),
.DATAIN(1'b0),
.C(1'b0),
.CE(1'b0),
.INC(1'b0),
.CINVCTRL(1'b0),
.CNTVALUEIN(5'd0),
.CNTVALUEOUT(),
.LD(1'b0),
.LDPIPEEN(1'b0),
.REGRST(1'b0)
);
assign phy_gtx_clk = phy_rgmii_tx_clk_int;
assign phy_txd[3:0] = phy_rgmii_txd_int;
assign phy_tx_en = phy_rgmii_tx_ctl_int;
assign phy_txd[7:4] = '0;
assign phy_tx_er = 1'b0;
assign phy_gmii_rx_clk_int = 1'b0;
assign phy_gmii_rxd_int = '0;
assign phy_gmii_rx_dv_int = 1'b0;
assign phy_gmii_rx_er_int = 1'b0;
assign phy_gmii_tx_clk_int = 1'b0;
end else begin : phy_if
assign phy_rgmii_rx_clk_int = 1'b0;
assign phy_rgmii_rxd_int = '0;
assign phy_rgmii_rx_ctl_int = 1'b0;
assign phy_gmii_rx_clk_int = phy_rx_clk;
assign phy_gmii_rxd_int = phy_rxd;
assign phy_gmii_rx_dv_int = phy_rx_dv;
assign phy_gmii_rx_er_int = phy_rx_er;
assign phy_gtx_clk = phy_gmii_gtx_clk_int;
assign phy_gmii_tx_clk_int = phy_tx_clk;
assign phy_txd = phy_gmii_txd_int;
assign phy_tx_en = phy_gmii_tx_en_int;
assign phy_tx_er = phy_gmii_tx_er_int;
end
fpga_core #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.USE_CLK90(USE_CLK90),
.BASET_PHY_TYPE(BASET_PHY_TYPE),
.SFP_INVERT(SFP_INVERT),
.CFG_LOW_LATENCY(CFG_LOW_LATENCY),
.COMBINED_MAC_PCS(COMBINED_MAC_PCS)
)
core_inst (
/*
* Clock: 125MHz
* Synchronous reset
*/
.clk(clk_int),
.clk90(clk90_int),
.rst(rst_int),
/*
* GPIO
*/
.btnu(btnu_int),
.btnl(btnl_int),
.btnd(btnd_int),
.btnr(btnr_int),
.btnc(btnc_int),
.sw(sw_int),
.led(led),
/*
* 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_tx_disable_b(sfp_tx_disable_b),
/*
* Ethernet: 1000BASE-T GMII/RGMII/SGMII
*/
.phy_rgmii_rx_clk(phy_rgmii_rx_clk_int),
.phy_rgmii_rxd(phy_rgmii_rxd_int),
.phy_rgmii_rx_ctl(phy_rgmii_rx_ctl_int),
.phy_rgmii_tx_clk(phy_rgmii_tx_clk_int),
.phy_rgmii_txd(phy_rgmii_txd_int),
.phy_rgmii_tx_ctl(phy_rgmii_tx_ctl_int),
.phy_gmii_rx_clk(phy_gmii_rx_clk_int),
.phy_gmii_rxd(phy_gmii_rxd_int),
.phy_gmii_rx_dv(phy_gmii_rx_dv_int),
.phy_gmii_rx_er(phy_gmii_rx_er_int),
.phy_gmii_gtx_clk(phy_gmii_gtx_clk_int),
.phy_gmii_tx_clk(phy_gmii_tx_clk_int),
.phy_gmii_txd(phy_gmii_txd_int),
.phy_gmii_tx_en(phy_gmii_tx_en_int),
.phy_gmii_tx_er(phy_gmii_tx_er_int),
.phy_reset_n(phy_reset_n),
.phy_int_n(phy_int_n)
);
endmodule
`resetall

View File

@@ -0,0 +1,766 @@
// 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 = "kintex7",
// Use 90 degree clock for RGMII transmit
parameter logic USE_CLK90 = 1'b1,
// BASE-T PHY type (GMII or RGMII)
parameter BASET_PHY_TYPE = "GMII",
// Invert SFP data pins
parameter logic SFP_INVERT = 1'b1,
// 10G MAC configuration
parameter logic CFG_LOW_LATENCY = 1'b1,
parameter logic COMBINED_MAC_PCS = 1'b1,
parameter MAC_DATA_W = 32
)
(
/*
* Clock: 125MHz
* Synchronous reset
*/
input wire logic clk,
input wire logic clk90,
input wire logic rst,
/*
* 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,
/*
* 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,
input wire logic sfp_rx_n,
output wire logic sfp_tx_p,
output wire logic sfp_tx_n,
input wire logic sfp_mgt_refclk_p,
input wire logic sfp_mgt_refclk_n,
output wire logic sfp_tx_disable_b,
/*
* Ethernet: 1000BASE-T
*/
input wire logic phy_rgmii_rx_clk,
input wire logic [3:0] phy_rgmii_rxd,
input wire logic phy_rgmii_rx_ctl,
output wire logic phy_rgmii_tx_clk,
output wire logic [3:0] phy_rgmii_txd,
output wire logic phy_rgmii_tx_ctl,
input wire logic phy_gmii_rx_clk,
input wire logic [7:0] phy_gmii_rxd,
input wire logic phy_gmii_rx_dv,
input wire logic phy_gmii_rx_er,
output wire logic phy_gmii_gtx_clk,
input wire logic phy_gmii_tx_clk,
output wire logic [7:0] phy_gmii_txd,
output wire logic phy_gmii_tx_en,
output wire logic phy_gmii_tx_er,
output wire logic phy_reset_n,
input wire logic phy_int_n
);
assign led = sw;
// 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),
.rst(rst),
/*
* 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/921600))
);
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("KC705"),
.XFCP_EXT_ID(0),
.XFCP_EXT_ID_STR("Taxi example"),
.PORTS($size(xfcp_sw_us))
)
xfcp_sw_inst (
.clk(clk),
.rst(rst),
/*
* 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),
.rst(rst),
/*
* 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[2]();
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),
.rst(rst),
/*
* 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),
.rst(rst),
/*
* 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)
);
// BASE-T PHY
assign phy_reset_n = !rst;
taxi_axis_if #(.DATA_W(8), .ID_W(8), .USER_EN(1), .USER_W(1)) axis_eth();
taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_tx_cpl();
if (BASET_PHY_TYPE == "GMII") begin : baset_mac_gmii
taxi_eth_mac_1g_gmii_fifo #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.PADDING_EN(1),
.MIN_FRAME_LEN(64),
.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("GMII0"),
.TX_FIFO_DEPTH(16384),
.TX_FRAME_FIFO(1),
.RX_FIFO_DEPTH(16384),
.RX_FRAME_FIFO(1)
)
eth_mac_inst (
.gtx_clk(clk),
.gtx_rst(rst),
.logic_clk(clk),
.logic_rst(rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_eth),
.m_axis_tx_cpl(axis_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_eth),
/*
* GMII interface
*/
.gmii_rx_clk(phy_gmii_rx_clk),
.gmii_rxd(phy_gmii_rxd),
.gmii_rx_dv(phy_gmii_rx_dv),
.gmii_rx_er(phy_gmii_rx_er),
.gmii_tx_clk(phy_gmii_gtx_clk),
.mii_tx_clk(phy_gmii_tx_clk),
.gmii_txd(phy_gmii_txd),
.gmii_tx_en(phy_gmii_tx_en),
.gmii_tx_er(phy_gmii_tx_er),
/*
* Statistics
*/
.stat_clk(clk),
.stat_rst(rst),
.m_axis_stat(axis_eth_stat[0]),
/*
* 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(),
.link_speed(),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(16'd9218),
.cfg_tx_ifg(8'd12),
.cfg_tx_enable(1'b1),
.cfg_rx_max_pkt_len(16'd9218),
.cfg_rx_enable(1'b1)
);
end else if (BASET_PHY_TYPE == "RGMII") begin : baset_mac_rgmii
taxi_eth_mac_1g_rgmii_fifo #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.USE_CLK90(USE_CLK90),
.PADDING_EN(1),
.MIN_FRAME_LEN(64),
.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("RGMII0"),
.TX_FIFO_DEPTH(16384),
.TX_FRAME_FIFO(1),
.RX_FIFO_DEPTH(16384),
.RX_FRAME_FIFO(1)
)
eth_mac_inst (
.gtx_clk(clk),
.gtx_clk90(clk90),
.gtx_rst(rst),
.logic_clk(clk),
.logic_rst(rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_eth),
.m_axis_tx_cpl(axis_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_eth),
/*
* RGMII interface
*/
.rgmii_rx_clk(phy_rgmii_rx_clk),
.rgmii_rxd(phy_rgmii_rxd),
.rgmii_rx_ctl(phy_rgmii_rx_ctl),
.rgmii_tx_clk(phy_rgmii_tx_clk),
.rgmii_txd(phy_rgmii_txd),
.rgmii_tx_ctl(phy_rgmii_tx_ctl),
/*
* Statistics
*/
.stat_clk(clk),
.stat_rst(rst),
.m_axis_stat(axis_eth_stat[0]),
/*
* 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(),
.link_speed(),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(16'd9218),
.cfg_tx_ifg(8'd12),
.cfg_tx_enable(1'b1),
.cfg_rx_max_pkt_len(16'd9218),
.cfg_rx_enable(1'b1)
);
assign phy_gmii_gtx_clk = 1'b0;
assign phy_gmii_txd = '0;
assign phy_gmii_tx_en = 1'b0;
assign phy_gmii_tx_er = 1'b0;
end
// SFP+
assign sfp_tx_disable_b = 1'b1;
wire sfp_tx_clk[1];
wire sfp_tx_rst[1];
wire sfp_rx_clk[1];
wire sfp_rx_rst[1];
wire sfp_rx_status[1];
wire sfp_gtpowergood;
wire sfp_mgt_refclk;
wire sfp_mgt_refclk_bufg;
wire sfp_rst;
taxi_axis_if #(.DATA_W(32), .ID_W(8), .USER_EN(1), .USER_W(1)) axis_sfp_tx[1]();
taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp_tx_cpl[1]();
taxi_axis_if #(.DATA_W(32), .ID_W(8), .USER_EN(1), .USER_W(1)) axis_sfp_rx[1]();
if (SIM) begin
assign sfp_mgt_refclk = sfp_mgt_refclk_p;
assign sfp_mgt_refclk_bufg = sfp_mgt_refclk;
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),
.out(sfp_rst)
);
wire sfp_tx_p_int[1];
wire sfp_tx_n_int[1];
assign sfp_tx_p = sfp_tx_p_int[0];
assign sfp_tx_n = sfp_tx_n_int[0];
// synthesis translate_off
`define SIM
// synthesis translate_on
taxi_eth_mac_25g_us #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.CNT(1),
// GT config
.CFG_LOW_LATENCY(CFG_LOW_LATENCY),
// GT type
.GT_TYPE("GTX"),
// GT parameters
// workaround for verilator bug
`ifndef SIM
.GT_TX_DIFFCTRL('{1{5'd8}}),
.GT_TX_POLARITY('{1{SFP_INVERT}}),
.GT_RX_POLARITY('{1{SFP_INVERT}}),
`endif
// 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(16+16),
.STAT_UPDATE_PERIOD(1024)
// workaround for verilator bug
`ifndef SIM
,
.STAT_STR_EN(1),
.STAT_PREFIX_STR('{"SFP"})
`endif
)
sfp_mac_inst (
.xcvr_ctrl_clk(clk),
.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_int),
.xcvr_txn(sfp_tx_n_int),
.xcvr_rxp('{1{sfp_rx_p}}),
.xcvr_rxn('{1{sfp_rx_n}}),
/*
* MAC clocks
*/
.rx_clk(sfp_rx_clk),
.rx_rst_in('{1{1'b0}}),
.rx_rst_out(sfp_rx_rst),
.tx_clk(sfp_tx_clk),
.tx_rst_in('{1{1'b0}}),
.tx_rst_out(sfp_tx_rst),
.ptp_sample_clk('{1{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('{1{'0}}),
.tx_ptp_ts_step('{1{1'b0}}),
.rx_ptp_ts('{1{'0}}),
.rx_ptp_ts_step('{1{1'b0}}),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req('{1{1'b0}}),
.tx_lfc_resend('{1{1'b0}}),
.rx_lfc_en('{1{1'b0}}),
.rx_lfc_req(),
.rx_lfc_ack('{1{1'b0}}),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req('{1{'0}}),
.tx_pfc_resend('{1{1'b0}}),
.rx_pfc_en('{1{'0}}),
.rx_pfc_req(),
.rx_pfc_ack('{1{'0}}),
/*
* Pause interface
*/
.tx_lfc_pause_en('{1{1'b0}}),
.tx_pause_req('{1{1'b0}}),
.tx_pause_ack(),
/*
* Statistics
*/
.stat_clk(clk),
.stat_rst(rst),
.m_axis_stat(axis_eth_stat[1]),
/*
* 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('{1{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('{1{16'd9218}}),
.cfg_tx_ifg('{1{8'd12}}),
.cfg_tx_enable('{1{1'b1}}),
.cfg_rx_max_pkt_len('{1{16'd9218}}),
.cfg_rx_enable('{1{1'b1}}),
.cfg_tx_prbs31_enable('{1{1'b0}}),
.cfg_rx_prbs31_enable('{1{1'b0}}),
.cfg_mcf_rx_eth_dst_mcast('{1{48'h01_80_C2_00_00_01}}),
.cfg_mcf_rx_check_eth_dst_mcast('{1{1'b1}}),
.cfg_mcf_rx_eth_dst_ucast('{1{48'd0}}),
.cfg_mcf_rx_check_eth_dst_ucast('{1{1'b0}}),
.cfg_mcf_rx_eth_src('{1{48'd0}}),
.cfg_mcf_rx_check_eth_src('{1{1'b0}}),
.cfg_mcf_rx_eth_type('{1{16'h8808}}),
.cfg_mcf_rx_opcode_lfc('{1{16'h0001}}),
.cfg_mcf_rx_check_opcode_lfc('{1{1'b1}}),
.cfg_mcf_rx_opcode_pfc('{1{16'h0101}}),
.cfg_mcf_rx_check_opcode_pfc('{1{1'b1}}),
.cfg_mcf_rx_forward('{1{1'b0}}),
.cfg_mcf_rx_enable('{1{1'b0}}),
.cfg_tx_lfc_eth_dst('{1{48'h01_80_C2_00_00_01}}),
.cfg_tx_lfc_eth_src('{1{48'h80_23_31_43_54_4C}}),
.cfg_tx_lfc_eth_type('{1{16'h8808}}),
.cfg_tx_lfc_opcode('{1{16'h0001}}),
.cfg_tx_lfc_en('{1{1'b0}}),
.cfg_tx_lfc_quanta('{1{16'hffff}}),
.cfg_tx_lfc_refresh('{1{16'h7fff}}),
.cfg_tx_pfc_eth_dst('{1{48'h01_80_C2_00_00_01}}),
.cfg_tx_pfc_eth_src('{1{48'h80_23_31_43_54_4C}}),
.cfg_tx_pfc_eth_type('{1{16'h8808}}),
.cfg_tx_pfc_opcode('{1{16'h0101}}),
.cfg_tx_pfc_en('{1{1'b0}}),
.cfg_tx_pfc_quanta('{1{'{8{16'hffff}}}}),
.cfg_tx_pfc_refresh('{1{'{8{16'h7fff}}}}),
.cfg_rx_lfc_opcode('{1{16'h0001}}),
.cfg_rx_lfc_en('{1{1'b0}}),
.cfg_rx_pfc_opcode('{1{16'h0101}}),
.cfg_rx_pfc_en('{1{1'b0}})
);
for (genvar n = 0; n < 1; 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'h80);
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,64 @@
# 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/us/taxi_eth_mac_25g_us.f
VERILOG_SOURCES += $(TAXI_SRC_DIR)/eth/rtl/taxi_eth_mac_1g_gmii_fifo.f
VERILOG_SOURCES += $(TAXI_SRC_DIR)/eth/rtl/taxi_eth_mac_1g_rgmii_fifo.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 := "\"kintex7\""
export PARAM_USE_CLK90 := "1'b1"
export PARAM_BASET_PHY_TYPE := "\"GMII\""
export PARAM_SFP_INVERT := "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,309 @@
#!/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, Timer, Combine
from cocotbext.eth import XgmiiFrame
from cocotbext.eth import GmiiFrame, GmiiSource, GmiiSink, GmiiPhy, RgmiiPhy
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)
if hasattr(dut, "baset_mac_gmii"):
self.baset_phy = GmiiPhy(dut.phy_gmii_txd, dut.phy_gmii_tx_er, dut.phy_gmii_tx_en, dut.phy_gmii_tx_clk, dut.phy_gmii_gtx_clk,
dut.phy_gmii_rxd, dut.phy_gmii_rx_er, dut.phy_gmii_rx_dv, dut.phy_gmii_rx_clk, speed=speed)
elif hasattr(dut, "baset_mac_rgmii"):
self.baset_phy = RgmiiPhy(dut.phy_rgmii_txd, dut.phy_rgmii_tx_ctl, dut.phy_rgmii_tx_clk,
dut.phy_rgmii_rxd, dut.phy_rgmii_rx_ctl, dut.phy_rgmii_rx_clk, speed=speed)
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=921600, bits=8, stop_bits=1)
self.uart_sink = UartSink(dut.uart_txd, baud=921600, 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)
cocotb.start_soon(self._run_clk())
async def init(self):
self.dut.rst.setimmediatevalue(0)
for k in range(10):
await RisingEdge(self.dut.clk)
self.dut.rst.value = 1
for k in range(10):
await RisingEdge(self.dut.clk)
self.dut.rst.value = 0
async def _run_clk(self):
t = Timer(2, 'ns')
while True:
self.dut.clk.value = 1
await t
self.dut.clk90.value = 1
await t
self.dut.clk.value = 0
await t
self.dut.clk90.value = 0
await t
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_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)
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 BASE-T MAC loopback test")
if hasattr(dut, "baset_mac_gmii"):
tests.append(cocotb.start_soon(mac_test(tb, tb.baset_phy.rx, tb.baset_phy.tx)))
elif hasattr(dut, "baset_mac_rgmii"):
tests.append(cocotb.start_soon(mac_test(tb, tb.baset_phy.rx, tb.baset_phy.tx)))
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)
await RisingEdge(dut.clk)
# 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("phy_type", ["GMII", "RGMII"])
def test_fpga_core(request, phy_type):
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", "us", "taxi_eth_mac_25g_us.f"),
os.path.join(taxi_src_dir, "eth", "rtl", "taxi_eth_mac_1g_gmii_fifo.f"),
os.path.join(taxi_src_dir, "eth", "rtl", "taxi_eth_mac_1g_rgmii_fifo.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'] = "\"artix7\""
parameters['USE_CLK90'] = "1'b1"
parameters['BASET_PHY_TYPE'] = f"\"{phy_type}\""
parameters['SFP_INVERT'] = "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,
)