mirror of
https://github.com/fpganinja/taxi.git
synced 2025-12-07 16:28:40 -08:00
example/HTG940: Add example design for HTG940
Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
@@ -92,6 +92,7 @@ To facilitate the dual-license model, contributions to the project can only be a
|
||||
Example designs are provided for several different FPGA boards, showcasing many of the capabilities of this library. Building the example designs will require the appropriate vendor toolchain and may also require tool and IP licenses.
|
||||
|
||||
* Digilent Arty A7 (Xilinx Artix 7 XC7A35T)
|
||||
* HiTech Global HTG-940 (Xilinx Virtex UltraScale+ XCVU9P/XCVU13P)
|
||||
* Xilinx KC705 (Xilinx Kintex 7 XC7K325T)
|
||||
* Xilinx KCU105 (Xilinx Kintex UltraScale XCKU040)
|
||||
* Xilinx KR260 (Xilinx Kria K26 SoM / Zynq UltraScale+ XCK26)
|
||||
|
||||
36
example/HTG940/fpga/README.md
Normal file
36
example/HTG940/fpga/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Taxi Example Design for HTG-940
|
||||
|
||||
## Introduction
|
||||
|
||||
This example design targets the HiTech Global HTG-940 FPGA board.
|
||||
|
||||
The design places a looped-back MAC on the BASE-T port, as well as a looped-back UART on on the USB UART connection.
|
||||
|
||||
* USB UART
|
||||
* Looped-back UART
|
||||
* RJ-45 Ethernet ports with TI DP83867IRPAP PHY
|
||||
* Looped-back MAC via RGMII
|
||||
|
||||
## Board details
|
||||
|
||||
* FPGA: xcvu9p-flgb2104-2-e
|
||||
* 1000BASE-T PHY: TI DP83867IRPAP via 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.
|
||||
|
||||
## 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.
|
||||
153
example/HTG940/fpga/common/vivado.mk
Normal file
153
example/HTG940/fpga/common/vivado.mk
Normal 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
|
||||
15
example/HTG940/fpga/eth_rgmii.xdc
Normal file
15
example/HTG940/fpga/eth_rgmii.xdc
Normal 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 DELAY_VALUE 0 [get_cells {phy_rx_ctl_idelay phy_rxd_idelay_bit[*].idelay_inst}]
|
||||
|
||||
# MMCM phase (RGMII)
|
||||
set_property CLKOUT1_PHASE 90 [get_cells clk_mmcm_inst]
|
||||
122
example/HTG940/fpga/fpga.xdc
Normal file
122
example/HTG940/fpga/fpga.xdc
Normal file
@@ -0,0 +1,122 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
# XDC constraints for the HiTech Global HTG-9200 board
|
||||
# part: xcvu9p-flgb2104-2-e
|
||||
# part: xcvu13p-fhgb2104-2-e
|
||||
|
||||
# General configuration
|
||||
set_property CFGBVS GND [current_design]
|
||||
set_property CONFIG_VOLTAGE 1.8 [current_design]
|
||||
set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
|
||||
set_property BITSTREAM.CONFIG.EXTMASTERCCLK_EN {DIV-1} [current_design]
|
||||
set_property BITSTREAM.CONFIG.SPI_32BIT_ADDR YES [current_design]
|
||||
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 8 [current_design]
|
||||
set_property BITSTREAM.CONFIG.SPI_FALL_EDGE YES [current_design]
|
||||
set_property BITSTREAM.CONFIG.OVERTEMPSHUTDOWN Enable [current_design]
|
||||
|
||||
# System clocks
|
||||
# DDR4 clocks from U37 (200 MHz)
|
||||
#set_property -dict {LOC BA34 IOSTANDARD DIFF_SSTL12} [get_ports sys_clk_ddr4_a_p]
|
||||
#set_property -dict {LOC BB34 IOSTANDARD DIFF_SSTL12} [get_ports sys_clk_ddr4_a_n]
|
||||
#create_clock -period 5.000 -name sys_clk_ddr4_a [get_ports sys_clk_ddr4_a_p]
|
||||
|
||||
# refclk from U39 (156.25 MHz)
|
||||
set_property -dict {LOC AW28 IOSTANDARD LVDS} [get_ports ref_clk_p]
|
||||
set_property -dict {LOC AY28 IOSTANDARD LVDS} [get_ports ref_clk_n]
|
||||
create_clock -period 6.400 -name ref_clk [get_ports ref_clk_p]
|
||||
|
||||
# LEDs
|
||||
set_property -dict {LOC AP28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports {led[0]}]
|
||||
set_property -dict {LOC AN28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports {led[1]}]
|
||||
set_property -dict {LOC AP26 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports {led[2]}]
|
||||
set_property -dict {LOC AP25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports {led[3]}]
|
||||
set_property -dict {LOC AR28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports {led[4]}]
|
||||
set_property -dict {LOC AR27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports {led[5]}]
|
||||
set_property -dict {LOC AT28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports {led[6]}]
|
||||
set_property -dict {LOC AR25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports {led[7]}]
|
||||
|
||||
set_false_path -to [get_ports {led[*]}]
|
||||
set_output_delay 0 [get_ports {led[*]}]
|
||||
|
||||
# Push buttons
|
||||
set_property -dict {LOC AJ34 IOSTANDARD LVCMOS12} [get_ports {btn[0]}]
|
||||
set_property -dict {LOC AK32 IOSTANDARD LVCMOS12} [get_ports {btn[1]}]
|
||||
|
||||
set_false_path -from [get_ports {btn[*]}]
|
||||
set_input_delay 0 [get_ports {btn[*]}]
|
||||
|
||||
# DIP switches
|
||||
set_property -dict {LOC BF33 IOSTANDARD LVCMOS12} [get_ports {sw[0]}]
|
||||
set_property -dict {LOC AK27 IOSTANDARD LVCMOS12} [get_ports {sw[1]}]
|
||||
set_property -dict {LOC AR32 IOSTANDARD LVCMOS12} [get_ports {sw[2]}]
|
||||
set_property -dict {LOC AR31 IOSTANDARD LVCMOS12} [get_ports {sw[3]}]
|
||||
set_property -dict {LOC AT32 IOSTANDARD LVCMOS12} [get_ports {sw[4]}]
|
||||
set_property -dict {LOC AW30 IOSTANDARD LVCMOS12} [get_ports {sw[5]}]
|
||||
set_property -dict {LOC BC32 IOSTANDARD LVCMOS12} [get_ports {sw[6]}]
|
||||
set_property -dict {LOC BC33 IOSTANDARD LVCMOS12} [get_ports {sw[7]}]
|
||||
|
||||
set_false_path -from [get_ports {sw[*]}]
|
||||
set_input_delay 0 [get_ports {sw[*]}]
|
||||
|
||||
# UART
|
||||
set_property -dict {LOC R15 IOSTANDARD LVCMOS18} [get_ports uart_txd]
|
||||
set_property -dict {LOC P15 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_rxd]
|
||||
set_property -dict {LOC L15 IOSTANDARD LVCMOS18} [get_ports uart_rts]
|
||||
set_property -dict {LOC D14 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_cts]
|
||||
set_property -dict {LOC P16 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_rst_n]
|
||||
|
||||
set_false_path -to [get_ports {uart_rxd uart_cts uart_rst_n}]
|
||||
set_output_delay 0 [get_ports {uart_rxd uart_cts uart_rst_n}]
|
||||
set_false_path -from [get_ports {uart_txd uart_rts}]
|
||||
set_input_delay 0 [get_ports {uart_txd uart_rts}]
|
||||
|
||||
# I2C
|
||||
set_property -dict {LOC AV28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_main_scl]
|
||||
set_property -dict {LOC AU27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_main_sda]
|
||||
set_property -dict {LOC AV27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_main_rst_n]
|
||||
|
||||
set_false_path -to [get_ports {i2c_main_sda i2c_main_scl i2c_main_rst_n}]
|
||||
set_output_delay 0 [get_ports {i2c_main_sda i2c_main_scl i2c_main_rst_n}]
|
||||
set_false_path -from [get_ports {i2c_main_sda i2c_main_scl}]
|
||||
set_input_delay 0 [get_ports {i2c_main_sda i2c_main_scl}]
|
||||
|
||||
# Gigabit Ethernet RGMII PHY
|
||||
set_property -dict {LOC G20 IOSTANDARD LVCMOS18} [get_ports {phy_rx_clk}] ;# from U2.43 // MAYBE
|
||||
set_property -dict {LOC A20 IOSTANDARD LVCMOS18} [get_ports {phy_rxd[0]}] ;# from U2.44
|
||||
set_property -dict {LOC D21 IOSTANDARD LVCMOS18} [get_ports {phy_rxd[1]}] ;# from U2.45
|
||||
set_property -dict {LOC E21 IOSTANDARD LVCMOS18} [get_ports {phy_rxd[2]}] ;# from U2.46
|
||||
set_property -dict {LOC C21 IOSTANDARD LVCMOS18} [get_ports {phy_rxd[3]}] ;# from U2.47
|
||||
set_property -dict {LOC B21 IOSTANDARD LVCMOS18} [get_ports {phy_rx_ctl}] ;# from U2.53
|
||||
set_property -dict {LOC B20 IOSTANDARD LVCMOS18 SLEW FAST DRIVE 12} [get_ports {phy_tx_clk}] ;# from U2.40
|
||||
set_property -dict {LOC D20 IOSTANDARD LVCMOS18 SLEW FAST DRIVE 12} [get_ports {phy_txd[0]}] ;# from U2.38
|
||||
set_property -dict {LOC A19 IOSTANDARD LVCMOS18 SLEW FAST DRIVE 12} [get_ports {phy_txd[1]}] ;# from U2.37
|
||||
set_property -dict {LOC B19 IOSTANDARD LVCMOS18 SLEW FAST DRIVE 12} [get_ports {phy_txd[2]}] ;# from U2.36
|
||||
set_property -dict {LOC E20 IOSTANDARD LVCMOS18 SLEW FAST DRIVE 12} [get_ports {phy_txd[3]}] ;# from U2.35
|
||||
set_property -dict {LOC G21 IOSTANDARD LVCMOS18 SLEW FAST DRIVE 12} [get_ports {phy_tx_ctl}] ;# from U2.52
|
||||
#set_property -dict {LOC G19 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {phy_mdio}] ;# from U2.21
|
||||
#set_property -dict {LOC A18 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {phy_mdc}] ;# from U2.20
|
||||
|
||||
create_clock -period 8.000 -name {phy_rx_clk} [get_ports {phy_rx_clk}]
|
||||
|
||||
#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}]
|
||||
|
||||
# QSPI flash
|
||||
#set_property -dict {LOC AM26 IOSTANDARD LVCMOS18 DRIVE 8} [get_ports {qspi_1_dq[0]}]
|
||||
#set_property -dict {LOC AN26 IOSTANDARD LVCMOS18 DRIVE 8} [get_ports {qspi_1_dq[1]}]
|
||||
#set_property -dict {LOC AL25 IOSTANDARD LVCMOS18 DRIVE 8} [get_ports {qspi_1_dq[2]}]
|
||||
#set_property -dict {LOC AM25 IOSTANDARD LVCMOS18 DRIVE 8} [get_ports {qspi_1_dq[3]}]
|
||||
#set_property -dict {LOC BF27 IOSTANDARD LVCMOS18 DRIVE 8} [get_ports {qspi_1_cs_n}]
|
||||
|
||||
#set_false_path -to [get_ports {qspi_1_dq[*] qspi_1_cs}]
|
||||
#set_output_delay 0 [get_ports {qspi_1_dq[*] qspi_1_cs}]
|
||||
#set_false_path -from [get_ports {qspi_1_dq}]
|
||||
#set_input_delay 0 [get_ports {qspi_1_dq}]
|
||||
86
example/HTG940/fpga/fpga_vu13p/Makefile
Normal file
86
example/HTG940/fpga/fpga_vu13p/Makefile
Normal file
@@ -0,0 +1,86 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
# FPGA settings
|
||||
FPGA_PART = xcvu13p-fhgb2104-2-e
|
||||
FPGA_TOP = fpga
|
||||
FPGA_ARCH = virtexuplus
|
||||
|
||||
# Files for synthesis
|
||||
SYN_FILES = ../rtl/fpga.sv
|
||||
SYN_FILES += ../rtl/fpga_core.sv
|
||||
SYN_FILES += ../lib/taxi/rtl/eth/taxi_eth_mac_1g_rgmii_fifo.f
|
||||
SYN_FILES += ../lib/taxi/rtl/lss/taxi_uart.f
|
||||
SYN_FILES += ../lib/taxi/rtl/io/taxi_debounce_switch.sv
|
||||
SYN_FILES += ../lib/taxi/rtl/sync/taxi_sync_reset.sv
|
||||
SYN_FILES += ../lib/taxi/rtl/sync/taxi_sync_signal.sv
|
||||
|
||||
# XDC files
|
||||
XDC_FILES = ../fpga.xdc
|
||||
XDC_FILES += ../eth_rgmii.xdc
|
||||
XDC_FILES += ../lib/taxi/syn/vivado/taxi_rgmii_phy_if.tcl
|
||||
XDC_FILES += ../lib/taxi/syn/vivado/taxi_eth_mac_1g_rgmii.tcl
|
||||
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/eth_xcvr_gt.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
|
||||
|
||||
$(PROJECT)_primary.mcs $(PROJECT)_secondary.mcs $(PROJECT)_primary.prm $(PROJECT)_secondary.prm: $(PROJECT).bit
|
||||
echo "write_cfgmem -force -format mcs -size 128 -interface SPIx8 -loadbit {up 0x0000000 $*.bit} -checksum -file $*.mcs" > generate_mcs.tcl
|
||||
echo "exit" >> generate_mcs.tcl
|
||||
vivado -nojournal -nolog -mode batch -source generate_mcs.tcl
|
||||
mkdir -p rev
|
||||
COUNT=100; \
|
||||
while [ -e rev/$*_rev$$COUNT.bit ]; \
|
||||
do COUNT=$$((COUNT+1)); done; \
|
||||
COUNT=$$((COUNT-1)); \
|
||||
for x in _primary.mcs _secondary.mcs _primary.prm _secondary.prm; \
|
||||
do cp $*$$x rev/$*_rev$$COUNT$$x; \
|
||||
echo "Output: rev/$*_rev$$COUNT$$x"; done;
|
||||
|
||||
flash: $(PROJECT)_primary.mcs $(PROJECT)_secondary.mcs $(PROJECT)_primary.prm $(PROJECT)_secondary.prm
|
||||
echo "open_hw_manager" > flash.tcl
|
||||
echo "connect_hw_server" >> flash.tcl
|
||||
echo "open_hw_target" >> flash.tcl
|
||||
echo "current_hw_device [lindex [get_hw_devices] 0]" >> flash.tcl
|
||||
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> flash.tcl
|
||||
echo "create_hw_cfgmem -hw_device [current_hw_device] [lindex [get_cfgmem_parts {s25fl512s-spi-x1_x2_x4_x8}] 0]" >> flash.tcl
|
||||
echo "current_hw_cfgmem -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM [current_hw_device]]" >> flash.tcl
|
||||
echo "set_property PROGRAM.FILES [list \"$(PROJECT)_primary.mcs\" \"$(PROJECT)_secondary.mcs\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.PRM_FILES [list \"$(PROJECT)_primary.prm\" \"$(PROJECT)_secondary.prm\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ERASE 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CFG_PROGRAM 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.VERIFY 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CHECKSUM 0 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ADDRESS_RANGE {use_file} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.UNUSED_PIN_TERMINATION {pull-none} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "create_hw_bitstream -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM_BITFILE [current_hw_device]]" >> flash.tcl
|
||||
echo "program_hw_devices [current_hw_device]" >> flash.tcl
|
||||
echo "refresh_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "program_hw_cfgmem -hw_cfgmem [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "boot_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "exit" >> flash.tcl
|
||||
vivado -nojournal -nolog -mode batch -source flash.tcl
|
||||
86
example/HTG940/fpga/fpga_vu9p/Makefile
Normal file
86
example/HTG940/fpga/fpga_vu9p/Makefile
Normal file
@@ -0,0 +1,86 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
# FPGA settings
|
||||
FPGA_PART = xcvu9p-flgb2104-2-e
|
||||
FPGA_TOP = fpga
|
||||
FPGA_ARCH = virtexuplus
|
||||
|
||||
# Files for synthesis
|
||||
SYN_FILES = ../rtl/fpga.sv
|
||||
SYN_FILES += ../rtl/fpga_core.sv
|
||||
SYN_FILES += ../lib/taxi/rtl/eth/taxi_eth_mac_1g_rgmii_fifo.f
|
||||
SYN_FILES += ../lib/taxi/rtl/lss/taxi_uart.f
|
||||
SYN_FILES += ../lib/taxi/rtl/io/taxi_debounce_switch.sv
|
||||
SYN_FILES += ../lib/taxi/rtl/sync/taxi_sync_reset.sv
|
||||
SYN_FILES += ../lib/taxi/rtl/sync/taxi_sync_signal.sv
|
||||
|
||||
# XDC files
|
||||
XDC_FILES = ../fpga.xdc
|
||||
XDC_FILES += ../eth_rgmii.xdc
|
||||
XDC_FILES += ../lib/taxi/syn/vivado/taxi_rgmii_phy_if.tcl
|
||||
XDC_FILES += ../lib/taxi/syn/vivado/taxi_eth_mac_1g_rgmii.tcl
|
||||
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/eth_xcvr_gt.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
|
||||
|
||||
$(PROJECT)_primary.mcs $(PROJECT)_secondary.mcs $(PROJECT)_primary.prm $(PROJECT)_secondary.prm: $(PROJECT).bit
|
||||
echo "write_cfgmem -force -format mcs -size 128 -interface SPIx8 -loadbit {up 0x0000000 $*.bit} -checksum -file $*.mcs" > generate_mcs.tcl
|
||||
echo "exit" >> generate_mcs.tcl
|
||||
vivado -nojournal -nolog -mode batch -source generate_mcs.tcl
|
||||
mkdir -p rev
|
||||
COUNT=100; \
|
||||
while [ -e rev/$*_rev$$COUNT.bit ]; \
|
||||
do COUNT=$$((COUNT+1)); done; \
|
||||
COUNT=$$((COUNT-1)); \
|
||||
for x in _primary.mcs _secondary.mcs _primary.prm _secondary.prm; \
|
||||
do cp $*$$x rev/$*_rev$$COUNT$$x; \
|
||||
echo "Output: rev/$*_rev$$COUNT$$x"; done;
|
||||
|
||||
flash: $(PROJECT)_primary.mcs $(PROJECT)_secondary.mcs $(PROJECT)_primary.prm $(PROJECT)_secondary.prm
|
||||
echo "open_hw_manager" > flash.tcl
|
||||
echo "connect_hw_server" >> flash.tcl
|
||||
echo "open_hw_target" >> flash.tcl
|
||||
echo "current_hw_device [lindex [get_hw_devices] 0]" >> flash.tcl
|
||||
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> flash.tcl
|
||||
echo "create_hw_cfgmem -hw_device [current_hw_device] [lindex [get_cfgmem_parts {s25fl512s-spi-x1_x2_x4_x8}] 0]" >> flash.tcl
|
||||
echo "current_hw_cfgmem -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM [current_hw_device]]" >> flash.tcl
|
||||
echo "set_property PROGRAM.FILES [list \"$(PROJECT)_primary.mcs\" \"$(PROJECT)_secondary.mcs\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.PRM_FILES [list \"$(PROJECT)_primary.prm\" \"$(PROJECT)_secondary.prm\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ERASE 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CFG_PROGRAM 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.VERIFY 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CHECKSUM 0 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ADDRESS_RANGE {use_file} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.UNUSED_PIN_TERMINATION {pull-none} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "create_hw_bitstream -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM_BITFILE [current_hw_device]]" >> flash.tcl
|
||||
echo "program_hw_devices [current_hw_device]" >> flash.tcl
|
||||
echo "refresh_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "program_hw_cfgmem -hw_cfgmem [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "boot_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "exit" >> flash.tcl
|
||||
vivado -nojournal -nolog -mode batch -source flash.tcl
|
||||
1
example/HTG940/fpga/lib/taxi
Symbolic link
1
example/HTG940/fpga/lib/taxi
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../
|
||||
373
example/HTG940/fpga/rtl/fpga.sv
Normal file
373
example/HTG940/fpga/rtl/fpga.sv
Normal file
@@ -0,0 +1,373 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 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 VENDOR = "XILINX",
|
||||
// device family
|
||||
parameter FAMILY = "zynquplus",
|
||||
// Use 90 degree clock for RGMII transmit
|
||||
parameter logic USE_CLK90 = 1'b0
|
||||
)
|
||||
(
|
||||
/*
|
||||
* Clock: 156.25 MHz LVDS
|
||||
*/
|
||||
input wire logic ref_clk_p,
|
||||
input wire logic ref_clk_n,
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
input wire logic [1:0] btn,
|
||||
input wire logic [7:0] sw,
|
||||
output wire logic [7:0] led,
|
||||
|
||||
/*
|
||||
* I2C for board management
|
||||
*/
|
||||
inout wire logic i2c_main_scl,
|
||||
inout wire logic i2c_main_sda,
|
||||
output wire logic i2c_main_rst_n,
|
||||
|
||||
/*
|
||||
* UART: 115200 bps, 8N1
|
||||
*/
|
||||
output wire logic uart_rxd,
|
||||
input wire logic uart_txd,
|
||||
input wire logic uart_rts,
|
||||
output wire logic uart_cts,
|
||||
output wire logic uart_rst_n,
|
||||
|
||||
/*
|
||||
* Ethernet: 1000BASE-T RGMII
|
||||
*/
|
||||
input wire logic phy_rx_clk,
|
||||
input wire logic [3:0] phy_rxd,
|
||||
input wire logic phy_rx_ctl,
|
||||
output wire logic phy_tx_clk,
|
||||
output wire logic [3:0] phy_txd,
|
||||
output wire logic phy_tx_ctl
|
||||
);
|
||||
|
||||
// Clock and reset
|
||||
|
||||
wire ref_clk_ibufg;
|
||||
|
||||
// Internal 125 MHz clock
|
||||
wire clk_125mhz_mmcm_out;
|
||||
wire clk90_125mhz_mmcm_out;
|
||||
wire clk_125mhz_int;
|
||||
wire clk90_125mhz_int;
|
||||
wire rst_125mhz_int;
|
||||
|
||||
// Internal 312.5 MHz clock
|
||||
wire clk_312mhz_mmcm_out;
|
||||
wire clk_312mhz_int;
|
||||
wire rst_312mhz_int;
|
||||
|
||||
wire mmcm_rst = ~btn[0];
|
||||
wire mmcm_locked;
|
||||
wire mmcm_clkfb;
|
||||
|
||||
IBUFGDS #(
|
||||
.DIFF_TERM("FALSE"),
|
||||
.IBUF_LOW_PWR("FALSE")
|
||||
)
|
||||
ref_clk_ibufg_inst (
|
||||
.O (ref_clk_ibufg),
|
||||
.I (ref_clk_p),
|
||||
.IB (ref_clk_n)
|
||||
);
|
||||
|
||||
// MMCM instance
|
||||
MMCME4_BASE #(
|
||||
// 156.25 MHz input
|
||||
.CLKIN1_PERIOD(6.4),
|
||||
.REF_JITTER1(0.010),
|
||||
// 156.25 MHz input / 1 = 156.25 MHz PFD (range 10 MHz to 500 MHz)
|
||||
.DIVCLK_DIVIDE(1),
|
||||
// 156.25 MHz PFD * 8 = 1250 MHz VCO (range 800 MHz to 1600 MHz)
|
||||
.CLKFBOUT_MULT_F(8),
|
||||
.CLKFBOUT_PHASE(0),
|
||||
// 1250 MHz / 10 = 125 MHz, 0 degrees
|
||||
.CLKOUT0_DIVIDE_F(10),
|
||||
.CLKOUT0_DUTY_CYCLE(0.5),
|
||||
.CLKOUT0_PHASE(0),
|
||||
// 1250 MHz / 10 = 125 MHz, 90 degrees
|
||||
.CLKOUT1_DIVIDE(10),
|
||||
.CLKOUT1_DUTY_CYCLE(0.5),
|
||||
.CLKOUT1_PHASE(90),
|
||||
// 1250 MHz / 4 = 312.5 MHz, 0 degrees
|
||||
.CLKOUT2_DIVIDE(4),
|
||||
.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 (
|
||||
// 156.25 MHz input
|
||||
.CLKIN1(ref_clk_ibufg),
|
||||
// direct clkfb feeback
|
||||
.CLKFBIN(mmcm_clkfb),
|
||||
.CLKFBOUT(mmcm_clkfb),
|
||||
.CLKFBOUTB(),
|
||||
// 125 MHz, 0 degrees
|
||||
.CLKOUT0(clk_125mhz_mmcm_out),
|
||||
.CLKOUT0B(),
|
||||
// 125 MHz, 90 degrees
|
||||
.CLKOUT1(clk90_125mhz_mmcm_out),
|
||||
.CLKOUT1B(),
|
||||
// 312.5 MHz, 0 degrees
|
||||
.CLKOUT2(clk_312mhz_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_125mhz_bufg_inst (
|
||||
.I(clk_125mhz_mmcm_out),
|
||||
.O(clk_125mhz_int)
|
||||
);
|
||||
|
||||
BUFG
|
||||
clk90_125mhz_bufg_inst (
|
||||
.I(clk90_125mhz_mmcm_out),
|
||||
.O(clk90_125mhz_int)
|
||||
);
|
||||
|
||||
BUFG
|
||||
clk_312mhz_bufg_inst (
|
||||
.I(clk_312mhz_mmcm_out),
|
||||
.O(clk_312mhz_int)
|
||||
);
|
||||
|
||||
taxi_sync_reset #(
|
||||
.N(4)
|
||||
)
|
||||
sync_reset_125mhz_inst (
|
||||
.clk(clk_125mhz_int),
|
||||
.rst(~mmcm_locked),
|
||||
.out(rst_125mhz_int)
|
||||
);
|
||||
|
||||
taxi_sync_reset #(
|
||||
.N(4)
|
||||
)
|
||||
sync_reset_312mhz_inst (
|
||||
.clk(clk_312mhz_int),
|
||||
.rst(~mmcm_locked),
|
||||
.out(rst_312mhz_int)
|
||||
);
|
||||
|
||||
// GPIO
|
||||
wire btn_int;
|
||||
wire [7:0] sw_int;
|
||||
|
||||
taxi_debounce_switch #(
|
||||
.WIDTH(9),
|
||||
.N(4),
|
||||
.RATE(125000)
|
||||
)
|
||||
debounce_switch_inst (
|
||||
.clk(clk_125mhz_int),
|
||||
.rst(rst_125mhz_int),
|
||||
.in({btn[1],
|
||||
~sw}),
|
||||
.out({btn_int,
|
||||
sw_int})
|
||||
);
|
||||
|
||||
wire uart_txd_int;
|
||||
wire uart_rts_int;
|
||||
|
||||
taxi_sync_signal #(
|
||||
.WIDTH(2),
|
||||
.N(2)
|
||||
)
|
||||
sync_signal_inst (
|
||||
.clk(clk_125mhz_int),
|
||||
.in({uart_txd, uart_rts}),
|
||||
.out({uart_txd_int, uart_rts_int})
|
||||
);
|
||||
|
||||
wire i2c_scl_i;
|
||||
wire i2c_scl_o;
|
||||
wire i2c_scl_t;
|
||||
wire i2c_sda_i;
|
||||
wire i2c_sda_o;
|
||||
wire i2c_sda_t;
|
||||
|
||||
assign i2c_scl_i = i2c_main_scl;
|
||||
assign i2c_main_scl = i2c_scl_t ? 1'bz : i2c_scl_o;
|
||||
assign i2c_sda_i = i2c_main_sda;
|
||||
assign i2c_main_sda = i2c_sda_t ? 1'bz : i2c_sda_o;
|
||||
assign i2c_main_rst_n = 1'b1;
|
||||
|
||||
// IODELAY elements for RGMII interface to PHY
|
||||
wire [3:0] phy_rxd_int;
|
||||
wire phy_rx_ctl_int;
|
||||
|
||||
IDELAYCTRL #(
|
||||
.SIM_DEVICE("ULTRASCALE")
|
||||
)
|
||||
idelayctrl_inst (
|
||||
.REFCLK(clk_312mhz_int),
|
||||
.RST(rst_312mhz_int),
|
||||
.RDY()
|
||||
);
|
||||
|
||||
for (genvar n = 0; n < 4; n = n + 1) begin : phy_rxd_idelay_bit
|
||||
|
||||
IDELAYE3 #(
|
||||
.DELAY_SRC("IDATAIN"),
|
||||
.CASCADE("NONE"),
|
||||
.DELAY_TYPE("FIXED"),
|
||||
.DELAY_VALUE(0),
|
||||
.REFCLK_FREQUENCY(312.5),
|
||||
.DELAY_FORMAT("TIME"),
|
||||
.UPDATE_MODE("SYNC"),
|
||||
.SIM_DEVICE("ULTRASCALE_PLUS")
|
||||
)
|
||||
idelay_inst (
|
||||
.CASC_IN(1'b0),
|
||||
.CASC_RETURN(1'b0),
|
||||
.CASC_OUT(),
|
||||
.IDATAIN(phy_rxd[n]),
|
||||
.DATAIN(1'b0),
|
||||
.DATAOUT(phy_rxd_int[n]),
|
||||
.CLK(1'b0),
|
||||
.EN_VTC(1'b1),
|
||||
.CE(1'b0),
|
||||
.INC(1'b0),
|
||||
.LOAD(1'b0),
|
||||
.RST(1'b0),
|
||||
.CNTVALUEIN(9'd0),
|
||||
.CNTVALUEOUT()
|
||||
);
|
||||
|
||||
end
|
||||
|
||||
IDELAYE3 #(
|
||||
.DELAY_SRC("IDATAIN"),
|
||||
.CASCADE("NONE"),
|
||||
.DELAY_TYPE("FIXED"),
|
||||
.DELAY_VALUE(0),
|
||||
.REFCLK_FREQUENCY(312.5),
|
||||
.DELAY_FORMAT("TIME"),
|
||||
.UPDATE_MODE("SYNC"),
|
||||
.SIM_DEVICE("ULTRASCALE_PLUS")
|
||||
)
|
||||
phy_rx_ctl_idelay (
|
||||
.CASC_IN(1'b0),
|
||||
.CASC_RETURN(1'b0),
|
||||
.CASC_OUT(),
|
||||
.IDATAIN(phy_rx_ctl),
|
||||
.DATAIN(1'b0),
|
||||
.DATAOUT(phy_rx_ctl_int),
|
||||
.CLK(1'b0),
|
||||
.EN_VTC(1'b1),
|
||||
.CE(1'b0),
|
||||
.INC(1'b0),
|
||||
.LOAD(1'b0),
|
||||
.RST(1'b0),
|
||||
.CNTVALUEIN(9'd0),
|
||||
.CNTVALUEOUT()
|
||||
);
|
||||
|
||||
fpga_core #(
|
||||
.SIM(SIM),
|
||||
.VENDOR(VENDOR),
|
||||
.FAMILY(FAMILY),
|
||||
.USE_CLK90(USE_CLK90)
|
||||
)
|
||||
core_inst (
|
||||
/*
|
||||
* Clock: 125MHz
|
||||
* Synchronous reset
|
||||
*/
|
||||
.clk(clk_125mhz_int),
|
||||
.clk90(clk90_125mhz_int),
|
||||
.rst(rst_125mhz_int),
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
.btn(btn_int),
|
||||
.sw(sw_int),
|
||||
.led(led),
|
||||
|
||||
/*
|
||||
* UART: 115200 bps, 8N1
|
||||
*/
|
||||
.uart_rxd(uart_rxd),
|
||||
.uart_txd(uart_txd_int),
|
||||
.uart_rts(uart_rts_int),
|
||||
.uart_cts(uart_cts),
|
||||
.uart_rst_n(uart_rst_n),
|
||||
|
||||
/*
|
||||
* Ethernet: 1000BASE-T RGMII
|
||||
*/
|
||||
.phy_rgmii_rx_clk(phy_rx_clk),
|
||||
.phy_rgmii_rxd(phy_rxd),
|
||||
.phy_rgmii_rx_ctl(phy_rx_ctl),
|
||||
.phy_rgmii_tx_clk(phy_tx_clk),
|
||||
.phy_rgmii_txd(phy_txd),
|
||||
.phy_rgmii_tx_ctl(phy_tx_ctl)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
176
example/HTG940/fpga/rtl/fpga_core.sv
Normal file
176
example/HTG940/fpga/rtl/fpga_core.sv
Normal file
@@ -0,0 +1,176 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 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 VENDOR = "XILINX",
|
||||
// device family
|
||||
parameter FAMILY = "zynquplus",
|
||||
// Use 90 degree clock for RGMII transmit
|
||||
parameter logic USE_CLK90 = 1'b1
|
||||
)
|
||||
(
|
||||
/*
|
||||
* Clock: 125MHz
|
||||
* Synchronous reset
|
||||
*/
|
||||
input wire logic clk,
|
||||
input wire logic clk90,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
input wire logic btn,
|
||||
input wire logic [7:0] sw,
|
||||
output wire logic [7:0] led,
|
||||
|
||||
/*
|
||||
* UART: 115200 bps, 8N1
|
||||
*/
|
||||
output wire logic uart_rxd,
|
||||
input wire logic uart_txd,
|
||||
input wire logic uart_rts,
|
||||
output wire logic uart_cts,
|
||||
output wire logic uart_rst_n,
|
||||
|
||||
/*
|
||||
* Ethernet: 1000BASE-T RGMII
|
||||
*/
|
||||
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
|
||||
);
|
||||
|
||||
assign led = sw;
|
||||
|
||||
// UART
|
||||
assign uart_cts = 1'b1;
|
||||
assign uart_rst_n = 1'b1;
|
||||
|
||||
taxi_axis_if #(.DATA_W(8)) axis_uart();
|
||||
|
||||
taxi_uart
|
||||
uut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4-Stream input (sink)
|
||||
*/
|
||||
.s_axis_tx(axis_uart),
|
||||
|
||||
/*
|
||||
* AXI4-Stream output (source)
|
||||
*/
|
||||
.m_axis_rx(axis_uart),
|
||||
|
||||
/*
|
||||
* UART interface
|
||||
*/
|
||||
.rxd(uart_txd),
|
||||
.txd(uart_rxd),
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
.tx_busy(),
|
||||
.rx_busy(),
|
||||
.rx_overrun_error(),
|
||||
.rx_frame_error(),
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
.prescale(16'(125000000/115200/8))
|
||||
);
|
||||
|
||||
// BASE-T PHY
|
||||
taxi_axis_if #(.DATA_W(8), .ID_W(8)) axis_eth();
|
||||
taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_tx_cpl();
|
||||
|
||||
taxi_eth_mac_1g_rgmii_fifo #(
|
||||
.SIM(SIM),
|
||||
.VENDOR(VENDOR),
|
||||
.FAMILY(FAMILY),
|
||||
.USE_CLK90(USE_CLK90),
|
||||
.PADDING_EN(1),
|
||||
.MIN_FRAME_LEN(64),
|
||||
.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),
|
||||
|
||||
/*
|
||||
* 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_ifg(8'd12),
|
||||
.cfg_tx_enable(1'b1),
|
||||
.cfg_rx_enable(1'b1)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
52
example/HTG940/fpga/tb/fpga_core/Makefile
Normal file
52
example/HTG940/fpga/tb/fpga_core/Makefile
Normal file
@@ -0,0 +1,52 @@
|
||||
# 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_rgmii_fifo.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
|
||||
|
||||
# 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 := "\"virtexuplus\""
|
||||
export PARAM_USE_CLK90 := "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
|
||||
206
example/HTG940/fpga/tb/fpga_core/test_fpga_core.py
Normal file
206
example/HTG940/fpga/tb/fpga_core/test_fpga_core.py
Normal file
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env python
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""
|
||||
|
||||
Copyright (c) 2020-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
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 GmiiFrame, RgmiiPhy
|
||||
from cocotbext.uart import UartSource, UartSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut, speed=1000e6):
|
||||
self.dut = dut
|
||||
|
||||
self.log = SimLog("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
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.uart_source = UartSource(dut.uart_txd, baud=115200, bits=8, stop_bits=1)
|
||||
self.uart_sink = UartSink(dut.uart_rxd, baud=115200, bits=8, stop_bits=1)
|
||||
|
||||
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 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")
|
||||
|
||||
|
||||
@cocotb.test()
|
||||
async def run_test(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.init()
|
||||
|
||||
tb.log.info("Start UART test")
|
||||
|
||||
uart_test_cr = cocotb.start_soon(uart_test(tb, tb.uart_source, tb.uart_sink))
|
||||
|
||||
tb.log.info("Start BASE-T MAC loopback test")
|
||||
|
||||
phy_test_cr = cocotb.start_soon(mac_test(tb, tb.baset_phy.rx, tb.baset_phy.tx))
|
||||
|
||||
await Combine(uart_test_cr, phy_test_cr)
|
||||
|
||||
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(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())
|
||||
|
||||
|
||||
def test_fpga_core(request):
|
||||
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_rgmii_fifo.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"),
|
||||
]
|
||||
|
||||
verilog_sources = process_f_files(verilog_sources)
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['SIM'] = "1'b1"
|
||||
parameters['VENDOR'] = "\"XILINX\""
|
||||
parameters['FAMILY'] = "\"virtexuplus\""
|
||||
parameters['USE_CLK90'] = "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,
|
||||
)
|
||||
Reference in New Issue
Block a user