diff --git a/example/STLV7325/fpga_gmii/Makefile b/example/STLV7325/fpga_gmii/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/example/STLV7325/fpga_gmii/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 Atlys --index 0 --file fpga/fpga.bit diff --git a/example/STLV7325/fpga_gmii/README.md b/example/STLV7325/fpga_gmii/README.md new file mode 100644 index 000000000..231fba104 --- /dev/null +++ b/example/STLV7325/fpga_gmii/README.md @@ -0,0 +1,33 @@ +# Verilog Ethernet STLV7325 Example Design + +## Introduction + +This example design targets the [STLV7325 FPGA board](https://pt.aliexpress.com/item/1005001275162791.html?spm=a2g0o.search0302.0.0.22c11dbbDG5aIf&algo_pvid=623aa8db-4f0e-45e6-ba67-87550cfad3b3&algo_expid=623aa8db-4f0e-45e6-ba67-87550cfad3b3-0&btsid=0b0a555c16203948412512389e047e&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_). + +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. + +Configure the PHY for GMII by placing J29 and J30 across pins 1 and 2 and +opening J64. + +* FPGA: XC7K325T-2FFG676 +* PHY: Marvell 88E1111 + +## 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 KC705 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/STLV7325/fpga_gmii/clock.xdc b/example/STLV7325/fpga_gmii/clock.xdc new file mode 100644 index 000000000..93d13048b --- /dev/null +++ b/example/STLV7325/fpga_gmii/clock.xdc @@ -0,0 +1,4 @@ +# Clock constraints + +# BUFGMUX outputs +set_clock_groups -physically_exclusive -group clk_mmcm_out -group phy_tx_clk diff --git a/example/STLV7325/fpga_gmii/common/vivado.mk b/example/STLV7325/fpga_gmii/common/vivado.mk new file mode 100644 index 000000000..ee83637e0 --- /dev/null +++ b/example/STLV7325/fpga_gmii/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 *.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 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 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/STLV7325/fpga_gmii/fpga.xdc b/example/STLV7325/fpga_gmii/fpga.xdc new file mode 100644 index 000000000..6630ab26e --- /dev/null +++ b/example/STLV7325/fpga_gmii/fpga.xdc @@ -0,0 +1,69 @@ +# XDC constraints for the Xilinx KC705 board +# part: xc7k325tffg676-2 + +# General configuration +set_property CFGBVS VCCO [current_design] +set_property CONFIG_VOLTAGE 2.5 [current_design] +set_property BITSTREAM.GENERAL.COMPRESS true [current_design] +set_property CLOCK_DEDICATED_ROUTE ANY_CMT_COLUMN [get_nets phy_tx_clk_IBUF] + +# System clocks +# 200 MHz +set_property -dict {LOC AB11 IOSTANDARD LVDS} [get_ports clk_200mhz_p] +set_property -dict {LOC AC11 IOSTANDARD LVDS} [get_ports clk_200mhz_n] +create_clock -period 5.000 -name clk_200mhz [get_ports clk_200mhz_p] + +# LEDs +set_property -dict {PACKAGE_PIN AA2 IOSTANDARD LVCMOS15} [get_ports {led[0]}] +set_property -dict {PACKAGE_PIN AD5 IOSTANDARD LVCMOS15} [get_ports {led[1]}] +set_property -dict {PACKAGE_PIN W10 IOSTANDARD LVCMOS15} [get_ports {led[2]}] +set_property -dict {PACKAGE_PIN Y10 IOSTANDARD LVCMOS15} [get_ports {led[3]}] +set_property -dict {PACKAGE_PIN AE10 IOSTANDARD LVCMOS15} [get_ports {led[4]}] +set_property -dict {PACKAGE_PIN W11 IOSTANDARD LVCMOS15} [get_ports {led[5]}] +set_property -dict {PACKAGE_PIN V11 IOSTANDARD LVCMOS15} [get_ports {led[6]}] +set_property -dict {PACKAGE_PIN Y12 IOSTANDARD LVCMOS15} [get_ports {led[7]}] + +set_false_path -to [get_ports {led[*]}] +set_output_delay 0 [get_ports {led[*]}] + +# Reset button +set_property -dict {LOC AC16 IOSTANDARD LVCMOS15} [get_ports reset] + +set_false_path -from [get_ports {reset}] +set_input_delay 0 [get_ports {reset}] + +# UART +set_property -dict {LOC M25 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports uart_txd] +set_property -dict {LOC L25 IOSTANDARD LVCMOS25} [get_ports uart_rxd] + +# Gigabit Ethernet GMII PHY +set_property -dict {LOC C12 IOSTANDARD LVCMOS25} [get_ports phy_rx_clk] ;# from U37.C1 RXCLK +set_property -dict {LOC H14 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[0]}] ;# from U37.B2 RXD0 +set_property -dict {LOC J14 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[1]}] ;# from U37.D3 RXD1 +set_property -dict {LOC J13 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[2]}] ;# from U37.C3 RXD2 +set_property -dict {LOC H13 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[3]}] ;# from U37.B3 RXD3 +set_property -dict {LOC B15 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[4]}] ;# from U37.C4 RXD4 +set_property -dict {LOC A15 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[5]}] ;# from U37.A1 RXD5 +set_property -dict {LOC B14 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[6]}] ;# from U37.A2 RXD6 +set_property -dict {LOC A14 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[7]}] ;# from U37.C5 RXD7 +set_property -dict {LOC G14 IOSTANDARD LVCMOS25} [get_ports phy_rx_dv] ;# from U37.B1 RXCTL_RXDV +set_property -dict {LOC F14 IOSTANDARD LVCMOS25} [get_ports phy_rx_er] ;# from U37.D4 RXER +set_property -dict {LOC F13 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_gtx_clk] ;# from U37.E2 TXC_GTXCLK +set_property -dict {LOC E12 IOSTANDARD LVCMOS25} [get_ports phy_tx_clk] ;# from U37.D1 TXCLK +set_property -dict {LOC G12 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[0]}] ;# from U37.F1 TXD0 +set_property -dict {LOC E11 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[1]}] ;# from U37.G2 TXD1 +set_property -dict {LOC G11 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[2]}] ;# from U37.G3 TXD2 +set_property -dict {LOC C14 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[3]}] ;# from U37.H1 TXD3 +set_property -dict {LOC D14 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[4]}] ;# from U37.H2 TXD4 +set_property -dict {LOC C13 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[5]}] ;# from U37.H3 TXD5 +set_property -dict {LOC C11 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[6]}] ;# from U37.J1 TXD6 +set_property -dict {LOC D13 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[7]}] ;# from U37.J2 TXD7 +set_property -dict {LOC F12 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_tx_en] ;# from U37.E1 TXCTL_TXEN +set_property -dict {LOC E13 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_tx_er] ;# from U37.F2 TXER +set_property -dict {LOC D11 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports phy_reset_n] ;# from U37.K3 RESET_B + +create_clock -period 40.000 -name phy_tx_clk [get_ports phy_tx_clk] +create_clock -period 8.000 -name phy_rx_clk [get_ports phy_rx_clk] + +set_false_path -to [get_ports {phy_reset_n}] +set_output_delay 0 [get_ports {phy_reset_n}] diff --git a/example/STLV7325/fpga_gmii/fpga/Makefile b/example/STLV7325/fpga_gmii/fpga/Makefile new file mode 100644 index 000000000..c3650675f --- /dev/null +++ b/example/STLV7325/fpga_gmii/fpga/Makefile @@ -0,0 +1,67 @@ + +# FPGA settings +FPGA_PART = xc7k325tffg676-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/oddr.v +SYN_FILES += lib/eth/rtl/ssio_sdr_in.v +SYN_FILES += lib/eth/rtl/ssio_sdr_out.v +SYN_FILES += lib/eth/rtl/gmii_phy_if.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_gmii_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_gmii.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 += clock.xdc +XDC_FILES += lib/eth/syn/gmii_phy_if.tcl +XDC_FILES += lib/eth/syn/eth_mac_1g_gmii.tcl +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/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/STLV7325/fpga_gmii/lib/eth b/example/STLV7325/fpga_gmii/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/example/STLV7325/fpga_gmii/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/example/STLV7325/fpga_gmii/tb/fpga_core/Makefile b/example/STLV7325/fpga_gmii/tb/fpga_core/Makefile new file mode 100644 index 000000000..5f583808b --- /dev/null +++ b/example/STLV7325/fpga_gmii/tb/fpga_core/Makefile @@ -0,0 +1,101 @@ +# 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/iddr.v +VERILOG_SOURCES += ../../lib/eth/rtl/oddr.v +VERILOG_SOURCES += ../../lib/eth/rtl/ssio_sdr_in.v +VERILOG_SOURCES += ../../lib/eth/rtl/ssio_sdr_out.v +VERILOG_SOURCES += ../../lib/eth/rtl/gmii_phy_if.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_mac_1g_gmii_fifo.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_mac_1g_gmii.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/STLV7325/fpga_gmii/tb/fpga_core/test_fpga_core.py b/example/STLV7325/fpga_gmii/tb/fpga_core/test_fpga_core.py new file mode 100644 index 000000000..9514c1acd --- /dev/null +++ b/example/STLV7325/fpga_gmii/tb/fpga_core/test_fpga_core.py @@ -0,0 +1,217 @@ +""" + +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.clock import Clock +from cocotb.triggers import RisingEdge + +from cocotbext.eth import GmiiFrame, GmiiPhy + + +class TB: + def __init__(self, dut, speed=1000e6): + self.dut = dut + + self.log = SimLog("cocotb.tb") + self.log.setLevel(logging.DEBUG) + + cocotb.fork(Clock(dut.clk, 8, units="ns").start()) + + # Ethernet + self.gmii_phy = GmiiPhy(dut.phy_txd, dut.phy_tx_er, dut.phy_tx_en, dut.phy_tx_clk, dut.phy_gtx_clk, + dut.phy_rxd, dut.phy_rx_er, dut.phy_rx_dv, 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_rxd.setimmediatevalue(1) + dut.uart_cts.setimmediatevalue(1) + + 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 + + +@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.gmii_phy.rx.send(test_frame) + + tb.log.info("receive ARP request") + + rx_frame = await tb.gmii_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.gmii_phy.rx.send(resp_frame) + + tb.log.info("receive UDP packet") + + rx_frame = await tb.gmii_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, "iddr.v"), + os.path.join(eth_rtl_dir, "oddr.v"), + os.path.join(eth_rtl_dir, "ssio_sdr_in.v"), + os.path.join(eth_rtl_dir, "ssio_sdr_out.v"), + os.path.join(eth_rtl_dir, "gmii_phy_if.v"), + os.path.join(eth_rtl_dir, "eth_mac_1g_gmii_fifo.v"), + os.path.join(eth_rtl_dir, "eth_mac_1g_gmii.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, + )