diff --git a/README.md b/README.md index b7f6e64aa..5980438a0 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ following boards: * BittWare 520N-MX (Intel Stratix 10 MX 1SM21CHU2F53E2VG) * Digilent Arty A7 (Xilinx Artix 7 XC7A35T) * Digilent Atlys (Xilinx Spartan 6 XC6SLX45) +* Digilent Genesys2 (Xilinx Kintex 7 XC7K325T) * Intel Cyclone 10 LP (Intel Cyclone 10 10CL025YU256I7G) * Terasic DE2-115 (Intel Cyclone IV E EP4CE115F29C7) * Terasic DE5-Net (Intel Stratix V 5SGXEA7N2F45C2) diff --git a/example/Genesys2/fpga_rgmii/Makefile b/example/Genesys2/fpga_rgmii/Makefile new file mode 100644 index 000000000..4c3c16e0b --- /dev/null +++ b/example/Genesys2/fpga_rgmii/Makefile @@ -0,0 +1,25 @@ +# Targets +TARGETS:= + +# Subdirectories +SUBDIRS = fpga +SUBDIRS_CLEAN = $(patsubst %,%.clean,$(SUBDIRS)) + +# Rules +.PHONY: all +all: $(SUBDIRS) $(TARGETS) + +.PHONY: $(SUBDIRS) +$(SUBDIRS): + cd $@ && $(MAKE) + +.PHONY: $(SUBDIRS_CLEAN) +$(SUBDIRS_CLEAN): + cd $(@:.clean=) && $(MAKE) clean + +.PHONY: clean +clean: $(SUBDIRS_CLEAN) + -rm -rf $(TARGETS) + +program: + #djtgcfg prog -d Genesys2 --index 0 --file fpga/fpga.bit diff --git a/example/Genesys2/fpga_rgmii/README.md b/example/Genesys2/fpga_rgmii/README.md new file mode 100644 index 000000000..e70fda81d --- /dev/null +++ b/example/Genesys2/fpga_rgmii/README.md @@ -0,0 +1,30 @@ +# Verilog Ethernet Genesys2 Example Design + +## Introduction + +This example design targets the Digilent Genesys2 FPGA board. + +The design by default listens to UDP port 1234 at IP address 192.168.1.128 and +will echo back any packets received. The design will also respond correctly +to ARP requests. + +* FPGA: XC7K325T-2FFG900C +* PHY: Realtek RTL8211E-VL + +## How to build + +Run make to build. Ensure that the Xilinx Vivado toolchain components are +in PATH. + +## How to test + +Run make program to program the Genesys2 board with Vivado. Then run + + netcat -u 192.168.1.128 1234 + +to open a UDP connection to port 1234. Any text entered into netcat will be +echoed back after pressing enter. + +It is also possible to use hping to test the design by running + + hping 192.168.1.128 -2 -p 1234 -d 1024 diff --git a/example/Genesys2/fpga_rgmii/common/vivado.mk b/example/Genesys2/fpga_rgmii/common/vivado.mk new file mode 100644 index 000000000..83bcb8d90 --- /dev/null +++ b/example/Genesys2/fpga_rgmii/common/vivado.mk @@ -0,0 +1,123 @@ +################################################################### +# +# Xilinx Vivado FPGA Makefile +# +# Copyright (c) 2016 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 - space-separated list of source files +# INC_FILES - space-separated list of include files +# XDC_FILES - space-separated list of timing constraint files +# XCI_FILES - space-separated list of IP XCI files +# +# 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: clean fpga + +# prevent make from deleting intermediate files and reports +.PRECIOUS: %.xpr %.bit %.mcs %.prm +.SECONDARY: + +CONFIG ?= config.mk +-include ../$(CONFIG) + +SYN_FILES_REL = $(patsubst %, ../%, $(SYN_FILES)) +INC_FILES_REL = $(patsubst %, ../%, $(INC_FILES)) +XCI_FILES_REL = $(patsubst %, ../%, $(XCI_FILES)) +IP_TCL_FILES_REL = $(patsubst %, ../%, $(IP_TCL_FILES)) + +ifdef XDC_FILES + XDC_FILES_REL = $(patsubst %, ../%, $(XDC_FILES)) +else + XDC_FILES_REL = $(FPGA_TOP).xdc +endif + +################################################################### +# Main Targets +# +# all: build everything +# clean: remove output files and project files +################################################################### + +all: fpga + +fpga: $(FPGA_TOP).bit + +vivado: $(FPGA_TOP).xpr + vivado $(FPGA_TOP).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 run_synth.tcl run_impl.tcl generate_bit.tcl + +clean: tmpclean + -rm -rf *.bit program.tcl generate_mcs.tcl *.mcs *.prm flash.tcl + +distclean: clean + -rm -rf rev + +################################################################### +# Target implementations +################################################################### + +# Vivado project file +%.xpr: Makefile $(XCI_FILES_REL) $(IP_TCL_FILES_REL) + 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) $*" > create_project.tcl + echo "add_files -fileset sources_1 defines.v" >> create_project.tcl + for x in $(SYN_FILES_REL); do echo "add_files -fileset sources_1 $$x" >> create_project.tcl; done + for x in $(XDC_FILES_REL); do echo "add_files -fileset constrs_1 $$x" >> create_project.tcl; done + for x in $(XCI_FILES_REL); do echo "import_ip $$x" >> create_project.tcl; done + for x in $(IP_TCL_FILES_REL); do echo "source $$x" >> create_project.tcl; done + echo "exit" >> create_project.tcl + vivado -nojournal -nolog -mode batch -source create_project.tcl + +# synthesis run +%.runs/synth_1/%.dcp: %.xpr $(SYN_FILES_REL) $(INC_FILES_REL) $(XDC_FILES_REL) + echo "open_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 + echo "exit" >> run_synth.tcl + vivado -nojournal -nolog -mode batch -source run_synth.tcl + +# implementation run +%.runs/impl_1/%_routed.dcp: %.runs/synth_1/%.dcp + echo "open_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 "exit" >> run_impl.tcl + vivado -nojournal -nolog -mode batch -source run_impl.tcl + +# bit file +%.bit: %.runs/impl_1/%_routed.dcp + echo "open_project $*.xpr" > generate_bit.tcl + echo "open_run impl_1" >> generate_bit.tcl + echo "write_bitstream -force $*.bit" >> generate_bit.tcl + echo "exit" >> generate_bit.tcl + vivado -nojournal -nolog -mode batch -source generate_bit.tcl + mkdir -p rev + EXT=bit; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do COUNT=$$((COUNT+1)); done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; diff --git a/example/Genesys2/fpga_rgmii/eth.xdc b/example/Genesys2/fpga_rgmii/eth.xdc new file mode 100644 index 000000000..d9780aa1c --- /dev/null +++ b/example/Genesys2/fpga_rgmii/eth.xdc @@ -0,0 +1,5 @@ +# Ethernet constraints + +# IDELAY on RGMII from PHY chip +set_property IDELAY_VALUE 0 [get_cells {phy_rx_ctl_idelay phy_rxd_idelay_*}] + diff --git a/example/Genesys2/fpga_rgmii/fpga.xdc b/example/Genesys2/fpga_rgmii/fpga.xdc new file mode 100644 index 000000000..d9f7cad3f --- /dev/null +++ b/example/Genesys2/fpga_rgmii/fpga.xdc @@ -0,0 +1,90 @@ +# XDC constraints for the Digilent Genesys 2 Rev. H board +# part: xc7k325tffg900c +# Adapted for Digilent Genesys2 board by Torsten Reuschel 2021 + +# General configuration +set_property CFGBVS VCCO [current_design] +set_property CONFIG_VOLTAGE 3.3 [current_design] +set_property BITSTREAM.GENERAL.COMPRESS true [current_design] + +# System clocks +# 200 MHz +set_property -dict {PACKAGE_PIN AD11 IOSTANDARD LVDS} [get_ports { clk_200mhz_n }]; #IO_L12N_T1_MRCC_33 Sch=sysclk_n +set_property -dict {PACKAGE_PIN AD12 IOSTANDARD LVDS} [get_ports { clk_200mhz_p }]; #IO_L12P_T1_MRCC_33 Sch=sysclk_p +create_clock -period 5.000 -name clk_200mhz [get_ports clk_200mhz_p] + +# LEDs +set_property -dict {PACKAGE_PIN T28 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports { led[0] }]; #IO_L11N_T1_SRCC_14 Sch=led[0] +set_property -dict {PACKAGE_PIN V19 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports { led[1] }]; #IO_L19P_T3_A10_D26_14 Sch=led[1] +set_property -dict {PACKAGE_PIN U30 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports { led[2] }]; #IO_L15N_T2_DQS_DOUT_CSO_B_14 Sch=led[2] +set_property -dict {PACKAGE_PIN U29 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports { led[3] }]; #IO_L15P_T2_DQS_RDWR_B_14 Sch=led[3] +set_property -dict {PACKAGE_PIN V20 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports { led[4] }]; #IO_L19N_T3_A09_D25_VREF_14 Sch=led[4] +set_property -dict {PACKAGE_PIN V26 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports { led[5] }]; #IO_L16P_T2_CSI_B_14 Sch=led[5] +set_property -dict {PACKAGE_PIN W24 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports { led[6] }]; #IO_L20N_T3_A07_D23_14 Sch=led[6] +set_property -dict {PACKAGE_PIN W23 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports { led[7] }]; #IO_L20P_T3_A08_D24_14 Sch=led[7] + +set_false_path -to [get_ports { led[*] }] +set_output_delay 0 [get_ports { led[*] }] + +# Reset button +set_property -dict {PACKAGE_PIN R19 IOSTANDARD LVCMOS33} [get_ports { reset_n }]; #IO_0_14 Sch=cpu_resetn + +set_false_path -from [get_ports { reset_n }] +set_input_delay 0 [get_ports { reset_n }] + +# Push buttons +set_property -dict {PACKAGE_PIN E18 IOSTANDARD LVCMOS12} [get_ports { btnc }]; #IO_25_17 Sch=btnc +set_property -dict {PACKAGE_PIN M19 IOSTANDARD LVCMOS12} [get_ports { btnd }]; #IO_0_15 Sch=btnd +set_property -dict {PACKAGE_PIN M20 IOSTANDARD LVCMOS12} [get_ports { btnl }]; #IO_L6P_T0_15 Sch=btnl +set_property -dict {PACKAGE_PIN C19 IOSTANDARD LVCMOS12} [get_ports { btnr }]; #IO_L24P_T3_17 Sch=btnr +set_property -dict {PACKAGE_PIN B19 IOSTANDARD LVCMOS12} [get_ports { btnu }]; #IO_L24N_T3_17 Sch=btnu + +set_false_path -from [get_ports { btnu btnl btnd btnr btnc }] +set_input_delay 0 [get_ports { btnu btnl btnd btnr btnc }] + +# Toggle switches +set_property -dict {PACKAGE_PIN G19 IOSTANDARD LVCMOS12} [get_ports { sw[0] }]; #IO_0_17 Sch=sw[0] +set_property -dict {PACKAGE_PIN G25 IOSTANDARD LVCMOS12} [get_ports { sw[1] }]; #IO_25_16 Sch=sw[1] +set_property -dict {PACKAGE_PIN H24 IOSTANDARD LVCMOS12} [get_ports { sw[2] }]; #IO_L19P_T3_16 Sch=sw[2] +set_property -dict {PACKAGE_PIN K19 IOSTANDARD LVCMOS12} [get_ports { sw[3] }]; #IO_L6P_T0_17 Sch=sw[3] + +set_false_path -from [get_ports { sw[*] }] +set_input_delay 0 [get_ports { sw[*] }] + +# UART +set_property -dict {PACKAGE_PIN Y20 IOSTANDARD LVCMOS33 } [get_ports { uart_rxd }]; #IO_0_12 Sch=uart_tx_in +set_property -dict {PACKAGE_PIN Y23 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports { uart_txd }]; #IO_L1P_T0_12 Sch=uart_rx_out +#set_property -dict { PACKAGE_PIN Y20 IOSTANDARD LVCMOS33 } [get_ports { uart_txd }]; #IO_0_12 Sch=uart_tx_in +#set_property -dict { PACKAGE_PIN Y23 IOSTANDARD LVCMOS33 } [get_ports { uart_rxd }]; #IO_L1P_T0_12 Sch=uart_rx_out + +set_false_path -to [get_ports {uart_txd}] +set_output_delay 0 [get_ports {uart_txd}] +set_false_path -from [get_ports {uart_rxd}] +set_input_delay 0 [get_ports {uart_rxd}] + +# Gigabit Ethernet GMII PHY +set_property -dict {PACKAGE_PIN AG10 IOSTANDARD LVCMOS15 } [get_ports { phy_rx_clk }]; #IO_L13P_T2_MRCC_33 Sch=eth_rx_clk +set_property -dict {PACKAGE_PIN AJ14 IOSTANDARD LVCMOS15 } [get_ports { phy_rxd[0] }]; #IO_L21N_T3_DQS_33 Sch=eth_rx_d[0] +set_property -dict {PACKAGE_PIN AH14 IOSTANDARD LVCMOS15 } [get_ports { phy_rxd[1] }]; #IO_L21P_T3_DQS_33 Sch=eth_rx_d[1] +set_property -dict {PACKAGE_PIN AK13 IOSTANDARD LVCMOS15 } [get_ports { phy_rxd[2] }]; #IO_L20N_T3_33 Sch=eth_rx_d[2] +set_property -dict {PACKAGE_PIN AJ13 IOSTANDARD LVCMOS15 } [get_ports { phy_rxd[3] }]; #IO_L22P_T3_33 Sch=eth_rx_d[3] +set_property -dict {PACKAGE_PIN AH11 IOSTANDARD LVCMOS15 } [get_ports { phy_rx_ctl }]; #IO_L18P_T2_33 Sch=eth_rx_ctl +set_property -dict {PACKAGE_PIN AE10 IOSTANDARD LVCMOS15 SLEW FAST DRIVE 16} [get_ports { phy_tx_clk }]; #IO_L14P_T2_SRCC_33 Sch=eth_tx_clk +set_property -dict {PACKAGE_PIN AJ12 IOSTANDARD LVCMOS15 SLEW FAST DRIVE 16} [get_ports { phy_txd[0] }]; #IO_L22N_T3_33 Sch=eth_tx_d[0] +set_property -dict {PACKAGE_PIN AK11 IOSTANDARD LVCMOS15 SLEW FAST DRIVE 16} [get_ports { phy_txd[1] }]; #IO_L17P_T2_33 Sch=eth_tx_d[1] +set_property -dict {PACKAGE_PIN AJ11 IOSTANDARD LVCMOS15 SLEW FAST DRIVE 16} [get_ports { phy_txd[2] }]; #IO_L18N_T2_33 Sch=eth_tx_d[2] +set_property -dict {PACKAGE_PIN AK10 IOSTANDARD LVCMOS15 SLEW FAST DRIVE 16} [get_ports { phy_txd[3] }]; #IO_L17N_T2_33 Sch=eth_tx_d[3] +set_property -dict {PACKAGE_PIN AK14 IOSTANDARD LVCMOS15 SLEW FAST DRIVE 16} [get_ports { phy_tx_ctl }]; #IO_L20P_T3_33 Sch=eth_tx_en +set_property -dict {PACKAGE_PIN AH24 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports { phy_reset_n }]; #IO_L14N_T2_SRCC_12 Sch=eth_phyrst_n +set_property -dict {PACKAGE_PIN AK16 IOSTANDARD LVCMOS18 } [get_ports { phy_int_n }]; #IO_L1P_T0_32 Sch=eth_intb +#set_property -dict {PACKAGE_PIN AK15 IOSTANDARD LVCMOS18} [get_ports phy_pme_n] +#set_property -dict {PACKAGE_PIN AG12 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports phy_mdio] +#set_property -dict {PACKAGE_PIN AF12 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports phy_mdc] + +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 }] + diff --git a/example/Genesys2/fpga_rgmii/fpga/Makefile b/example/Genesys2/fpga_rgmii/fpga/Makefile new file mode 100644 index 000000000..17ba63b2f --- /dev/null +++ b/example/Genesys2/fpga_rgmii/fpga/Makefile @@ -0,0 +1,67 @@ + +# FPGA settings +FPGA_PART = xc7k325tffg900-2 +FPGA_TOP = fpga +FPGA_ARCH = kintex7 + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += lib/eth/rtl/iddr.v +SYN_FILES += lib/eth/rtl/oddr.v +SYN_FILES += lib/eth/rtl/ssio_ddr_in.v +SYN_FILES += lib/eth/rtl/rgmii_phy_if.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_rgmii_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_rgmii.v +SYN_FILES += lib/eth/rtl/eth_mac_1g.v +SYN_FILES += lib/eth/rtl/axis_gmii_rx.v +SYN_FILES += lib/eth/rtl/axis_gmii_tx.v +SYN_FILES += lib/eth/rtl/lfsr.v +SYN_FILES += lib/eth/rtl/eth_axis_rx.v +SYN_FILES += lib/eth/rtl/eth_axis_tx.v +SYN_FILES += lib/eth/rtl/udp_complete.v +SYN_FILES += lib/eth/rtl/udp_checksum_gen.v +SYN_FILES += lib/eth/rtl/udp.v +SYN_FILES += lib/eth/rtl/udp_ip_rx.v +SYN_FILES += lib/eth/rtl/udp_ip_tx.v +SYN_FILES += lib/eth/rtl/ip_complete.v +SYN_FILES += lib/eth/rtl/ip.v +SYN_FILES += lib/eth/rtl/ip_eth_rx.v +SYN_FILES += lib/eth/rtl/ip_eth_tx.v +SYN_FILES += lib/eth/rtl/ip_arb_mux.v +SYN_FILES += lib/eth/rtl/arp.v +SYN_FILES += lib/eth/rtl/arp_cache.v +SYN_FILES += lib/eth/rtl/arp_eth_rx.v +SYN_FILES += lib/eth/rtl/arp_eth_tx.v +SYN_FILES += lib/eth/rtl/eth_arb_mux.v +SYN_FILES += lib/eth/lib/axis/rtl/arbiter.v +SYN_FILES += lib/eth/lib/axis/rtl/priority_encoder.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_fifo.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_async_fifo.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_async_fifo_adapter.v +SYN_FILES += lib/eth/lib/axis/rtl/sync_reset.v + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += eth.xdc +XDC_FILES += lib/eth/syn/vivado/rgmii_phy_if.tcl +XDC_FILES += lib/eth/syn/vivado/eth_mac_1g_rgmii.tcl +XDC_FILES += lib/eth/syn/vivado/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/vivado/axis_async_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/vivado/sync_reset.tcl + +include ../common/vivado.mk + +program: $(FPGA_TOP).bit + echo "open_hw" > program.tcl + echo "connect_hw_server" >> program.tcl + echo "open_hw_target" >> program.tcl + echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl + echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl + echo "set_property PROGRAM.FILE {$(FPGA_TOP).bit} [current_hw_device]" >> program.tcl + echo "program_hw_devices [current_hw_device]" >> program.tcl + echo "exit" >> program.tcl + vivado -nojournal -nolog -mode batch -source program.tcl + diff --git a/example/Genesys2/fpga_rgmii/fpga/generate_bit_iodelay.tcl b/example/Genesys2/fpga_rgmii/fpga/generate_bit_iodelay.tcl new file mode 100644 index 000000000..d97f96678 --- /dev/null +++ b/example/Genesys2/fpga_rgmii/fpga/generate_bit_iodelay.tcl @@ -0,0 +1,6 @@ +open_project fpga.xpr +open_run impl_1 +set_property IDELAY_VALUE 0 [get_cells {phy_rx_ctl_idelay phy_rxd_idelay_*}] +set_property CLKOUT1_PHASE 90 [get_cells clk_mmcm_inst] +write_bitstream -force fpga.bit +exit diff --git a/example/Genesys2/fpga_rgmii/lib/eth b/example/Genesys2/fpga_rgmii/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/example/Genesys2/fpga_rgmii/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/example/Genesys2/fpga_rgmii/rtl/debounce_switch.v b/example/Genesys2/fpga_rgmii/rtl/debounce_switch.v new file mode 100644 index 000000000..956f7a4d1 --- /dev/null +++ b/example/Genesys2/fpga_rgmii/rtl/debounce_switch.v @@ -0,0 +1,89 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog-2001 +`default_nettype none //do not allow undeclared wires +`timescale 1 ns / 1 ps + +/* + * Synchronizes switch and button inputs with a slow sampled shift register + */ +module debounce_switch #( + parameter WIDTH=1, // width of the input and output signals + parameter N=3, // length of shift register + parameter RATE=125000 // clock division factor +)( + input wire clk, + input wire rst, + input wire [WIDTH-1:0] in, + output wire [WIDTH-1:0] out +); + +reg [23:0] cnt_reg = 24'd0; + +reg [N-1:0] debounce_reg[WIDTH-1:0]; + +reg [WIDTH-1:0] state; + +/* + * The synchronized output is the state register + */ +assign out = state; + +integer k; + +always @(posedge clk or posedge rst) begin + if (rst) begin + cnt_reg <= 0; + state <= 0; + + for (k = 0; k < WIDTH; k = k + 1) begin + debounce_reg[k] <= 0; + end + end else begin + if (cnt_reg < RATE) begin + cnt_reg <= cnt_reg + 24'd1; + end else begin + cnt_reg <= 24'd0; + end + + if (cnt_reg == 24'd0) begin + for (k = 0; k < WIDTH; k = k + 1) begin + debounce_reg[k] <= {debounce_reg[k][N-2:0], in[k]}; + end + end + + for (k = 0; k < WIDTH; k = k + 1) begin + if (|debounce_reg[k] == 0) begin + state[k] <= 0; + end else if (&debounce_reg[k] == 1) begin + state[k] <= 1; + end else begin + state[k] <= state[k]; + end + end + end +end + +endmodule diff --git a/example/Genesys2/fpga_rgmii/rtl/fpga.v b/example/Genesys2/fpga_rgmii/rtl/fpga.v new file mode 100644 index 000000000..a4bbb491c --- /dev/null +++ b/example/Genesys2/fpga_rgmii/rtl/fpga.v @@ -0,0 +1,366 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich +Adaptation to Digilent Genesys2 by Torsten Reuschel 2021 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 200MHz + * Reset: Push button, active low + */ + input wire clk_200mhz_p, + input wire clk_200mhz_n, + input wire reset_n, + + /* + * GPIO + */ + input wire btnu, + input wire btnl, + input wire btnd, + input wire btnr, + input wire btnc, + input wire [3:0] sw, + output wire [7:0] led, + + /* + * Ethernet: 1000BASE-T RGMII + */ + input wire phy_rx_clk, + input wire [3:0] phy_rxd, + input wire phy_rx_ctl, + output wire phy_tx_clk, + output wire [3:0] phy_txd, + output wire phy_tx_ctl, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 500000 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd +); + +// 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_n; +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 +// 200 MHz in, 125 MHz out +// PFD range: 10 MHz to 500 MHz +// VCO range: 600 MHz to 1440 MHz +// M = 5, D = 1 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +// Need two 125 MHz outputs with 90 degree offset +// Also need 200 MHz out for IODELAY +// 1000 / 5 = 200 MHz +MMCME2_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(8), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(8), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(90), + .CLKOUT2_DIVIDE(5), + .CLKOUT2_DUTY_CYCLE(0.5), + .CLKOUT2_PHASE(0), + .CLKOUT3_DIVIDE(1), + .CLKOUT3_DUTY_CYCLE(0.5), + .CLKOUT3_PHASE(0), + .CLKOUT4_DIVIDE(1), + .CLKOUT4_DUTY_CYCLE(0.5), + .CLKOUT4_PHASE(0), + .CLKOUT5_DIVIDE(1), + .CLKOUT5_DUTY_CYCLE(0.5), + .CLKOUT5_PHASE(0), + .CLKOUT6_DIVIDE(1), + .CLKOUT6_DUTY_CYCLE(0.5), + .CLKOUT6_PHASE(0), + .CLKFBOUT_MULT_F(5), + .CLKFBOUT_PHASE(0), + .DIVCLK_DIVIDE(1), + .REF_JITTER1(0.010), + .CLKIN1_PERIOD(5.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_200mhz_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(clk90_mmcm_out), + .CLKOUT1B(), + .CLKOUT2(clk_200mhz_mmcm_out), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .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) +); + +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; + +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; +sync_signal #( + .WIDTH(2), + .N(2) +) +sync_signal_inst ( + .clk(clk_int), + .in({uart_rxd}), + .out({uart_rxd_int}) +); + +// IODELAY elements for RGMII interface to PHY +wire [3:0] phy_rxd_delay; +wire phy_rx_ctl_delay; + +IDELAYCTRL +idelayctrl_inst ( + .REFCLK(clk_200mhz_int), + .RST(rst_int), + .RDY() +); + +IDELAYE2 #( + .IDELAY_TYPE("FIXED") +) +phy_rxd_idelay_0 ( + .IDATAIN(phy_rxd[0]), + .DATAOUT(phy_rxd_delay[0]), + .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) +); + +IDELAYE2 #( + .IDELAY_TYPE("FIXED") +) +phy_rxd_idelay_1 ( + .IDATAIN(phy_rxd[1]), + .DATAOUT(phy_rxd_delay[1]), + .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) +); + +IDELAYE2 #( + .IDELAY_TYPE("FIXED") +) +phy_rxd_idelay_2 ( + .IDATAIN(phy_rxd[2]), + .DATAOUT(phy_rxd_delay[2]), + .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) +); + +IDELAYE2 #( + .IDELAY_TYPE("FIXED") +) +phy_rxd_idelay_3 ( + .IDATAIN(phy_rxd[3]), + .DATAOUT(phy_rxd_delay[3]), + .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) +); + +IDELAYE2 #( + .IDELAY_TYPE("FIXED") +) +phy_rx_ctl_idelay ( + .IDATAIN(phy_rx_ctl), + .DATAOUT(phy_rx_ctl_delay), + .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) +); + +fpga_core #( + .TARGET("XILINX") +) +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), + /* + * Ethernet: 1000BASE-T RGMII + */ + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd_delay), + .phy_rx_ctl(phy_rx_ctl_delay), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_ctl(phy_tx_ctl), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd) +); + +endmodule diff --git a/example/Genesys2/fpga_rgmii/rtl/fpga_core.v b/example/Genesys2/fpga_rgmii/rtl/fpga_core.v new file mode 100644 index 000000000..0be6e7a01 --- /dev/null +++ b/example/Genesys2/fpga_rgmii/rtl/fpga_core.v @@ -0,0 +1,582 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich +Adaptation to Digilent Genesys2 by Torsten Reuschel 2021 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "GENERIC" +) +( + /* + * Clock: 125MHz + * Synchronous reset + */ + input wire clk, + input wire clk90, + input wire rst, + + /* + * GPIO + */ + input wire btnu, + input wire btnl, + input wire btnd, + input wire btnr, + input wire btnc, + input wire [7:0] sw, + output wire [7:0] led, + + /* + * Ethernet: 1000BASE-T RGMII + */ + input wire phy_rx_clk, + input wire [3:0] phy_rxd, + input wire phy_rx_ctl, + output wire phy_tx_clk, + output wire [3:0] phy_txd, + output wire phy_tx_ctl, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 115200 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd +); + +// AXI between MAC and Ethernet modules +wire [7:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tready; +wire rx_axis_tlast; +wire rx_axis_tuser; + +wire [7:0] tx_axis_tdata; +wire tx_axis_tvalid; +wire tx_axis_tready; +wire tx_axis_tlast; +wire tx_axis_tuser; + +// Ethernet frame between Ethernet modules and UDP stack +wire rx_eth_hdr_ready; +wire rx_eth_hdr_valid; +wire [47:0] rx_eth_dest_mac; +wire [47:0] rx_eth_src_mac; +wire [15:0] rx_eth_type; +wire [7:0] rx_eth_payload_axis_tdata; +wire rx_eth_payload_axis_tvalid; +wire rx_eth_payload_axis_tready; +wire rx_eth_payload_axis_tlast; +wire rx_eth_payload_axis_tuser; + +wire tx_eth_hdr_ready; +wire tx_eth_hdr_valid; +wire [47:0] tx_eth_dest_mac; +wire [47:0] tx_eth_src_mac; +wire [15:0] tx_eth_type; +wire [7:0] tx_eth_payload_axis_tdata; +wire tx_eth_payload_axis_tvalid; +wire tx_eth_payload_axis_tready; +wire tx_eth_payload_axis_tlast; +wire tx_eth_payload_axis_tuser; + +// IP frame connections +wire rx_ip_hdr_valid; +wire rx_ip_hdr_ready; +wire [47:0] rx_ip_eth_dest_mac; +wire [47:0] rx_ip_eth_src_mac; +wire [15:0] rx_ip_eth_type; +wire [3:0] rx_ip_version; +wire [3:0] rx_ip_ihl; +wire [5:0] rx_ip_dscp; +wire [1:0] rx_ip_ecn; +wire [15:0] rx_ip_length; +wire [15:0] rx_ip_identification; +wire [2:0] rx_ip_flags; +wire [12:0] rx_ip_fragment_offset; +wire [7:0] rx_ip_ttl; +wire [7:0] rx_ip_protocol; +wire [15:0] rx_ip_header_checksum; +wire [31:0] rx_ip_source_ip; +wire [31:0] rx_ip_dest_ip; +wire [7:0] rx_ip_payload_axis_tdata; +wire rx_ip_payload_axis_tvalid; +wire rx_ip_payload_axis_tready; +wire rx_ip_payload_axis_tlast; +wire rx_ip_payload_axis_tuser; + +wire tx_ip_hdr_valid; +wire tx_ip_hdr_ready; +wire [5:0] tx_ip_dscp; +wire [1:0] tx_ip_ecn; +wire [15:0] tx_ip_length; +wire [7:0] tx_ip_ttl; +wire [7:0] tx_ip_protocol; +wire [31:0] tx_ip_source_ip; +wire [31:0] tx_ip_dest_ip; +wire [7:0] tx_ip_payload_axis_tdata; +wire tx_ip_payload_axis_tvalid; +wire tx_ip_payload_axis_tready; +wire tx_ip_payload_axis_tlast; +wire tx_ip_payload_axis_tuser; + +// UDP frame connections +wire rx_udp_hdr_valid; +wire rx_udp_hdr_ready; +wire [47:0] rx_udp_eth_dest_mac; +wire [47:0] rx_udp_eth_src_mac; +wire [15:0] rx_udp_eth_type; +wire [3:0] rx_udp_ip_version; +wire [3:0] rx_udp_ip_ihl; +wire [5:0] rx_udp_ip_dscp; +wire [1:0] rx_udp_ip_ecn; +wire [15:0] rx_udp_ip_length; +wire [15:0] rx_udp_ip_identification; +wire [2:0] rx_udp_ip_flags; +wire [12:0] rx_udp_ip_fragment_offset; +wire [7:0] rx_udp_ip_ttl; +wire [7:0] rx_udp_ip_protocol; +wire [15:0] rx_udp_ip_header_checksum; +wire [31:0] rx_udp_ip_source_ip; +wire [31:0] rx_udp_ip_dest_ip; +wire [15:0] rx_udp_source_port; +wire [15:0] rx_udp_dest_port; +wire [15:0] rx_udp_length; +wire [15:0] rx_udp_checksum; +wire [7:0] rx_udp_payload_axis_tdata; +wire rx_udp_payload_axis_tvalid; +wire rx_udp_payload_axis_tready; +wire rx_udp_payload_axis_tlast; +wire rx_udp_payload_axis_tuser; + +wire tx_udp_hdr_valid; +wire tx_udp_hdr_ready; +wire [5:0] tx_udp_ip_dscp; +wire [1:0] tx_udp_ip_ecn; +wire [7:0] tx_udp_ip_ttl; +wire [31:0] tx_udp_ip_source_ip; +wire [31:0] tx_udp_ip_dest_ip; +wire [15:0] tx_udp_source_port; +wire [15:0] tx_udp_dest_port; +wire [15:0] tx_udp_length; +wire [15:0] tx_udp_checksum; +wire [7:0] tx_udp_payload_axis_tdata; +wire tx_udp_payload_axis_tvalid; +wire tx_udp_payload_axis_tready; +wire tx_udp_payload_axis_tlast; +wire tx_udp_payload_axis_tuser; + +wire [7:0] rx_fifo_udp_payload_axis_tdata; +wire rx_fifo_udp_payload_axis_tvalid; +wire rx_fifo_udp_payload_axis_tready; +wire rx_fifo_udp_payload_axis_tlast; +wire rx_fifo_udp_payload_axis_tuser; + +wire [7:0] tx_fifo_udp_payload_axis_tdata; +wire tx_fifo_udp_payload_axis_tvalid; +wire tx_fifo_udp_payload_axis_tready; +wire tx_fifo_udp_payload_axis_tlast; +wire tx_fifo_udp_payload_axis_tuser; + +// Configuration +wire [47:0] local_mac = 48'h02_00_00_00_00_00; +wire [31:0] local_ip = {8'd192, 8'd168, 8'd1, 8'd128}; +wire [31:0] gateway_ip = {8'd192, 8'd168, 8'd1, 8'd1}; +wire [31:0] subnet_mask = {8'd255, 8'd255, 8'd255, 8'd0}; + +// IP ports not used +assign rx_ip_hdr_ready = 1; +assign rx_ip_payload_axis_tready = 1; + +assign tx_ip_hdr_valid = 0; +assign tx_ip_dscp = 0; +assign tx_ip_ecn = 0; +assign tx_ip_length = 0; +assign tx_ip_ttl = 0; +assign tx_ip_protocol = 0; +assign tx_ip_source_ip = 0; +assign tx_ip_dest_ip = 0; +assign tx_ip_payload_axis_tdata = 0; +assign tx_ip_payload_axis_tvalid = 0; +assign tx_ip_payload_axis_tlast = 0; +assign tx_ip_payload_axis_tuser = 0; + +// Loop back UDP +wire match_cond = rx_udp_dest_port == 1234; +wire no_match = !match_cond; + +reg match_cond_reg = 0; +reg no_match_reg = 0; + +always @(posedge clk) begin + if (rst) begin + match_cond_reg <= 0; + no_match_reg <= 0; + end else begin + if (rx_udp_payload_axis_tvalid) begin + if ((!match_cond_reg && !no_match_reg) || + (rx_udp_payload_axis_tvalid && rx_udp_payload_axis_tready && rx_udp_payload_axis_tlast)) begin + match_cond_reg <= match_cond; + no_match_reg <= no_match; + end + end else begin + match_cond_reg <= 0; + no_match_reg <= 0; + end + end +end + +assign tx_udp_hdr_valid = rx_udp_hdr_valid && match_cond; +assign rx_udp_hdr_ready = (tx_eth_hdr_ready && match_cond) || no_match; +assign tx_udp_ip_dscp = 0; +assign tx_udp_ip_ecn = 0; +assign tx_udp_ip_ttl = 64; +assign tx_udp_ip_source_ip = local_ip; +assign tx_udp_ip_dest_ip = rx_udp_ip_source_ip; +assign tx_udp_source_port = rx_udp_dest_port; +assign tx_udp_dest_port = rx_udp_source_port; +assign tx_udp_length = rx_udp_length; +assign tx_udp_checksum = 0; + +assign tx_udp_payload_axis_tdata = tx_fifo_udp_payload_axis_tdata; +assign tx_udp_payload_axis_tvalid = tx_fifo_udp_payload_axis_tvalid; +assign tx_fifo_udp_payload_axis_tready = tx_udp_payload_axis_tready; +assign tx_udp_payload_axis_tlast = tx_fifo_udp_payload_axis_tlast; +assign tx_udp_payload_axis_tuser = tx_fifo_udp_payload_axis_tuser; + +assign rx_fifo_udp_payload_axis_tdata = rx_udp_payload_axis_tdata; +assign rx_fifo_udp_payload_axis_tvalid = rx_udp_payload_axis_tvalid && match_cond_reg; +assign rx_udp_payload_axis_tready = (rx_fifo_udp_payload_axis_tready && match_cond_reg) || no_match_reg; +assign rx_fifo_udp_payload_axis_tlast = rx_udp_payload_axis_tlast; +assign rx_fifo_udp_payload_axis_tuser = rx_udp_payload_axis_tuser; + +// Place first payload byte onto LEDs +reg valid_last = 0; +reg [7:0] led_reg = 0; + +always @(posedge clk) begin + if (rst) begin + led_reg <= 0; + end else begin + if (tx_udp_payload_axis_tvalid) begin + if (!valid_last) begin + led_reg <= tx_udp_payload_axis_tdata; + valid_last <= 1'b1; + end + if (tx_udp_payload_axis_tlast) begin + valid_last <= 1'b0; + end + end + end +end + +//assign led = sw; +assign led = led_reg; +assign phy_reset_n = !rst; + +assign uart_txd = 0; + +eth_mac_1g_rgmii_fifo #( + .TARGET(TARGET), + .IODDR_STYLE("IODDR"), + .CLOCK_INPUT_STYLE("BUFR"), + .USE_CLK90("TRUE"), + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_DEPTH(4096), + .TX_FRAME_FIFO(1), + .RX_FIFO_DEPTH(4096), + .RX_FRAME_FIFO(1) +) +eth_mac_inst ( + .gtx_clk(clk), + .gtx_clk90(clk90), + .gtx_rst(rst), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tvalid(tx_axis_tvalid), + .tx_axis_tready(tx_axis_tready), + .tx_axis_tlast(tx_axis_tlast), + .tx_axis_tuser(tx_axis_tuser), + + .rx_axis_tdata(rx_axis_tdata), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + + .rgmii_rx_clk(phy_rx_clk), + .rgmii_rxd(phy_rxd), + .rgmii_rx_ctl(phy_rx_ctl), + .rgmii_tx_clk(phy_tx_clk), + .rgmii_txd(phy_txd), + .rgmii_tx_ctl(phy_tx_ctl), + + .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(), + .speed(), + + .ifg_delay(12) +); + +eth_axis_rx +eth_axis_rx_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(rx_axis_tdata), + .s_axis_tvalid(rx_axis_tvalid), + .s_axis_tready(rx_axis_tready), + .s_axis_tlast(rx_axis_tlast), + .s_axis_tuser(rx_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(rx_eth_hdr_valid), + .m_eth_hdr_ready(rx_eth_hdr_ready), + .m_eth_dest_mac(rx_eth_dest_mac), + .m_eth_src_mac(rx_eth_src_mac), + .m_eth_type(rx_eth_type), + .m_eth_payload_axis_tdata(rx_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(rx_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(rx_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(rx_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(rx_eth_payload_axis_tuser), + // Status signals + .busy(), + .error_header_early_termination() +); + +eth_axis_tx +eth_axis_tx_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(tx_eth_hdr_valid), + .s_eth_hdr_ready(tx_eth_hdr_ready), + .s_eth_dest_mac(tx_eth_dest_mac), + .s_eth_src_mac(tx_eth_src_mac), + .s_eth_type(tx_eth_type), + .s_eth_payload_axis_tdata(tx_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(tx_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(tx_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(tx_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(tx_eth_payload_axis_tuser), + // AXI output + .m_axis_tdata(tx_axis_tdata), + .m_axis_tvalid(tx_axis_tvalid), + .m_axis_tready(tx_axis_tready), + .m_axis_tlast(tx_axis_tlast), + .m_axis_tuser(tx_axis_tuser), + // Status signals + .busy() +); + +udp_complete +udp_complete_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(rx_eth_hdr_valid), + .s_eth_hdr_ready(rx_eth_hdr_ready), + .s_eth_dest_mac(rx_eth_dest_mac), + .s_eth_src_mac(rx_eth_src_mac), + .s_eth_type(rx_eth_type), + .s_eth_payload_axis_tdata(rx_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(rx_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(rx_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(rx_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(rx_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(tx_eth_hdr_valid), + .m_eth_hdr_ready(tx_eth_hdr_ready), + .m_eth_dest_mac(tx_eth_dest_mac), + .m_eth_src_mac(tx_eth_src_mac), + .m_eth_type(tx_eth_type), + .m_eth_payload_axis_tdata(tx_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(tx_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(tx_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(tx_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(tx_eth_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(tx_ip_hdr_valid), + .s_ip_hdr_ready(tx_ip_hdr_ready), + .s_ip_dscp(tx_ip_dscp), + .s_ip_ecn(tx_ip_ecn), + .s_ip_length(tx_ip_length), + .s_ip_ttl(tx_ip_ttl), + .s_ip_protocol(tx_ip_protocol), + .s_ip_source_ip(tx_ip_source_ip), + .s_ip_dest_ip(tx_ip_dest_ip), + .s_ip_payload_axis_tdata(tx_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(tx_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(tx_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(tx_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(tx_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(rx_ip_hdr_valid), + .m_ip_hdr_ready(rx_ip_hdr_ready), + .m_ip_eth_dest_mac(rx_ip_eth_dest_mac), + .m_ip_eth_src_mac(rx_ip_eth_src_mac), + .m_ip_eth_type(rx_ip_eth_type), + .m_ip_version(rx_ip_version), + .m_ip_ihl(rx_ip_ihl), + .m_ip_dscp(rx_ip_dscp), + .m_ip_ecn(rx_ip_ecn), + .m_ip_length(rx_ip_length), + .m_ip_identification(rx_ip_identification), + .m_ip_flags(rx_ip_flags), + .m_ip_fragment_offset(rx_ip_fragment_offset), + .m_ip_ttl(rx_ip_ttl), + .m_ip_protocol(rx_ip_protocol), + .m_ip_header_checksum(rx_ip_header_checksum), + .m_ip_source_ip(rx_ip_source_ip), + .m_ip_dest_ip(rx_ip_dest_ip), + .m_ip_payload_axis_tdata(rx_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(rx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(rx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(rx_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(rx_ip_payload_axis_tuser), + // UDP frame input + .s_udp_hdr_valid(tx_udp_hdr_valid), + .s_udp_hdr_ready(tx_udp_hdr_ready), + .s_udp_ip_dscp(tx_udp_ip_dscp), + .s_udp_ip_ecn(tx_udp_ip_ecn), + .s_udp_ip_ttl(tx_udp_ip_ttl), + .s_udp_ip_source_ip(tx_udp_ip_source_ip), + .s_udp_ip_dest_ip(tx_udp_ip_dest_ip), + .s_udp_source_port(tx_udp_source_port), + .s_udp_dest_port(tx_udp_dest_port), + .s_udp_length(tx_udp_length), + .s_udp_checksum(tx_udp_checksum), + .s_udp_payload_axis_tdata(tx_udp_payload_axis_tdata), + .s_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(tx_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(tx_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(tx_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(rx_udp_hdr_valid), + .m_udp_hdr_ready(rx_udp_hdr_ready), + .m_udp_eth_dest_mac(rx_udp_eth_dest_mac), + .m_udp_eth_src_mac(rx_udp_eth_src_mac), + .m_udp_eth_type(rx_udp_eth_type), + .m_udp_ip_version(rx_udp_ip_version), + .m_udp_ip_ihl(rx_udp_ip_ihl), + .m_udp_ip_dscp(rx_udp_ip_dscp), + .m_udp_ip_ecn(rx_udp_ip_ecn), + .m_udp_ip_length(rx_udp_ip_length), + .m_udp_ip_identification(rx_udp_ip_identification), + .m_udp_ip_flags(rx_udp_ip_flags), + .m_udp_ip_fragment_offset(rx_udp_ip_fragment_offset), + .m_udp_ip_ttl(rx_udp_ip_ttl), + .m_udp_ip_protocol(rx_udp_ip_protocol), + .m_udp_ip_header_checksum(rx_udp_ip_header_checksum), + .m_udp_ip_source_ip(rx_udp_ip_source_ip), + .m_udp_ip_dest_ip(rx_udp_ip_dest_ip), + .m_udp_source_port(rx_udp_source_port), + .m_udp_dest_port(rx_udp_dest_port), + .m_udp_length(rx_udp_length), + .m_udp_checksum(rx_udp_checksum), + .m_udp_payload_axis_tdata(rx_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(rx_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(rx_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(rx_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(rx_udp_payload_axis_tuser), + // Status signals + .ip_rx_busy(), + .ip_tx_busy(), + .udp_rx_busy(), + .udp_tx_busy(), + .ip_rx_error_header_early_termination(), + .ip_rx_error_payload_early_termination(), + .ip_rx_error_invalid_header(), + .ip_rx_error_invalid_checksum(), + .ip_tx_error_payload_early_termination(), + .ip_tx_error_arp_failed(), + .udp_rx_error_header_early_termination(), + .udp_rx_error_payload_early_termination(), + .udp_tx_error_payload_early_termination(), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_arp_cache(0) +); + +axis_fifo #( + .DEPTH(8192), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(0) +) +udp_payload_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata(rx_fifo_udp_payload_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_fifo_udp_payload_axis_tvalid), + .s_axis_tready(rx_fifo_udp_payload_axis_tready), + .s_axis_tlast(rx_fifo_udp_payload_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_udp_payload_axis_tuser), + + // AXI output + .m_axis_tdata(tx_fifo_udp_payload_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_udp_payload_axis_tvalid), + .m_axis_tready(tx_fifo_udp_payload_axis_tready), + .m_axis_tlast(tx_fifo_udp_payload_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_udp_payload_axis_tuser), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +endmodule diff --git a/example/Genesys2/fpga_rgmii/rtl/sync_signal.v b/example/Genesys2/fpga_rgmii/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/example/Genesys2/fpga_rgmii/rtl/sync_signal.v @@ -0,0 +1,58 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog-2001 + +`timescale 1 ns / 1 ps + +/* + * Synchronizes an asyncronous signal to a given clock by using a pipeline of + * two registers. + */ +module sync_signal #( + parameter WIDTH=1, // width of the input and output signals + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire [WIDTH-1:0] in, + output wire [WIDTH-1:0] out +); + +reg [WIDTH-1:0] sync_reg[N-1:0]; + +/* + * The synchronized output is the last register in the pipeline. + */ +assign out = sync_reg[N-1]; + +integer k; + +always @(posedge clk) begin + sync_reg[0] <= in; + for (k = 1; k < N; k = k + 1) begin + sync_reg[k] <= sync_reg[k-1]; + end +end + +endmodule diff --git a/example/Genesys2/fpga_rgmii/tb/fpga_core/Makefile b/example/Genesys2/fpga_rgmii/tb/fpga_core/Makefile new file mode 100644 index 000000000..c6328a449 --- /dev/null +++ b/example/Genesys2/fpga_rgmii/tb/fpga_core/Makefile @@ -0,0 +1,100 @@ +# Copyright (c) 2020 Alex Forencich +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +TOPLEVEL_LANG = verilog + +SIM ?= icarus +WAVES ?= 0 + +COCOTB_HDL_TIMEUNIT = 1ns +COCOTB_HDL_TIMEPRECISION = 1ps + +DUT = fpga_core +TOPLEVEL = $(DUT) +MODULE = test_$(DUT) +VERILOG_SOURCES += ../../rtl/$(DUT).v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_mac_1g_rgmii_fifo.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_mac_1g_rgmii.v +VERILOG_SOURCES += ../../lib/eth/rtl/iddr.v +VERILOG_SOURCES += ../../lib/eth/rtl/oddr.v +VERILOG_SOURCES += ../../lib/eth/rtl/ssio_ddr_in.v +VERILOG_SOURCES += ../../lib/eth/rtl/rgmii_phy_if.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_mac_1g.v +VERILOG_SOURCES += ../../lib/eth/rtl/axis_gmii_rx.v +VERILOG_SOURCES += ../../lib/eth/rtl/axis_gmii_tx.v +VERILOG_SOURCES += ../../lib/eth/rtl/lfsr.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_axis_rx.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_axis_tx.v +VERILOG_SOURCES += ../../lib/eth/rtl/udp_complete.v +VERILOG_SOURCES += ../../lib/eth/rtl/udp_checksum_gen.v +VERILOG_SOURCES += ../../lib/eth/rtl/udp.v +VERILOG_SOURCES += ../../lib/eth/rtl/udp_ip_rx.v +VERILOG_SOURCES += ../../lib/eth/rtl/udp_ip_tx.v +VERILOG_SOURCES += ../../lib/eth/rtl/ip_complete.v +VERILOG_SOURCES += ../../lib/eth/rtl/ip.v +VERILOG_SOURCES += ../../lib/eth/rtl/ip_eth_rx.v +VERILOG_SOURCES += ../../lib/eth/rtl/ip_eth_tx.v +VERILOG_SOURCES += ../../lib/eth/rtl/ip_arb_mux.v +VERILOG_SOURCES += ../../lib/eth/rtl/arp.v +VERILOG_SOURCES += ../../lib/eth/rtl/arp_cache.v +VERILOG_SOURCES += ../../lib/eth/rtl/arp_eth_rx.v +VERILOG_SOURCES += ../../lib/eth/rtl/arp_eth_tx.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_arb_mux.v +VERILOG_SOURCES += ../../lib/eth/lib/axis/rtl/arbiter.v +VERILOG_SOURCES += ../../lib/eth/lib/axis/rtl/priority_encoder.v +VERILOG_SOURCES += ../../lib/eth/lib/axis/rtl/axis_fifo.v +VERILOG_SOURCES += ../../lib/eth/lib/axis/rtl/axis_async_fifo.v +VERILOG_SOURCES += ../../lib/eth/lib/axis/rtl/axis_async_fifo_adapter.v + +# module parameters +#export PARAM_A ?= value + +ifeq ($(SIM), icarus) + PLUSARGS += -fst + +# COMPILE_ARGS += -P $(TOPLEVEL).A=$(PARAM_A) + + ifeq ($(WAVES), 1) + VERILOG_SOURCES += iverilog_dump.v + COMPILE_ARGS += -s iverilog_dump + endif +else ifeq ($(SIM), verilator) + COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH + +# COMPILE_ARGS += -GA=$(PARAM_A) + + ifeq ($(WAVES), 1) + COMPILE_ARGS += --trace-fst + endif +endif + +include $(shell cocotb-config --makefiles)/Makefile.sim + +iverilog_dump.v: + echo 'module iverilog_dump();' > $@ + echo 'initial begin' >> $@ + echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@ + echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@ + echo 'end' >> $@ + echo 'endmodule' >> $@ + +clean:: + @rm -rf iverilog_dump.v + @rm -rf dump.fst $(TOPLEVEL).fst diff --git a/example/Genesys2/fpga_rgmii/tb/fpga_core/test_fpga_core.py b/example/Genesys2/fpga_rgmii/tb/fpga_core/test_fpga_core.py new file mode 100644 index 000000000..5302b99f2 --- /dev/null +++ b/example/Genesys2/fpga_rgmii/tb/fpga_core/test_fpga_core.py @@ -0,0 +1,229 @@ +""" + +Copyright (c) 2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +import logging +import os + +from scapy.layers.l2 import Ether, ARP +from scapy.layers.inet import IP, UDP + +import cocotb_test.simulator + +import cocotb +from cocotb.log import SimLog +from cocotb.triggers import RisingEdge, Timer + +from cocotbext.eth import GmiiFrame, RgmiiPhy + + +class TB: + def __init__(self, dut, speed=1000e6): + self.dut = dut + + self.log = SimLog("cocotb.tb") + self.log.setLevel(logging.DEBUG) + + self.rgmii_phy = RgmiiPhy(dut.phy_txd, dut.phy_tx_ctl, dut.phy_tx_clk, + dut.phy_rxd, dut.phy_rx_ctl, dut.phy_rx_clk, speed=speed) + + 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_txd.setimmediatevalue(1) + dut.uart_rts.setimmediatevalue(1) + + dut.clk.setimmediatevalue(0) + dut.clk90.setimmediatevalue(0) + + cocotb.fork(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 <= 1 + + for k in range(10): + await RisingEdge(self.dut.clk) + + self.dut.rst <= 0 + + async def _run_clk(self): + t = Timer(2, 'ns') + while True: + self.dut.clk <= 1 + await t + self.dut.clk90 <= 1 + await t + self.dut.clk <= 0 + await t + self.dut.clk90 <= 0 + await t + + +@cocotb.test() +async def run_test(dut): + + tb = TB(dut) + + await tb.init() + + tb.log.info("test UDP RX packet") + + payload = bytes([x % 256 for x in range(256)]) + eth = Ether(src='5a:51:52:53:54:55', dst='02:00:00:00:00:00') + ip = IP(src='192.168.1.100', dst='192.168.1.128') + udp = UDP(sport=5678, dport=1234) + test_pkt = eth / ip / udp / payload + + test_frame = GmiiFrame.from_payload(test_pkt.build()) + + await tb.rgmii_phy.rx.send(test_frame) + + tb.log.info("receive ARP request") + + rx_frame = await tb.rgmii_phy.tx.recv() + + rx_pkt = Ether(bytes(rx_frame.get_payload())) + + tb.log.info("RX packet: %s", repr(rx_pkt)) + + assert rx_pkt.dst == 'ff:ff:ff:ff:ff:ff' + assert rx_pkt.src == test_pkt.dst + assert rx_pkt[ARP].hwtype == 1 + assert rx_pkt[ARP].ptype == 0x0800 + assert rx_pkt[ARP].hwlen == 6 + assert rx_pkt[ARP].plen == 4 + assert rx_pkt[ARP].op == 1 + assert rx_pkt[ARP].hwsrc == test_pkt.dst + assert rx_pkt[ARP].psrc == test_pkt[IP].dst + assert rx_pkt[ARP].hwdst == '00:00:00:00:00:00' + assert rx_pkt[ARP].pdst == test_pkt[IP].src + + tb.log.info("send ARP response") + + eth = Ether(src=test_pkt.src, dst=test_pkt.dst) + arp = ARP(hwtype=1, ptype=0x0800, hwlen=6, plen=4, op=2, + hwsrc=test_pkt.src, psrc=test_pkt[IP].src, + hwdst=test_pkt.dst, pdst=test_pkt[IP].dst) + resp_pkt = eth / arp + + resp_frame = GmiiFrame.from_payload(resp_pkt.build()) + + await tb.rgmii_phy.rx.send(resp_frame) + + tb.log.info("receive UDP packet") + + rx_frame = await tb.rgmii_phy.tx.recv() + + rx_pkt = Ether(bytes(rx_frame.get_payload())) + + tb.log.info("RX packet: %s", repr(rx_pkt)) + + assert rx_pkt.dst == test_pkt.src + assert rx_pkt.src == test_pkt.dst + assert rx_pkt[IP].dst == test_pkt[IP].src + assert rx_pkt[IP].src == test_pkt[IP].dst + assert rx_pkt[UDP].dport == test_pkt[UDP].sport + assert rx_pkt[UDP].sport == test_pkt[UDP].dport + assert rx_pkt[UDP].payload == test_pkt[UDP].payload + + 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')) +axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'eth', 'lib', 'axis', 'rtl')) +eth_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'eth', 'rtl')) + + +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}.v"), + os.path.join(eth_rtl_dir, "eth_mac_1g_rgmii_fifo.v"), + os.path.join(eth_rtl_dir, "eth_mac_1g_rgmii.v"), + os.path.join(eth_rtl_dir, "iddr.v"), + os.path.join(eth_rtl_dir, "oddr.v"), + os.path.join(eth_rtl_dir, "ssio_ddr_in.v"), + os.path.join(eth_rtl_dir, "rgmii_phy_if.v"), + os.path.join(eth_rtl_dir, "eth_mac_1g.v"), + os.path.join(eth_rtl_dir, "axis_gmii_rx.v"), + os.path.join(eth_rtl_dir, "axis_gmii_tx.v"), + os.path.join(eth_rtl_dir, "lfsr.v"), + os.path.join(eth_rtl_dir, "eth_axis_rx.v"), + os.path.join(eth_rtl_dir, "eth_axis_tx.v"), + os.path.join(eth_rtl_dir, "udp_complete.v"), + os.path.join(eth_rtl_dir, "udp_checksum_gen.v"), + os.path.join(eth_rtl_dir, "udp.v"), + os.path.join(eth_rtl_dir, "udp_ip_rx.v"), + os.path.join(eth_rtl_dir, "udp_ip_tx.v"), + os.path.join(eth_rtl_dir, "ip_complete.v"), + os.path.join(eth_rtl_dir, "ip.v"), + os.path.join(eth_rtl_dir, "ip_eth_rx.v"), + os.path.join(eth_rtl_dir, "ip_eth_tx.v"), + os.path.join(eth_rtl_dir, "ip_arb_mux.v"), + os.path.join(eth_rtl_dir, "arp.v"), + os.path.join(eth_rtl_dir, "arp_cache.v"), + os.path.join(eth_rtl_dir, "arp_eth_rx.v"), + os.path.join(eth_rtl_dir, "arp_eth_tx.v"), + os.path.join(eth_rtl_dir, "eth_arb_mux.v"), + os.path.join(axis_rtl_dir, "arbiter.v"), + os.path.join(axis_rtl_dir, "priority_encoder.v"), + os.path.join(axis_rtl_dir, "axis_fifo.v"), + os.path.join(axis_rtl_dir, "axis_async_fifo.v"), + os.path.join(axis_rtl_dir, "axis_async_fifo_adapter.v"), + ] + + parameters = {} + + # parameters['A'] = val + + 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( + python_search=[tests_dir], + verilog_sources=verilog_sources, + toplevel=toplevel, + module=module, + parameters=parameters, + sim_build=sim_build, + extra_env=extra_env, + ) diff --git a/lib/axis/rtl/axis_async_fifo.v b/lib/axis/rtl/axis_async_fifo.v index 2d89f2da4..b6aafd57c 100644 --- a/lib/axis/rtl/axis_async_fifo.v +++ b/lib/axis/rtl/axis_async_fifo.v @@ -119,7 +119,7 @@ module axis_async_fifo # output wire m_status_good_frame ); -parameter ADDR_WIDTH = (KEEP_ENABLE && KEEP_WIDTH > 1) ? $clog2(DEPTH/KEEP_WIDTH) : $clog2(DEPTH); +localparam ADDR_WIDTH = (KEEP_ENABLE && KEEP_WIDTH > 1) ? $clog2(DEPTH/KEEP_WIDTH) : $clog2(DEPTH); // check configuration initial begin diff --git a/lib/axis/rtl/axis_async_fifo_adapter.v b/lib/axis/rtl/axis_async_fifo_adapter.v index 76a293a69..8ec330852 100644 --- a/lib/axis/rtl/axis_async_fifo_adapter.v +++ b/lib/axis/rtl/axis_async_fifo_adapter.v @@ -125,17 +125,17 @@ module axis_async_fifo_adapter # ); // force keep width to 1 when disabled -parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; -parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; +localparam S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; +localparam M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; // bus word sizes (must be identical) -parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT; -parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT; +localparam S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT; +localparam M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT; // output bus is wider -parameter EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT; +localparam EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT; // total data and keep widths -parameter DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH; -parameter KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT; +localparam DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH; +localparam KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT; // bus width assertions initial begin diff --git a/lib/axis/rtl/axis_fifo.v b/lib/axis/rtl/axis_fifo.v index bf3be6e19..ae1572fa9 100644 --- a/lib/axis/rtl/axis_fifo.v +++ b/lib/axis/rtl/axis_fifo.v @@ -115,7 +115,7 @@ module axis_fifo # output wire status_good_frame ); -parameter ADDR_WIDTH = (KEEP_ENABLE && KEEP_WIDTH > 1) ? $clog2(DEPTH/KEEP_WIDTH) : $clog2(DEPTH); +localparam ADDR_WIDTH = (KEEP_ENABLE && KEEP_WIDTH > 1) ? $clog2(DEPTH/KEEP_WIDTH) : $clog2(DEPTH); // check configuration initial begin diff --git a/lib/axis/rtl/priority_encoder.v b/lib/axis/rtl/priority_encoder.v index cf82512ba..29a2531c7 100644 --- a/lib/axis/rtl/priority_encoder.v +++ b/lib/axis/rtl/priority_encoder.v @@ -44,8 +44,8 @@ module priority_encoder # output wire [WIDTH-1:0] output_unencoded ); -parameter LEVELS = WIDTH > 2 ? $clog2(WIDTH) : 1; -parameter W = 2**LEVELS; +localparam LEVELS = WIDTH > 2 ? $clog2(WIDTH) : 1; +localparam W = 2**LEVELS; // pad input to even power of two wire [W-1:0] input_padded = {{W-WIDTH{1'b0}}, input_unencoded}; diff --git a/rtl/arp_eth_rx.v b/rtl/arp_eth_rx.v index 1bac60c6f..69e25bec5 100644 --- a/rtl/arp_eth_rx.v +++ b/rtl/arp_eth_rx.v @@ -86,11 +86,11 @@ module arp_eth_rx # output wire error_invalid_header ); -parameter CYCLE_COUNT = (28+KEEP_WIDTH-1)/KEEP_WIDTH; +localparam CYCLE_COUNT = (28+KEEP_WIDTH-1)/KEEP_WIDTH; -parameter PTR_WIDTH = $clog2(CYCLE_COUNT); +localparam PTR_WIDTH = $clog2(CYCLE_COUNT); -parameter OFFSET = 28 % KEEP_WIDTH; +localparam OFFSET = 28 % KEEP_WIDTH; // bus width assertions initial begin diff --git a/rtl/arp_eth_tx.v b/rtl/arp_eth_tx.v index 9ef78f5b4..e8d6ff8dc 100644 --- a/rtl/arp_eth_tx.v +++ b/rtl/arp_eth_tx.v @@ -82,11 +82,11 @@ module arp_eth_tx # output wire busy ); -parameter CYCLE_COUNT = (28+KEEP_WIDTH-1)/KEEP_WIDTH; +localparam CYCLE_COUNT = (28+KEEP_WIDTH-1)/KEEP_WIDTH; -parameter PTR_WIDTH = $clog2(CYCLE_COUNT); +localparam PTR_WIDTH = $clog2(CYCLE_COUNT); -parameter OFFSET = 28 % KEEP_WIDTH; +localparam OFFSET = 28 % KEEP_WIDTH; // bus width assertions initial begin diff --git a/rtl/eth_arb_mux.v b/rtl/eth_arb_mux.v index 1956f444b..beca2f046 100644 --- a/rtl/eth_arb_mux.v +++ b/rtl/eth_arb_mux.v @@ -87,7 +87,7 @@ module eth_arb_mux # output wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser ); -parameter CL_S_COUNT = $clog2(S_COUNT); +localparam CL_S_COUNT = $clog2(S_COUNT); reg frame_reg = 1'b0, frame_next; diff --git a/rtl/eth_axis_rx.v b/rtl/eth_axis_rx.v index f75fee232..93df6f790 100644 --- a/rtl/eth_axis_rx.v +++ b/rtl/eth_axis_rx.v @@ -77,11 +77,11 @@ module eth_axis_rx # output wire error_header_early_termination ); -parameter CYCLE_COUNT = (14+KEEP_WIDTH-1)/KEEP_WIDTH; +localparam CYCLE_COUNT = (14+KEEP_WIDTH-1)/KEEP_WIDTH; -parameter PTR_WIDTH = $clog2(CYCLE_COUNT); +localparam PTR_WIDTH = $clog2(CYCLE_COUNT); -parameter OFFSET = 14 % KEEP_WIDTH; +localparam OFFSET = 14 % KEEP_WIDTH; // bus width assertions initial begin diff --git a/rtl/eth_axis_tx.v b/rtl/eth_axis_tx.v index d6dac7e52..eab02a8ef 100644 --- a/rtl/eth_axis_tx.v +++ b/rtl/eth_axis_tx.v @@ -76,11 +76,11 @@ module eth_axis_tx # output wire busy ); -parameter CYCLE_COUNT = (14+KEEP_WIDTH-1)/KEEP_WIDTH; +localparam CYCLE_COUNT = (14+KEEP_WIDTH-1)/KEEP_WIDTH; -parameter PTR_WIDTH = $clog2(CYCLE_COUNT); +localparam PTR_WIDTH = $clog2(CYCLE_COUNT); -parameter OFFSET = 14 % KEEP_WIDTH; +localparam OFFSET = 14 % KEEP_WIDTH; // bus width assertions initial begin diff --git a/rtl/ip_arb_mux.v b/rtl/ip_arb_mux.v index eb55776e3..a17b64187 100644 --- a/rtl/ip_arb_mux.v +++ b/rtl/ip_arb_mux.v @@ -113,7 +113,7 @@ module ip_arb_mux # output wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser ); -parameter CL_S_COUNT = $clog2(S_COUNT); +localparam CL_S_COUNT = $clog2(S_COUNT); reg frame_reg = 1'b0, frame_next; diff --git a/rtl/lfsr.v b/rtl/lfsr.v index 024724475..540d66321 100644 --- a/rtl/lfsr.v +++ b/rtl/lfsr.v @@ -362,10 +362,10 @@ end `ifdef SIMULATION // "AUTO" style is "REDUCTION" for faster simulation -parameter STYLE_INT = (STYLE == "AUTO") ? "REDUCTION" : STYLE; +localparam STYLE_INT = (STYLE == "AUTO") ? "REDUCTION" : STYLE; `else // "AUTO" style is "LOOP" for better synthesis result -parameter STYLE_INT = (STYLE == "AUTO") ? "LOOP" : STYLE; +localparam STYLE_INT = (STYLE == "AUTO") ? "LOOP" : STYLE; `endif genvar n; diff --git a/rtl/udp_checksum_gen.v b/rtl/udp_checksum_gen.v index 3038506bf..aa51c2267 100644 --- a/rtl/udp_checksum_gen.v +++ b/rtl/udp_checksum_gen.v @@ -140,7 +140,7 @@ header fields in parallel along with the UDP payload in a separate AXI stream. */ -parameter HEADER_FIFO_ADDR_WIDTH = $clog2(HEADER_FIFO_DEPTH); +localparam HEADER_FIFO_ADDR_WIDTH = $clog2(HEADER_FIFO_DEPTH); localparam [2:0] STATE_IDLE = 3'd0,