diff --git a/fpga/lib/eth/.gitignore b/fpga/lib/eth/.gitignore new file mode 100644 index 000000000..964df4d4a --- /dev/null +++ b/fpga/lib/eth/.gitignore @@ -0,0 +1,6 @@ +*~ +*.lxt +*.pyc +*.vvp +*.kate-swp + diff --git a/fpga/lib/eth/.travis.yml b/fpga/lib/eth/.travis.yml new file mode 100644 index 000000000..15f9a3c31 --- /dev/null +++ b/fpga/lib/eth/.travis.yml @@ -0,0 +1,15 @@ +language: python +python: + - "3.6" +before_install: + - export d=`pwd` + - export PYTHON_EXE=`which python` + - sudo apt-get update -qq + - sudo apt-get install -y iverilog + - git clone https://github.com/jandecaluwe/myhdl.git + - cd $d/myhdl && sudo $PYTHON_EXE setup.py install + - cd $d/myhdl/cosimulation/icarus && make && sudo install -m 0755 -D ./myhdl.vpi /usr/lib/x86_64-linux-gnu/ivl/myhdl.vpi + - cd $d +script: + - cd tb && py.test + diff --git a/fpga/lib/eth/AUTHORS b/fpga/lib/eth/AUTHORS new file mode 100644 index 000000000..7dab2b3a5 --- /dev/null +++ b/fpga/lib/eth/AUTHORS @@ -0,0 +1 @@ +Alex Forencich diff --git a/fpga/lib/eth/COPYING b/fpga/lib/eth/COPYING new file mode 100644 index 000000000..dc298464e --- /dev/null +++ b/fpga/lib/eth/COPYING @@ -0,0 +1,19 @@ +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. diff --git a/fpga/lib/eth/README b/fpga/lib/eth/README new file mode 120000 index 000000000..42061c01a --- /dev/null +++ b/fpga/lib/eth/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/fpga/lib/eth/README.md b/fpga/lib/eth/README.md new file mode 100644 index 000000000..612296390 --- /dev/null +++ b/fpga/lib/eth/README.md @@ -0,0 +1,562 @@ +# Verilog Ethernet Components Readme + +For more information and updates: http://alexforencich.com/wiki/en/verilog/ethernet/start + +GitHub repository: https://github.com/alexforencich/verilog-ethernet + +## Introduction + +Collection of Ethernet-related components for gigabit, 10G, and 25G packet +processing (8 bit and 64 bit datapaths). Includes modules for handling +Ethernet frames as well as IP, UDP, and ARP and the components for +constructing a complete UDP/IP stack. Includes MAC modules for gigabit and +10G/25G, a 10G/25G PCS/PMA PHY module, and a 10G/25G combination MAC/PCS/PMA +module. Also includes full MyHDL testbench with intelligent bus cosimulation +endpoints. + +For IP and ARP support only, use ip_complete (1G) or ip_complete_64 (10G/25G). + +For UDP, IP, and ARP support, use udp_complete (1G) or udp_complete_64 +(10G/25G). + +Top level gigabit and 10G/25G MAC modules are eth_mac_*, with various +interfaces and with/without FIFOs. Top level 10G/25G PCS/PMA PHY module is +eth_phy_10g. Top level 10G/25G MAC/PCS/PMA combination module is +eth_mac_phy_10g. + +## Documentation + +### arp module + +ARP handling logic with parametrizable retry timeout parameters. + +### arp_64 module + +ARP handling logic with parametrizable retry timeout parameters and 64 bit +datapath for 10G/25G Ethernet. + +### arp_cache module + +Basic hash-based cache for ARP entries. Parametrizable depth. + +### arp_eth_rx module + +ARP frame receiver. + +### arp_eth_rx_64 module + +ARP frame receiver with 64 bit datapath for 10G/25G Ethernet. + +### arp_eth_tx module + +ARP frame transmitter. + +### arp_eth_tx_64 module + +ARP frame transmitter with 64 bit datapath for 10G/25G Ethernet. + +### axis_eth_fcs module + +Ethernet frame check sequence calculator. + +### axis_eth_fcs_64 module + +Ethernet frame check sequence calculator with 64 bit datapath for 10G/25G +Ethernet. + +### axis_eth_fcs_check module + +Ethernet frame check sequence checker. + +### axis_eth_fcs_insert module + +Ethernet frame check sequence inserter. + +### axis_gmii_rx module + +AXI stream GMII/MII frame receiver with clock enable and MII select. + +### axis_gmii_tx module + +AXI stream GMII/MII frame transmitter with clock enable and MII select. + +### axis_xgmii_rx_32 module + +AXI stream XGMII frame receiver with 32 bit datapath. + +### axis_xgmii_rx_64 module + +AXI stream XGMII frame receiver with 64 bit datapath. + +### axis_xgmii_tx_32 module + +AXI stream XGMII frame transmitter with 32 bit datapath. + +### axis_xgmii_tx_64 module + +AXI stream XGMII frame transmitter with 64 bit datapath. + +### eth_arb_mux module + +Ethernet frame arbitrated muliplexer with parametrizable data width and port +count. Supports priority and round-robin arbitration. + +### eth_axis_rx module + +Ethernet frame receiver. + +### eth_axis_rx_64 module + +Ethernet frame receiver with 64 bit datapath for 10G/25G Ethernet. + +### eth_axis_tx module + +Ethernet frame transmitter. + +### eth_axis_tx_64 module + +Ethernet frame transmitter with 64 bit datapath for 10G/25G Ethernet. + +### eth_demux module + +Ethernet frame demuliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### eth_mac_1g module + +Gigabit Ethernet MAC with GMII interface. + +### eth_mac_1g_fifo module + +Gigabit Ethernet MAC with GMII interface and FIFOs. + +### eth_mac_1g_gmii module + +Tri-mode Ethernet MAC with GMII/MII interface and automatic PHY rate +adaptation logic. + +### eth_mac_1g_gmii_fifo module + +Tri-mode Ethernet MAC with GMII/MII interface, FIFOs, and automatic PHY rate +adaptation logic. + +### eth_mac_1g_rgmii module + +Tri-mode Ethernet MAC with RGMII interface and automatic PHY rate adaptation +logic. + +### eth_mac_1g_rgmii_fifo module + +Tri-mode Ethernet MAC with RGMII interface, FIFOs, and automatic PHY rate +adaptation logic. + +### eth_mac_10g module + +10G/25G Ethernet MAC with XGMII interface. Datapath selectable between 32 and +64 bits. + +### eth_mac_10g_fifo module + +10G/25G Ethernet MAC with XGMII interface and FIFOs. Datapath selectable +between 32 and 64 bits. + +### eth_mac_mii module + +Ethernet MAC with MII interface. + +### eth_mac_mii_fifo module + +Ethernet MAC with MII interface and FIFOs. + +### eth_mac_phy_10g module + +10G/25G Ethernet MAC/PHY combination module with SERDES interface. + +### eth_mac_phy_10g_fifo module + +10G/25G Ethernet MAC/PHY combination module with SERDES interface and FIFOs. + +### eth_mac_phy_10g_rx module + +10G/25G Ethernet MAC/PHY combination module with SERDES interface, RX path. + +### eth_mac_phy_10g_tx module + +10G/25G Ethernet MAC/PHY combination module with SERDES interface, TX path. + +### eth_mux module + +Ethernet frame muliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### eth_phy_10g module + +10G/25G Ethernet PCS/PMA PHY. + +### eth_phy_10g_rx module + +10G/25G Ethernet PCS/PMA PHY receive-side logic. + +### eth_phy_10g_rx_ber_mon module + +10G/25G Ethernet PCS/PMA PHY BER monitor. + +### eth_phy_10g_rx_frame_sync module + +10G/25G Ethernet PCS/PMA PHY frame synchronizer. + +### eth_phy_10g_tx module + +10G/25G Ethernet PCS/PMA PHY transmit-side logic. + +### gmii_phy_if module + +GMII/MII PHY interface and clocking logic. + +### ip module + +IPv4 block with 8 bit data width for gigabit Ethernet. Manages IPv4 packet +transmssion and reception. Interfaces with ARP module for MAC address lookup. + +### ip_64 module + +IPv4 block with 64 bit data width for 10G/25G Ethernet. Manages IPv4 packet +transmssion and reception. Interfaces with ARP module for MAC address lookup. + +### ip_arb_mux module + +IP frame arbitrated muliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### ip_complete module + +IPv4 module with ARP integration. + +Top level for gigabit IP stack. + +### ip_complete_64 module + +IPv4 module with ARP integration and 64 bit data width for 10G/25G Ethernet. + +Top level for 10G/25G IP stack. + +### ip_demux module + +IP frame demuliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### ip_eth_rx module + +IP frame receiver. + +### ip_eth_rx_64 module + +IP frame receiver with 64 bit datapath for 10G/25G Ethernet. + +### ip_eth_tx module + +IP frame transmitter. + +### ip_eth_tx_64 module + +IP frame transmitter with 64 bit datapath for 10G/25G Ethernet. + +### ip_mux module + +IP frame muliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### lfsr module + +Fully parametrizable combinatorial parallel LFSR/CRC module. + +### mii_phy_if module + +MII PHY interface and clocking logic. + +### ptp_clock module + +PTP clock module with PPS output. Generates both 64 bit and 96 bit timestamp +formats. Fine frequeny adjustment supported with configurable fractional +nanoseconds field. + +### rgmii_phy_if module + +RGMII PHY interface and clocking logic. + +### udp module + +UDP block with 8 bit data width for gigabit Ethernet. Manages UDP packet +transmssion and reception. + +### udp_64 module + +UDP block with 64 bit data width for 10G/25G Ethernet. Manages UDP packet +transmssion and reception. + +### udp_arb_mux module + +UDP frame arbitrated muliplexer with parametrizable data width and port +count. Supports priority and round-robin arbitration. + +### udp_checksum_gen module + +UDP checksum generator module. Calculates UDP length, IP length, and +UDP checksum fields. + +### udp_checksum_gen_64 module + +UDP checksum generator module with 64 bit datapath. Calculates UDP +length, IP length, and UDP checksum fields. + +### udp_complete module + +UDP module with IPv4 and ARP integration. + +Top level for gigabit UDP stack. + +### udp_complete_64 module + +UDP module with IPv4 and ARP integration and 64 bit data width for 10G +Ethernet. + +Top level for 10G/25G UDP stack. + +### udp_demux module + +UDP frame demuliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### udp_ip_rx module + +UDP frame receiver. + +### udp_ip_rx_64 module + +UDP frame receiver with 64 bit datapath for 10G/25G Ethernet. + +### udp_ip_tx module + +UDP frame transmitter. + +### udp_ip_tx_64 module + +UDP frame transmitter with 64 bit datapath for 10G/25G Ethernet. + +### udp_mux module + +UDP frame muliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### xgmii_baser_dec_64 module + +XGMII 10GBASE-R decoder for 10G PCS/PMA PHY. + +### xgmii_baser_enc_64 module + +XGMII 10GBASE-R encoder for 10G PCS/PMA PHY. + +### xgmii_deinterleave module + +XGMII de-interleaver for interfacing with PHY cores that interleave the +control and data lines. + +### xgmii_interleave module + +XGMII interleaver for interfacing with PHY cores that interleave the control +and data lines. + +### Common signals + + tdata : Data (width generally DATA_WIDTH) + tkeep : Data word valid (width generally KEEP_WIDTH, present on _64 modules) + tvalid : Data valid + tready : Sink ready + tlast : End-of-frame + tuser : Bad frame (valid with tlast & tvalid) + +### Source Files + + rtl/arp.v : ARP handling logic + rtl/arp_64.v : ARP handling logic (64 bit) + rtl/arp_cache.v : ARP LRU cache + rtl/arp_eth_rx.v : ARP frame receiver + rtl/arp_eth_rx_64.v : ARP frame receiver (64 bit) + rtl/arp_eth_tx.v : ARP frame transmitter + rtl/arp_eth_tx_64.v : ARP frame transmitter (64 bit) + rtl/eth_arb_mux.py : Ethernet frame arbitrated multiplexer generator + rtl/axis_eth_fcs.v : Ethernet FCS calculator + rtl/axis_eth_fcs_64.v : Ethernet FCS calculator (64 bit) + rtl/axis_eth_fcs_insert.v : Ethernet FCS inserter + rtl/axis_eth_fcs_check.v : Ethernet FCS checker + rtl/axis_gmii_rx.v : AXI stream GMII/MII receiver + rtl/axis_gmii_tx.v : AXI stream GMII/MII transmitter + rtl/axis_xgmii_rx_32.v : AXI stream XGMII receiver (32 bit) + rtl/axis_xgmii_rx_64.v : AXI stream XGMII receiver (64 bit) + rtl/axis_xgmii_tx_32.v : AXI stream XGMII transmitter (32 bit) + rtl/axis_xgmii_tx_64.v : AXI stream XGMII transmitter (64 bit) + rtl/eth_arb_mux.v : Ethernet frame arbitrated multiplexer + rtl/eth_axis_rx.v : Ethernet frame receiver + rtl/eth_axis_rx_64.v : Ethernet frame receiver (64 bit) + rtl/eth_axis_tx.v : Ethernet frame transmitter + rtl/eth_axis_tx_64.v : Ethernet frame transmitter (64 bit) + rtl/eth_demux.v : Ethernet frame demultiplexer + rtl/eth_mac_1g.v : Gigabit Ethernet GMII MAC + rtl/eth_mac_1g_fifo.v : Gigabit Ethernet GMII MAC with FIFO + rtl/eth_mac_1g_gmii.v : Tri-mode Ethernet GMII/MII MAC + rtl/eth_mac_1g_gmii_fifo.v : Tri-mode Ethernet GMII/MII MAC with FIFO + rtl/eth_mac_1g_rgmii.v : Tri-mode Ethernet RGMII MAC + rtl/eth_mac_1g_rgmii_fifo.v : Tri-mode Ethernet RGMII MAC with FIFO + rtl/eth_mac_10g.v : 10G/25G Ethernet XGMII MAC + rtl/eth_mac_10g_fifo.v : 10G/25G Ethernet XGMII MAC with FIFO + rtl/eth_mac_mii.v : Ethernet MII MAC + rtl/eth_mac_mii_fifo.v : Ethernet MII MAC with FIFO + rtl/eth_mac_phy_10g.v : 10G/25G Ethernet XGMII MAC/PHY + rtl/eth_mac_phy_10g_fifo.v : 10G/25G Ethernet XGMII MAC/PHY with FIFO + rtl/eth_mac_phy_10g_rx.v : 10G/25G Ethernet XGMII MAC/PHY RX with FIFO + rtl/eth_mac_phy_10g_tx.v : 10G/25G Ethernet XGMII MAC/PHY TX with FIFO + rtl/eth_mux.v : Ethernet frame multiplexer + rtl/gmii_phy_if.v : GMII PHY interface + rtl/iddr.v : Generic DDR input register + rtl/ip.v : IPv4 block + rtl/ip_64.v : IPv4 block (64 bit) + rtl/ip_arb_mux.v : IP frame arbitrated multiplexer + rtl/ip_complete.v : IPv4 stack (IP-ARP integration) + rtl/ip_complete_64.v : IPv4 stack (IP-ARP integration) (64 bit) + rtl/ip_demux.v : IP frame demultiplexer + rtl/ip_eth_rx.v : IPv4 frame receiver + rtl/ip_eth_rx_64.v : IPv4 frame receiver (64 bit) + rtl/ip_eth_tx.v : IPv4 frame transmitter + rtl/ip_eth_tx_64.v : IPv4 frame transmitter (64 bit) + rtl/ip_mux.v : IP frame multiplexer + rtl/lfsr.v : Generic LFSR/CRC module + rtl/mii_phy_if.v : MII PHY interface + rtl/oddr.v : Generic DDR output register + rtl/ptp_clock.v : PTP clock + rtl/rgmii_phy_if.v : RGMII PHY interface + rtl/ssio_ddr_in.v : Generic source synchronous IO DDR input module + rtl/ssio_ddr_in_diff.v : Generic source synchronous IO DDR differential input module + rtl/ssio_ddr_out.v : Generic source synchronous IO DDR output module + rtl/ssio_ddr_out_diff.v : Generic source synchronous IO DDR differential output module + rtl/ssio_sdr_in.v : Generic source synchronous IO SDR input module + rtl/ssio_sdr_in_diff.v : Generic source synchronous IO SDR differential input module + rtl/ssio_sdr_out.v : Generic source synchronous IO SDR output module + rtl/ssio_sdr_out_diff.v : Generic source synchronous IO SDR differential output module + rtl/udp.v : UDP block + rtl/udp_64.v : UDP block (64 bit) + rtl/udp_arb_mux.v : UDP frame arbitrated multiplexer + rtl/udp_checksum_gen.v : UDP checksum generator + rtl/udp_checksum_gen_64.v : UDP checksum generator (64 bit) + rtl/udp_complete.v : UDP stack (IP-ARP-UDP) + rtl/udp_complete_64.v : UDP stack (IP-ARP-UDP) (64 bit) + rtl/udp_demux.v : UDP frame demultiplexer + rtl/udp_ip_rx.v : UDP frame receiver + rtl/udp_ip_rx_64.v : UDP frame receiver (64 bit) + rtl/udp_ip_tx.v : UDP frame transmitter + rtl/udp_ip_tx_64.v : UDP frame transmitter (64 bit) + rtl/udp_mux.v : UDP frame multiplexer + rtl/xgmii_baser_dec_64.v : XGMII 10GBASE-R decoder + rtl/xgmii_baser_enc_64.v : XGMII 10GBASE-R encoder + rtl/xgmii_deinterleave.v : XGMII data/control de-interleaver + rtl/xgmii_interleave.v : XGMII data/control interleaver + +### AXI Stream Interface Example + +transfer with header data + + __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + ______________ ___________ + hdr_ready \_________________/ + _____ + hdr_valid ________/ \_____________________________ + _____ + hdr_data XXXXXXXXX_HDR_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + ___________ _____ _____ + tdata XXXXXXXXX_A0________X_A1__X_A2__XXXXXXXXXXXX + ___________ _____ _____ + tkeep XXXXXXXXX_K0________X_K1__X_K2__XXXXXXXXXXXX + _______________________ + tvalid ________/ \___________ + _________________ + tready ______________/ \___________ + _____ + tlast __________________________/ \___________ + + tuser ____________________________________________ + + +two byte transfer with sink pause after each byte + + __ __ __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _________________ + tdata XXXXXXXXX_D0__X_D1______________XXXXXXXXXXXXXXXXXXXXXXXX + _____ _________________ + tkeep XXXXXXXXX_K0__X_K1______________XXXXXXXXXXXXXXXXXXXXXXXX + _______________________ + tvalid ________/ \_______________________ + ______________ _____ ___________ + tready \___________/ \___________/ + _________________ + tlast ______________/ \_______________________ + + tuser ________________________________________________________ + + +two back-to-back packets, no pauses + + __ __ __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _____ _____ _____ _____ _____ + tdata XXXXXXXXX_A0__X_A1__X_A2__X_B0__X_B1__X_B2__XXXXXXXXXXXX + _____ _____ _____ _____ _____ _____ + tkeep XXXXXXXXX_K0__X_K1__X_K2__X_K0__X_K1__X_K2__XXXXXXXXXXXX + ___________________________________ + tvalid ________/ \___________ + ________________________________________________________ + tready + _____ _____ + tlast ____________________/ \___________/ \___________ + + tuser ________________________________________________________ + + +bad frame + + __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _____ _____ + tdata XXXXXXXXX_A0__X_A1__X_A2__XXXXXXXXXXXX + _____ _____ _____ + tkeep XXXXXXXXX_K0__X_K1__X_K2__XXXXXXXXXXXX + _________________ + tvalid ________/ \___________ + ______________________________________ + tready + _____ + tlast ____________________/ \___________ + _____ + tuser ____________________/ \___________ + + +## Testing + +Running the included testbenches requires MyHDL and Icarus Verilog. Make sure +that myhdl.vpi is installed properly for cosimulation to work correctly. The +testbenches can be run with a Python test runner like nose or py.test, or the +individual test scripts can be run with python directly. + +### Testbench Files + + tb/arp_ep.py : MyHDL ARP frame endpoints + tb/axis_ep.py : MyHDL AXI Stream endpoints + tb/baser_serdes.py : MyHDL 10GBASE-R SERDES endpoints + tb/eth_ep.py : MyHDL Ethernet frame endpoints + tb/gmii_ep.py : MyHDL GMII endpoints + tb/ip_ep.py : MyHDL IP frame endpoints + tb/mii_ep.py : MyHDL MII endpoints + tb/ptp.py : MyHDL PTP clock model + tb/rgmii_ep.py : MyHDL RGMII endpoints + tb/udp_ep.py : MyHDL UDP frame endpoints + tb/xgmii_ep.py : MyHDL XGMII endpoints diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/Makefile b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/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/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/README.md b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/README.md new file mode 100644 index 000000000..89881d19c --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/README.md @@ -0,0 +1,24 @@ +# Verilog Ethernet ADM-PCIE-9V3 Example Design + +## Introduction + +This example design targets the Alpha Data ADM-PCIE-9V3 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: xcvu3p-ffvc1517-2-i +PHY: 10G BASE-R PHY IP core and internal GTY transceiver + +## 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 ADM-PCIE-9V3 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. + diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/common/vivado.mk b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/fpga.xdc b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/fpga.xdc new file mode 100644 index 000000000..0ba473e92 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/fpga.xdc @@ -0,0 +1,176 @@ +# XDC constraints for the ADM-PCIE-9V3 +# part: xcvu3p-ffvc1517-2-i + +# 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.UNUSEDPIN {Pullnone} [current_design] +set_property BITSTREAM.CONFIG.OVERTEMPSHUTDOWN Enable [current_design] + +# 300 MHz system clock +set_property -dict {LOC AP26 IOSTANDARD LVDS DIFF_TERM_ADV TERM_100} [get_ports clk_300mhz_p] +set_property -dict {LOC AP27 IOSTANDARD LVDS DIFF_TERM_ADV TERM_100} [get_ports clk_300mhz_n] +create_clock -period 3.333 -name clk_300mhz [get_ports clk_300mhz_p] + +# LEDs +set_property -dict {LOC AT27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {user_led_g[0]}] +set_property -dict {LOC AU27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {user_led_g[1]}] +set_property -dict {LOC AU23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {user_led_r}] +set_property -dict {LOC AH24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {front_led[0]}] +set_property -dict {LOC AJ23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {front_led[1]}] + +# Switches +set_property -dict {LOC AV27 IOSTANDARD LVCMOS18} [get_ports {user_sw[0]}] +set_property -dict {LOC AW27 IOSTANDARD LVCMOS18} [get_ports {user_sw[1]}] + +# GPIO +#set_property -dict {LOC G30 IOSTANDARD LVCMOS18} [get_ports gpio_p[0]] +#set_property -dict {LOC F30 IOSTANDARD LVCMOS18} [get_ports gpio_n[0]] +#set_property -dict {LOC J31 IOSTANDARD LVCMOS18} [get_ports gpio_p[1]] +#set_property -dict {LOC H31 IOSTANDARD LVCMOS18} [get_ports gpio_n[1]] + +# QSFP28 Interfaces +set_property -dict {LOC G38 } [get_ports qsfp_0_rx_0_p] ;# MGTYRXN0_128 GTYE3_CHANNEL_X0Y16 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC G39 } [get_ports qsfp_0_rx_0_n] ;# MGTYRXP0_128 GTYE3_CHANNEL_X0Y16 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC E38 } [get_ports qsfp_0_rx_1_p] ;# MGTYRXN1_128 GTYE3_CHANNEL_X0Y17 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC E39 } [get_ports qsfp_0_rx_1_n] ;# MGTYRXP1_128 GTYE3_CHANNEL_X0Y17 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC C38 } [get_ports qsfp_0_rx_2_p] ;# MGTYRXN2_128 GTYE3_CHANNEL_X0Y18 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC C39 } [get_ports qsfp_0_rx_2_n] ;# MGTYRXP2_128 GTYE3_CHANNEL_X0Y18 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC B36 } [get_ports qsfp_0_rx_3_p] ;# MGTYRXN3_128 GTYE3_CHANNEL_X0Y19 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC B37 } [get_ports qsfp_0_rx_3_n] ;# MGTYRXP3_128 GTYE3_CHANNEL_X0Y19 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC F35 } [get_ports qsfp_0_tx_0_p] ;# MGTYTXN0_128 GTYE3_CHANNEL_X0Y16 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC F36 } [get_ports qsfp_0_tx_0_n] ;# MGTYTXP0_128 GTYE3_CHANNEL_X0Y16 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC D35 } [get_ports qsfp_0_tx_1_p] ;# MGTYTXN1_128 GTYE3_CHANNEL_X0Y17 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC D36 } [get_ports qsfp_0_tx_1_n] ;# MGTYTXP1_128 GTYE3_CHANNEL_X0Y17 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC C33 } [get_ports qsfp_0_tx_2_p] ;# MGTYTXN2_128 GTYE3_CHANNEL_X0Y18 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC C34 } [get_ports qsfp_0_tx_2_n] ;# MGTYTXP2_128 GTYE3_CHANNEL_X0Y18 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC A33 } [get_ports qsfp_0_tx_3_p] ;# MGTYTXN3_128 GTYE3_CHANNEL_X0Y19 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC A34 } [get_ports qsfp_0_tx_3_n] ;# MGTYTXP3_128 GTYE3_CHANNEL_X0Y19 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC N33 } [get_ports qsfp_0_mgt_refclk_p] ;# MGTREFCLK0P_128 from ? +#set_property -dict {LOC N34 } [get_ports qsfp_0_mgt_refclk_n] ;# MGTREFCLK0N_128 from ? +set_property -dict {LOC F29 IOSTANDARD LVCMOS18 PULLUP true} [get_ports qsfp_0_modprs_l] +set_property -dict {LOC D31 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports qsfp_0_sel_l] + +# 161.1328125 MHz MGT reference clock +create_clock -period 6.206 -name qsfp_0_mgt_refclk [get_ports qsfp_0_mgt_refclk_p] + +set_property -dict {LOC R38 } [get_ports qsfp_1_rx_0_p] ;# MGTYRXN0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC R39 } [get_ports qsfp_1_rx_0_n] ;# MGTYRXP0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC N38 } [get_ports qsfp_1_rx_1_p] ;# MGTYRXN1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC N39 } [get_ports qsfp_1_rx_1_n] ;# MGTYRXP1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC L38 } [get_ports qsfp_1_rx_2_p] ;# MGTYRXN2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC L39 } [get_ports qsfp_1_rx_2_n] ;# MGTYRXP2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC J38 } [get_ports qsfp_1_rx_3_p] ;# MGTYRXN3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC J39 } [get_ports qsfp_1_rx_3_n] ;# MGTYRXP3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC P35 } [get_ports qsfp_1_tx_0_p] ;# MGTYTXN0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC P36 } [get_ports qsfp_1_tx_0_n] ;# MGTYTXP0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC M35 } [get_ports qsfp_1_tx_1_p] ;# MGTYTXN1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC M36 } [get_ports qsfp_1_tx_1_n] ;# MGTYTXP1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC K35 } [get_ports qsfp_1_tx_2_p] ;# MGTYTXN2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC K36 } [get_ports qsfp_1_tx_2_n] ;# MGTYTXP2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC H35 } [get_ports qsfp_1_tx_3_p] ;# MGTYTXN3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC H36 } [get_ports qsfp_1_tx_3_n] ;# MGTYTXP3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC U33 } [get_ports qsfp_1_mgt_refclk_p] ;# MGTREFCLK0P_127 from ? +#set_property -dict {LOC U34 } [get_ports qsfp_1_mgt_refclk_n] ;# MGTREFCLK0N_127 from ? +set_property -dict {LOC F33 IOSTANDARD LVCMOS18 PULLUP true} [get_ports qsfp_1_modprs_l] +set_property -dict {LOC D30 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports qsfp_1_sel_l] + +# 161.1328125 MHz MGT reference clock +create_clock -period 6.206 -name qsfp_1_mgt_refclk [get_ports qsfp_1_mgt_refclk_p] + +set_property -dict {LOC B29 IOSTANDARD LVCMOS18 PULLUP true} [get_ports qsfp_reset_l] +set_property -dict {LOC C29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports qsfp_int_l] +#set_property -dict {LOC A28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports qsfp_i2c_scl] +#set_property -dict {LOC A29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports qsfp_i2c_sda] + +# PCIe Interface +#set_property -dict {LOC J2 } [get_ports {pcie_rx_p[0]}] ;# MGTYRXP3_227 GTYE3_CHANNEL_X0Y7 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC J1 } [get_ports {pcie_rx_n[0]}] ;# MGTYTXP3_227 GTYE3_CHANNEL_X0Y7 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC H5 } [get_ports {pcie_tx_p[0]}] ;# MGTYTXN3_227 GTYE3_CHANNEL_X0Y7 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC H4 } [get_ports {pcie_tx_n[0]}] ;# MGTYTXP3_227 GTYE3_CHANNEL_X0Y7 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC L2 } [get_ports {pcie_rx_p[1]}] ;# MGTYTXN2_227 GTYE3_CHANNEL_X0Y6 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC L1 } [get_ports {pcie_rx_n[1]}] ;# MGTYTXP2_227 GTYE3_CHANNEL_X0Y6 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC K5 } [get_ports {pcie_tx_p[1]}] ;# MGTYTXN2_227 GTYE3_CHANNEL_X0Y6 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC K4 } [get_ports {pcie_tx_n[1]}] ;# MGTYTXP2_227 GTYE3_CHANNEL_X0Y6 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC N2 } [get_ports {pcie_rx_p[2]}] ;# MGTYTXN1_227 GTYE3_CHANNEL_X0Y5 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC N1 } [get_ports {pcie_rx_n[2]}] ;# MGTYTXP1_227 GTYE3_CHANNEL_X0Y5 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC M5 } [get_ports {pcie_tx_p[2]}] ;# MGTYTXN1_227 GTYE3_CHANNEL_X0Y5 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC M4 } [get_ports {pcie_tx_n[2]}] ;# MGTYTXP1_227 GTYE3_CHANNEL_X0Y5 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC R2 } [get_ports {pcie_rx_p[3]}] ;# MGTYTXN0_227 GTYE3_CHANNEL_X0Y4 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC R1 } [get_ports {pcie_rx_n[3]}] ;# MGTYTXP0_227 GTYE3_CHANNEL_X0Y4 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC P5 } [get_ports {pcie_tx_p[3]}] ;# MGTYTXN0_227 GTYE3_CHANNEL_X0Y4 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC P4 } [get_ports {pcie_tx_n[3]}] ;# MGTYTXP0_227 GTYE3_CHANNEL_X0Y4 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC U2 } [get_ports {pcie_rx_p[4]}] ;# MGTYTXN3_226 GTYE3_CHANNEL_X0Y3 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC U1 } [get_ports {pcie_rx_n[4]}] ;# MGTYTXP3_226 GTYE3_CHANNEL_X0Y3 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC T5 } [get_ports {pcie_tx_p[4]}] ;# MGTYTXN3_226 GTYE3_CHANNEL_X0Y3 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC T4 } [get_ports {pcie_tx_n[4]}] ;# MGTYTXP3_226 GTYE3_CHANNEL_X0Y3 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC W2 } [get_ports {pcie_rx_p[5]}] ;# MGTYTXN2_226 GTYE3_CHANNEL_X0Y2 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC W1 } [get_ports {pcie_rx_n[5]}] ;# MGTYTXP2_226 GTYE3_CHANNEL_X0Y2 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC V5 } [get_ports {pcie_tx_p[5]}] ;# MGTYTXN2_226 GTYE3_CHANNEL_X0Y2 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC V4 } [get_ports {pcie_tx_n[5]}] ;# MGTYTXP2_226 GTYE3_CHANNEL_X0Y2 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AA2 } [get_ports {pcie_rx_p[6]}] ;# MGTYTXN1_226 GTYE3_CHANNEL_X0Y1 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AA1 } [get_ports {pcie_rx_n[6]}] ;# MGTYTXP1_226 GTYE3_CHANNEL_X0Y1 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AB5 } [get_ports {pcie_tx_p[6]}] ;# MGTYTXN1_226 GTYE3_CHANNEL_X0Y1 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AB4 } [get_ports {pcie_tx_n[6]}] ;# MGTYTXP1_226 GTYE3_CHANNEL_X0Y1 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AC2 } [get_ports {pcie_rx_p[7]}] ;# MGTYTXN0_226 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AC1 } [get_ports {pcie_rx_n[7]}] ;# MGTYTXP0_226 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AD5 } [get_ports {pcie_tx_p[7]}] ;# MGTYTXN0_226 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AD4 } [get_ports {pcie_tx_n[7]}] ;# MGTYTXP0_226 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AE2 } [get_ports {pcie_rx_p[8]}] ;# MGTYTXN3_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AE1 } [get_ports {pcie_rx_n[8]}] ;# MGTYTXP3_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AF5 } [get_ports {pcie_tx_p[8]}] ;# MGTYTXN3_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AF4 } [get_ports {pcie_tx_n[8]}] ;# MGTYTXP3_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AG2 } [get_ports {pcie_rx_p[9]}] ;# MGTYTXN2_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AG1 } [get_ports {pcie_rx_n[9]}] ;# MGTYTXP2_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AH5 } [get_ports {pcie_tx_p[9]}] ;# MGTYTXN2_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AH4 } [get_ports {pcie_tx_n[9]}] ;# MGTYTXP2_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AJ2 } [get_ports {pcie_rx_p[10]}] ;# MGTYTXN1_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AJ1 } [get_ports {pcie_rx_n[10]}] ;# MGTYTXP1_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AK5 } [get_ports {pcie_tx_p[10]}] ;# MGTYTXN1_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AK4 } [get_ports {pcie_tx_n[10]}] ;# MGTYTXP1_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AL2 } [get_ports {pcie_rx_p[11]}] ;# MGTYTXN0_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AL1 } [get_ports {pcie_rx_n[11]}] ;# MGTYTXP0_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AM5 } [get_ports {pcie_tx_p[11]}] ;# MGTYTXN0_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AM4 } [get_ports {pcie_tx_n[11]}] ;# MGTYTXP0_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AN2 } [get_ports {pcie_rx_p[12]}] ;# MGTYTXN3_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AN1 } [get_ports {pcie_rx_n[12]}] ;# MGTYTXP3_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AP5 } [get_ports {pcie_tx_p[12]}] ;# MGTYTXN3_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AP4 } [get_ports {pcie_tx_n[12]}] ;# MGTYTXP3_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AR2 } [get_ports {pcie_rx_p[13]}] ;# MGTYTXN2_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AR1 } [get_ports {pcie_rx_n[13]}] ;# MGTYTXP2_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AT5 } [get_ports {pcie_tx_p[13]}] ;# MGTYTXN2_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AT4 } [get_ports {pcie_tx_n[13]}] ;# MGTYTXP2_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AU2 } [get_ports {pcie_rx_p[14]}] ;# MGTYTXN1_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AU1 } [get_ports {pcie_rx_n[14]}] ;# MGTYTXP1_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AU7 } [get_ports {pcie_tx_p[14]}] ;# MGTYTXN1_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AU6 } [get_ports {pcie_tx_n[14]}] ;# MGTYTXP1_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AV4 } [get_ports {pcie_rx_p[15]}] ;# MGTYTXN0_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AV3 } [get_ports {pcie_rx_n[15]}] ;# MGTYTXP0_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AW7 } [get_ports {pcie_tx_p[15]}] ;# MGTYTXN0_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AW6 } [get_ports {pcie_tx_n[15]}] ;# MGTYTXP0_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AA7 } [get_ports pcie_refclk_1_p] ;# MGTREFCLK0P_226 +#set_property -dict {LOC AA6 } [get_ports pcie_refclk_1_n] ;# MGTREFCLK0N_226 +#set_property -dict {LOC AJ7 } [get_ports pcie_refclk_2_p] ;# MGTREFCLK0P_224 +#set_property -dict {LOC AJ6 } [get_ports pcie_refclk_2_n] ;# MGTREFCLK0N_224 +#set_property -dict {LOC AJ31 IOSTANDARD LVCMOS18 PULLUP true} [get_ports perst_0] +#set_property -dict {LOC AH29 IOSTANDARD LVCMOS18 PULLUP true} [get_ports perst_1] + +# 100 MHz MGT reference clock +#create_clock -period 10 -name pcie_mgt_refclk_1 [get_ports pcie_refclk_1_p] +#create_clock -period 10 -name pcie_mgt_refclk_2 [get_ports pcie_refclk_2_p] + +# QSPI flash +#set_property -dict {LOC AB10 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi_clk}] +#set_property -dict {LOC AB8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi0_dq[0]}] +#set_property -dict {LOC AD8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi0_dq[1]}] +#set_property -dict {LOC Y8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi0_dq[2]}] +#set_property -dict {LOC AC8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi0_dq[3]}] +#set_property -dict {LOC AF30 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi1_dq[0]}] +#set_property -dict {LOC AG30 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi1_dq[1]}] +#set_property -dict {LOC AF28 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi1_dq[2]}] +#set_property -dict {LOC AG28 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi1_dq[3]}] diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/fpga/Makefile b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/fpga/Makefile new file mode 100644 index 000000000..1a17dbdac --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/fpga/Makefile @@ -0,0 +1,106 @@ + +# FPGA settings +FPGA_PART = xcvu3p-ffvc1517-2-i +FPGA_TOP = fpga +FPGA_ARCH = virtexuplus + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += lib/eth/rtl/eth_mac_10g_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_10g.v +SYN_FILES += lib/eth/rtl/axis_xgmii_rx_64.v +SYN_FILES += lib/eth/rtl/axis_xgmii_tx_64.v +SYN_FILES += lib/eth/rtl/eth_phy_10g.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_if.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_frame_sync.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_ber_mon.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx_if.v +SYN_FILES += lib/eth/rtl/xgmii_baser_dec_64.v +SYN_FILES += lib/eth/rtl/xgmii_baser_enc_64.v +SYN_FILES += lib/eth/rtl/lfsr.v +SYN_FILES += lib/eth/rtl/eth_axis_rx_64.v +SYN_FILES += lib/eth/rtl/eth_axis_tx_64.v +SYN_FILES += lib/eth/rtl/udp_complete_64.v +SYN_FILES += lib/eth/rtl/udp_checksum_gen_64.v +SYN_FILES += lib/eth/rtl/udp_64.v +SYN_FILES += lib/eth/rtl/udp_ip_rx_64.v +SYN_FILES += lib/eth/rtl/udp_ip_tx_64.v +SYN_FILES += lib/eth/rtl/ip_complete_64.v +SYN_FILES += lib/eth/rtl/ip_64.v +SYN_FILES += lib/eth/rtl/ip_eth_rx_64.v +SYN_FILES += lib/eth/rtl/ip_eth_tx_64.v +SYN_FILES += lib/eth/rtl/ip_arb_mux.v +SYN_FILES += lib/eth/rtl/arp_64.v +SYN_FILES += lib/eth/rtl/arp_cache.v +SYN_FILES += lib/eth/rtl/arp_eth_rx_64.v +SYN_FILES += lib/eth/rtl/arp_eth_tx_64.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 + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl + +# IP +XCI_FILES += ip/gtwizard_ultrascale_0.xci + +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 + +%_primary.mcs %_secondary.mcs %_primary.prm %_secondary.prm: %.bit + echo "write_cfgmem -force -format mcs -size 64 -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: $(FPGA_TOP)_primary.mcs $(FPGA_TOP)_secondary.mcs $(FPGA_TOP)_primary.prm $(FPGA_TOP)_secondary.prm + echo "open_hw" > 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 {mt25qu256-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 \"$(FPGA_TOP)_primary.mcs\" \"$(FPGA_TOP)_secondary.mcs\"] [current_hw_cfgmem]" >> flash.tcl + echo "set_property PROGRAM.PRM_FILES [list \"$(FPGA_TOP)_primary.prm\" \"$(FPGA_TOP)_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 + diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/ip/gtwizard_ultrascale_0.xci b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/ip/gtwizard_ultrascale_0.xci new file mode 100644 index 000000000..ef03a5467 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/ip/gtwizard_ultrascale_0.xci @@ -0,0 +1,1407 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gtwizard_ultrascale_0 + + + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111000000000000" + 2 + 2578.125 + 0 + 0 + 125 + 67 + 3 + 2 + 0 + 2 + 0 + 0 + 1 + 0 + 1 + 0 + 250 + 0 + 0 + 0 + 0 + 0 + 1 + "00000000" + "00000000" + 1 + 4 + 0 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000" + 0 + "00000000" + 1 + 0 + 5000 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + 0 + "1010000011" + 0 + "0101111100" + 4 + 1 + 64 + 10.3125 + 15 + 1 + 156.2500000 + 4 + 0 + 0x000000000000000000000000000000000000000000000000 + 161.1328125 + 0 + 0 + 0 + 1 + 1 + 0 + 64 + 156.2500000 + 156.2500000 + 0 + 257.8125 + 0 + 8 + 2 + 0 + 0 + 0 + 156.25 + 0 + 0 + 1 + 4 + 1 + 64 + 10.3125 + 15 + 1 + 156.2500000 + 4 + 0 + 161.1328125 + 0 + 0 + 1 + 1 + 0 + 64 + 156.2500000 + 156.2500000 + 1 + X0Y19 X0Y18 X0Y17 X0Y16 X0Y15 X0Y14 X0Y13 X0Y12 + gtwizard_ultrascale_0 + 0 + 0 + + 125 + BOTH + 0 + GTY + 2 + 20 + 96 + 1 + gtye4 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + -1 + -1 + -1 + -1 + 1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + -1 + 0 + 0 + 0 + -1 + 0 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 13 + 0 + 10GBASE-R + 5 + 156.2500000 + 8 + 2 + 156.2500000 + false + CORE + NONE + CORE + CORE + EXAMPLE_DESIGN + CORE + EXAMPLE_DESIGN + CORE + false + NAME + false + 250 + false + false + 250 + GTY-10GBASE-R + 0 + MULTI + 1 + ENABLE + DISABLE + ENABLE + 00000000 + false + false + false + false + false + false + false + false + 00000000 + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 4 + 1 + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + false + false + false + false + false + false + false + false + 00000000 + DISABLE + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 0 + 5000 + ENABLE + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 1 + false + 0000000000 + false + 1010000011 + NONE + false + 0101111100 + true + 0 + AC + 64B66B_ASYNC + true + AUTO + 64 + 6.1862627 + -20 + 10.3125 + X0Y15 + RXPROGDIVCLK + QPLL0 + 200 + 0 + + 161.1328125 + + OFF + 0 + PROGRAMMABLE + 800 + 64 + 15 + false + 0 + 10.3125 + 257.8125 + 0 + false + QPLL0 + 156.25 + 1 + ENABLE + 64B66B_ASYNC + CUSTOM + true + 64 + 10.3125 + X0Y15 + TXPROGDIVCLK + QPLL0 + 0 + 161.1328125 + + 64 + false + 1 + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + true + true + false + true + true + true + false + true + true + true + false + false + false + false + false + true + false + false + false + false + false + true + true + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + virtexuplus + + + xcvu3p + ffvc1517 + VERILOG + + MIXED + -2 + + I + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/lib/eth b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/debounce_switch.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/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 + +`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/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/fpga.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/fpga.v new file mode 100644 index 000000000..00e6ae2cd --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/fpga.v @@ -0,0 +1,880 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 300MHz LVDS + */ + input wire clk_300mhz_p, + input wire clk_300mhz_n, + + /* + * GPIO + */ + output wire [1:0] user_led_g, + output wire user_led_r, + output wire [1:0] front_led, + input wire [1:0] user_sw, + + /* + * Ethernet: QSFP28 + */ + output wire qsfp_0_tx_0_p, + output wire qsfp_0_tx_0_n, + input wire qsfp_0_rx_0_p, + input wire qsfp_0_rx_0_n, + output wire qsfp_0_tx_1_p, + output wire qsfp_0_tx_1_n, + input wire qsfp_0_rx_1_p, + input wire qsfp_0_rx_1_n, + output wire qsfp_0_tx_2_p, + output wire qsfp_0_tx_2_n, + input wire qsfp_0_rx_2_p, + input wire qsfp_0_rx_2_n, + output wire qsfp_0_tx_3_p, + output wire qsfp_0_tx_3_n, + input wire qsfp_0_rx_3_p, + input wire qsfp_0_rx_3_n, + input wire qsfp_0_mgt_refclk_p, + input wire qsfp_0_mgt_refclk_n, + input wire qsfp_0_modprs_l, + output wire qsfp_0_sel_l, + + output wire qsfp_1_tx_0_p, + output wire qsfp_1_tx_0_n, + input wire qsfp_1_rx_0_p, + input wire qsfp_1_rx_0_n, + output wire qsfp_1_tx_1_p, + output wire qsfp_1_tx_1_n, + input wire qsfp_1_rx_1_p, + input wire qsfp_1_rx_1_n, + output wire qsfp_1_tx_2_p, + output wire qsfp_1_tx_2_n, + input wire qsfp_1_rx_2_p, + input wire qsfp_1_rx_2_n, + output wire qsfp_1_tx_3_p, + output wire qsfp_1_tx_3_n, + input wire qsfp_1_rx_3_p, + input wire qsfp_1_rx_3_n, + input wire qsfp_1_mgt_refclk_p, + input wire qsfp_1_mgt_refclk_n, + input wire qsfp_1_modprs_l, + output wire qsfp_1_sel_l, + + output wire qsfp_reset_l, + input wire qsfp_int_l +); + +// Clock and reset + +wire clk_300mhz_ibufg; +wire clk_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire rst_125mhz_int; + +// Internal 156.25 MHz clock +wire clk_156mhz_int; +wire rst_156mhz_int; + +wire mmcm_rst = 1'b0; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS #( + .DIFF_TERM("FALSE"), + .IBUF_LOW_PWR("FALSE") +) +clk_300mhz_ibufg_inst ( + .O (clk_300mhz_ibufg), + .I (clk_300mhz_p), + .IB (clk_300mhz_n) +); + +// MMCM instance +// 300 MHz in, 125 MHz out +// PFD range: 10 MHz to 500 MHz +// VCO range: 800 MHz to 1600 MHz +// M = 10, D = 3 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +MMCME3_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(8), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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(10), + .CLKFBOUT_PHASE(0), + .DIVCLK_DIVIDE(3), + .REF_JITTER1(0.010), + .CLKIN1_PERIOD(3.333), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_300mhz_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_125mhz_bufg_inst ( + .I(clk_125mhz_mmcm_out), + .O(clk_125mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_int) +); + +// GPIO +wire [1:0] user_sw_int; + +debounce_switch #( + .WIDTH(2), + .N(4), + .RATE(125000) +) +debounce_switch_inst ( + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + .in({user_sw}), + .out({user_sw_int}) +); + +// XGMII 10G PHY +assign qsfp_0_sel_l = 1'b0; + +wire qsfp_0_tx_clk_0_int; +wire qsfp_0_tx_rst_0_int; +wire [63:0] qsfp_0_txd_0_int; +wire [7:0] qsfp_0_txc_0_int; +wire qsfp_0_rx_clk_0_int; +wire qsfp_0_rx_rst_0_int; +wire [63:0] qsfp_0_rxd_0_int; +wire [7:0] qsfp_0_rxc_0_int; +wire qsfp_0_tx_clk_1_int; +wire qsfp_0_tx_rst_1_int; +wire [63:0] qsfp_0_txd_1_int; +wire [7:0] qsfp_0_txc_1_int; +wire qsfp_0_rx_clk_1_int; +wire qsfp_0_rx_rst_1_int; +wire [63:0] qsfp_0_rxd_1_int; +wire [7:0] qsfp_0_rxc_1_int; +wire qsfp_0_tx_clk_2_int; +wire qsfp_0_tx_rst_2_int; +wire [63:0] qsfp_0_txd_2_int; +wire [7:0] qsfp_0_txc_2_int; +wire qsfp_0_rx_clk_2_int; +wire qsfp_0_rx_rst_2_int; +wire [63:0] qsfp_0_rxd_2_int; +wire [7:0] qsfp_0_rxc_2_int; +wire qsfp_0_tx_clk_3_int; +wire qsfp_0_tx_rst_3_int; +wire [63:0] qsfp_0_txd_3_int; +wire [7:0] qsfp_0_txc_3_int; +wire qsfp_0_rx_clk_3_int; +wire qsfp_0_rx_rst_3_int; +wire [63:0] qsfp_0_rxd_3_int; +wire [7:0] qsfp_0_rxc_3_int; + +assign qsfp_1_sel_l = 1'b0; + +wire qsfp_1_tx_clk_0_int; +wire qsfp_1_tx_rst_0_int; +wire [63:0] qsfp_1_txd_0_int; +wire [7:0] qsfp_1_txc_0_int; +wire qsfp_1_rx_clk_0_int; +wire qsfp_1_rx_rst_0_int; +wire [63:0] qsfp_1_rxd_0_int; +wire [7:0] qsfp_1_rxc_0_int; +wire qsfp_1_tx_clk_1_int; +wire qsfp_1_tx_rst_1_int; +wire [63:0] qsfp_1_txd_1_int; +wire [7:0] qsfp_1_txc_1_int; +wire qsfp_1_rx_clk_1_int; +wire qsfp_1_rx_rst_1_int; +wire [63:0] qsfp_1_rxd_1_int; +wire [7:0] qsfp_1_rxc_1_int; +wire qsfp_1_tx_clk_2_int; +wire qsfp_1_tx_rst_2_int; +wire [63:0] qsfp_1_txd_2_int; +wire [7:0] qsfp_1_txc_2_int; +wire qsfp_1_rx_clk_2_int; +wire qsfp_1_rx_rst_2_int; +wire [63:0] qsfp_1_rxd_2_int; +wire [7:0] qsfp_1_rxc_2_int; +wire qsfp_1_tx_clk_3_int; +wire qsfp_1_tx_rst_3_int; +wire [63:0] qsfp_1_txd_3_int; +wire [7:0] qsfp_1_txc_3_int; +wire qsfp_1_rx_clk_3_int; +wire qsfp_1_rx_rst_3_int; +wire [63:0] qsfp_1_rxd_3_int; +wire [7:0] qsfp_1_rxc_3_int; + +assign qsfp_reset_l = 1'b1; + +wire qsfp_0_rx_block_lock_0; +wire qsfp_0_rx_block_lock_1; +wire qsfp_0_rx_block_lock_2; +wire qsfp_0_rx_block_lock_3; + +wire qsfp_1_rx_block_lock_0; +wire qsfp_1_rx_block_lock_1; +wire qsfp_1_rx_block_lock_2; +wire qsfp_1_rx_block_lock_3; + +wire qsfp_0_mgt_refclk; +wire qsfp_1_mgt_refclk; + +wire [7:0] gt_txclkout; +wire gt_txusrclk; + +wire [7:0] gt_rxclkout; +wire [7:0] gt_rxusrclk; + +wire gt_reset_tx_done; +wire gt_reset_rx_done; + +wire [7:0] gt_txprgdivresetdone; +wire [7:0] gt_txpmaresetdone; +wire [7:0] gt_rxprgdivresetdone; +wire [7:0] gt_rxpmaresetdone; + +wire gt_tx_reset = ~((>_txprgdivresetdone) & (>_txpmaresetdone)); +wire gt_rx_reset = ~>_rxpmaresetdone; + +reg gt_userclk_tx_active = 1'b0; +reg [7:0] gt_userclk_rx_active = 1'b0; + +IBUFDS_GTE4 ibufds_gte4_qsfp_0_mgt_refclk_inst ( + .I (qsfp_0_mgt_refclk_p), + .IB (qsfp_0_mgt_refclk_n), + .CEB (1'b0), + .O (qsfp_0_mgt_refclk), + .ODIV2 () +); + +IBUFDS_GTE4 ibufds_gte4_qsfp_1_mgt_refclk_inst ( + .I (qsfp_1_mgt_refclk_p), + .IB (qsfp_1_mgt_refclk_n), + .CEB (1'b0), + .O (qsfp_1_mgt_refclk), + .ODIV2 () +); + + +BUFG_GT bufg_gt_tx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_tx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_txclkout[0]), + .O (gt_txusrclk) +); + +assign clk_156mhz_int = gt_txusrclk; + +always @(posedge gt_txusrclk, posedge gt_tx_reset) begin + if (gt_tx_reset) begin + gt_userclk_tx_active <= 1'b0; + end else begin + gt_userclk_tx_active <= 1'b1; + end +end + +generate + +genvar n; + +for (n = 0; n < 8; n = n + 1) begin + + BUFG_GT bufg_gt_rx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_rx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_rxclkout[n]), + .O (gt_rxusrclk[n]) + ); + + always @(posedge gt_rxusrclk[n], posedge gt_rx_reset) begin + if (gt_rx_reset) begin + gt_userclk_rx_active[n] <= 1'b0; + end else begin + gt_userclk_rx_active[n] <= 1'b1; + end + end + +end + +endgenerate + +sync_reset #( + .N(4) +) +sync_reset_156mhz_inst ( + .clk(clk_156mhz_int), + .rst(~gt_reset_tx_done), + .sync_reset_out(rst_156mhz_int) +); + +wire [5:0] qsfp_0_gt_txheader_0; +wire [63:0] qsfp_0_gt_txdata_0; +wire qsfp_0_gt_rxgearboxslip_0; +wire [5:0] qsfp_0_gt_rxheader_0; +wire [1:0] qsfp_0_gt_rxheadervalid_0; +wire [63:0] qsfp_0_gt_rxdata_0; +wire [1:0] qsfp_0_gt_rxdatavalid_0; + +wire [5:0] qsfp_0_gt_txheader_1; +wire [63:0] qsfp_0_gt_txdata_1; +wire qsfp_0_gt_rxgearboxslip_1; +wire [5:0] qsfp_0_gt_rxheader_1; +wire [1:0] qsfp_0_gt_rxheadervalid_1; +wire [63:0] qsfp_0_gt_rxdata_1; +wire [1:0] qsfp_0_gt_rxdatavalid_1; + +wire [5:0] qsfp_0_gt_txheader_2; +wire [63:0] qsfp_0_gt_txdata_2; +wire qsfp_0_gt_rxgearboxslip_2; +wire [5:0] qsfp_0_gt_rxheader_2; +wire [1:0] qsfp_0_gt_rxheadervalid_2; +wire [63:0] qsfp_0_gt_rxdata_2; +wire [1:0] qsfp_0_gt_rxdatavalid_2; + +wire [5:0] qsfp_0_gt_txheader_3; +wire [63:0] qsfp_0_gt_txdata_3; +wire qsfp_0_gt_rxgearboxslip_3; +wire [5:0] qsfp_0_gt_rxheader_3; +wire [1:0] qsfp_0_gt_rxheadervalid_3; +wire [63:0] qsfp_0_gt_rxdata_3; +wire [1:0] qsfp_0_gt_rxdatavalid_3; + +wire [5:0] qsfp_1_gt_txheader_0; +wire [63:0] qsfp_1_gt_txdata_0; +wire qsfp_1_gt_rxgearboxslip_0; +wire [5:0] qsfp_1_gt_rxheader_0; +wire [1:0] qsfp_1_gt_rxheadervalid_0; +wire [63:0] qsfp_1_gt_rxdata_0; +wire [1:0] qsfp_1_gt_rxdatavalid_0; + +wire [5:0] qsfp_1_gt_txheader_1; +wire [63:0] qsfp_1_gt_txdata_1; +wire qsfp_1_gt_rxgearboxslip_1; +wire [5:0] qsfp_1_gt_rxheader_1; +wire [1:0] qsfp_1_gt_rxheadervalid_1; +wire [63:0] qsfp_1_gt_rxdata_1; +wire [1:0] qsfp_1_gt_rxdatavalid_1; + +wire [5:0] qsfp_1_gt_txheader_2; +wire [63:0] qsfp_1_gt_txdata_2; +wire qsfp_1_gt_rxgearboxslip_2; +wire [5:0] qsfp_1_gt_rxheader_2; +wire [1:0] qsfp_1_gt_rxheadervalid_2; +wire [63:0] qsfp_1_gt_rxdata_2; +wire [1:0] qsfp_1_gt_rxdatavalid_2; + +wire [5:0] qsfp_1_gt_txheader_3; +wire [63:0] qsfp_1_gt_txdata_3; +wire qsfp_1_gt_rxgearboxslip_3; +wire [5:0] qsfp_1_gt_rxheader_3; +wire [1:0] qsfp_1_gt_rxheadervalid_3; +wire [63:0] qsfp_1_gt_rxdata_3; +wire [1:0] qsfp_1_gt_rxdatavalid_3; + +gtwizard_ultrascale_0 +qsfp_gty_inst ( + .gtwiz_userclk_tx_active_in(>_userclk_tx_active), + .gtwiz_userclk_rx_active_in(>_userclk_rx_active), + + .gtwiz_reset_clk_freerun_in(clk_125mhz_int), + .gtwiz_reset_all_in(rst_125mhz_int), + + .gtwiz_reset_tx_pll_and_datapath_in(1'b0), + .gtwiz_reset_tx_datapath_in(1'b0), + + .gtwiz_reset_rx_pll_and_datapath_in(1'b0), + .gtwiz_reset_rx_datapath_in(1'b0), + + .gtwiz_reset_rx_cdr_stable_out(), + + .gtwiz_reset_tx_done_out(gt_reset_tx_done), + .gtwiz_reset_rx_done_out(gt_reset_rx_done), + + .gtrefclk00_in({qsfp_0_mgt_refclk, qsfp_1_mgt_refclk}), + + .qpll0outclk_out(), + .qpll0outrefclk_out(), + + .gtyrxn_in({qsfp_0_rx_3_n, qsfp_0_rx_2_n, qsfp_0_rx_1_n, qsfp_0_rx_0_n, qsfp_1_rx_3_n, qsfp_1_rx_2_n, qsfp_1_rx_1_n, qsfp_1_rx_0_n}), + .gtyrxp_in({qsfp_0_rx_3_p, qsfp_0_rx_2_p, qsfp_0_rx_1_p, qsfp_0_rx_0_p, qsfp_1_rx_3_p, qsfp_1_rx_2_p, qsfp_1_rx_1_p, qsfp_1_rx_0_p}), + + .rxusrclk_in(gt_rxusrclk), + .rxusrclk2_in(gt_rxusrclk), + + .gtwiz_userdata_tx_in({qsfp_0_gt_txdata_3, qsfp_0_gt_txdata_2, qsfp_0_gt_txdata_1, qsfp_0_gt_txdata_0, qsfp_1_gt_txdata_3, qsfp_1_gt_txdata_2, qsfp_1_gt_txdata_1, qsfp_1_gt_txdata_0}), + .txheader_in({qsfp_0_gt_txheader_3, qsfp_0_gt_txheader_2, qsfp_0_gt_txheader_1, qsfp_0_gt_txheader_0, qsfp_1_gt_txheader_3, qsfp_1_gt_txheader_2, qsfp_1_gt_txheader_1, qsfp_1_gt_txheader_0}), + .txsequence_in({8{1'b0}}), + + .txusrclk_in({8{gt_txusrclk}}), + .txusrclk2_in({8{gt_txusrclk}}), + + .gtpowergood_out(), + + .gtytxn_out({qsfp_0_tx_3_n, qsfp_0_tx_2_n, qsfp_0_tx_1_n, qsfp_0_tx_0_n, qsfp_1_tx_3_n, qsfp_1_tx_2_n, qsfp_1_tx_1_n, qsfp_1_tx_0_n}), + .gtytxp_out({qsfp_0_tx_3_p, qsfp_0_tx_2_p, qsfp_0_tx_1_p, qsfp_0_tx_0_p, qsfp_1_tx_3_p, qsfp_1_tx_2_p, qsfp_1_tx_1_p, qsfp_1_tx_0_p}), + + .rxgearboxslip_in({qsfp_0_gt_rxgearboxslip_3, qsfp_0_gt_rxgearboxslip_2, qsfp_0_gt_rxgearboxslip_1, qsfp_0_gt_rxgearboxslip_0, qsfp_1_gt_rxgearboxslip_3, qsfp_1_gt_rxgearboxslip_2, qsfp_1_gt_rxgearboxslip_1, qsfp_1_gt_rxgearboxslip_0}), + .gtwiz_userdata_rx_out({qsfp_0_gt_rxdata_3, qsfp_0_gt_rxdata_2, qsfp_0_gt_rxdata_1, qsfp_0_gt_rxdata_0, qsfp_1_gt_rxdata_3, qsfp_1_gt_rxdata_2, qsfp_1_gt_rxdata_1, qsfp_1_gt_rxdata_0}), + .rxdatavalid_out({qsfp_0_gt_rxdatavalid_3, qsfp_0_gt_rxdatavalid_2, qsfp_0_gt_rxdatavalid_1, qsfp_0_gt_rxdatavalid_0, qsfp_1_gt_rxdatavalid_3, qsfp_1_gt_rxdatavalid_2, qsfp_1_gt_rxdatavalid_1, qsfp_1_gt_rxdatavalid_0}), + .rxheader_out({qsfp_0_gt_rxheader_3, qsfp_0_gt_rxheader_2, qsfp_0_gt_rxheader_1, qsfp_0_gt_rxheader_0, qsfp_1_gt_rxheader_3, qsfp_1_gt_rxheader_2, qsfp_1_gt_rxheader_1, qsfp_1_gt_rxheader_0}), + .rxheadervalid_out({qsfp_0_gt_rxheadervalid_3, qsfp_0_gt_rxheadervalid_2, qsfp_0_gt_rxheadervalid_1, qsfp_0_gt_rxheadervalid_0, qsfp_1_gt_rxheadervalid_3, qsfp_1_gt_rxheadervalid_2, qsfp_1_gt_rxheadervalid_1, qsfp_1_gt_rxheadervalid_0}), + .rxoutclk_out(gt_rxclkout), + .rxpmaresetdone_out(gt_rxpmaresetdone), + .rxprgdivresetdone_out(gt_rxprgdivresetdone), + .rxstartofseq_out(), + + .txoutclk_out(gt_txclkout), + .txpmaresetdone_out(gt_txpmaresetdone), + .txprgdivresetdone_out(gt_txprgdivresetdone) +); + +assign qsfp_0_tx_clk_0_int = clk_156mhz_int; +assign qsfp_0_tx_rst_0_int = rst_156mhz_int; + +assign qsfp_0_rx_clk_0_int = gt_rxusrclk[4]; + +sync_reset #( + .N(4) +) +qsfp_0_rx_rst_0_reset_sync_inst ( + .clk(qsfp_0_rx_clk_0_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_0_rx_rst_0_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_0_phy_0_inst ( + .tx_clk(qsfp_0_tx_clk_0_int), + .tx_rst(qsfp_0_tx_rst_0_int), + .rx_clk(qsfp_0_rx_clk_0_int), + .rx_rst(qsfp_0_rx_rst_0_int), + .xgmii_txd(qsfp_0_txd_0_int), + .xgmii_txc(qsfp_0_txc_0_int), + .xgmii_rxd(qsfp_0_rxd_0_int), + .xgmii_rxc(qsfp_0_rxc_0_int), + .serdes_tx_data(qsfp_0_gt_txdata_0), + .serdes_tx_hdr(qsfp_0_gt_txheader_0), + .serdes_rx_data(qsfp_0_gt_rxdata_0), + .serdes_rx_hdr(qsfp_0_gt_rxheader_0), + .serdes_rx_bitslip(qsfp_0_gt_rxgearboxslip_0), + .rx_block_lock(qsfp_0_rx_block_lock_0), + .rx_high_ber() +); + +assign qsfp_0_tx_clk_1_int = clk_156mhz_int; +assign qsfp_0_tx_rst_1_int = rst_156mhz_int; + +assign qsfp_0_rx_clk_1_int = gt_rxusrclk[5]; + +sync_reset #( + .N(4) +) +qsfp_0_rx_rst_1_reset_sync_inst ( + .clk(qsfp_0_rx_clk_1_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_0_rx_rst_1_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_0_phy_1_inst ( + .tx_clk(qsfp_0_tx_clk_1_int), + .tx_rst(qsfp_0_tx_rst_1_int), + .rx_clk(qsfp_0_rx_clk_1_int), + .rx_rst(qsfp_0_rx_rst_1_int), + .xgmii_txd(qsfp_0_txd_1_int), + .xgmii_txc(qsfp_0_txc_1_int), + .xgmii_rxd(qsfp_0_rxd_1_int), + .xgmii_rxc(qsfp_0_rxc_1_int), + .serdes_tx_data(qsfp_0_gt_txdata_1), + .serdes_tx_hdr(qsfp_0_gt_txheader_1), + .serdes_rx_data(qsfp_0_gt_rxdata_1), + .serdes_rx_hdr(qsfp_0_gt_rxheader_1), + .serdes_rx_bitslip(qsfp_0_gt_rxgearboxslip_1), + .rx_block_lock(qsfp_0_rx_block_lock_1), + .rx_high_ber() +); + +assign qsfp_0_tx_clk_2_int = clk_156mhz_int; +assign qsfp_0_tx_rst_2_int = rst_156mhz_int; + +assign qsfp_0_rx_clk_2_int = gt_rxusrclk[6]; + +sync_reset #( + .N(4) +) +qsfp_0_rx_rst_2_reset_sync_inst ( + .clk(qsfp_0_rx_clk_2_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_0_rx_rst_2_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_0_phy_2_inst ( + .tx_clk(qsfp_0_tx_clk_2_int), + .tx_rst(qsfp_0_tx_rst_2_int), + .rx_clk(qsfp_0_rx_clk_2_int), + .rx_rst(qsfp_0_rx_rst_2_int), + .xgmii_txd(qsfp_0_txd_2_int), + .xgmii_txc(qsfp_0_txc_2_int), + .xgmii_rxd(qsfp_0_rxd_2_int), + .xgmii_rxc(qsfp_0_rxc_2_int), + .serdes_tx_data(qsfp_0_gt_txdata_2), + .serdes_tx_hdr(qsfp_0_gt_txheader_2), + .serdes_rx_data(qsfp_0_gt_rxdata_2), + .serdes_rx_hdr(qsfp_0_gt_rxheader_2), + .serdes_rx_bitslip(qsfp_0_gt_rxgearboxslip_2), + .rx_block_lock(qsfp_0_rx_block_lock_2), + .rx_high_ber() +); + +assign qsfp_0_tx_clk_3_int = clk_156mhz_int; +assign qsfp_0_tx_rst_3_int = rst_156mhz_int; + +assign qsfp_0_rx_clk_3_int = gt_rxusrclk[7]; + +sync_reset #( + .N(4) +) +qsfp_0_rx_rst_3_reset_sync_inst ( + .clk(qsfp_0_rx_clk_3_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_0_rx_rst_3_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_0_phy_3_inst ( + .tx_clk(qsfp_0_tx_clk_3_int), + .tx_rst(qsfp_0_tx_rst_3_int), + .rx_clk(qsfp_0_rx_clk_3_int), + .rx_rst(qsfp_0_rx_rst_3_int), + .xgmii_txd(qsfp_0_txd_3_int), + .xgmii_txc(qsfp_0_txc_3_int), + .xgmii_rxd(qsfp_0_rxd_3_int), + .xgmii_rxc(qsfp_0_rxc_3_int), + .serdes_tx_data(qsfp_0_gt_txdata_3), + .serdes_tx_hdr(qsfp_0_gt_txheader_3), + .serdes_rx_data(qsfp_0_gt_rxdata_3), + .serdes_rx_hdr(qsfp_0_gt_rxheader_3), + .serdes_rx_bitslip(qsfp_0_gt_rxgearboxslip_3), + .rx_block_lock(qsfp_0_rx_block_lock_3), + .rx_high_ber() +); + +assign qsfp_1_tx_clk_0_int = clk_156mhz_int; +assign qsfp_1_tx_rst_0_int = rst_156mhz_int; + +assign qsfp_1_rx_clk_0_int = gt_rxusrclk[0]; + +sync_reset #( + .N(4) +) +qsfp_1_rx_rst_0_reset_sync_inst ( + .clk(qsfp_1_rx_clk_0_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_1_rx_rst_0_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_1_phy_0_inst ( + .tx_clk(qsfp_1_tx_clk_0_int), + .tx_rst(qsfp_1_tx_rst_0_int), + .rx_clk(qsfp_1_rx_clk_0_int), + .rx_rst(qsfp_1_rx_rst_0_int), + .xgmii_txd(qsfp_1_txd_0_int), + .xgmii_txc(qsfp_1_txc_0_int), + .xgmii_rxd(qsfp_1_rxd_0_int), + .xgmii_rxc(qsfp_1_rxc_0_int), + .serdes_tx_data(qsfp_1_gt_txdata_0), + .serdes_tx_hdr(qsfp_1_gt_txheader_0), + .serdes_rx_data(qsfp_1_gt_rxdata_0), + .serdes_rx_hdr(qsfp_1_gt_rxheader_0), + .serdes_rx_bitslip(qsfp_1_gt_rxgearboxslip_0), + .rx_block_lock(qsfp_1_rx_block_lock_0), + .rx_high_ber() +); + +assign qsfp_1_tx_clk_1_int = clk_156mhz_int; +assign qsfp_1_tx_rst_1_int = rst_156mhz_int; + +assign qsfp_1_rx_clk_1_int = gt_rxusrclk[1]; + +sync_reset #( + .N(4) +) +qsfp_1_rx_rst_1_reset_sync_inst ( + .clk(qsfp_1_rx_clk_1_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_1_rx_rst_1_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_1_phy_1_inst ( + .tx_clk(qsfp_1_tx_clk_1_int), + .tx_rst(qsfp_1_tx_rst_1_int), + .rx_clk(qsfp_1_rx_clk_1_int), + .rx_rst(qsfp_1_rx_rst_1_int), + .xgmii_txd(qsfp_1_txd_1_int), + .xgmii_txc(qsfp_1_txc_1_int), + .xgmii_rxd(qsfp_1_rxd_1_int), + .xgmii_rxc(qsfp_1_rxc_1_int), + .serdes_tx_data(qsfp_1_gt_txdata_1), + .serdes_tx_hdr(qsfp_1_gt_txheader_1), + .serdes_rx_data(qsfp_1_gt_rxdata_1), + .serdes_rx_hdr(qsfp_1_gt_rxheader_1), + .serdes_rx_bitslip(qsfp_1_gt_rxgearboxslip_1), + .rx_block_lock(qsfp_1_rx_block_lock_1), + .rx_high_ber() +); + +assign qsfp_1_tx_clk_2_int = clk_156mhz_int; +assign qsfp_1_tx_rst_2_int = rst_156mhz_int; + +assign qsfp_1_rx_clk_2_int = gt_rxusrclk[2]; + +sync_reset #( + .N(4) +) +qsfp_1_rx_rst_2_reset_sync_inst ( + .clk(qsfp_1_rx_clk_2_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_1_rx_rst_2_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_1_phy_2_inst ( + .tx_clk(qsfp_1_tx_clk_2_int), + .tx_rst(qsfp_1_tx_rst_2_int), + .rx_clk(qsfp_1_rx_clk_2_int), + .rx_rst(qsfp_1_rx_rst_2_int), + .xgmii_txd(qsfp_1_txd_2_int), + .xgmii_txc(qsfp_1_txc_2_int), + .xgmii_rxd(qsfp_1_rxd_2_int), + .xgmii_rxc(qsfp_1_rxc_2_int), + .serdes_tx_data(qsfp_1_gt_txdata_2), + .serdes_tx_hdr(qsfp_1_gt_txheader_2), + .serdes_rx_data(qsfp_1_gt_rxdata_2), + .serdes_rx_hdr(qsfp_1_gt_rxheader_2), + .serdes_rx_bitslip(qsfp_1_gt_rxgearboxslip_2), + .rx_block_lock(qsfp_1_rx_block_lock_2), + .rx_high_ber() +); + +assign qsfp_1_tx_clk_3_int = clk_156mhz_int; +assign qsfp_1_tx_rst_3_int = rst_156mhz_int; + +assign qsfp_1_rx_clk_3_int = gt_rxusrclk[3]; + +sync_reset #( + .N(4) +) +qsfp_1_rx_rst_3_reset_sync_inst ( + .clk(qsfp_1_rx_clk_3_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_1_rx_rst_3_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_1_phy_3_inst ( + .tx_clk(qsfp_1_tx_clk_3_int), + .tx_rst(qsfp_1_tx_rst_3_int), + .rx_clk(qsfp_1_rx_clk_3_int), + .rx_rst(qsfp_1_rx_rst_3_int), + .xgmii_txd(qsfp_1_txd_3_int), + .xgmii_txc(qsfp_1_txc_3_int), + .xgmii_rxd(qsfp_1_rxd_3_int), + .xgmii_rxc(qsfp_1_rxc_3_int), + .serdes_tx_data(qsfp_1_gt_txdata_3), + .serdes_tx_hdr(qsfp_1_gt_txheader_3), + .serdes_rx_data(qsfp_1_gt_rxdata_3), + .serdes_rx_hdr(qsfp_1_gt_rxheader_3), + .serdes_rx_bitslip(qsfp_1_gt_rxgearboxslip_3), + .rx_block_lock(qsfp_1_rx_block_lock_3), + .rx_high_ber() +); + +//assign led = sw[0] ? {qsfp_1_rx_block_lock_4, qsfp_1_rx_block_lock_3, qsfp_1_rx_block_lock_2, qsfp_1_rx_block_lock_1, qsfp_0_rx_block_lock_4, qsfp_0_rx_block_lock_3, qsfp_0_rx_block_lock_2, qsfp_0_rx_block_lock_1} : led_int; +assign front_led = {1'b0, qsfp_0_rx_block_lock_0}; + +fpga_core +core_inst ( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + .clk(clk_156mhz_int), + .rst(rst_156mhz_int), + /* + * GPIO + */ + .user_led_g(user_led_g), + .user_led_r(user_led_r), + //.front_led(front_led), + .user_sw(user_sw_int), + + /* + * Ethernet: QSFP28 + */ + .qsfp_0_tx_clk_0(qsfp_0_tx_clk_0_int), + .qsfp_0_tx_rst_0(qsfp_0_tx_rst_0_int), + .qsfp_0_txd_0(qsfp_0_txd_0_int), + .qsfp_0_txc_0(qsfp_0_txc_0_int), + .qsfp_0_rx_clk_0(qsfp_0_rx_clk_0_int), + .qsfp_0_rx_rst_0(qsfp_0_rx_rst_0_int), + .qsfp_0_rxd_0(qsfp_0_rxd_0_int), + .qsfp_0_rxc_0(qsfp_0_rxc_0_int), + .qsfp_0_tx_clk_1(qsfp_0_tx_clk_1_int), + .qsfp_0_tx_rst_1(qsfp_0_tx_rst_1_int), + .qsfp_0_txd_1(qsfp_0_txd_1_int), + .qsfp_0_txc_1(qsfp_0_txc_1_int), + .qsfp_0_rx_clk_1(qsfp_0_rx_clk_1_int), + .qsfp_0_rx_rst_1(qsfp_0_rx_rst_1_int), + .qsfp_0_rxd_1(qsfp_0_rxd_1_int), + .qsfp_0_rxc_1(qsfp_0_rxc_1_int), + .qsfp_0_tx_clk_2(qsfp_0_tx_clk_2_int), + .qsfp_0_tx_rst_2(qsfp_0_tx_rst_2_int), + .qsfp_0_txd_2(qsfp_0_txd_2_int), + .qsfp_0_txc_2(qsfp_0_txc_2_int), + .qsfp_0_rx_clk_2(qsfp_0_rx_clk_2_int), + .qsfp_0_rx_rst_2(qsfp_0_rx_rst_2_int), + .qsfp_0_rxd_2(qsfp_0_rxd_2_int), + .qsfp_0_rxc_2(qsfp_0_rxc_2_int), + .qsfp_0_tx_clk_3(qsfp_0_tx_clk_3_int), + .qsfp_0_tx_rst_3(qsfp_0_tx_rst_3_int), + .qsfp_0_txd_3(qsfp_0_txd_3_int), + .qsfp_0_txc_3(qsfp_0_txc_3_int), + .qsfp_0_rx_clk_3(qsfp_0_rx_clk_3_int), + .qsfp_0_rx_rst_3(qsfp_0_rx_rst_3_int), + .qsfp_0_rxd_3(qsfp_0_rxd_3_int), + .qsfp_0_rxc_3(qsfp_0_rxc_3_int), + .qsfp_1_tx_clk_0(qsfp_1_tx_clk_0_int), + .qsfp_1_tx_rst_0(qsfp_1_tx_rst_0_int), + .qsfp_1_txd_0(qsfp_1_txd_0_int), + .qsfp_1_txc_0(qsfp_1_txc_0_int), + .qsfp_1_rx_clk_0(qsfp_1_rx_clk_0_int), + .qsfp_1_rx_rst_0(qsfp_1_rx_rst_0_int), + .qsfp_1_rxd_0(qsfp_1_rxd_0_int), + .qsfp_1_rxc_0(qsfp_1_rxc_0_int), + .qsfp_1_tx_clk_1(qsfp_1_tx_clk_1_int), + .qsfp_1_tx_rst_1(qsfp_1_tx_rst_1_int), + .qsfp_1_txd_1(qsfp_1_txd_1_int), + .qsfp_1_txc_1(qsfp_1_txc_1_int), + .qsfp_1_rx_clk_1(qsfp_1_rx_clk_1_int), + .qsfp_1_rx_rst_1(qsfp_1_rx_rst_1_int), + .qsfp_1_rxd_1(qsfp_1_rxd_1_int), + .qsfp_1_rxc_1(qsfp_1_rxc_1_int), + .qsfp_1_tx_clk_2(qsfp_1_tx_clk_2_int), + .qsfp_1_tx_rst_2(qsfp_1_tx_rst_2_int), + .qsfp_1_txd_2(qsfp_1_txd_2_int), + .qsfp_1_txc_2(qsfp_1_txc_2_int), + .qsfp_1_rx_clk_2(qsfp_1_rx_clk_2_int), + .qsfp_1_rx_rst_2(qsfp_1_rx_rst_2_int), + .qsfp_1_rxd_2(qsfp_1_rxd_2_int), + .qsfp_1_rxc_2(qsfp_1_rxc_2_int), + .qsfp_1_tx_clk_3(qsfp_1_tx_clk_3_int), + .qsfp_1_tx_rst_3(qsfp_1_tx_rst_3_int), + .qsfp_1_txd_3(qsfp_1_txd_3_int), + .qsfp_1_txc_3(qsfp_1_txc_3_int), + .qsfp_1_rx_clk_3(qsfp_1_rx_clk_3_int), + .qsfp_1_rx_rst_3(qsfp_1_rx_rst_3_int), + .qsfp_1_rxd_3(qsfp_1_rxd_3_int), + .qsfp_1_rxc_3(qsfp_1_rxc_3_int) +); + +endmodule diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/fpga_core.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/fpga_core.v new file mode 100644 index 000000000..93f5ddc41 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/fpga_core.v @@ -0,0 +1,659 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * Clock: 156.25MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + + /* + * GPIO + */ + output wire [1:0] user_led_g, + output wire user_led_r, + output wire [1:0] front_led, + input wire [1:0] user_sw, + + /* + * Ethernet: QSFP28 + */ + input wire qsfp_0_tx_clk_0, + input wire qsfp_0_tx_rst_0, + output wire [63:0] qsfp_0_txd_0, + output wire [7:0] qsfp_0_txc_0, + input wire qsfp_0_rx_clk_0, + input wire qsfp_0_rx_rst_0, + input wire [63:0] qsfp_0_rxd_0, + input wire [7:0] qsfp_0_rxc_0, + input wire qsfp_0_tx_clk_1, + input wire qsfp_0_tx_rst_1, + output wire [63:0] qsfp_0_txd_1, + output wire [7:0] qsfp_0_txc_1, + input wire qsfp_0_rx_clk_1, + input wire qsfp_0_rx_rst_1, + input wire [63:0] qsfp_0_rxd_1, + input wire [7:0] qsfp_0_rxc_1, + input wire qsfp_0_tx_clk_2, + input wire qsfp_0_tx_rst_2, + output wire [63:0] qsfp_0_txd_2, + output wire [7:0] qsfp_0_txc_2, + input wire qsfp_0_rx_clk_2, + input wire qsfp_0_rx_rst_2, + input wire [63:0] qsfp_0_rxd_2, + input wire [7:0] qsfp_0_rxc_2, + input wire qsfp_0_tx_clk_3, + input wire qsfp_0_tx_rst_3, + output wire [63:0] qsfp_0_txd_3, + output wire [7:0] qsfp_0_txc_3, + input wire qsfp_0_rx_clk_3, + input wire qsfp_0_rx_rst_3, + input wire [63:0] qsfp_0_rxd_3, + input wire [7:0] qsfp_0_rxc_3, + input wire qsfp_1_tx_clk_0, + input wire qsfp_1_tx_rst_0, + output wire [63:0] qsfp_1_txd_0, + output wire [7:0] qsfp_1_txc_0, + input wire qsfp_1_rx_clk_0, + input wire qsfp_1_rx_rst_0, + input wire [63:0] qsfp_1_rxd_0, + input wire [7:0] qsfp_1_rxc_0, + input wire qsfp_1_tx_clk_1, + input wire qsfp_1_tx_rst_1, + output wire [63:0] qsfp_1_txd_1, + output wire [7:0] qsfp_1_txc_1, + input wire qsfp_1_rx_clk_1, + input wire qsfp_1_rx_rst_1, + input wire [63:0] qsfp_1_rxd_1, + input wire [7:0] qsfp_1_rxc_1, + input wire qsfp_1_tx_clk_2, + input wire qsfp_1_tx_rst_2, + output wire [63:0] qsfp_1_txd_2, + output wire [7:0] qsfp_1_txc_2, + input wire qsfp_1_rx_clk_2, + input wire qsfp_1_rx_rst_2, + input wire [63:0] qsfp_1_rxd_2, + input wire [7:0] qsfp_1_rxc_2, + input wire qsfp_1_tx_clk_3, + input wire qsfp_1_tx_rst_3, + output wire [63:0] qsfp_1_txd_3, + output wire [7:0] qsfp_1_txc_3, + input wire qsfp_1_rx_clk_3, + input wire qsfp_1_rx_rst_3, + input wire [63:0] qsfp_1_rxd_3, + input wire [7:0] qsfp_1_rxc_3 +); + +// AXI between MAC and Ethernet modules +wire [63:0] rx_axis_tdata; +wire [7:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tready; +wire rx_axis_tlast; +wire rx_axis_tuser; + +wire [63:0] tx_axis_tdata; +wire [7:0] tx_axis_tkeep; +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 [63:0] rx_eth_payload_axis_tdata; +wire [7:0] rx_eth_payload_axis_tkeep; +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 [63:0] tx_eth_payload_axis_tdata; +wire [7:0] tx_eth_payload_axis_tkeep; +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 [63:0] rx_ip_payload_axis_tdata; +wire [7:0] rx_ip_payload_axis_tkeep; +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 [63:0] tx_ip_payload_axis_tdata; +wire [7:0] tx_ip_payload_axis_tkeep; +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 [63:0] rx_udp_payload_axis_tdata; +wire [7:0] rx_udp_payload_axis_tkeep; +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 [63:0] tx_udp_payload_axis_tdata; +wire [7:0] tx_udp_payload_axis_tkeep; +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 [63:0] rx_fifo_udp_payload_axis_tdata; +wire [7:0] rx_fifo_udp_payload_axis_tkeep; +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 [63:0] tx_fifo_udp_payload_axis_tdata; +wire [7:0] tx_fifo_udp_payload_axis_tkeep; +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_tkeep = 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_tkeep = tx_fifo_udp_payload_axis_tkeep; +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_tkeep = rx_udp_payload_axis_tkeep; +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 + valid_last <= tx_udp_payload_axis_tvalid; + if (tx_udp_payload_axis_tvalid && !valid_last) begin + led_reg <= tx_udp_payload_axis_tdata; + end + end +end + +assign user_led_g = ~led_reg[1:0]; +assign user_led_r = 1'b1; +assign front_led = 2'b00; + +assign phy_reset_n = !rst; + +assign qsfp_0_txd_1 = 64'h0707070707070707; +assign qsfp_0_txc_1 = 8'hff; +assign qsfp_0_txd_2 = 64'h0707070707070707; +assign qsfp_0_txc_2 = 8'hff; +assign qsfp_0_txd_3 = 64'h0707070707070707; +assign qsfp_0_txc_3 = 8'hff; + +assign qsfp_1_txd_0 = 64'h0707070707070707; +assign qsfp_1_txc_0 = 8'hff; +assign qsfp_1_txd_1 = 64'h0707070707070707; +assign qsfp_1_txc_1 = 8'hff; +assign qsfp_1_txd_2 = 64'h0707070707070707; +assign qsfp_1_txc_2 = 8'hff; +assign qsfp_1_txd_3 = 64'h0707070707070707; +assign qsfp_1_txc_3 = 8'hff; + +eth_mac_10g_fifo #( + .ENABLE_PADDING(1), + .ENABLE_DIC(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(9), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(9), + .RX_FRAME_FIFO(1) +) +eth_mac_10g_fifo_inst ( + .rx_clk(qsfp_0_rx_clk_0), + .rx_rst(qsfp_0_rx_rst_0), + .tx_clk(qsfp_0_tx_clk_0), + .tx_rst(qsfp_0_tx_rst_0), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + + .xgmii_rxd(qsfp_0_rxd_0), + .xgmii_rxc(qsfp_0_rxc_0), + .xgmii_txd(qsfp_0_txd_0), + .xgmii_txc(qsfp_0_txc_0), + + .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(), + + .ifg_delay(8'd12) +); + +eth_axis_rx_64 +eth_axis_rx_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(rx_axis_tdata), + .s_axis_tkeep(rx_axis_tkeep), + .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_tkeep(rx_eth_payload_axis_tkeep), + .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_64 +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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_axis_tkeep), + .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_64 +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_tkeep(rx_eth_payload_axis_tkeep), + .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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_ip_payload_axis_tkeep), + .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_tkeep(rx_ip_payload_axis_tkeep), + .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_tkeep(tx_udp_payload_axis_tkeep), + .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_tkeep(rx_udp_payload_axis_tkeep), + .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(1'b0) +); + +axis_fifo #( + .ADDR_WIDTH(10), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .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(rx_fifo_udp_payload_axis_tkeep), + .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(tx_fifo_udp_payload_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/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/sync_reset.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/sync_signal.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/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/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/arp_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/axis_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/eth_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/ip_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/test_fpga_core.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/test_fpga_core.py new file mode 100755 index 000000000..35b21e22b --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/test_fpga_core.py @@ -0,0 +1,462 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import xgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_10g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_10g.v") +srcs.append("../lib/eth/rtl/axis_xgmii_rx_64.v") +srcs.append("../lib/eth/rtl/axis_xgmii_tx_64.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx_64.v") +srcs.append("../lib/eth/rtl/eth_axis_tx_64.v") +srcs.append("../lib/eth/rtl/udp_complete_64.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen_64.v") +srcs.append("../lib/eth/rtl/udp_64.v") +srcs.append("../lib/eth/rtl/udp_ip_rx_64.v") +srcs.append("../lib/eth/rtl/udp_ip_tx_64.v") +srcs.append("../lib/eth/rtl/ip_complete_64.v") +srcs.append("../lib/eth/rtl/ip_64.v") +srcs.append("../lib/eth/rtl/ip_eth_rx_64.v") +srcs.append("../lib/eth/rtl/ip_eth_tx_64.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp_64.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx_64.v") +srcs.append("../lib/eth/rtl/arp_eth_tx_64.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + user_sw = Signal(intbv(0)[2:]) + qsfp_0_tx_clk_0 = Signal(bool(0)) + qsfp_0_tx_rst_0 = Signal(bool(0)) + qsfp_0_rx_clk_0 = Signal(bool(0)) + qsfp_0_rx_rst_0 = Signal(bool(0)) + qsfp_0_rxd_0 = Signal(intbv(0)[64:]) + qsfp_0_rxc_0 = Signal(intbv(0)[8:]) + qsfp_0_tx_clk_1 = Signal(bool(0)) + qsfp_0_tx_rst_1 = Signal(bool(0)) + qsfp_0_rx_clk_1 = Signal(bool(0)) + qsfp_0_rx_rst_1 = Signal(bool(0)) + qsfp_0_rxd_1 = Signal(intbv(0)[64:]) + qsfp_0_rxc_1 = Signal(intbv(0)[8:]) + qsfp_0_tx_clk_2 = Signal(bool(0)) + qsfp_0_tx_rst_2 = Signal(bool(0)) + qsfp_0_rx_clk_2 = Signal(bool(0)) + qsfp_0_rx_rst_2 = Signal(bool(0)) + qsfp_0_rxd_2 = Signal(intbv(0)[64:]) + qsfp_0_rxc_2 = Signal(intbv(0)[8:]) + qsfp_0_tx_clk_3 = Signal(bool(0)) + qsfp_0_tx_rst_3 = Signal(bool(0)) + qsfp_0_rx_clk_3 = Signal(bool(0)) + qsfp_0_rx_rst_3 = Signal(bool(0)) + qsfp_0_rxd_3 = Signal(intbv(0)[64:]) + qsfp_0_rxc_3 = Signal(intbv(0)[8:]) + qsfp_1_tx_clk_0 = Signal(bool(0)) + qsfp_1_tx_rst_0 = Signal(bool(0)) + qsfp_1_rx_clk_0 = Signal(bool(0)) + qsfp_1_rx_rst_0 = Signal(bool(0)) + qsfp_1_rxd_0 = Signal(intbv(0)[64:]) + qsfp_1_rxc_0 = Signal(intbv(0)[8:]) + qsfp_1_tx_clk_1 = Signal(bool(0)) + qsfp_1_tx_rst_1 = Signal(bool(0)) + qsfp_1_rx_clk_1 = Signal(bool(0)) + qsfp_1_rx_rst_1 = Signal(bool(0)) + qsfp_1_rxd_1 = Signal(intbv(0)[64:]) + qsfp_1_rxc_1 = Signal(intbv(0)[8:]) + qsfp_1_tx_clk_2 = Signal(bool(0)) + qsfp_1_tx_rst_2 = Signal(bool(0)) + qsfp_1_rx_clk_2 = Signal(bool(0)) + qsfp_1_rx_rst_2 = Signal(bool(0)) + qsfp_1_rxd_2 = Signal(intbv(0)[64:]) + qsfp_1_rxc_2 = Signal(intbv(0)[8:]) + qsfp_1_tx_clk_3 = Signal(bool(0)) + qsfp_1_tx_rst_3 = Signal(bool(0)) + qsfp_1_rx_clk_3 = Signal(bool(0)) + qsfp_1_rx_rst_3 = Signal(bool(0)) + qsfp_1_rxd_3 = Signal(intbv(0)[64:]) + qsfp_1_rxc_3 = Signal(intbv(0)[8:]) + + # Outputs + user_led_g = Signal(intbv(0)[2:]) + user_led_r = Signal(bool(0)) + front_led = Signal(intbv(0)[2:]) + qsfp_0_txd_0 = Signal(intbv(0)[64:]) + qsfp_0_txc_0 = Signal(intbv(0)[8:]) + qsfp_0_txd_1 = Signal(intbv(0)[64:]) + qsfp_0_txc_1 = Signal(intbv(0)[8:]) + qsfp_0_txd_2 = Signal(intbv(0)[64:]) + qsfp_0_txc_2 = Signal(intbv(0)[8:]) + qsfp_0_txd_3 = Signal(intbv(0)[64:]) + qsfp_0_txc_3 = Signal(intbv(0)[8:]) + qsfp_1_txd_0 = Signal(intbv(0)[64:]) + qsfp_1_txc_0 = Signal(intbv(0)[8:]) + qsfp_1_txd_1 = Signal(intbv(0)[64:]) + qsfp_1_txc_1 = Signal(intbv(0)[8:]) + qsfp_1_txd_2 = Signal(intbv(0)[64:]) + qsfp_1_txc_2 = Signal(intbv(0)[8:]) + qsfp_1_txd_3 = Signal(intbv(0)[64:]) + qsfp_1_txc_3 = Signal(intbv(0)[8:]) + + # sources and sinks + qsfp_0_0_source = xgmii_ep.XGMIISource() + qsfp_0_0_source_logic = qsfp_0_0_source.create_logic(qsfp_0_rx_clk_0, qsfp_0_rx_rst_0, txd=qsfp_0_rxd_0, txc=qsfp_0_rxc_0, name='qsfp_0_0_source') + + qsfp_0_0_sink = xgmii_ep.XGMIISink() + qsfp_0_0_sink_logic = qsfp_0_0_sink.create_logic(qsfp_0_tx_clk_0, qsfp_0_tx_rst_0, rxd=qsfp_0_txd_0, rxc=qsfp_0_txc_0, name='qsfp_0_0_sink') + + qsfp_0_1_source = xgmii_ep.XGMIISource() + qsfp_0_1_source_logic = qsfp_0_1_source.create_logic(qsfp_0_rx_clk_1, qsfp_0_rx_rst_1, txd=qsfp_0_rxd_1, txc=qsfp_0_rxc_1, name='qsfp_0_1_source') + + qsfp_0_1_sink = xgmii_ep.XGMIISink() + qsfp_0_1_sink_logic = qsfp_0_1_sink.create_logic(qsfp_0_tx_clk_1, qsfp_0_tx_rst_1, rxd=qsfp_0_txd_1, rxc=qsfp_0_txc_1, name='qsfp_0_1_sink') + + qsfp_0_2_source = xgmii_ep.XGMIISource() + qsfp_0_2_source_logic = qsfp_0_2_source.create_logic(qsfp_0_rx_clk_2, qsfp_0_rx_rst_2, txd=qsfp_0_rxd_2, txc=qsfp_0_rxc_2, name='qsfp_0_2_source') + + qsfp_0_2_sink = xgmii_ep.XGMIISink() + qsfp_0_2_sink_logic = qsfp_0_2_sink.create_logic(qsfp_0_tx_clk_2, qsfp_0_tx_rst_2, rxd=qsfp_0_txd_2, rxc=qsfp_0_txc_2, name='qsfp_0_2_sink') + + qsfp_0_3_source = xgmii_ep.XGMIISource() + qsfp_0_3_source_logic = qsfp_0_3_source.create_logic(qsfp_0_rx_clk_3, qsfp_0_rx_rst_3, txd=qsfp_0_rxd_3, txc=qsfp_0_rxc_3, name='qsfp_0_3_source') + + qsfp_0_3_sink = xgmii_ep.XGMIISink() + qsfp_0_3_sink_logic = qsfp_0_3_sink.create_logic(qsfp_0_tx_clk_3, qsfp_0_tx_rst_3, rxd=qsfp_0_txd_3, rxc=qsfp_0_txc_3, name='qsfp_0_3_sink') + + qsfp_1_0_source = xgmii_ep.XGMIISource() + qsfp_1_0_source_logic = qsfp_1_0_source.create_logic(qsfp_1_rx_clk_0, qsfp_1_rx_rst_0, txd=qsfp_1_rxd_0, txc=qsfp_1_rxc_0, name='qsfp_1_0_source') + + qsfp_1_0_sink = xgmii_ep.XGMIISink() + qsfp_1_0_sink_logic = qsfp_1_0_sink.create_logic(qsfp_1_tx_clk_0, qsfp_1_tx_rst_0, rxd=qsfp_1_txd_0, rxc=qsfp_1_txc_0, name='qsfp_1_0_sink') + + qsfp_1_1_source = xgmii_ep.XGMIISource() + qsfp_1_1_source_logic = qsfp_1_1_source.create_logic(qsfp_1_rx_clk_1, qsfp_1_rx_rst_1, txd=qsfp_1_rxd_1, txc=qsfp_1_rxc_1, name='qsfp_1_1_source') + + qsfp_1_1_sink = xgmii_ep.XGMIISink() + qsfp_1_1_sink_logic = qsfp_1_1_sink.create_logic(qsfp_1_tx_clk_1, qsfp_1_tx_rst_1, rxd=qsfp_1_txd_1, rxc=qsfp_1_txc_1, name='qsfp_1_1_sink') + + qsfp_1_2_source = xgmii_ep.XGMIISource() + qsfp_1_2_source_logic = qsfp_1_2_source.create_logic(qsfp_1_rx_clk_2, qsfp_1_rx_rst_2, txd=qsfp_1_rxd_2, txc=qsfp_1_rxc_2, name='qsfp_1_2_source') + + qsfp_1_2_sink = xgmii_ep.XGMIISink() + qsfp_1_2_sink_logic = qsfp_1_2_sink.create_logic(qsfp_1_tx_clk_2, qsfp_1_tx_rst_2, rxd=qsfp_1_txd_2, rxc=qsfp_1_txc_2, name='qsfp_1_2_sink') + + qsfp_1_3_source = xgmii_ep.XGMIISource() + qsfp_1_3_source_logic = qsfp_1_3_source.create_logic(qsfp_1_rx_clk_3, qsfp_1_rx_rst_3, txd=qsfp_1_rxd_3, txc=qsfp_1_rxc_3, name='qsfp_1_3_source') + + qsfp_1_3_sink = xgmii_ep.XGMIISink() + qsfp_1_3_sink_logic = qsfp_1_3_sink.create_logic(qsfp_1_tx_clk_3, qsfp_1_tx_rst_3, rxd=qsfp_1_txd_3, rxc=qsfp_1_txc_3, name='qsfp_1_3_sink') + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + user_led_g=user_led_g, + user_led_r=user_led_r, + front_led=front_led, + user_sw=user_sw, + + qsfp_0_tx_clk_0=qsfp_0_tx_clk_0, + qsfp_0_tx_rst_0=qsfp_0_tx_rst_0, + qsfp_0_txd_0=qsfp_0_txd_0, + qsfp_0_txc_0=qsfp_0_txc_0, + qsfp_0_rx_clk_0=qsfp_0_rx_clk_0, + qsfp_0_rx_rst_0=qsfp_0_rx_rst_0, + qsfp_0_rxd_0=qsfp_0_rxd_0, + qsfp_0_rxc_0=qsfp_0_rxc_0, + qsfp_0_tx_clk_1=qsfp_0_tx_clk_1, + qsfp_0_tx_rst_1=qsfp_0_tx_rst_1, + qsfp_0_txd_1=qsfp_0_txd_1, + qsfp_0_txc_1=qsfp_0_txc_1, + qsfp_0_rx_clk_1=qsfp_0_rx_clk_1, + qsfp_0_rx_rst_1=qsfp_0_rx_rst_1, + qsfp_0_rxd_1=qsfp_0_rxd_1, + qsfp_0_rxc_1=qsfp_0_rxc_1, + qsfp_0_tx_clk_2=qsfp_0_tx_clk_2, + qsfp_0_tx_rst_2=qsfp_0_tx_rst_2, + qsfp_0_txd_2=qsfp_0_txd_2, + qsfp_0_txc_2=qsfp_0_txc_2, + qsfp_0_rx_clk_2=qsfp_0_rx_clk_2, + qsfp_0_rx_rst_2=qsfp_0_rx_rst_2, + qsfp_0_rxd_2=qsfp_0_rxd_2, + qsfp_0_rxc_2=qsfp_0_rxc_2, + qsfp_0_tx_clk_3=qsfp_0_tx_clk_3, + qsfp_0_tx_rst_3=qsfp_0_tx_rst_3, + qsfp_0_txd_3=qsfp_0_txd_3, + qsfp_0_txc_3=qsfp_0_txc_3, + qsfp_0_rx_clk_3=qsfp_0_rx_clk_3, + qsfp_0_rx_rst_3=qsfp_0_rx_rst_3, + qsfp_0_rxd_3=qsfp_0_rxd_3, + qsfp_0_rxc_3=qsfp_0_rxc_3, + qsfp_1_tx_clk_0=qsfp_1_tx_clk_0, + qsfp_1_tx_rst_0=qsfp_1_tx_rst_0, + qsfp_1_txd_0=qsfp_1_txd_0, + qsfp_1_txc_0=qsfp_1_txc_0, + qsfp_1_rx_clk_0=qsfp_1_rx_clk_0, + qsfp_1_rx_rst_0=qsfp_1_rx_rst_0, + qsfp_1_rxd_0=qsfp_1_rxd_0, + qsfp_1_rxc_0=qsfp_1_rxc_0, + qsfp_1_tx_clk_1=qsfp_1_tx_clk_1, + qsfp_1_tx_rst_1=qsfp_1_tx_rst_1, + qsfp_1_txd_1=qsfp_1_txd_1, + qsfp_1_txc_1=qsfp_1_txc_1, + qsfp_1_rx_clk_1=qsfp_1_rx_clk_1, + qsfp_1_rx_rst_1=qsfp_1_rx_rst_1, + qsfp_1_rxd_1=qsfp_1_rxd_1, + qsfp_1_rxc_1=qsfp_1_rxc_1, + qsfp_1_tx_clk_2=qsfp_1_tx_clk_2, + qsfp_1_tx_rst_2=qsfp_1_tx_rst_2, + qsfp_1_txd_2=qsfp_1_txd_2, + qsfp_1_txc_2=qsfp_1_txc_2, + qsfp_1_rx_clk_2=qsfp_1_rx_clk_2, + qsfp_1_rx_rst_2=qsfp_1_rx_rst_2, + qsfp_1_rxd_2=qsfp_1_rxd_2, + qsfp_1_rxc_2=qsfp_1_rxc_2, + qsfp_1_tx_clk_3=qsfp_1_tx_clk_3, + qsfp_1_tx_rst_3=qsfp_1_tx_rst_3, + qsfp_1_txd_3=qsfp_1_txd_3, + qsfp_1_txc_3=qsfp_1_txc_3, + qsfp_1_rx_clk_3=qsfp_1_rx_clk_3, + qsfp_1_rx_rst_3=qsfp_1_rx_rst_3, + qsfp_1_rxd_3=qsfp_1_rxd_3, + qsfp_1_rxc_3=qsfp_1_rxc_3 + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + qsfp_0_tx_clk_0.next = not qsfp_0_tx_clk_0 + qsfp_0_rx_clk_0.next = not qsfp_0_rx_clk_0 + qsfp_0_tx_clk_1.next = not qsfp_0_tx_clk_1 + qsfp_0_rx_clk_1.next = not qsfp_0_rx_clk_1 + qsfp_0_tx_clk_2.next = not qsfp_0_tx_clk_2 + qsfp_0_rx_clk_2.next = not qsfp_0_rx_clk_2 + qsfp_0_tx_clk_3.next = not qsfp_0_tx_clk_3 + qsfp_0_rx_clk_3.next = not qsfp_0_rx_clk_3 + qsfp_1_tx_clk_0.next = not qsfp_1_tx_clk_0 + qsfp_1_rx_clk_0.next = not qsfp_1_rx_clk_0 + qsfp_1_tx_clk_1.next = not qsfp_1_tx_clk_1 + qsfp_1_rx_clk_1.next = not qsfp_1_rx_clk_1 + qsfp_1_tx_clk_2.next = not qsfp_1_tx_clk_2 + qsfp_1_rx_clk_2.next = not qsfp_1_rx_clk_2 + qsfp_1_tx_clk_3.next = not qsfp_1_tx_clk_3 + qsfp_1_rx_clk_3.next = not qsfp_1_rx_clk_3 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + qsfp_0_tx_rst_1.next = 1 + qsfp_0_rx_rst_1.next = 1 + qsfp_0_tx_rst_2.next = 1 + qsfp_0_rx_rst_2.next = 1 + qsfp_0_tx_rst_3.next = 1 + qsfp_0_rx_rst_3.next = 1 + qsfp_0_tx_rst_0.next = 1 + qsfp_0_rx_rst_0.next = 1 + qsfp_1_tx_rst_1.next = 1 + qsfp_1_rx_rst_1.next = 1 + qsfp_1_tx_rst_2.next = 1 + qsfp_1_rx_rst_2.next = 1 + qsfp_1_tx_rst_3.next = 1 + qsfp_1_rx_rst_3.next = 1 + qsfp_1_tx_rst_0.next = 1 + qsfp_1_rx_rst_0.next = 1 + yield clk.posedge + rst.next = 0 + qsfp_0_tx_rst_1.next = 0 + qsfp_0_rx_rst_1.next = 0 + qsfp_0_tx_rst_2.next = 0 + qsfp_0_rx_rst_2.next = 0 + qsfp_0_tx_rst_3.next = 0 + qsfp_0_rx_rst_3.next = 0 + qsfp_0_tx_rst_0.next = 0 + qsfp_0_rx_rst_0.next = 0 + qsfp_1_tx_rst_1.next = 0 + qsfp_1_rx_rst_1.next = 0 + qsfp_1_tx_rst_2.next = 0 + qsfp_1_rx_rst_2.next = 0 + qsfp_1_tx_rst_3.next = 0 + qsfp_1_rx_rst_3.next = 0 + qsfp_1_tx_rst_0.next = 0 + qsfp_1_rx_rst_0.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + qsfp_0_0_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while qsfp_0_0_sink.empty(): + yield clk.posedge + + rx_frame = qsfp_0_0_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + qsfp_0_0_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while qsfp_0_0_sink.empty(): + yield clk.posedge + + rx_frame = qsfp_0_0_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert qsfp_0_0_source.empty() + assert qsfp_0_0_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/test_fpga_core.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/test_fpga_core.v new file mode 100644 index 000000000..003072a91 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/test_fpga_core.v @@ -0,0 +1,269 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [1:0] user_sw = 0; +reg qsfp_0_tx_clk_0 = 0; +reg qsfp_0_tx_rst_0 = 0; +reg qsfp_0_rx_clk_0 = 0; +reg qsfp_0_rx_rst_0 = 0; +reg [63:0] qsfp_0_rxd_0 = 0; +reg [7:0] qsfp_0_rxc_0 = 0; +reg qsfp_0_tx_clk_1 = 0; +reg qsfp_0_tx_rst_1 = 0; +reg qsfp_0_rx_clk_1 = 0; +reg qsfp_0_rx_rst_1 = 0; +reg [63:0] qsfp_0_rxd_1 = 0; +reg [7:0] qsfp_0_rxc_1 = 0; +reg qsfp_0_tx_clk_2 = 0; +reg qsfp_0_tx_rst_2 = 0; +reg qsfp_0_rx_clk_2 = 0; +reg qsfp_0_rx_rst_2 = 0; +reg [63:0] qsfp_0_rxd_2 = 0; +reg [7:0] qsfp_0_rxc_2 = 0; +reg qsfp_0_tx_clk_3 = 0; +reg qsfp_0_tx_rst_3 = 0; +reg qsfp_0_rx_clk_3 = 0; +reg qsfp_0_rx_rst_3 = 0; +reg [63:0] qsfp_0_rxd_3 = 0; +reg [7:0] qsfp_0_rxc_3 = 0; +reg qsfp_1_tx_clk_0 = 0; +reg qsfp_1_tx_rst_0 = 0; +reg qsfp_1_rx_clk_0 = 0; +reg qsfp_1_rx_rst_0 = 0; +reg [63:0] qsfp_1_rxd_0 = 0; +reg [7:0] qsfp_1_rxc_0 = 0; +reg qsfp_1_tx_clk_1 = 0; +reg qsfp_1_tx_rst_1 = 0; +reg qsfp_1_rx_clk_1 = 0; +reg qsfp_1_rx_rst_1 = 0; +reg [63:0] qsfp_1_rxd_1 = 0; +reg [7:0] qsfp_1_rxc_1 = 0; +reg qsfp_1_tx_clk_2 = 0; +reg qsfp_1_tx_rst_2 = 0; +reg qsfp_1_rx_clk_2 = 0; +reg qsfp_1_rx_rst_2 = 0; +reg [63:0] qsfp_1_rxd_2 = 0; +reg [7:0] qsfp_1_rxc_2 = 0; +reg qsfp_1_tx_clk_3 = 0; +reg qsfp_1_tx_rst_3 = 0; +reg qsfp_1_rx_clk_3 = 0; +reg qsfp_1_rx_rst_3 = 0; +reg [63:0] qsfp_1_rxd_3 = 0; +reg [7:0] qsfp_1_rxc_3 = 0; + +// Outputs +wire [1:0] user_led_g; +wire user_led_r; +wire [1:0] front_led; +wire [63:0] qsfp_0_txd_0; +wire [7:0] qsfp_0_txc_0; +wire [63:0] qsfp_0_txd_1; +wire [7:0] qsfp_0_txc_1; +wire [63:0] qsfp_0_txd_2; +wire [7:0] qsfp_0_txc_2; +wire [63:0] qsfp_0_txd_3; +wire [7:0] qsfp_0_txc_3; +wire [63:0] qsfp_1_txd_0; +wire [7:0] qsfp_1_txc_0; +wire [63:0] qsfp_1_txd_1; +wire [7:0] qsfp_1_txc_1; +wire [63:0] qsfp_1_txd_2; +wire [7:0] qsfp_1_txc_2; +wire [63:0] qsfp_1_txd_3; +wire [7:0] qsfp_1_txc_3; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + user_sw, + qsfp_0_tx_clk_0, + qsfp_0_tx_rst_0, + qsfp_0_rx_clk_0, + qsfp_0_rx_rst_0, + qsfp_0_rxd_0, + qsfp_0_rxc_0, + qsfp_0_tx_clk_1, + qsfp_0_tx_rst_1, + qsfp_0_rx_clk_1, + qsfp_0_rx_rst_1, + qsfp_0_rxd_1, + qsfp_0_rxc_1, + qsfp_0_tx_clk_2, + qsfp_0_tx_rst_2, + qsfp_0_rx_clk_2, + qsfp_0_rx_rst_2, + qsfp_0_rxd_2, + qsfp_0_rxc_2, + qsfp_0_tx_clk_3, + qsfp_0_tx_rst_3, + qsfp_0_rx_clk_3, + qsfp_0_rx_rst_3, + qsfp_0_rxd_3, + qsfp_0_rxc_3, + qsfp_1_tx_clk_0, + qsfp_1_tx_rst_0, + qsfp_1_rx_clk_0, + qsfp_1_rx_rst_0, + qsfp_1_rxd_0, + qsfp_1_rxc_0, + qsfp_1_tx_clk_1, + qsfp_1_tx_rst_1, + qsfp_1_rx_clk_1, + qsfp_1_rx_rst_1, + qsfp_1_rxd_1, + qsfp_1_rxc_1, + qsfp_1_tx_clk_2, + qsfp_1_tx_rst_2, + qsfp_1_rx_clk_2, + qsfp_1_rx_rst_2, + qsfp_1_rxd_2, + qsfp_1_rxc_2, + qsfp_1_tx_clk_3, + qsfp_1_tx_rst_3, + qsfp_1_rx_clk_3, + qsfp_1_rx_rst_3, + qsfp_1_rxd_3, + qsfp_1_rxc_3 + ); + $to_myhdl( + user_led_g, + user_led_r, + front_led, + qsfp_0_txd_0, + qsfp_0_txc_0, + qsfp_0_txd_1, + qsfp_0_txc_1, + qsfp_0_txd_2, + qsfp_0_txc_2, + qsfp_0_txd_3, + qsfp_0_txc_3, + qsfp_1_txd_0, + qsfp_1_txc_0, + qsfp_1_txd_1, + qsfp_1_txc_1, + qsfp_1_txd_2, + qsfp_1_txc_2, + qsfp_1_txd_3, + qsfp_1_txc_3 + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .user_led_g(user_led_g), + .user_led_r(user_led_r), + .front_led(front_led), + .user_sw(user_sw), + .qsfp_0_tx_clk_0(qsfp_0_tx_clk_0), + .qsfp_0_tx_rst_0(qsfp_0_tx_rst_0), + .qsfp_0_txd_0(qsfp_0_txd_0), + .qsfp_0_txc_0(qsfp_0_txc_0), + .qsfp_0_rx_clk_0(qsfp_0_rx_clk_0), + .qsfp_0_rx_rst_0(qsfp_0_rx_rst_0), + .qsfp_0_rxd_0(qsfp_0_rxd_0), + .qsfp_0_rxc_0(qsfp_0_rxc_0), + .qsfp_0_tx_clk_1(qsfp_0_tx_clk_1), + .qsfp_0_tx_rst_1(qsfp_0_tx_rst_1), + .qsfp_0_txd_1(qsfp_0_txd_1), + .qsfp_0_txc_1(qsfp_0_txc_1), + .qsfp_0_rx_clk_1(qsfp_0_rx_clk_1), + .qsfp_0_rx_rst_1(qsfp_0_rx_rst_1), + .qsfp_0_rxd_1(qsfp_0_rxd_1), + .qsfp_0_rxc_1(qsfp_0_rxc_1), + .qsfp_0_tx_clk_2(qsfp_0_tx_clk_2), + .qsfp_0_tx_rst_2(qsfp_0_tx_rst_2), + .qsfp_0_txd_2(qsfp_0_txd_2), + .qsfp_0_txc_2(qsfp_0_txc_2), + .qsfp_0_rx_clk_2(qsfp_0_rx_clk_2), + .qsfp_0_rx_rst_2(qsfp_0_rx_rst_2), + .qsfp_0_rxd_2(qsfp_0_rxd_2), + .qsfp_0_rxc_2(qsfp_0_rxc_2), + .qsfp_0_tx_clk_3(qsfp_0_tx_clk_3), + .qsfp_0_tx_rst_3(qsfp_0_tx_rst_3), + .qsfp_0_txd_3(qsfp_0_txd_3), + .qsfp_0_txc_3(qsfp_0_txc_3), + .qsfp_0_rx_clk_3(qsfp_0_rx_clk_3), + .qsfp_0_rx_rst_3(qsfp_0_rx_rst_3), + .qsfp_0_rxd_3(qsfp_0_rxd_3), + .qsfp_0_rxc_3(qsfp_0_rxc_3), + .qsfp_1_tx_clk_0(qsfp_1_tx_clk_0), + .qsfp_1_tx_rst_0(qsfp_1_tx_rst_0), + .qsfp_1_txd_0(qsfp_1_txd_0), + .qsfp_1_txc_0(qsfp_1_txc_0), + .qsfp_1_rx_clk_0(qsfp_1_rx_clk_0), + .qsfp_1_rx_rst_0(qsfp_1_rx_rst_0), + .qsfp_1_rxd_0(qsfp_1_rxd_0), + .qsfp_1_rxc_0(qsfp_1_rxc_0), + .qsfp_1_tx_clk_1(qsfp_1_tx_clk_1), + .qsfp_1_tx_rst_1(qsfp_1_tx_rst_1), + .qsfp_1_txd_1(qsfp_1_txd_1), + .qsfp_1_txc_1(qsfp_1_txc_1), + .qsfp_1_rx_clk_1(qsfp_1_rx_clk_1), + .qsfp_1_rx_rst_1(qsfp_1_rx_rst_1), + .qsfp_1_rxd_1(qsfp_1_rxd_1), + .qsfp_1_rxc_1(qsfp_1_rxc_1), + .qsfp_1_tx_clk_2(qsfp_1_tx_clk_2), + .qsfp_1_tx_rst_2(qsfp_1_tx_rst_2), + .qsfp_1_txd_2(qsfp_1_txd_2), + .qsfp_1_txc_2(qsfp_1_txc_2), + .qsfp_1_rx_clk_2(qsfp_1_rx_clk_2), + .qsfp_1_rx_rst_2(qsfp_1_rx_rst_2), + .qsfp_1_rxd_2(qsfp_1_rxd_2), + .qsfp_1_rxc_2(qsfp_1_rxc_2), + .qsfp_1_tx_clk_3(qsfp_1_tx_clk_3), + .qsfp_1_tx_rst_3(qsfp_1_tx_rst_3), + .qsfp_1_txd_3(qsfp_1_txd_3), + .qsfp_1_txc_3(qsfp_1_txc_3), + .qsfp_1_rx_clk_3(qsfp_1_rx_clk_3), + .qsfp_1_rx_rst_3(qsfp_1_rx_rst_3), + .qsfp_1_rxd_3(qsfp_1_rxd_3), + .qsfp_1_rxc_3(qsfp_1_rxc_3) +); + +endmodule diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/udp_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/xgmii_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/xgmii_ep.py new file mode 120000 index 000000000..63b6d3567 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_10g/tb/xgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/xgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/Makefile b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/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/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/README.md b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/README.md new file mode 100644 index 000000000..691f825b8 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/README.md @@ -0,0 +1,24 @@ +# Verilog Ethernet ADM-PCIE-9V3 Example Design + +## Introduction + +This example design targets the Alpha Data ADM-PCIE-9V3 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: xcvu3p-ffvc1517-2-i +PHY: 25G BASE-R PHY IP core and internal GTY transceiver + +## 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 ADM-PCIE-9V3 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. + diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/common/vivado.mk b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/fpga.xdc b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/fpga.xdc new file mode 100644 index 000000000..0ba473e92 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/fpga.xdc @@ -0,0 +1,176 @@ +# XDC constraints for the ADM-PCIE-9V3 +# part: xcvu3p-ffvc1517-2-i + +# 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.UNUSEDPIN {Pullnone} [current_design] +set_property BITSTREAM.CONFIG.OVERTEMPSHUTDOWN Enable [current_design] + +# 300 MHz system clock +set_property -dict {LOC AP26 IOSTANDARD LVDS DIFF_TERM_ADV TERM_100} [get_ports clk_300mhz_p] +set_property -dict {LOC AP27 IOSTANDARD LVDS DIFF_TERM_ADV TERM_100} [get_ports clk_300mhz_n] +create_clock -period 3.333 -name clk_300mhz [get_ports clk_300mhz_p] + +# LEDs +set_property -dict {LOC AT27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {user_led_g[0]}] +set_property -dict {LOC AU27 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {user_led_g[1]}] +set_property -dict {LOC AU23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {user_led_r}] +set_property -dict {LOC AH24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {front_led[0]}] +set_property -dict {LOC AJ23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {front_led[1]}] + +# Switches +set_property -dict {LOC AV27 IOSTANDARD LVCMOS18} [get_ports {user_sw[0]}] +set_property -dict {LOC AW27 IOSTANDARD LVCMOS18} [get_ports {user_sw[1]}] + +# GPIO +#set_property -dict {LOC G30 IOSTANDARD LVCMOS18} [get_ports gpio_p[0]] +#set_property -dict {LOC F30 IOSTANDARD LVCMOS18} [get_ports gpio_n[0]] +#set_property -dict {LOC J31 IOSTANDARD LVCMOS18} [get_ports gpio_p[1]] +#set_property -dict {LOC H31 IOSTANDARD LVCMOS18} [get_ports gpio_n[1]] + +# QSFP28 Interfaces +set_property -dict {LOC G38 } [get_ports qsfp_0_rx_0_p] ;# MGTYRXN0_128 GTYE3_CHANNEL_X0Y16 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC G39 } [get_ports qsfp_0_rx_0_n] ;# MGTYRXP0_128 GTYE3_CHANNEL_X0Y16 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC E38 } [get_ports qsfp_0_rx_1_p] ;# MGTYRXN1_128 GTYE3_CHANNEL_X0Y17 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC E39 } [get_ports qsfp_0_rx_1_n] ;# MGTYRXP1_128 GTYE3_CHANNEL_X0Y17 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC C38 } [get_ports qsfp_0_rx_2_p] ;# MGTYRXN2_128 GTYE3_CHANNEL_X0Y18 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC C39 } [get_ports qsfp_0_rx_2_n] ;# MGTYRXP2_128 GTYE3_CHANNEL_X0Y18 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC B36 } [get_ports qsfp_0_rx_3_p] ;# MGTYRXN3_128 GTYE3_CHANNEL_X0Y19 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC B37 } [get_ports qsfp_0_rx_3_n] ;# MGTYRXP3_128 GTYE3_CHANNEL_X0Y19 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC F35 } [get_ports qsfp_0_tx_0_p] ;# MGTYTXN0_128 GTYE3_CHANNEL_X0Y16 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC F36 } [get_ports qsfp_0_tx_0_n] ;# MGTYTXP0_128 GTYE3_CHANNEL_X0Y16 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC D35 } [get_ports qsfp_0_tx_1_p] ;# MGTYTXN1_128 GTYE3_CHANNEL_X0Y17 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC D36 } [get_ports qsfp_0_tx_1_n] ;# MGTYTXP1_128 GTYE3_CHANNEL_X0Y17 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC C33 } [get_ports qsfp_0_tx_2_p] ;# MGTYTXN2_128 GTYE3_CHANNEL_X0Y18 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC C34 } [get_ports qsfp_0_tx_2_n] ;# MGTYTXP2_128 GTYE3_CHANNEL_X0Y18 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC A33 } [get_ports qsfp_0_tx_3_p] ;# MGTYTXN3_128 GTYE3_CHANNEL_X0Y19 / GTYE3_COMMON_X0Y4 +#set_property -dict {LOC A34 } [get_ports qsfp_0_tx_3_n] ;# MGTYTXP3_128 GTYE3_CHANNEL_X0Y19 / GTYE3_COMMON_X0Y4 +set_property -dict {LOC N33 } [get_ports qsfp_0_mgt_refclk_p] ;# MGTREFCLK0P_128 from ? +#set_property -dict {LOC N34 } [get_ports qsfp_0_mgt_refclk_n] ;# MGTREFCLK0N_128 from ? +set_property -dict {LOC F29 IOSTANDARD LVCMOS18 PULLUP true} [get_ports qsfp_0_modprs_l] +set_property -dict {LOC D31 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports qsfp_0_sel_l] + +# 161.1328125 MHz MGT reference clock +create_clock -period 6.206 -name qsfp_0_mgt_refclk [get_ports qsfp_0_mgt_refclk_p] + +set_property -dict {LOC R38 } [get_ports qsfp_1_rx_0_p] ;# MGTYRXN0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC R39 } [get_ports qsfp_1_rx_0_n] ;# MGTYRXP0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC N38 } [get_ports qsfp_1_rx_1_p] ;# MGTYRXN1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC N39 } [get_ports qsfp_1_rx_1_n] ;# MGTYRXP1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC L38 } [get_ports qsfp_1_rx_2_p] ;# MGTYRXN2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC L39 } [get_ports qsfp_1_rx_2_n] ;# MGTYRXP2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC J38 } [get_ports qsfp_1_rx_3_p] ;# MGTYRXN3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC J39 } [get_ports qsfp_1_rx_3_n] ;# MGTYRXP3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC P35 } [get_ports qsfp_1_tx_0_p] ;# MGTYTXN0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC P36 } [get_ports qsfp_1_tx_0_n] ;# MGTYTXP0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC M35 } [get_ports qsfp_1_tx_1_p] ;# MGTYTXN1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC M36 } [get_ports qsfp_1_tx_1_n] ;# MGTYTXP1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC K35 } [get_ports qsfp_1_tx_2_p] ;# MGTYTXN2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC K36 } [get_ports qsfp_1_tx_2_n] ;# MGTYTXP2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC H35 } [get_ports qsfp_1_tx_3_p] ;# MGTYTXN3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC H36 } [get_ports qsfp_1_tx_3_n] ;# MGTYTXP3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC U33 } [get_ports qsfp_1_mgt_refclk_p] ;# MGTREFCLK0P_127 from ? +#set_property -dict {LOC U34 } [get_ports qsfp_1_mgt_refclk_n] ;# MGTREFCLK0N_127 from ? +set_property -dict {LOC F33 IOSTANDARD LVCMOS18 PULLUP true} [get_ports qsfp_1_modprs_l] +set_property -dict {LOC D30 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports qsfp_1_sel_l] + +# 161.1328125 MHz MGT reference clock +create_clock -period 6.206 -name qsfp_1_mgt_refclk [get_ports qsfp_1_mgt_refclk_p] + +set_property -dict {LOC B29 IOSTANDARD LVCMOS18 PULLUP true} [get_ports qsfp_reset_l] +set_property -dict {LOC C29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports qsfp_int_l] +#set_property -dict {LOC A28 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports qsfp_i2c_scl] +#set_property -dict {LOC A29 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports qsfp_i2c_sda] + +# PCIe Interface +#set_property -dict {LOC J2 } [get_ports {pcie_rx_p[0]}] ;# MGTYRXP3_227 GTYE3_CHANNEL_X0Y7 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC J1 } [get_ports {pcie_rx_n[0]}] ;# MGTYTXP3_227 GTYE3_CHANNEL_X0Y7 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC H5 } [get_ports {pcie_tx_p[0]}] ;# MGTYTXN3_227 GTYE3_CHANNEL_X0Y7 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC H4 } [get_ports {pcie_tx_n[0]}] ;# MGTYTXP3_227 GTYE3_CHANNEL_X0Y7 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC L2 } [get_ports {pcie_rx_p[1]}] ;# MGTYTXN2_227 GTYE3_CHANNEL_X0Y6 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC L1 } [get_ports {pcie_rx_n[1]}] ;# MGTYTXP2_227 GTYE3_CHANNEL_X0Y6 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC K5 } [get_ports {pcie_tx_p[1]}] ;# MGTYTXN2_227 GTYE3_CHANNEL_X0Y6 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC K4 } [get_ports {pcie_tx_n[1]}] ;# MGTYTXP2_227 GTYE3_CHANNEL_X0Y6 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC N2 } [get_ports {pcie_rx_p[2]}] ;# MGTYTXN1_227 GTYE3_CHANNEL_X0Y5 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC N1 } [get_ports {pcie_rx_n[2]}] ;# MGTYTXP1_227 GTYE3_CHANNEL_X0Y5 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC M5 } [get_ports {pcie_tx_p[2]}] ;# MGTYTXN1_227 GTYE3_CHANNEL_X0Y5 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC M4 } [get_ports {pcie_tx_n[2]}] ;# MGTYTXP1_227 GTYE3_CHANNEL_X0Y5 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC R2 } [get_ports {pcie_rx_p[3]}] ;# MGTYTXN0_227 GTYE3_CHANNEL_X0Y4 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC R1 } [get_ports {pcie_rx_n[3]}] ;# MGTYTXP0_227 GTYE3_CHANNEL_X0Y4 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC P5 } [get_ports {pcie_tx_p[3]}] ;# MGTYTXN0_227 GTYE3_CHANNEL_X0Y4 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC P4 } [get_ports {pcie_tx_n[3]}] ;# MGTYTXP0_227 GTYE3_CHANNEL_X0Y4 / GTYE3_COMMON_X0Y1 +#set_property -dict {LOC U2 } [get_ports {pcie_rx_p[4]}] ;# MGTYTXN3_226 GTYE3_CHANNEL_X0Y3 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC U1 } [get_ports {pcie_rx_n[4]}] ;# MGTYTXP3_226 GTYE3_CHANNEL_X0Y3 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC T5 } [get_ports {pcie_tx_p[4]}] ;# MGTYTXN3_226 GTYE3_CHANNEL_X0Y3 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC T4 } [get_ports {pcie_tx_n[4]}] ;# MGTYTXP3_226 GTYE3_CHANNEL_X0Y3 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC W2 } [get_ports {pcie_rx_p[5]}] ;# MGTYTXN2_226 GTYE3_CHANNEL_X0Y2 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC W1 } [get_ports {pcie_rx_n[5]}] ;# MGTYTXP2_226 GTYE3_CHANNEL_X0Y2 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC V5 } [get_ports {pcie_tx_p[5]}] ;# MGTYTXN2_226 GTYE3_CHANNEL_X0Y2 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC V4 } [get_ports {pcie_tx_n[5]}] ;# MGTYTXP2_226 GTYE3_CHANNEL_X0Y2 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AA2 } [get_ports {pcie_rx_p[6]}] ;# MGTYTXN1_226 GTYE3_CHANNEL_X0Y1 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AA1 } [get_ports {pcie_rx_n[6]}] ;# MGTYTXP1_226 GTYE3_CHANNEL_X0Y1 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AB5 } [get_ports {pcie_tx_p[6]}] ;# MGTYTXN1_226 GTYE3_CHANNEL_X0Y1 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AB4 } [get_ports {pcie_tx_n[6]}] ;# MGTYTXP1_226 GTYE3_CHANNEL_X0Y1 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AC2 } [get_ports {pcie_rx_p[7]}] ;# MGTYTXN0_226 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AC1 } [get_ports {pcie_rx_n[7]}] ;# MGTYTXP0_226 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AD5 } [get_ports {pcie_tx_p[7]}] ;# MGTYTXN0_226 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AD4 } [get_ports {pcie_tx_n[7]}] ;# MGTYTXP0_226 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AE2 } [get_ports {pcie_rx_p[8]}] ;# MGTYTXN3_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AE1 } [get_ports {pcie_rx_n[8]}] ;# MGTYTXP3_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AF5 } [get_ports {pcie_tx_p[8]}] ;# MGTYTXN3_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AF4 } [get_ports {pcie_tx_n[8]}] ;# MGTYTXP3_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AG2 } [get_ports {pcie_rx_p[9]}] ;# MGTYTXN2_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AG1 } [get_ports {pcie_rx_n[9]}] ;# MGTYTXP2_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AH5 } [get_ports {pcie_tx_p[9]}] ;# MGTYTXN2_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AH4 } [get_ports {pcie_tx_n[9]}] ;# MGTYTXP2_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AJ2 } [get_ports {pcie_rx_p[10]}] ;# MGTYTXN1_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AJ1 } [get_ports {pcie_rx_n[10]}] ;# MGTYTXP1_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AK5 } [get_ports {pcie_tx_p[10]}] ;# MGTYTXN1_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AK4 } [get_ports {pcie_tx_n[10]}] ;# MGTYTXP1_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AL2 } [get_ports {pcie_rx_p[11]}] ;# MGTYTXN0_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AL1 } [get_ports {pcie_rx_n[11]}] ;# MGTYTXP0_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AM5 } [get_ports {pcie_tx_p[11]}] ;# MGTYTXN0_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AM4 } [get_ports {pcie_tx_n[11]}] ;# MGTYTXP0_225 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AN2 } [get_ports {pcie_rx_p[12]}] ;# MGTYTXN3_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AN1 } [get_ports {pcie_rx_n[12]}] ;# MGTYTXP3_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AP5 } [get_ports {pcie_tx_p[12]}] ;# MGTYTXN3_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AP4 } [get_ports {pcie_tx_n[12]}] ;# MGTYTXP3_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AR2 } [get_ports {pcie_rx_p[13]}] ;# MGTYTXN2_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AR1 } [get_ports {pcie_rx_n[13]}] ;# MGTYTXP2_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AT5 } [get_ports {pcie_tx_p[13]}] ;# MGTYTXN2_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AT4 } [get_ports {pcie_tx_n[13]}] ;# MGTYTXP2_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AU2 } [get_ports {pcie_rx_p[14]}] ;# MGTYTXN1_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AU1 } [get_ports {pcie_rx_n[14]}] ;# MGTYTXP1_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AU7 } [get_ports {pcie_tx_p[14]}] ;# MGTYTXN1_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AU6 } [get_ports {pcie_tx_n[14]}] ;# MGTYTXP1_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AV4 } [get_ports {pcie_rx_p[15]}] ;# MGTYTXN0_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AV3 } [get_ports {pcie_rx_n[15]}] ;# MGTYTXP0_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AW7 } [get_ports {pcie_tx_p[15]}] ;# MGTYTXN0_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AW6 } [get_ports {pcie_tx_n[15]}] ;# MGTYTXP0_224 GTYE3_CHANNEL_X0Y0 / GTYE3_COMMON_X0Y0 +#set_property -dict {LOC AA7 } [get_ports pcie_refclk_1_p] ;# MGTREFCLK0P_226 +#set_property -dict {LOC AA6 } [get_ports pcie_refclk_1_n] ;# MGTREFCLK0N_226 +#set_property -dict {LOC AJ7 } [get_ports pcie_refclk_2_p] ;# MGTREFCLK0P_224 +#set_property -dict {LOC AJ6 } [get_ports pcie_refclk_2_n] ;# MGTREFCLK0N_224 +#set_property -dict {LOC AJ31 IOSTANDARD LVCMOS18 PULLUP true} [get_ports perst_0] +#set_property -dict {LOC AH29 IOSTANDARD LVCMOS18 PULLUP true} [get_ports perst_1] + +# 100 MHz MGT reference clock +#create_clock -period 10 -name pcie_mgt_refclk_1 [get_ports pcie_refclk_1_p] +#create_clock -period 10 -name pcie_mgt_refclk_2 [get_ports pcie_refclk_2_p] + +# QSPI flash +#set_property -dict {LOC AB10 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi_clk}] +#set_property -dict {LOC AB8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi0_dq[0]}] +#set_property -dict {LOC AD8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi0_dq[1]}] +#set_property -dict {LOC Y8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi0_dq[2]}] +#set_property -dict {LOC AC8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi0_dq[3]}] +#set_property -dict {LOC AF30 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi1_dq[0]}] +#set_property -dict {LOC AG30 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi1_dq[1]}] +#set_property -dict {LOC AF28 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi1_dq[2]}] +#set_property -dict {LOC AG28 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {qspi1_dq[3]}] diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/fpga/Makefile b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/fpga/Makefile new file mode 100644 index 000000000..1a17dbdac --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/fpga/Makefile @@ -0,0 +1,106 @@ + +# FPGA settings +FPGA_PART = xcvu3p-ffvc1517-2-i +FPGA_TOP = fpga +FPGA_ARCH = virtexuplus + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += lib/eth/rtl/eth_mac_10g_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_10g.v +SYN_FILES += lib/eth/rtl/axis_xgmii_rx_64.v +SYN_FILES += lib/eth/rtl/axis_xgmii_tx_64.v +SYN_FILES += lib/eth/rtl/eth_phy_10g.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_if.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_frame_sync.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_ber_mon.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx_if.v +SYN_FILES += lib/eth/rtl/xgmii_baser_dec_64.v +SYN_FILES += lib/eth/rtl/xgmii_baser_enc_64.v +SYN_FILES += lib/eth/rtl/lfsr.v +SYN_FILES += lib/eth/rtl/eth_axis_rx_64.v +SYN_FILES += lib/eth/rtl/eth_axis_tx_64.v +SYN_FILES += lib/eth/rtl/udp_complete_64.v +SYN_FILES += lib/eth/rtl/udp_checksum_gen_64.v +SYN_FILES += lib/eth/rtl/udp_64.v +SYN_FILES += lib/eth/rtl/udp_ip_rx_64.v +SYN_FILES += lib/eth/rtl/udp_ip_tx_64.v +SYN_FILES += lib/eth/rtl/ip_complete_64.v +SYN_FILES += lib/eth/rtl/ip_64.v +SYN_FILES += lib/eth/rtl/ip_eth_rx_64.v +SYN_FILES += lib/eth/rtl/ip_eth_tx_64.v +SYN_FILES += lib/eth/rtl/ip_arb_mux.v +SYN_FILES += lib/eth/rtl/arp_64.v +SYN_FILES += lib/eth/rtl/arp_cache.v +SYN_FILES += lib/eth/rtl/arp_eth_rx_64.v +SYN_FILES += lib/eth/rtl/arp_eth_tx_64.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 + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl + +# IP +XCI_FILES += ip/gtwizard_ultrascale_0.xci + +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 + +%_primary.mcs %_secondary.mcs %_primary.prm %_secondary.prm: %.bit + echo "write_cfgmem -force -format mcs -size 64 -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: $(FPGA_TOP)_primary.mcs $(FPGA_TOP)_secondary.mcs $(FPGA_TOP)_primary.prm $(FPGA_TOP)_secondary.prm + echo "open_hw" > 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 {mt25qu256-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 \"$(FPGA_TOP)_primary.mcs\" \"$(FPGA_TOP)_secondary.mcs\"] [current_hw_cfgmem]" >> flash.tcl + echo "set_property PROGRAM.PRM_FILES [list \"$(FPGA_TOP)_primary.prm\" \"$(FPGA_TOP)_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 + diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/ip/gtwizard_ultrascale_0.xci b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/ip/gtwizard_ultrascale_0.xci new file mode 100644 index 000000000..9862aadf8 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/ip/gtwizard_ultrascale_0.xci @@ -0,0 +1,1411 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gtwizard_ultrascale_0 + + + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111000000000000" + 2 + 2578.125 + 0 + 0 + 125 + 67 + 3 + 2 + 0 + 2 + 0 + 0 + 1 + 0 + 1 + 0 + 250 + 0 + 0 + 0 + 0 + 0 + 1 + "00000000" + "00000000" + 1 + 4 + 0 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000" + 0 + "00000000" + 1 + 0 + 5000 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + 0 + "1010000011" + 0 + "0101111100" + 4 + 1 + 64 + 25.78125 + 15 + 1 + 390.6250000 + 4 + 0 + 0x000000000000000000000000000000000000000000000000 + 161.1328125 + 0 + 0 + 0 + 1 + 1 + 0 + 64 + 390.6250000 + 390.6250000 + 0 + 257.8125 + 0 + 8 + 2 + 0 + 0 + 0 + 390.625 + 0 + 0 + 1 + 4 + 1 + 64 + 25.78125 + 15 + 1 + 390.6250000 + 4 + 0 + 161.1328125 + 0 + 0 + 1 + 1 + 0 + 64 + 390.6250000 + 390.6250000 + 1 + X0Y19 X0Y18 X0Y17 X0Y16 X0Y15 X0Y14 X0Y13 X0Y12 + gtwizard_ultrascale_0 + 0 + 0 + + 125 + BOTH + 0 + GTY + 2 + 30 + 96 + 3 + gtye4 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + -1 + -1 + -1 + -1 + 1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + -1 + 0 + 0 + 0 + -1 + 0 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 27 + 0 + 10GBASE-R + 9 + 390.6250000 + 8 + 2 + 390.6250000 + false + CORE + NONE + CORE + CORE + EXAMPLE_DESIGN + CORE + EXAMPLE_DESIGN + CORE + false + NAME + false + 250 + false + false + 250 + GTY-10GBASE-R + 0 + MULTI + 1 + ENABLE + DISABLE + ENABLE + 00000000 + false + false + false + false + false + false + false + false + 00000000 + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 4 + 1 + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + false + false + false + false + false + false + false + false + 00000000 + DISABLE + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 0 + 5000 + ENABLE + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 1 + false + 0000000000 + false + 1010000011 + NONE + false + 0101111100 + true + 0 + AC + 64B66B_ASYNC + true + AUTO + 64 + 10 + -20 + 25.78125 + X0Y15 + RXPROGDIVCLK + QPLL0 + 200 + 0 + + 161.1328125 + + OFF + 0 + PROGRAMMABLE + 800 + 64 + 15 + false + 0 + 10.3125 + 257.8125 + 0 + false + QPLL0 + 390.625 + 1 + ENABLE + 64B66B_ASYNC + CUSTOM + true + 64 + 25.78125 + X0Y15 + TXPROGDIVCLK + QPLL0 + 0 + 161.1328125 + + 64 + false + 1 + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + true + true + false + true + true + true + false + true + true + true + false + false + false + false + false + true + false + false + false + false + false + true + true + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + virtexuplus + + + xcvu3p + ffvc1517 + VERILOG + + MIXED + -2 + + I + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/lib/eth b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/debounce_switch.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/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 + +`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/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/fpga.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/fpga.v new file mode 100644 index 000000000..82be12252 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/fpga.v @@ -0,0 +1,904 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 300MHz LVDS + */ + input wire clk_300mhz_p, + input wire clk_300mhz_n, + + /* + * GPIO + */ + output wire [1:0] user_led_g, + output wire user_led_r, + output wire [1:0] front_led, + input wire [1:0] user_sw, + + /* + * Ethernet: QSFP28 + */ + output wire qsfp_0_tx_0_p, + output wire qsfp_0_tx_0_n, + input wire qsfp_0_rx_0_p, + input wire qsfp_0_rx_0_n, + output wire qsfp_0_tx_1_p, + output wire qsfp_0_tx_1_n, + input wire qsfp_0_rx_1_p, + input wire qsfp_0_rx_1_n, + output wire qsfp_0_tx_2_p, + output wire qsfp_0_tx_2_n, + input wire qsfp_0_rx_2_p, + input wire qsfp_0_rx_2_n, + output wire qsfp_0_tx_3_p, + output wire qsfp_0_tx_3_n, + input wire qsfp_0_rx_3_p, + input wire qsfp_0_rx_3_n, + input wire qsfp_0_mgt_refclk_p, + input wire qsfp_0_mgt_refclk_n, + input wire qsfp_0_modprs_l, + output wire qsfp_0_sel_l, + + output wire qsfp_1_tx_0_p, + output wire qsfp_1_tx_0_n, + input wire qsfp_1_rx_0_p, + input wire qsfp_1_rx_0_n, + output wire qsfp_1_tx_1_p, + output wire qsfp_1_tx_1_n, + input wire qsfp_1_rx_1_p, + input wire qsfp_1_rx_1_n, + output wire qsfp_1_tx_2_p, + output wire qsfp_1_tx_2_n, + input wire qsfp_1_rx_2_p, + input wire qsfp_1_rx_2_n, + output wire qsfp_1_tx_3_p, + output wire qsfp_1_tx_3_n, + input wire qsfp_1_rx_3_p, + input wire qsfp_1_rx_3_n, + input wire qsfp_1_mgt_refclk_p, + input wire qsfp_1_mgt_refclk_n, + input wire qsfp_1_modprs_l, + output wire qsfp_1_sel_l, + + output wire qsfp_reset_l, + input wire qsfp_int_l +); + +// Clock and reset + +wire clk_300mhz_ibufg; +wire clk_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire rst_125mhz_int; + +// Internal 156.25 MHz clock +wire clk_156mhz_int; +wire rst_156mhz_int; + +wire mmcm_rst = 1'b0; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS #( + .DIFF_TERM("FALSE"), + .IBUF_LOW_PWR("FALSE") +) +clk_300mhz_ibufg_inst ( + .O (clk_300mhz_ibufg), + .I (clk_300mhz_p), + .IB (clk_300mhz_n) +); + +// MMCM instance +// 300 MHz in, 125 MHz out +// PFD range: 10 MHz to 500 MHz +// VCO range: 800 MHz to 1600 MHz +// M = 10, D = 3 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +MMCME3_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(8), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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(10), + .CLKFBOUT_PHASE(0), + .DIVCLK_DIVIDE(3), + .REF_JITTER1(0.010), + .CLKIN1_PERIOD(3.333), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_300mhz_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_125mhz_bufg_inst ( + .I(clk_125mhz_mmcm_out), + .O(clk_125mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_int) +); + +// GPIO +wire [1:0] user_sw_int; + +debounce_switch #( + .WIDTH(2), + .N(4), + .RATE(125000) +) +debounce_switch_inst ( + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + .in({user_sw}), + .out({user_sw_int}) +); + +// XGMII 10G PHY +assign qsfp_0_sel_l = 1'b0; + +wire qsfp_0_tx_clk_0_int; +wire qsfp_0_tx_rst_0_int; +wire [63:0] qsfp_0_txd_0_int; +wire [7:0] qsfp_0_txc_0_int; +wire qsfp_0_rx_clk_0_int; +wire qsfp_0_rx_rst_0_int; +wire [63:0] qsfp_0_rxd_0_int; +wire [7:0] qsfp_0_rxc_0_int; +wire qsfp_0_tx_clk_1_int; +wire qsfp_0_tx_rst_1_int; +wire [63:0] qsfp_0_txd_1_int; +wire [7:0] qsfp_0_txc_1_int; +wire qsfp_0_rx_clk_1_int; +wire qsfp_0_rx_rst_1_int; +wire [63:0] qsfp_0_rxd_1_int; +wire [7:0] qsfp_0_rxc_1_int; +wire qsfp_0_tx_clk_2_int; +wire qsfp_0_tx_rst_2_int; +wire [63:0] qsfp_0_txd_2_int; +wire [7:0] qsfp_0_txc_2_int; +wire qsfp_0_rx_clk_2_int; +wire qsfp_0_rx_rst_2_int; +wire [63:0] qsfp_0_rxd_2_int; +wire [7:0] qsfp_0_rxc_2_int; +wire qsfp_0_tx_clk_3_int; +wire qsfp_0_tx_rst_3_int; +wire [63:0] qsfp_0_txd_3_int; +wire [7:0] qsfp_0_txc_3_int; +wire qsfp_0_rx_clk_3_int; +wire qsfp_0_rx_rst_3_int; +wire [63:0] qsfp_0_rxd_3_int; +wire [7:0] qsfp_0_rxc_3_int; + +assign qsfp_1_sel_l = 1'b0; + +wire qsfp_1_tx_clk_0_int; +wire qsfp_1_tx_rst_0_int; +wire [63:0] qsfp_1_txd_0_int; +wire [7:0] qsfp_1_txc_0_int; +wire qsfp_1_rx_clk_0_int; +wire qsfp_1_rx_rst_0_int; +wire [63:0] qsfp_1_rxd_0_int; +wire [7:0] qsfp_1_rxc_0_int; +wire qsfp_1_tx_clk_1_int; +wire qsfp_1_tx_rst_1_int; +wire [63:0] qsfp_1_txd_1_int; +wire [7:0] qsfp_1_txc_1_int; +wire qsfp_1_rx_clk_1_int; +wire qsfp_1_rx_rst_1_int; +wire [63:0] qsfp_1_rxd_1_int; +wire [7:0] qsfp_1_rxc_1_int; +wire qsfp_1_tx_clk_2_int; +wire qsfp_1_tx_rst_2_int; +wire [63:0] qsfp_1_txd_2_int; +wire [7:0] qsfp_1_txc_2_int; +wire qsfp_1_rx_clk_2_int; +wire qsfp_1_rx_rst_2_int; +wire [63:0] qsfp_1_rxd_2_int; +wire [7:0] qsfp_1_rxc_2_int; +wire qsfp_1_tx_clk_3_int; +wire qsfp_1_tx_rst_3_int; +wire [63:0] qsfp_1_txd_3_int; +wire [7:0] qsfp_1_txc_3_int; +wire qsfp_1_rx_clk_3_int; +wire qsfp_1_rx_rst_3_int; +wire [63:0] qsfp_1_rxd_3_int; +wire [7:0] qsfp_1_rxc_3_int; + +assign qsfp_reset_l = 1'b1; + +wire qsfp_0_rx_block_lock_0; +wire qsfp_0_rx_block_lock_1; +wire qsfp_0_rx_block_lock_2; +wire qsfp_0_rx_block_lock_3; + +wire qsfp_1_rx_block_lock_0; +wire qsfp_1_rx_block_lock_1; +wire qsfp_1_rx_block_lock_2; +wire qsfp_1_rx_block_lock_3; + +wire qsfp_0_mgt_refclk; +wire qsfp_1_mgt_refclk; + +wire [7:0] gt_txclkout; +wire gt_txusrclk; + +wire [7:0] gt_rxclkout; +wire [7:0] gt_rxusrclk; + +wire gt_reset_tx_done; +wire gt_reset_rx_done; + +wire [7:0] gt_txprgdivresetdone; +wire [7:0] gt_txpmaresetdone; +wire [7:0] gt_rxprgdivresetdone; +wire [7:0] gt_rxpmaresetdone; + +wire gt_tx_reset = ~((>_txprgdivresetdone) & (>_txpmaresetdone)); +wire gt_rx_reset = ~>_rxpmaresetdone; + +reg gt_userclk_tx_active = 1'b0; +reg [7:0] gt_userclk_rx_active = 1'b0; + +IBUFDS_GTE4 ibufds_gte4_qsfp_0_mgt_refclk_inst ( + .I (qsfp_0_mgt_refclk_p), + .IB (qsfp_0_mgt_refclk_n), + .CEB (1'b0), + .O (qsfp_0_mgt_refclk), + .ODIV2 () +); + +IBUFDS_GTE4 ibufds_gte4_qsfp_1_mgt_refclk_inst ( + .I (qsfp_1_mgt_refclk_p), + .IB (qsfp_1_mgt_refclk_n), + .CEB (1'b0), + .O (qsfp_1_mgt_refclk), + .ODIV2 () +); + + +BUFG_GT bufg_gt_tx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_tx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_txclkout[0]), + .O (gt_txusrclk) +); + +assign clk_156mhz_int = gt_txusrclk; + +always @(posedge gt_txusrclk, posedge gt_tx_reset) begin + if (gt_tx_reset) begin + gt_userclk_tx_active <= 1'b0; + end else begin + gt_userclk_tx_active <= 1'b1; + end +end + +generate + +genvar n; + +for (n = 0; n < 8; n = n + 1) begin + + BUFG_GT bufg_gt_rx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_rx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_rxclkout[n]), + .O (gt_rxusrclk[n]) + ); + + always @(posedge gt_rxusrclk[n], posedge gt_rx_reset) begin + if (gt_rx_reset) begin + gt_userclk_rx_active[n] <= 1'b0; + end else begin + gt_userclk_rx_active[n] <= 1'b1; + end + end + +end + +endgenerate + +sync_reset #( + .N(4) +) +sync_reset_156mhz_inst ( + .clk(clk_156mhz_int), + .rst(~gt_reset_tx_done), + .sync_reset_out(rst_156mhz_int) +); + +wire [5:0] qsfp_0_gt_txheader_0; +wire [63:0] qsfp_0_gt_txdata_0; +wire qsfp_0_gt_rxgearboxslip_0; +wire [5:0] qsfp_0_gt_rxheader_0; +wire [1:0] qsfp_0_gt_rxheadervalid_0; +wire [63:0] qsfp_0_gt_rxdata_0; +wire [1:0] qsfp_0_gt_rxdatavalid_0; + +wire [5:0] qsfp_0_gt_txheader_1; +wire [63:0] qsfp_0_gt_txdata_1; +wire qsfp_0_gt_rxgearboxslip_1; +wire [5:0] qsfp_0_gt_rxheader_1; +wire [1:0] qsfp_0_gt_rxheadervalid_1; +wire [63:0] qsfp_0_gt_rxdata_1; +wire [1:0] qsfp_0_gt_rxdatavalid_1; + +wire [5:0] qsfp_0_gt_txheader_2; +wire [63:0] qsfp_0_gt_txdata_2; +wire qsfp_0_gt_rxgearboxslip_2; +wire [5:0] qsfp_0_gt_rxheader_2; +wire [1:0] qsfp_0_gt_rxheadervalid_2; +wire [63:0] qsfp_0_gt_rxdata_2; +wire [1:0] qsfp_0_gt_rxdatavalid_2; + +wire [5:0] qsfp_0_gt_txheader_3; +wire [63:0] qsfp_0_gt_txdata_3; +wire qsfp_0_gt_rxgearboxslip_3; +wire [5:0] qsfp_0_gt_rxheader_3; +wire [1:0] qsfp_0_gt_rxheadervalid_3; +wire [63:0] qsfp_0_gt_rxdata_3; +wire [1:0] qsfp_0_gt_rxdatavalid_3; + +wire [5:0] qsfp_1_gt_txheader_0; +wire [63:0] qsfp_1_gt_txdata_0; +wire qsfp_1_gt_rxgearboxslip_0; +wire [5:0] qsfp_1_gt_rxheader_0; +wire [1:0] qsfp_1_gt_rxheadervalid_0; +wire [63:0] qsfp_1_gt_rxdata_0; +wire [1:0] qsfp_1_gt_rxdatavalid_0; + +wire [5:0] qsfp_1_gt_txheader_1; +wire [63:0] qsfp_1_gt_txdata_1; +wire qsfp_1_gt_rxgearboxslip_1; +wire [5:0] qsfp_1_gt_rxheader_1; +wire [1:0] qsfp_1_gt_rxheadervalid_1; +wire [63:0] qsfp_1_gt_rxdata_1; +wire [1:0] qsfp_1_gt_rxdatavalid_1; + +wire [5:0] qsfp_1_gt_txheader_2; +wire [63:0] qsfp_1_gt_txdata_2; +wire qsfp_1_gt_rxgearboxslip_2; +wire [5:0] qsfp_1_gt_rxheader_2; +wire [1:0] qsfp_1_gt_rxheadervalid_2; +wire [63:0] qsfp_1_gt_rxdata_2; +wire [1:0] qsfp_1_gt_rxdatavalid_2; + +wire [5:0] qsfp_1_gt_txheader_3; +wire [63:0] qsfp_1_gt_txdata_3; +wire qsfp_1_gt_rxgearboxslip_3; +wire [5:0] qsfp_1_gt_rxheader_3; +wire [1:0] qsfp_1_gt_rxheadervalid_3; +wire [63:0] qsfp_1_gt_rxdata_3; +wire [1:0] qsfp_1_gt_rxdatavalid_3; + +gtwizard_ultrascale_0 +qsfp_gty_inst ( + .gtwiz_userclk_tx_active_in(>_userclk_tx_active), + .gtwiz_userclk_rx_active_in(>_userclk_rx_active), + + .gtwiz_reset_clk_freerun_in(clk_125mhz_int), + .gtwiz_reset_all_in(rst_125mhz_int), + + .gtwiz_reset_tx_pll_and_datapath_in(1'b0), + .gtwiz_reset_tx_datapath_in(1'b0), + + .gtwiz_reset_rx_pll_and_datapath_in(1'b0), + .gtwiz_reset_rx_datapath_in(1'b0), + + .gtwiz_reset_rx_cdr_stable_out(), + + .gtwiz_reset_tx_done_out(gt_reset_tx_done), + .gtwiz_reset_rx_done_out(gt_reset_rx_done), + + .gtrefclk00_in({qsfp_0_mgt_refclk, qsfp_1_mgt_refclk}), + + .qpll0outclk_out(), + .qpll0outrefclk_out(), + + .gtyrxn_in({qsfp_0_rx_3_n, qsfp_0_rx_2_n, qsfp_0_rx_1_n, qsfp_0_rx_0_n, qsfp_1_rx_3_n, qsfp_1_rx_2_n, qsfp_1_rx_1_n, qsfp_1_rx_0_n}), + .gtyrxp_in({qsfp_0_rx_3_p, qsfp_0_rx_2_p, qsfp_0_rx_1_p, qsfp_0_rx_0_p, qsfp_1_rx_3_p, qsfp_1_rx_2_p, qsfp_1_rx_1_p, qsfp_1_rx_0_p}), + + .rxusrclk_in(gt_rxusrclk), + .rxusrclk2_in(gt_rxusrclk), + + .gtwiz_userdata_tx_in({qsfp_0_gt_txdata_3, qsfp_0_gt_txdata_2, qsfp_0_gt_txdata_1, qsfp_0_gt_txdata_0, qsfp_1_gt_txdata_3, qsfp_1_gt_txdata_2, qsfp_1_gt_txdata_1, qsfp_1_gt_txdata_0}), + .txheader_in({qsfp_0_gt_txheader_3, qsfp_0_gt_txheader_2, qsfp_0_gt_txheader_1, qsfp_0_gt_txheader_0, qsfp_1_gt_txheader_3, qsfp_1_gt_txheader_2, qsfp_1_gt_txheader_1, qsfp_1_gt_txheader_0}), + .txsequence_in({8{1'b0}}), + + .txusrclk_in({8{gt_txusrclk}}), + .txusrclk2_in({8{gt_txusrclk}}), + + .gtpowergood_out(), + + .gtytxn_out({qsfp_0_tx_3_n, qsfp_0_tx_2_n, qsfp_0_tx_1_n, qsfp_0_tx_0_n, qsfp_1_tx_3_n, qsfp_1_tx_2_n, qsfp_1_tx_1_n, qsfp_1_tx_0_n}), + .gtytxp_out({qsfp_0_tx_3_p, qsfp_0_tx_2_p, qsfp_0_tx_1_p, qsfp_0_tx_0_p, qsfp_1_tx_3_p, qsfp_1_tx_2_p, qsfp_1_tx_1_p, qsfp_1_tx_0_p}), + + .rxgearboxslip_in({qsfp_0_gt_rxgearboxslip_3, qsfp_0_gt_rxgearboxslip_2, qsfp_0_gt_rxgearboxslip_1, qsfp_0_gt_rxgearboxslip_0, qsfp_1_gt_rxgearboxslip_3, qsfp_1_gt_rxgearboxslip_2, qsfp_1_gt_rxgearboxslip_1, qsfp_1_gt_rxgearboxslip_0}), + .gtwiz_userdata_rx_out({qsfp_0_gt_rxdata_3, qsfp_0_gt_rxdata_2, qsfp_0_gt_rxdata_1, qsfp_0_gt_rxdata_0, qsfp_1_gt_rxdata_3, qsfp_1_gt_rxdata_2, qsfp_1_gt_rxdata_1, qsfp_1_gt_rxdata_0}), + .rxdatavalid_out({qsfp_0_gt_rxdatavalid_3, qsfp_0_gt_rxdatavalid_2, qsfp_0_gt_rxdatavalid_1, qsfp_0_gt_rxdatavalid_0, qsfp_1_gt_rxdatavalid_3, qsfp_1_gt_rxdatavalid_2, qsfp_1_gt_rxdatavalid_1, qsfp_1_gt_rxdatavalid_0}), + .rxheader_out({qsfp_0_gt_rxheader_3, qsfp_0_gt_rxheader_2, qsfp_0_gt_rxheader_1, qsfp_0_gt_rxheader_0, qsfp_1_gt_rxheader_3, qsfp_1_gt_rxheader_2, qsfp_1_gt_rxheader_1, qsfp_1_gt_rxheader_0}), + .rxheadervalid_out({qsfp_0_gt_rxheadervalid_3, qsfp_0_gt_rxheadervalid_2, qsfp_0_gt_rxheadervalid_1, qsfp_0_gt_rxheadervalid_0, qsfp_1_gt_rxheadervalid_3, qsfp_1_gt_rxheadervalid_2, qsfp_1_gt_rxheadervalid_1, qsfp_1_gt_rxheadervalid_0}), + .rxoutclk_out(gt_rxclkout), + .rxpmaresetdone_out(gt_rxpmaresetdone), + .rxprgdivresetdone_out(gt_rxprgdivresetdone), + .rxstartofseq_out(), + + .txoutclk_out(gt_txclkout), + .txpmaresetdone_out(gt_txpmaresetdone), + .txprgdivresetdone_out(gt_txprgdivresetdone) +); + +assign qsfp_0_tx_clk_0_int = clk_156mhz_int; +assign qsfp_0_tx_rst_0_int = rst_156mhz_int; + +assign qsfp_0_rx_clk_0_int = gt_rxusrclk[4]; + +sync_reset #( + .N(4) +) +qsfp_0_rx_rst_0_reset_sync_inst ( + .clk(qsfp_0_rx_clk_0_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_0_rx_rst_0_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp_0_phy_0_inst ( + .tx_clk(qsfp_0_tx_clk_0_int), + .tx_rst(qsfp_0_tx_rst_0_int), + .rx_clk(qsfp_0_rx_clk_0_int), + .rx_rst(qsfp_0_rx_rst_0_int), + .xgmii_txd(qsfp_0_txd_0_int), + .xgmii_txc(qsfp_0_txc_0_int), + .xgmii_rxd(qsfp_0_rxd_0_int), + .xgmii_rxc(qsfp_0_rxc_0_int), + .serdes_tx_data(qsfp_0_gt_txdata_0), + .serdes_tx_hdr(qsfp_0_gt_txheader_0), + .serdes_rx_data(qsfp_0_gt_rxdata_0), + .serdes_rx_hdr(qsfp_0_gt_rxheader_0), + .serdes_rx_bitslip(qsfp_0_gt_rxgearboxslip_0), + .rx_block_lock(qsfp_0_rx_block_lock_0), + .rx_high_ber() +); + +assign qsfp_0_tx_clk_1_int = clk_156mhz_int; +assign qsfp_0_tx_rst_1_int = rst_156mhz_int; + +assign qsfp_0_rx_clk_1_int = gt_rxusrclk[5]; + +sync_reset #( + .N(4) +) +qsfp_0_rx_rst_1_reset_sync_inst ( + .clk(qsfp_0_rx_clk_1_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_0_rx_rst_1_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp_0_phy_1_inst ( + .tx_clk(qsfp_0_tx_clk_1_int), + .tx_rst(qsfp_0_tx_rst_1_int), + .rx_clk(qsfp_0_rx_clk_1_int), + .rx_rst(qsfp_0_rx_rst_1_int), + .xgmii_txd(qsfp_0_txd_1_int), + .xgmii_txc(qsfp_0_txc_1_int), + .xgmii_rxd(qsfp_0_rxd_1_int), + .xgmii_rxc(qsfp_0_rxc_1_int), + .serdes_tx_data(qsfp_0_gt_txdata_1), + .serdes_tx_hdr(qsfp_0_gt_txheader_1), + .serdes_rx_data(qsfp_0_gt_rxdata_1), + .serdes_rx_hdr(qsfp_0_gt_rxheader_1), + .serdes_rx_bitslip(qsfp_0_gt_rxgearboxslip_1), + .rx_block_lock(qsfp_0_rx_block_lock_1), + .rx_high_ber() +); + +assign qsfp_0_tx_clk_2_int = clk_156mhz_int; +assign qsfp_0_tx_rst_2_int = rst_156mhz_int; + +assign qsfp_0_rx_clk_2_int = gt_rxusrclk[6]; + +sync_reset #( + .N(4) +) +qsfp_0_rx_rst_2_reset_sync_inst ( + .clk(qsfp_0_rx_clk_2_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_0_rx_rst_2_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp_0_phy_2_inst ( + .tx_clk(qsfp_0_tx_clk_2_int), + .tx_rst(qsfp_0_tx_rst_2_int), + .rx_clk(qsfp_0_rx_clk_2_int), + .rx_rst(qsfp_0_rx_rst_2_int), + .xgmii_txd(qsfp_0_txd_2_int), + .xgmii_txc(qsfp_0_txc_2_int), + .xgmii_rxd(qsfp_0_rxd_2_int), + .xgmii_rxc(qsfp_0_rxc_2_int), + .serdes_tx_data(qsfp_0_gt_txdata_2), + .serdes_tx_hdr(qsfp_0_gt_txheader_2), + .serdes_rx_data(qsfp_0_gt_rxdata_2), + .serdes_rx_hdr(qsfp_0_gt_rxheader_2), + .serdes_rx_bitslip(qsfp_0_gt_rxgearboxslip_2), + .rx_block_lock(qsfp_0_rx_block_lock_2), + .rx_high_ber() +); + +assign qsfp_0_tx_clk_3_int = clk_156mhz_int; +assign qsfp_0_tx_rst_3_int = rst_156mhz_int; + +assign qsfp_0_rx_clk_3_int = gt_rxusrclk[7]; + +sync_reset #( + .N(4) +) +qsfp_0_rx_rst_3_reset_sync_inst ( + .clk(qsfp_0_rx_clk_3_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_0_rx_rst_3_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp_0_phy_3_inst ( + .tx_clk(qsfp_0_tx_clk_3_int), + .tx_rst(qsfp_0_tx_rst_3_int), + .rx_clk(qsfp_0_rx_clk_3_int), + .rx_rst(qsfp_0_rx_rst_3_int), + .xgmii_txd(qsfp_0_txd_3_int), + .xgmii_txc(qsfp_0_txc_3_int), + .xgmii_rxd(qsfp_0_rxd_3_int), + .xgmii_rxc(qsfp_0_rxc_3_int), + .serdes_tx_data(qsfp_0_gt_txdata_3), + .serdes_tx_hdr(qsfp_0_gt_txheader_3), + .serdes_rx_data(qsfp_0_gt_rxdata_3), + .serdes_rx_hdr(qsfp_0_gt_rxheader_3), + .serdes_rx_bitslip(qsfp_0_gt_rxgearboxslip_3), + .rx_block_lock(qsfp_0_rx_block_lock_3), + .rx_high_ber() +); + +assign qsfp_1_tx_clk_0_int = clk_156mhz_int; +assign qsfp_1_tx_rst_0_int = rst_156mhz_int; + +assign qsfp_1_rx_clk_0_int = gt_rxusrclk[0]; + +sync_reset #( + .N(4) +) +qsfp_1_rx_rst_0_reset_sync_inst ( + .clk(qsfp_1_rx_clk_0_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_1_rx_rst_0_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp_1_phy_0_inst ( + .tx_clk(qsfp_1_tx_clk_0_int), + .tx_rst(qsfp_1_tx_rst_0_int), + .rx_clk(qsfp_1_rx_clk_0_int), + .rx_rst(qsfp_1_rx_rst_0_int), + .xgmii_txd(qsfp_1_txd_0_int), + .xgmii_txc(qsfp_1_txc_0_int), + .xgmii_rxd(qsfp_1_rxd_0_int), + .xgmii_rxc(qsfp_1_rxc_0_int), + .serdes_tx_data(qsfp_1_gt_txdata_0), + .serdes_tx_hdr(qsfp_1_gt_txheader_0), + .serdes_rx_data(qsfp_1_gt_rxdata_0), + .serdes_rx_hdr(qsfp_1_gt_rxheader_0), + .serdes_rx_bitslip(qsfp_1_gt_rxgearboxslip_0), + .rx_block_lock(qsfp_1_rx_block_lock_0), + .rx_high_ber() +); + +assign qsfp_1_tx_clk_1_int = clk_156mhz_int; +assign qsfp_1_tx_rst_1_int = rst_156mhz_int; + +assign qsfp_1_rx_clk_1_int = gt_rxusrclk[1]; + +sync_reset #( + .N(4) +) +qsfp_1_rx_rst_1_reset_sync_inst ( + .clk(qsfp_1_rx_clk_1_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_1_rx_rst_1_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp_1_phy_1_inst ( + .tx_clk(qsfp_1_tx_clk_1_int), + .tx_rst(qsfp_1_tx_rst_1_int), + .rx_clk(qsfp_1_rx_clk_1_int), + .rx_rst(qsfp_1_rx_rst_1_int), + .xgmii_txd(qsfp_1_txd_1_int), + .xgmii_txc(qsfp_1_txc_1_int), + .xgmii_rxd(qsfp_1_rxd_1_int), + .xgmii_rxc(qsfp_1_rxc_1_int), + .serdes_tx_data(qsfp_1_gt_txdata_1), + .serdes_tx_hdr(qsfp_1_gt_txheader_1), + .serdes_rx_data(qsfp_1_gt_rxdata_1), + .serdes_rx_hdr(qsfp_1_gt_rxheader_1), + .serdes_rx_bitslip(qsfp_1_gt_rxgearboxslip_1), + .rx_block_lock(qsfp_1_rx_block_lock_1), + .rx_high_ber() +); + +assign qsfp_1_tx_clk_2_int = clk_156mhz_int; +assign qsfp_1_tx_rst_2_int = rst_156mhz_int; + +assign qsfp_1_rx_clk_2_int = gt_rxusrclk[2]; + +sync_reset #( + .N(4) +) +qsfp_1_rx_rst_2_reset_sync_inst ( + .clk(qsfp_1_rx_clk_2_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_1_rx_rst_2_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp_1_phy_2_inst ( + .tx_clk(qsfp_1_tx_clk_2_int), + .tx_rst(qsfp_1_tx_rst_2_int), + .rx_clk(qsfp_1_rx_clk_2_int), + .rx_rst(qsfp_1_rx_rst_2_int), + .xgmii_txd(qsfp_1_txd_2_int), + .xgmii_txc(qsfp_1_txc_2_int), + .xgmii_rxd(qsfp_1_rxd_2_int), + .xgmii_rxc(qsfp_1_rxc_2_int), + .serdes_tx_data(qsfp_1_gt_txdata_2), + .serdes_tx_hdr(qsfp_1_gt_txheader_2), + .serdes_rx_data(qsfp_1_gt_rxdata_2), + .serdes_rx_hdr(qsfp_1_gt_rxheader_2), + .serdes_rx_bitslip(qsfp_1_gt_rxgearboxslip_2), + .rx_block_lock(qsfp_1_rx_block_lock_2), + .rx_high_ber() +); + +assign qsfp_1_tx_clk_3_int = clk_156mhz_int; +assign qsfp_1_tx_rst_3_int = rst_156mhz_int; + +assign qsfp_1_rx_clk_3_int = gt_rxusrclk[3]; + +sync_reset #( + .N(4) +) +qsfp_1_rx_rst_3_reset_sync_inst ( + .clk(qsfp_1_rx_clk_3_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_1_rx_rst_3_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp_1_phy_3_inst ( + .tx_clk(qsfp_1_tx_clk_3_int), + .tx_rst(qsfp_1_tx_rst_3_int), + .rx_clk(qsfp_1_rx_clk_3_int), + .rx_rst(qsfp_1_rx_rst_3_int), + .xgmii_txd(qsfp_1_txd_3_int), + .xgmii_txc(qsfp_1_txc_3_int), + .xgmii_rxd(qsfp_1_rxd_3_int), + .xgmii_rxc(qsfp_1_rxc_3_int), + .serdes_tx_data(qsfp_1_gt_txdata_3), + .serdes_tx_hdr(qsfp_1_gt_txheader_3), + .serdes_rx_data(qsfp_1_gt_rxdata_3), + .serdes_rx_hdr(qsfp_1_gt_rxheader_3), + .serdes_rx_bitslip(qsfp_1_gt_rxgearboxslip_3), + .rx_block_lock(qsfp_1_rx_block_lock_3), + .rx_high_ber() +); + +//assign led = sw[0] ? {qsfp_1_rx_block_lock_4, qsfp_1_rx_block_lock_3, qsfp_1_rx_block_lock_2, qsfp_1_rx_block_lock_1, qsfp_0_rx_block_lock_4, qsfp_0_rx_block_lock_3, qsfp_0_rx_block_lock_2, qsfp_0_rx_block_lock_1} : led_int; +assign front_led = {1'b0, qsfp_0_rx_block_lock_0}; + +fpga_core +core_inst ( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + .clk(clk_156mhz_int), + .rst(rst_156mhz_int), + /* + * GPIO + */ + .user_led_g(user_led_g), + .user_led_r(user_led_r), + //.front_led(front_led), + .user_sw(user_sw_int), + + /* + * Ethernet: QSFP28 + */ + .qsfp_0_tx_clk_0(qsfp_0_tx_clk_0_int), + .qsfp_0_tx_rst_0(qsfp_0_tx_rst_0_int), + .qsfp_0_txd_0(qsfp_0_txd_0_int), + .qsfp_0_txc_0(qsfp_0_txc_0_int), + .qsfp_0_rx_clk_0(qsfp_0_rx_clk_0_int), + .qsfp_0_rx_rst_0(qsfp_0_rx_rst_0_int), + .qsfp_0_rxd_0(qsfp_0_rxd_0_int), + .qsfp_0_rxc_0(qsfp_0_rxc_0_int), + .qsfp_0_tx_clk_1(qsfp_0_tx_clk_1_int), + .qsfp_0_tx_rst_1(qsfp_0_tx_rst_1_int), + .qsfp_0_txd_1(qsfp_0_txd_1_int), + .qsfp_0_txc_1(qsfp_0_txc_1_int), + .qsfp_0_rx_clk_1(qsfp_0_rx_clk_1_int), + .qsfp_0_rx_rst_1(qsfp_0_rx_rst_1_int), + .qsfp_0_rxd_1(qsfp_0_rxd_1_int), + .qsfp_0_rxc_1(qsfp_0_rxc_1_int), + .qsfp_0_tx_clk_2(qsfp_0_tx_clk_2_int), + .qsfp_0_tx_rst_2(qsfp_0_tx_rst_2_int), + .qsfp_0_txd_2(qsfp_0_txd_2_int), + .qsfp_0_txc_2(qsfp_0_txc_2_int), + .qsfp_0_rx_clk_2(qsfp_0_rx_clk_2_int), + .qsfp_0_rx_rst_2(qsfp_0_rx_rst_2_int), + .qsfp_0_rxd_2(qsfp_0_rxd_2_int), + .qsfp_0_rxc_2(qsfp_0_rxc_2_int), + .qsfp_0_tx_clk_3(qsfp_0_tx_clk_3_int), + .qsfp_0_tx_rst_3(qsfp_0_tx_rst_3_int), + .qsfp_0_txd_3(qsfp_0_txd_3_int), + .qsfp_0_txc_3(qsfp_0_txc_3_int), + .qsfp_0_rx_clk_3(qsfp_0_rx_clk_3_int), + .qsfp_0_rx_rst_3(qsfp_0_rx_rst_3_int), + .qsfp_0_rxd_3(qsfp_0_rxd_3_int), + .qsfp_0_rxc_3(qsfp_0_rxc_3_int), + .qsfp_1_tx_clk_0(qsfp_1_tx_clk_0_int), + .qsfp_1_tx_rst_0(qsfp_1_tx_rst_0_int), + .qsfp_1_txd_0(qsfp_1_txd_0_int), + .qsfp_1_txc_0(qsfp_1_txc_0_int), + .qsfp_1_rx_clk_0(qsfp_1_rx_clk_0_int), + .qsfp_1_rx_rst_0(qsfp_1_rx_rst_0_int), + .qsfp_1_rxd_0(qsfp_1_rxd_0_int), + .qsfp_1_rxc_0(qsfp_1_rxc_0_int), + .qsfp_1_tx_clk_1(qsfp_1_tx_clk_1_int), + .qsfp_1_tx_rst_1(qsfp_1_tx_rst_1_int), + .qsfp_1_txd_1(qsfp_1_txd_1_int), + .qsfp_1_txc_1(qsfp_1_txc_1_int), + .qsfp_1_rx_clk_1(qsfp_1_rx_clk_1_int), + .qsfp_1_rx_rst_1(qsfp_1_rx_rst_1_int), + .qsfp_1_rxd_1(qsfp_1_rxd_1_int), + .qsfp_1_rxc_1(qsfp_1_rxc_1_int), + .qsfp_1_tx_clk_2(qsfp_1_tx_clk_2_int), + .qsfp_1_tx_rst_2(qsfp_1_tx_rst_2_int), + .qsfp_1_txd_2(qsfp_1_txd_2_int), + .qsfp_1_txc_2(qsfp_1_txc_2_int), + .qsfp_1_rx_clk_2(qsfp_1_rx_clk_2_int), + .qsfp_1_rx_rst_2(qsfp_1_rx_rst_2_int), + .qsfp_1_rxd_2(qsfp_1_rxd_2_int), + .qsfp_1_rxc_2(qsfp_1_rxc_2_int), + .qsfp_1_tx_clk_3(qsfp_1_tx_clk_3_int), + .qsfp_1_tx_rst_3(qsfp_1_tx_rst_3_int), + .qsfp_1_txd_3(qsfp_1_txd_3_int), + .qsfp_1_txc_3(qsfp_1_txc_3_int), + .qsfp_1_rx_clk_3(qsfp_1_rx_clk_3_int), + .qsfp_1_rx_rst_3(qsfp_1_rx_rst_3_int), + .qsfp_1_rxd_3(qsfp_1_rxd_3_int), + .qsfp_1_rxc_3(qsfp_1_rxc_3_int) +); + +endmodule diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/fpga_core.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/fpga_core.v new file mode 100644 index 000000000..93f5ddc41 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/fpga_core.v @@ -0,0 +1,659 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * Clock: 156.25MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + + /* + * GPIO + */ + output wire [1:0] user_led_g, + output wire user_led_r, + output wire [1:0] front_led, + input wire [1:0] user_sw, + + /* + * Ethernet: QSFP28 + */ + input wire qsfp_0_tx_clk_0, + input wire qsfp_0_tx_rst_0, + output wire [63:0] qsfp_0_txd_0, + output wire [7:0] qsfp_0_txc_0, + input wire qsfp_0_rx_clk_0, + input wire qsfp_0_rx_rst_0, + input wire [63:0] qsfp_0_rxd_0, + input wire [7:0] qsfp_0_rxc_0, + input wire qsfp_0_tx_clk_1, + input wire qsfp_0_tx_rst_1, + output wire [63:0] qsfp_0_txd_1, + output wire [7:0] qsfp_0_txc_1, + input wire qsfp_0_rx_clk_1, + input wire qsfp_0_rx_rst_1, + input wire [63:0] qsfp_0_rxd_1, + input wire [7:0] qsfp_0_rxc_1, + input wire qsfp_0_tx_clk_2, + input wire qsfp_0_tx_rst_2, + output wire [63:0] qsfp_0_txd_2, + output wire [7:0] qsfp_0_txc_2, + input wire qsfp_0_rx_clk_2, + input wire qsfp_0_rx_rst_2, + input wire [63:0] qsfp_0_rxd_2, + input wire [7:0] qsfp_0_rxc_2, + input wire qsfp_0_tx_clk_3, + input wire qsfp_0_tx_rst_3, + output wire [63:0] qsfp_0_txd_3, + output wire [7:0] qsfp_0_txc_3, + input wire qsfp_0_rx_clk_3, + input wire qsfp_0_rx_rst_3, + input wire [63:0] qsfp_0_rxd_3, + input wire [7:0] qsfp_0_rxc_3, + input wire qsfp_1_tx_clk_0, + input wire qsfp_1_tx_rst_0, + output wire [63:0] qsfp_1_txd_0, + output wire [7:0] qsfp_1_txc_0, + input wire qsfp_1_rx_clk_0, + input wire qsfp_1_rx_rst_0, + input wire [63:0] qsfp_1_rxd_0, + input wire [7:0] qsfp_1_rxc_0, + input wire qsfp_1_tx_clk_1, + input wire qsfp_1_tx_rst_1, + output wire [63:0] qsfp_1_txd_1, + output wire [7:0] qsfp_1_txc_1, + input wire qsfp_1_rx_clk_1, + input wire qsfp_1_rx_rst_1, + input wire [63:0] qsfp_1_rxd_1, + input wire [7:0] qsfp_1_rxc_1, + input wire qsfp_1_tx_clk_2, + input wire qsfp_1_tx_rst_2, + output wire [63:0] qsfp_1_txd_2, + output wire [7:0] qsfp_1_txc_2, + input wire qsfp_1_rx_clk_2, + input wire qsfp_1_rx_rst_2, + input wire [63:0] qsfp_1_rxd_2, + input wire [7:0] qsfp_1_rxc_2, + input wire qsfp_1_tx_clk_3, + input wire qsfp_1_tx_rst_3, + output wire [63:0] qsfp_1_txd_3, + output wire [7:0] qsfp_1_txc_3, + input wire qsfp_1_rx_clk_3, + input wire qsfp_1_rx_rst_3, + input wire [63:0] qsfp_1_rxd_3, + input wire [7:0] qsfp_1_rxc_3 +); + +// AXI between MAC and Ethernet modules +wire [63:0] rx_axis_tdata; +wire [7:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tready; +wire rx_axis_tlast; +wire rx_axis_tuser; + +wire [63:0] tx_axis_tdata; +wire [7:0] tx_axis_tkeep; +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 [63:0] rx_eth_payload_axis_tdata; +wire [7:0] rx_eth_payload_axis_tkeep; +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 [63:0] tx_eth_payload_axis_tdata; +wire [7:0] tx_eth_payload_axis_tkeep; +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 [63:0] rx_ip_payload_axis_tdata; +wire [7:0] rx_ip_payload_axis_tkeep; +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 [63:0] tx_ip_payload_axis_tdata; +wire [7:0] tx_ip_payload_axis_tkeep; +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 [63:0] rx_udp_payload_axis_tdata; +wire [7:0] rx_udp_payload_axis_tkeep; +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 [63:0] tx_udp_payload_axis_tdata; +wire [7:0] tx_udp_payload_axis_tkeep; +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 [63:0] rx_fifo_udp_payload_axis_tdata; +wire [7:0] rx_fifo_udp_payload_axis_tkeep; +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 [63:0] tx_fifo_udp_payload_axis_tdata; +wire [7:0] tx_fifo_udp_payload_axis_tkeep; +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_tkeep = 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_tkeep = tx_fifo_udp_payload_axis_tkeep; +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_tkeep = rx_udp_payload_axis_tkeep; +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 + valid_last <= tx_udp_payload_axis_tvalid; + if (tx_udp_payload_axis_tvalid && !valid_last) begin + led_reg <= tx_udp_payload_axis_tdata; + end + end +end + +assign user_led_g = ~led_reg[1:0]; +assign user_led_r = 1'b1; +assign front_led = 2'b00; + +assign phy_reset_n = !rst; + +assign qsfp_0_txd_1 = 64'h0707070707070707; +assign qsfp_0_txc_1 = 8'hff; +assign qsfp_0_txd_2 = 64'h0707070707070707; +assign qsfp_0_txc_2 = 8'hff; +assign qsfp_0_txd_3 = 64'h0707070707070707; +assign qsfp_0_txc_3 = 8'hff; + +assign qsfp_1_txd_0 = 64'h0707070707070707; +assign qsfp_1_txc_0 = 8'hff; +assign qsfp_1_txd_1 = 64'h0707070707070707; +assign qsfp_1_txc_1 = 8'hff; +assign qsfp_1_txd_2 = 64'h0707070707070707; +assign qsfp_1_txc_2 = 8'hff; +assign qsfp_1_txd_3 = 64'h0707070707070707; +assign qsfp_1_txc_3 = 8'hff; + +eth_mac_10g_fifo #( + .ENABLE_PADDING(1), + .ENABLE_DIC(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(9), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(9), + .RX_FRAME_FIFO(1) +) +eth_mac_10g_fifo_inst ( + .rx_clk(qsfp_0_rx_clk_0), + .rx_rst(qsfp_0_rx_rst_0), + .tx_clk(qsfp_0_tx_clk_0), + .tx_rst(qsfp_0_tx_rst_0), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + + .xgmii_rxd(qsfp_0_rxd_0), + .xgmii_rxc(qsfp_0_rxc_0), + .xgmii_txd(qsfp_0_txd_0), + .xgmii_txc(qsfp_0_txc_0), + + .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(), + + .ifg_delay(8'd12) +); + +eth_axis_rx_64 +eth_axis_rx_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(rx_axis_tdata), + .s_axis_tkeep(rx_axis_tkeep), + .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_tkeep(rx_eth_payload_axis_tkeep), + .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_64 +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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_axis_tkeep), + .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_64 +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_tkeep(rx_eth_payload_axis_tkeep), + .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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_ip_payload_axis_tkeep), + .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_tkeep(rx_ip_payload_axis_tkeep), + .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_tkeep(tx_udp_payload_axis_tkeep), + .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_tkeep(rx_udp_payload_axis_tkeep), + .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(1'b0) +); + +axis_fifo #( + .ADDR_WIDTH(10), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .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(rx_fifo_udp_payload_axis_tkeep), + .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(tx_fifo_udp_payload_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/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/sync_reset.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/sync_signal.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/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/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/arp_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/axis_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/eth_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/ip_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/test_fpga_core.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/test_fpga_core.py new file mode 100755 index 000000000..35b21e22b --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/test_fpga_core.py @@ -0,0 +1,462 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import xgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_10g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_10g.v") +srcs.append("../lib/eth/rtl/axis_xgmii_rx_64.v") +srcs.append("../lib/eth/rtl/axis_xgmii_tx_64.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx_64.v") +srcs.append("../lib/eth/rtl/eth_axis_tx_64.v") +srcs.append("../lib/eth/rtl/udp_complete_64.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen_64.v") +srcs.append("../lib/eth/rtl/udp_64.v") +srcs.append("../lib/eth/rtl/udp_ip_rx_64.v") +srcs.append("../lib/eth/rtl/udp_ip_tx_64.v") +srcs.append("../lib/eth/rtl/ip_complete_64.v") +srcs.append("../lib/eth/rtl/ip_64.v") +srcs.append("../lib/eth/rtl/ip_eth_rx_64.v") +srcs.append("../lib/eth/rtl/ip_eth_tx_64.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp_64.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx_64.v") +srcs.append("../lib/eth/rtl/arp_eth_tx_64.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + user_sw = Signal(intbv(0)[2:]) + qsfp_0_tx_clk_0 = Signal(bool(0)) + qsfp_0_tx_rst_0 = Signal(bool(0)) + qsfp_0_rx_clk_0 = Signal(bool(0)) + qsfp_0_rx_rst_0 = Signal(bool(0)) + qsfp_0_rxd_0 = Signal(intbv(0)[64:]) + qsfp_0_rxc_0 = Signal(intbv(0)[8:]) + qsfp_0_tx_clk_1 = Signal(bool(0)) + qsfp_0_tx_rst_1 = Signal(bool(0)) + qsfp_0_rx_clk_1 = Signal(bool(0)) + qsfp_0_rx_rst_1 = Signal(bool(0)) + qsfp_0_rxd_1 = Signal(intbv(0)[64:]) + qsfp_0_rxc_1 = Signal(intbv(0)[8:]) + qsfp_0_tx_clk_2 = Signal(bool(0)) + qsfp_0_tx_rst_2 = Signal(bool(0)) + qsfp_0_rx_clk_2 = Signal(bool(0)) + qsfp_0_rx_rst_2 = Signal(bool(0)) + qsfp_0_rxd_2 = Signal(intbv(0)[64:]) + qsfp_0_rxc_2 = Signal(intbv(0)[8:]) + qsfp_0_tx_clk_3 = Signal(bool(0)) + qsfp_0_tx_rst_3 = Signal(bool(0)) + qsfp_0_rx_clk_3 = Signal(bool(0)) + qsfp_0_rx_rst_3 = Signal(bool(0)) + qsfp_0_rxd_3 = Signal(intbv(0)[64:]) + qsfp_0_rxc_3 = Signal(intbv(0)[8:]) + qsfp_1_tx_clk_0 = Signal(bool(0)) + qsfp_1_tx_rst_0 = Signal(bool(0)) + qsfp_1_rx_clk_0 = Signal(bool(0)) + qsfp_1_rx_rst_0 = Signal(bool(0)) + qsfp_1_rxd_0 = Signal(intbv(0)[64:]) + qsfp_1_rxc_0 = Signal(intbv(0)[8:]) + qsfp_1_tx_clk_1 = Signal(bool(0)) + qsfp_1_tx_rst_1 = Signal(bool(0)) + qsfp_1_rx_clk_1 = Signal(bool(0)) + qsfp_1_rx_rst_1 = Signal(bool(0)) + qsfp_1_rxd_1 = Signal(intbv(0)[64:]) + qsfp_1_rxc_1 = Signal(intbv(0)[8:]) + qsfp_1_tx_clk_2 = Signal(bool(0)) + qsfp_1_tx_rst_2 = Signal(bool(0)) + qsfp_1_rx_clk_2 = Signal(bool(0)) + qsfp_1_rx_rst_2 = Signal(bool(0)) + qsfp_1_rxd_2 = Signal(intbv(0)[64:]) + qsfp_1_rxc_2 = Signal(intbv(0)[8:]) + qsfp_1_tx_clk_3 = Signal(bool(0)) + qsfp_1_tx_rst_3 = Signal(bool(0)) + qsfp_1_rx_clk_3 = Signal(bool(0)) + qsfp_1_rx_rst_3 = Signal(bool(0)) + qsfp_1_rxd_3 = Signal(intbv(0)[64:]) + qsfp_1_rxc_3 = Signal(intbv(0)[8:]) + + # Outputs + user_led_g = Signal(intbv(0)[2:]) + user_led_r = Signal(bool(0)) + front_led = Signal(intbv(0)[2:]) + qsfp_0_txd_0 = Signal(intbv(0)[64:]) + qsfp_0_txc_0 = Signal(intbv(0)[8:]) + qsfp_0_txd_1 = Signal(intbv(0)[64:]) + qsfp_0_txc_1 = Signal(intbv(0)[8:]) + qsfp_0_txd_2 = Signal(intbv(0)[64:]) + qsfp_0_txc_2 = Signal(intbv(0)[8:]) + qsfp_0_txd_3 = Signal(intbv(0)[64:]) + qsfp_0_txc_3 = Signal(intbv(0)[8:]) + qsfp_1_txd_0 = Signal(intbv(0)[64:]) + qsfp_1_txc_0 = Signal(intbv(0)[8:]) + qsfp_1_txd_1 = Signal(intbv(0)[64:]) + qsfp_1_txc_1 = Signal(intbv(0)[8:]) + qsfp_1_txd_2 = Signal(intbv(0)[64:]) + qsfp_1_txc_2 = Signal(intbv(0)[8:]) + qsfp_1_txd_3 = Signal(intbv(0)[64:]) + qsfp_1_txc_3 = Signal(intbv(0)[8:]) + + # sources and sinks + qsfp_0_0_source = xgmii_ep.XGMIISource() + qsfp_0_0_source_logic = qsfp_0_0_source.create_logic(qsfp_0_rx_clk_0, qsfp_0_rx_rst_0, txd=qsfp_0_rxd_0, txc=qsfp_0_rxc_0, name='qsfp_0_0_source') + + qsfp_0_0_sink = xgmii_ep.XGMIISink() + qsfp_0_0_sink_logic = qsfp_0_0_sink.create_logic(qsfp_0_tx_clk_0, qsfp_0_tx_rst_0, rxd=qsfp_0_txd_0, rxc=qsfp_0_txc_0, name='qsfp_0_0_sink') + + qsfp_0_1_source = xgmii_ep.XGMIISource() + qsfp_0_1_source_logic = qsfp_0_1_source.create_logic(qsfp_0_rx_clk_1, qsfp_0_rx_rst_1, txd=qsfp_0_rxd_1, txc=qsfp_0_rxc_1, name='qsfp_0_1_source') + + qsfp_0_1_sink = xgmii_ep.XGMIISink() + qsfp_0_1_sink_logic = qsfp_0_1_sink.create_logic(qsfp_0_tx_clk_1, qsfp_0_tx_rst_1, rxd=qsfp_0_txd_1, rxc=qsfp_0_txc_1, name='qsfp_0_1_sink') + + qsfp_0_2_source = xgmii_ep.XGMIISource() + qsfp_0_2_source_logic = qsfp_0_2_source.create_logic(qsfp_0_rx_clk_2, qsfp_0_rx_rst_2, txd=qsfp_0_rxd_2, txc=qsfp_0_rxc_2, name='qsfp_0_2_source') + + qsfp_0_2_sink = xgmii_ep.XGMIISink() + qsfp_0_2_sink_logic = qsfp_0_2_sink.create_logic(qsfp_0_tx_clk_2, qsfp_0_tx_rst_2, rxd=qsfp_0_txd_2, rxc=qsfp_0_txc_2, name='qsfp_0_2_sink') + + qsfp_0_3_source = xgmii_ep.XGMIISource() + qsfp_0_3_source_logic = qsfp_0_3_source.create_logic(qsfp_0_rx_clk_3, qsfp_0_rx_rst_3, txd=qsfp_0_rxd_3, txc=qsfp_0_rxc_3, name='qsfp_0_3_source') + + qsfp_0_3_sink = xgmii_ep.XGMIISink() + qsfp_0_3_sink_logic = qsfp_0_3_sink.create_logic(qsfp_0_tx_clk_3, qsfp_0_tx_rst_3, rxd=qsfp_0_txd_3, rxc=qsfp_0_txc_3, name='qsfp_0_3_sink') + + qsfp_1_0_source = xgmii_ep.XGMIISource() + qsfp_1_0_source_logic = qsfp_1_0_source.create_logic(qsfp_1_rx_clk_0, qsfp_1_rx_rst_0, txd=qsfp_1_rxd_0, txc=qsfp_1_rxc_0, name='qsfp_1_0_source') + + qsfp_1_0_sink = xgmii_ep.XGMIISink() + qsfp_1_0_sink_logic = qsfp_1_0_sink.create_logic(qsfp_1_tx_clk_0, qsfp_1_tx_rst_0, rxd=qsfp_1_txd_0, rxc=qsfp_1_txc_0, name='qsfp_1_0_sink') + + qsfp_1_1_source = xgmii_ep.XGMIISource() + qsfp_1_1_source_logic = qsfp_1_1_source.create_logic(qsfp_1_rx_clk_1, qsfp_1_rx_rst_1, txd=qsfp_1_rxd_1, txc=qsfp_1_rxc_1, name='qsfp_1_1_source') + + qsfp_1_1_sink = xgmii_ep.XGMIISink() + qsfp_1_1_sink_logic = qsfp_1_1_sink.create_logic(qsfp_1_tx_clk_1, qsfp_1_tx_rst_1, rxd=qsfp_1_txd_1, rxc=qsfp_1_txc_1, name='qsfp_1_1_sink') + + qsfp_1_2_source = xgmii_ep.XGMIISource() + qsfp_1_2_source_logic = qsfp_1_2_source.create_logic(qsfp_1_rx_clk_2, qsfp_1_rx_rst_2, txd=qsfp_1_rxd_2, txc=qsfp_1_rxc_2, name='qsfp_1_2_source') + + qsfp_1_2_sink = xgmii_ep.XGMIISink() + qsfp_1_2_sink_logic = qsfp_1_2_sink.create_logic(qsfp_1_tx_clk_2, qsfp_1_tx_rst_2, rxd=qsfp_1_txd_2, rxc=qsfp_1_txc_2, name='qsfp_1_2_sink') + + qsfp_1_3_source = xgmii_ep.XGMIISource() + qsfp_1_3_source_logic = qsfp_1_3_source.create_logic(qsfp_1_rx_clk_3, qsfp_1_rx_rst_3, txd=qsfp_1_rxd_3, txc=qsfp_1_rxc_3, name='qsfp_1_3_source') + + qsfp_1_3_sink = xgmii_ep.XGMIISink() + qsfp_1_3_sink_logic = qsfp_1_3_sink.create_logic(qsfp_1_tx_clk_3, qsfp_1_tx_rst_3, rxd=qsfp_1_txd_3, rxc=qsfp_1_txc_3, name='qsfp_1_3_sink') + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + user_led_g=user_led_g, + user_led_r=user_led_r, + front_led=front_led, + user_sw=user_sw, + + qsfp_0_tx_clk_0=qsfp_0_tx_clk_0, + qsfp_0_tx_rst_0=qsfp_0_tx_rst_0, + qsfp_0_txd_0=qsfp_0_txd_0, + qsfp_0_txc_0=qsfp_0_txc_0, + qsfp_0_rx_clk_0=qsfp_0_rx_clk_0, + qsfp_0_rx_rst_0=qsfp_0_rx_rst_0, + qsfp_0_rxd_0=qsfp_0_rxd_0, + qsfp_0_rxc_0=qsfp_0_rxc_0, + qsfp_0_tx_clk_1=qsfp_0_tx_clk_1, + qsfp_0_tx_rst_1=qsfp_0_tx_rst_1, + qsfp_0_txd_1=qsfp_0_txd_1, + qsfp_0_txc_1=qsfp_0_txc_1, + qsfp_0_rx_clk_1=qsfp_0_rx_clk_1, + qsfp_0_rx_rst_1=qsfp_0_rx_rst_1, + qsfp_0_rxd_1=qsfp_0_rxd_1, + qsfp_0_rxc_1=qsfp_0_rxc_1, + qsfp_0_tx_clk_2=qsfp_0_tx_clk_2, + qsfp_0_tx_rst_2=qsfp_0_tx_rst_2, + qsfp_0_txd_2=qsfp_0_txd_2, + qsfp_0_txc_2=qsfp_0_txc_2, + qsfp_0_rx_clk_2=qsfp_0_rx_clk_2, + qsfp_0_rx_rst_2=qsfp_0_rx_rst_2, + qsfp_0_rxd_2=qsfp_0_rxd_2, + qsfp_0_rxc_2=qsfp_0_rxc_2, + qsfp_0_tx_clk_3=qsfp_0_tx_clk_3, + qsfp_0_tx_rst_3=qsfp_0_tx_rst_3, + qsfp_0_txd_3=qsfp_0_txd_3, + qsfp_0_txc_3=qsfp_0_txc_3, + qsfp_0_rx_clk_3=qsfp_0_rx_clk_3, + qsfp_0_rx_rst_3=qsfp_0_rx_rst_3, + qsfp_0_rxd_3=qsfp_0_rxd_3, + qsfp_0_rxc_3=qsfp_0_rxc_3, + qsfp_1_tx_clk_0=qsfp_1_tx_clk_0, + qsfp_1_tx_rst_0=qsfp_1_tx_rst_0, + qsfp_1_txd_0=qsfp_1_txd_0, + qsfp_1_txc_0=qsfp_1_txc_0, + qsfp_1_rx_clk_0=qsfp_1_rx_clk_0, + qsfp_1_rx_rst_0=qsfp_1_rx_rst_0, + qsfp_1_rxd_0=qsfp_1_rxd_0, + qsfp_1_rxc_0=qsfp_1_rxc_0, + qsfp_1_tx_clk_1=qsfp_1_tx_clk_1, + qsfp_1_tx_rst_1=qsfp_1_tx_rst_1, + qsfp_1_txd_1=qsfp_1_txd_1, + qsfp_1_txc_1=qsfp_1_txc_1, + qsfp_1_rx_clk_1=qsfp_1_rx_clk_1, + qsfp_1_rx_rst_1=qsfp_1_rx_rst_1, + qsfp_1_rxd_1=qsfp_1_rxd_1, + qsfp_1_rxc_1=qsfp_1_rxc_1, + qsfp_1_tx_clk_2=qsfp_1_tx_clk_2, + qsfp_1_tx_rst_2=qsfp_1_tx_rst_2, + qsfp_1_txd_2=qsfp_1_txd_2, + qsfp_1_txc_2=qsfp_1_txc_2, + qsfp_1_rx_clk_2=qsfp_1_rx_clk_2, + qsfp_1_rx_rst_2=qsfp_1_rx_rst_2, + qsfp_1_rxd_2=qsfp_1_rxd_2, + qsfp_1_rxc_2=qsfp_1_rxc_2, + qsfp_1_tx_clk_3=qsfp_1_tx_clk_3, + qsfp_1_tx_rst_3=qsfp_1_tx_rst_3, + qsfp_1_txd_3=qsfp_1_txd_3, + qsfp_1_txc_3=qsfp_1_txc_3, + qsfp_1_rx_clk_3=qsfp_1_rx_clk_3, + qsfp_1_rx_rst_3=qsfp_1_rx_rst_3, + qsfp_1_rxd_3=qsfp_1_rxd_3, + qsfp_1_rxc_3=qsfp_1_rxc_3 + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + qsfp_0_tx_clk_0.next = not qsfp_0_tx_clk_0 + qsfp_0_rx_clk_0.next = not qsfp_0_rx_clk_0 + qsfp_0_tx_clk_1.next = not qsfp_0_tx_clk_1 + qsfp_0_rx_clk_1.next = not qsfp_0_rx_clk_1 + qsfp_0_tx_clk_2.next = not qsfp_0_tx_clk_2 + qsfp_0_rx_clk_2.next = not qsfp_0_rx_clk_2 + qsfp_0_tx_clk_3.next = not qsfp_0_tx_clk_3 + qsfp_0_rx_clk_3.next = not qsfp_0_rx_clk_3 + qsfp_1_tx_clk_0.next = not qsfp_1_tx_clk_0 + qsfp_1_rx_clk_0.next = not qsfp_1_rx_clk_0 + qsfp_1_tx_clk_1.next = not qsfp_1_tx_clk_1 + qsfp_1_rx_clk_1.next = not qsfp_1_rx_clk_1 + qsfp_1_tx_clk_2.next = not qsfp_1_tx_clk_2 + qsfp_1_rx_clk_2.next = not qsfp_1_rx_clk_2 + qsfp_1_tx_clk_3.next = not qsfp_1_tx_clk_3 + qsfp_1_rx_clk_3.next = not qsfp_1_rx_clk_3 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + qsfp_0_tx_rst_1.next = 1 + qsfp_0_rx_rst_1.next = 1 + qsfp_0_tx_rst_2.next = 1 + qsfp_0_rx_rst_2.next = 1 + qsfp_0_tx_rst_3.next = 1 + qsfp_0_rx_rst_3.next = 1 + qsfp_0_tx_rst_0.next = 1 + qsfp_0_rx_rst_0.next = 1 + qsfp_1_tx_rst_1.next = 1 + qsfp_1_rx_rst_1.next = 1 + qsfp_1_tx_rst_2.next = 1 + qsfp_1_rx_rst_2.next = 1 + qsfp_1_tx_rst_3.next = 1 + qsfp_1_rx_rst_3.next = 1 + qsfp_1_tx_rst_0.next = 1 + qsfp_1_rx_rst_0.next = 1 + yield clk.posedge + rst.next = 0 + qsfp_0_tx_rst_1.next = 0 + qsfp_0_rx_rst_1.next = 0 + qsfp_0_tx_rst_2.next = 0 + qsfp_0_rx_rst_2.next = 0 + qsfp_0_tx_rst_3.next = 0 + qsfp_0_rx_rst_3.next = 0 + qsfp_0_tx_rst_0.next = 0 + qsfp_0_rx_rst_0.next = 0 + qsfp_1_tx_rst_1.next = 0 + qsfp_1_rx_rst_1.next = 0 + qsfp_1_tx_rst_2.next = 0 + qsfp_1_rx_rst_2.next = 0 + qsfp_1_tx_rst_3.next = 0 + qsfp_1_rx_rst_3.next = 0 + qsfp_1_tx_rst_0.next = 0 + qsfp_1_rx_rst_0.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + qsfp_0_0_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while qsfp_0_0_sink.empty(): + yield clk.posedge + + rx_frame = qsfp_0_0_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + qsfp_0_0_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while qsfp_0_0_sink.empty(): + yield clk.posedge + + rx_frame = qsfp_0_0_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert qsfp_0_0_source.empty() + assert qsfp_0_0_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/test_fpga_core.v b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/test_fpga_core.v new file mode 100644 index 000000000..003072a91 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/test_fpga_core.v @@ -0,0 +1,269 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [1:0] user_sw = 0; +reg qsfp_0_tx_clk_0 = 0; +reg qsfp_0_tx_rst_0 = 0; +reg qsfp_0_rx_clk_0 = 0; +reg qsfp_0_rx_rst_0 = 0; +reg [63:0] qsfp_0_rxd_0 = 0; +reg [7:0] qsfp_0_rxc_0 = 0; +reg qsfp_0_tx_clk_1 = 0; +reg qsfp_0_tx_rst_1 = 0; +reg qsfp_0_rx_clk_1 = 0; +reg qsfp_0_rx_rst_1 = 0; +reg [63:0] qsfp_0_rxd_1 = 0; +reg [7:0] qsfp_0_rxc_1 = 0; +reg qsfp_0_tx_clk_2 = 0; +reg qsfp_0_tx_rst_2 = 0; +reg qsfp_0_rx_clk_2 = 0; +reg qsfp_0_rx_rst_2 = 0; +reg [63:0] qsfp_0_rxd_2 = 0; +reg [7:0] qsfp_0_rxc_2 = 0; +reg qsfp_0_tx_clk_3 = 0; +reg qsfp_0_tx_rst_3 = 0; +reg qsfp_0_rx_clk_3 = 0; +reg qsfp_0_rx_rst_3 = 0; +reg [63:0] qsfp_0_rxd_3 = 0; +reg [7:0] qsfp_0_rxc_3 = 0; +reg qsfp_1_tx_clk_0 = 0; +reg qsfp_1_tx_rst_0 = 0; +reg qsfp_1_rx_clk_0 = 0; +reg qsfp_1_rx_rst_0 = 0; +reg [63:0] qsfp_1_rxd_0 = 0; +reg [7:0] qsfp_1_rxc_0 = 0; +reg qsfp_1_tx_clk_1 = 0; +reg qsfp_1_tx_rst_1 = 0; +reg qsfp_1_rx_clk_1 = 0; +reg qsfp_1_rx_rst_1 = 0; +reg [63:0] qsfp_1_rxd_1 = 0; +reg [7:0] qsfp_1_rxc_1 = 0; +reg qsfp_1_tx_clk_2 = 0; +reg qsfp_1_tx_rst_2 = 0; +reg qsfp_1_rx_clk_2 = 0; +reg qsfp_1_rx_rst_2 = 0; +reg [63:0] qsfp_1_rxd_2 = 0; +reg [7:0] qsfp_1_rxc_2 = 0; +reg qsfp_1_tx_clk_3 = 0; +reg qsfp_1_tx_rst_3 = 0; +reg qsfp_1_rx_clk_3 = 0; +reg qsfp_1_rx_rst_3 = 0; +reg [63:0] qsfp_1_rxd_3 = 0; +reg [7:0] qsfp_1_rxc_3 = 0; + +// Outputs +wire [1:0] user_led_g; +wire user_led_r; +wire [1:0] front_led; +wire [63:0] qsfp_0_txd_0; +wire [7:0] qsfp_0_txc_0; +wire [63:0] qsfp_0_txd_1; +wire [7:0] qsfp_0_txc_1; +wire [63:0] qsfp_0_txd_2; +wire [7:0] qsfp_0_txc_2; +wire [63:0] qsfp_0_txd_3; +wire [7:0] qsfp_0_txc_3; +wire [63:0] qsfp_1_txd_0; +wire [7:0] qsfp_1_txc_0; +wire [63:0] qsfp_1_txd_1; +wire [7:0] qsfp_1_txc_1; +wire [63:0] qsfp_1_txd_2; +wire [7:0] qsfp_1_txc_2; +wire [63:0] qsfp_1_txd_3; +wire [7:0] qsfp_1_txc_3; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + user_sw, + qsfp_0_tx_clk_0, + qsfp_0_tx_rst_0, + qsfp_0_rx_clk_0, + qsfp_0_rx_rst_0, + qsfp_0_rxd_0, + qsfp_0_rxc_0, + qsfp_0_tx_clk_1, + qsfp_0_tx_rst_1, + qsfp_0_rx_clk_1, + qsfp_0_rx_rst_1, + qsfp_0_rxd_1, + qsfp_0_rxc_1, + qsfp_0_tx_clk_2, + qsfp_0_tx_rst_2, + qsfp_0_rx_clk_2, + qsfp_0_rx_rst_2, + qsfp_0_rxd_2, + qsfp_0_rxc_2, + qsfp_0_tx_clk_3, + qsfp_0_tx_rst_3, + qsfp_0_rx_clk_3, + qsfp_0_rx_rst_3, + qsfp_0_rxd_3, + qsfp_0_rxc_3, + qsfp_1_tx_clk_0, + qsfp_1_tx_rst_0, + qsfp_1_rx_clk_0, + qsfp_1_rx_rst_0, + qsfp_1_rxd_0, + qsfp_1_rxc_0, + qsfp_1_tx_clk_1, + qsfp_1_tx_rst_1, + qsfp_1_rx_clk_1, + qsfp_1_rx_rst_1, + qsfp_1_rxd_1, + qsfp_1_rxc_1, + qsfp_1_tx_clk_2, + qsfp_1_tx_rst_2, + qsfp_1_rx_clk_2, + qsfp_1_rx_rst_2, + qsfp_1_rxd_2, + qsfp_1_rxc_2, + qsfp_1_tx_clk_3, + qsfp_1_tx_rst_3, + qsfp_1_rx_clk_3, + qsfp_1_rx_rst_3, + qsfp_1_rxd_3, + qsfp_1_rxc_3 + ); + $to_myhdl( + user_led_g, + user_led_r, + front_led, + qsfp_0_txd_0, + qsfp_0_txc_0, + qsfp_0_txd_1, + qsfp_0_txc_1, + qsfp_0_txd_2, + qsfp_0_txc_2, + qsfp_0_txd_3, + qsfp_0_txc_3, + qsfp_1_txd_0, + qsfp_1_txc_0, + qsfp_1_txd_1, + qsfp_1_txc_1, + qsfp_1_txd_2, + qsfp_1_txc_2, + qsfp_1_txd_3, + qsfp_1_txc_3 + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .user_led_g(user_led_g), + .user_led_r(user_led_r), + .front_led(front_led), + .user_sw(user_sw), + .qsfp_0_tx_clk_0(qsfp_0_tx_clk_0), + .qsfp_0_tx_rst_0(qsfp_0_tx_rst_0), + .qsfp_0_txd_0(qsfp_0_txd_0), + .qsfp_0_txc_0(qsfp_0_txc_0), + .qsfp_0_rx_clk_0(qsfp_0_rx_clk_0), + .qsfp_0_rx_rst_0(qsfp_0_rx_rst_0), + .qsfp_0_rxd_0(qsfp_0_rxd_0), + .qsfp_0_rxc_0(qsfp_0_rxc_0), + .qsfp_0_tx_clk_1(qsfp_0_tx_clk_1), + .qsfp_0_tx_rst_1(qsfp_0_tx_rst_1), + .qsfp_0_txd_1(qsfp_0_txd_1), + .qsfp_0_txc_1(qsfp_0_txc_1), + .qsfp_0_rx_clk_1(qsfp_0_rx_clk_1), + .qsfp_0_rx_rst_1(qsfp_0_rx_rst_1), + .qsfp_0_rxd_1(qsfp_0_rxd_1), + .qsfp_0_rxc_1(qsfp_0_rxc_1), + .qsfp_0_tx_clk_2(qsfp_0_tx_clk_2), + .qsfp_0_tx_rst_2(qsfp_0_tx_rst_2), + .qsfp_0_txd_2(qsfp_0_txd_2), + .qsfp_0_txc_2(qsfp_0_txc_2), + .qsfp_0_rx_clk_2(qsfp_0_rx_clk_2), + .qsfp_0_rx_rst_2(qsfp_0_rx_rst_2), + .qsfp_0_rxd_2(qsfp_0_rxd_2), + .qsfp_0_rxc_2(qsfp_0_rxc_2), + .qsfp_0_tx_clk_3(qsfp_0_tx_clk_3), + .qsfp_0_tx_rst_3(qsfp_0_tx_rst_3), + .qsfp_0_txd_3(qsfp_0_txd_3), + .qsfp_0_txc_3(qsfp_0_txc_3), + .qsfp_0_rx_clk_3(qsfp_0_rx_clk_3), + .qsfp_0_rx_rst_3(qsfp_0_rx_rst_3), + .qsfp_0_rxd_3(qsfp_0_rxd_3), + .qsfp_0_rxc_3(qsfp_0_rxc_3), + .qsfp_1_tx_clk_0(qsfp_1_tx_clk_0), + .qsfp_1_tx_rst_0(qsfp_1_tx_rst_0), + .qsfp_1_txd_0(qsfp_1_txd_0), + .qsfp_1_txc_0(qsfp_1_txc_0), + .qsfp_1_rx_clk_0(qsfp_1_rx_clk_0), + .qsfp_1_rx_rst_0(qsfp_1_rx_rst_0), + .qsfp_1_rxd_0(qsfp_1_rxd_0), + .qsfp_1_rxc_0(qsfp_1_rxc_0), + .qsfp_1_tx_clk_1(qsfp_1_tx_clk_1), + .qsfp_1_tx_rst_1(qsfp_1_tx_rst_1), + .qsfp_1_txd_1(qsfp_1_txd_1), + .qsfp_1_txc_1(qsfp_1_txc_1), + .qsfp_1_rx_clk_1(qsfp_1_rx_clk_1), + .qsfp_1_rx_rst_1(qsfp_1_rx_rst_1), + .qsfp_1_rxd_1(qsfp_1_rxd_1), + .qsfp_1_rxc_1(qsfp_1_rxc_1), + .qsfp_1_tx_clk_2(qsfp_1_tx_clk_2), + .qsfp_1_tx_rst_2(qsfp_1_tx_rst_2), + .qsfp_1_txd_2(qsfp_1_txd_2), + .qsfp_1_txc_2(qsfp_1_txc_2), + .qsfp_1_rx_clk_2(qsfp_1_rx_clk_2), + .qsfp_1_rx_rst_2(qsfp_1_rx_rst_2), + .qsfp_1_rxd_2(qsfp_1_rxd_2), + .qsfp_1_rxc_2(qsfp_1_rxc_2), + .qsfp_1_tx_clk_3(qsfp_1_tx_clk_3), + .qsfp_1_tx_rst_3(qsfp_1_tx_rst_3), + .qsfp_1_txd_3(qsfp_1_txd_3), + .qsfp_1_txc_3(qsfp_1_txc_3), + .qsfp_1_rx_clk_3(qsfp_1_rx_clk_3), + .qsfp_1_rx_rst_3(qsfp_1_rx_rst_3), + .qsfp_1_rxd_3(qsfp_1_rxd_3), + .qsfp_1_rxc_3(qsfp_1_rxc_3) +); + +endmodule diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/udp_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/xgmii_ep.py b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/xgmii_ep.py new file mode 120000 index 000000000..63b6d3567 --- /dev/null +++ b/fpga/lib/eth/example/ADM_PCIE_9V3/fpga_25g/tb/xgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/xgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ATLYS/fpga/Makefile b/fpga/lib/eth/example/ATLYS/fpga/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/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/fpga/lib/eth/example/ATLYS/fpga/clock.ucf b/fpga/lib/eth/example/ATLYS/fpga/clock.ucf new file mode 100644 index 000000000..cbb807916 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/clock.ucf @@ -0,0 +1,6 @@ +# UCF file for clock module domain crossing constraints + +NET "clk_int" TNM = "ffs_clk_int"; +NET "core_inst/eth_mac_inst/rx_clk" TNM = "ffs_gmii_rx_clk"; +TIMESPEC "TS_clk_int_to_gmii_rx_clk" = FROM "ffs_clk_int" TO "ffs_gmii_rx_clk" 10 ns; +TIMESPEC "TS_gmii_rx_clk_to_clk_int" = FROM "ffs_gmii_rx_clk" TO "ffs_clk_int" 10 ns; diff --git a/fpga/lib/eth/example/ATLYS/fpga/common/xilinx.mk b/fpga/lib/eth/example/ATLYS/fpga/common/xilinx.mk new file mode 100644 index 000000000..f10a45f8e --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/common/xilinx.mk @@ -0,0 +1,191 @@ +############################################################################# +# Author: Lane Brooks/Keith Fife +# Date: 04/28/2006 +# License: GPL +# Desc: This is a Makefile intended to take a verilog rtl design +# through the Xilinx ISE tools to generate configuration files for +# Xilinx FPGAs. This file is generic and just a template. As such +# all design specific options such as synthesis files, fpga part type, +# prom part type, etc should be set in the top Makefile prior to +# including this file. Alternatively, all parameters can be passed +# in from the command line as well. +# +############################################################################## +# +# Parameter: +# SYN_FILES - Space seperated list of files to be synthesized +# PART - FPGA part (see Xilinx documentation) +# PROM - PROM part +# NGC_PATHS - Space seperated list of any dirs with pre-compiled ngc files. +# UCF_FILES - Space seperated list of user constraint files. Defaults to xilinx/$(FPGA_TOP).ucf +# +# +# Example Calling Makefile: +# +# SYN_FILES = fpga.v fifo.v clks.v +# PART = xc3s1000 +# FPGA_TOP = fpga +# PROM = xc18v04 +# NGC_PATH = ipLib1 ipLib2 +# FPGA_ARCH = spartan6 +# SPI_PROM_SIZE = (in bytes) +# include xilinx.mk +############################################################################# +# +# Command Line Example: +# make -f xilinx.mk PART=xc3s1000-4fg320 SYN_FILES="fpga.v test.v" FPGA_TOP=fpga +# +############################################################################## +# +# Required Setup: +# +# %.ucf - user constraint file. Needed by ngdbuild +# +# Optional Files: +# %.xcf - user constraint file. Needed by xst. +# %.ut - File for pin states needed by bitgen + + +.PHONY: clean bit prom fpga spi + + +# Mark the intermediate files as PRECIOUS to prevent make from +# deleting them (see make manual section 10.4). +.PRECIOUS: %.ngc %.ngd %_map.ncd %.ncd %.twr %.bit %_timesim.v + +# include the local Makefile for project for any project specific targets +CONFIG ?= config.mk +-include ../$(CONFIG) + +SYN_FILES_REL = $(patsubst %, ../%, $(SYN_FILES)) +INC_FILES_REL = $(patsubst %, ../%, $(INC_FILES)) +INC_PATHS_REL = $(patsubst %, ../%, $(INC_PATHS)) +NGC_PATHS_REL = $(patsubst %, ../%, $(NGC_PATHS)) + +ifdef UCF_FILES + UCF_FILES_REL = $(patsubst %, ../%, $(UCF_FILES)) +else + UCF_FILES_REL = $(FPGA_TOP).ucf +endif + + + +fpga: $(FPGA_TOP).bit + +mcs: $(FPGA_TOP).mcs + +prom: $(FPGA_TOP).spi + +spi: $(FPGA_TOP).spi + +fpgasim: $(FPGA_TOP)_sim.v + + +########################### XST TEMPLATES ############################ +# There are 2 files that XST uses for synthesis that we auto generate. +# The first is a project file which is just a list of all the verilog +# files. The second is the src file which passes XST all the options. +# See XST user manual for XST options. +%.ngc: $(SYN_FILES_REL) $(INC_FILES_REL) + rm -rf xst $*.prj $*.xst defines.v + touch defines.v + mkdir -p xst/tmp + for x in $(DEFS); do echo '`define' $$x >> defines.v; done + echo verilog work defines.v > $*.prj + for x in $(SYN_FILES_REL); do echo verilog work $$x >> $*.prj; done + @echo "set -tmpdir ./xst/tmp" >> $*.xst + @echo "set -xsthdpdir ./xst" >> $*.xst + @echo "run" >> $*.xst + @echo "-ifn $*.prj" >> $*.xst + @echo "-ifmt mixed" >> $*.xst + @echo "-top $*" >> $*.xst + @echo "-ofn $*" >> $*.xst + @echo "-ofmt NGC" >> $*.xst + @echo "-opt_mode Speed" >> $*.xst + @echo "-opt_level 1" >> $*.xst + # @echo "-verilog2001 YES" >> $*.xst + @echo "-keep_hierarchy NO" >> $*.xst + @echo "-p $(FPGA_PART)" >> $*.xst + xst -ifn $*.xst -ofn $*.log + + +########################### ISE TRANSLATE ############################ +# ngdbuild will automatically use a ucf called %.ucf if one is found. +# We setup the dependancy such that %.ucf file is required. If any +# pre-compiled ncd files are needed, set the NGC_PATH variable as a space +# seperated list of directories that include the pre-compiled ngc files. +%.ngd: %.ngc $(UCF_FILES_REL) + ngdbuild -dd ngdbuild $(patsubst %,-sd %, $(NGC_PATHS_REL)) $(patsubst %,-uc %, $(UCF_FILES_REL)) -p $(FPGA_PART) $< $@ + + +########################### ISE MAP ################################### +ifeq ($(FPGA_ARCH),spartan6) + MAP_OPTS= -register_duplication on -timing -xe n +else + MAP_OPTS= -cm speed -register_duplication on -timing -xe n -pr b +endif + +%_map.ncd: %.ngd + map -p $(FPGA_PART) $(MAP_OPTS) -w -o $@ $< $*.pcf + +# map -p $(FPGA_PART) -cm area -pr b -k 4 -c 100 -o $@ $< $*.pcf + + +########################### ISE PnR ################################### +%.ncd: %_map.ncd + par -w -ol high $< $@ $*.pcf + +# par -w -ol std -t 1 $< $@ $*.pcf + + +##################### ISE Static Timing Analysis ##################### +%.twr: %.ncd + -trce -e 3 -l 3 -u -xml $* $< -o $@ $*.pcf + +%_sim.v: %.ncd + netgen -s 4 -pcf $*.pcf -sdf_anno true -ism -sdf_path netgen -w -dir . -ofmt verilog -sim $< $@ + +# netgen -ise "/home/lane/Second/xilinx/Second/Second" -intstyle ise -s 4 -pcf Second.pcf -sdf_anno true -sdf_path netgen/par -w -dir netgen/par -ofmt verilog -sim Second.ncd Second_timesim.v + + +########################### ISE Bitgen ############################# +%.bit: %.twr + bitgen $(BITGEN_OPTIONS) -w $*.ncd $*.bit + mkdir -p rev + EXT=bit; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +########################### ISE Promgen ############################# +%.mcs: %.bit + promgen -spi -w -p mcs -s $(SPI_PROM_SIZE) -o $@ -u 0 $< + # promgen -w -p mcs -c FF -o $@ -u 0 $< -x $(PROM) + mkdir -p rev + EXT=mcs; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +%.spi: %.mcs + objcopy -I ihex -O binary $< $@ + EXT=spi; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + + +tmpclean: + -rm -rf xst ngdbuild *_map.* *.ncd *.ngc *.log *.xst *.prj *.lso *~ *.pcf *.bld *.ngd *.xpi *_pad.* *.unroutes *.twx *.par *.twr *.pad *.drc *.bgn *.prm *.sig netgen *.v *.nlf *.xml + +clean: tmpclean + -rm -rf *.bit *.mcs + +# clean everything +distclean: clean + -rm -rf rev + diff --git a/fpga/lib/eth/example/ATLYS/fpga/fpga.ucf b/fpga/lib/eth/example/ATLYS/fpga/fpga.ucf new file mode 100644 index 000000000..292615b76 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/fpga.ucf @@ -0,0 +1,194 @@ +# User Constraints File for the Digilent Atlys board, rev C + +CONFIG PART = xc6slx45-2csg324; + +# 100MHz Clock: I/O Bank 1 +NET "clk" LOC = "L15" | IOSTANDARD=LVCMOS33; # IO_L42P_GCLK7_M1UDM (GCLK) +NET "clk" TNM_NET = "sys_clk_pin"; +TIMESPEC "TS_sys_clk_pin" = PERIOD "sys_clk_pin" 100000 kHz; + +# Light Emitting Diodes (not used) +NET "led<0>" LOC = "U18" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L52N_M1DQ15 (LD0) +NET "led<1>" LOC = "M14" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L53P (LD1) +NET "led<2>" LOC = "N14" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L53N_VREF (LD2) +NET "led<3>" LOC = "L14" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L61P (LD3) +NET "led<4>" LOC = "M13" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L61N (LD4) +NET "led<5>" LOC = "D4" | IOSTANDARD=LVCMOS33 | SLEW=QUIETIO | DRIVE=2; # Bank = 0, IO_L1P_HSWAPEN_0 (HSWAP/LD5) +NET "led<6>" LOC = "P16" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L74N_DOUT_BUSY_1 (LD6) +NET "led<7>" LOC = "N12" | IOSTANDARD=LVCMOS33 | SLEW=QUIETIO | DRIVE=2; # Bank = 2, IO_L13P_M1_2 (M1/LD7) + +# Reset Button: I/O Bank 2 (not used) +NET "reset_n" LOC = "T15" | IOSTANDARD=LVCMOS33; # IO_L1N_M0_CMPMISO_2 (M0/RESET) + +# Push Buttons: I/O Bank 3 (not used) +NET "btnu" LOC = "N4"; # IO_L1P (BTNU) +NET "btnl" LOC = "P4"; # IO_L2P (BTNL) +NET "btnd" LOC = "P3"; # IO_L2N (BTND) +NET "btnr" LOC = "F6"; # IO_L55P_M3A13 (BTNR) +NET "btnc" LOC = "F5"; # IO_L55N_M3A14 (BTNC) + +# Toggle Switches (not used) +NET "sw<0>" LOC = "A10"; # Bank = 0, IO_L37N_GCLK12 (SW0) +NET "sw<1>" LOC = "D14"; # Bank = 0, IO_L65P_SCP3 (SW1) +NET "sw<2>" LOC = "C14"; # Bank = 0, IO_L65N_SCP2 (SW2) +NET "sw<3>" LOC = "P15"; # Bank = 1, IO_L74P_AWAKE_1 (SW3) +NET "sw<4>" LOC = "P12"; # Bank = 2, IO_L13N_D10 (SW4) +NET "sw<5>" LOC = "R5"; # Bank = 2, IO_L48P_D7 (SW5) +NET "sw<6>" LOC = "T5"; # Bank = 2, IO_L48N_RDWR_B_VREF_2 (SW6) +NET "sw<7>" LOC = "E4"; # Bank = 3, IO_L54P_M3RESET (SW7) + +# Marvell M88E1111 Tri-Mode Ethernet PHY (1000BASE-T): I/O Bank 1 +# Interrupt, Reset, MDIO +#NET "phy_int_n" LOC = "L16" | IOSTANDARD=LVCMOS25; # IO_L42N_GCLK6_TRDY1_M1LDM (E-INT) +NET "phy_reset_n" LOC = "G13" | IOSTANDARD=LVCMOS25; # IO_L32N_A16_M1A9 (E-RESET) +#NET "phy_mdc" LOC = "F16" | IOSTANDARD=LVCMOS25; # IO_L1N_A24_VREF (E-MDC) +#NET "phy_mdio" LOC = "N17" | IOSTANDARD=LVCMOS25; # IO_L48P_HDC_M1DQ8 (E-MDIO) +# GMII Transmit +NET "phy_gtx_clk" LOC = "L12" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L40P_GCLK11_M1A5 (E-GTXCLK) +NET "phy_tx_clk" LOC = "K16" | IOSTANDARD=LVCMOS25; # IO_L41N_GCLK8_M1CASN (E-TXCLK) +NET "phy_txd<0>" LOC = "H16" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L37N_A6_M1A1 (E-TXD0) +NET "phy_txd<1>" LOC = "H13" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L36P_A9_M1BA0 (E-TXD1) +NET "phy_txd<2>" LOC = "K14" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L39N_M1ODT (E-TXD2) +NET "phy_txd<3>" LOC = "K13" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L34N_A12_M1BA2 (E-TXD3) +NET "phy_txd<4>" LOC = "J13" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L39P_M1A3 (E-TXD4) +NET "phy_txd<5>" LOC = "G14" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L30N_A20_M1A11 (E-TXD5) +NET "phy_txd<6>" LOC = "H12" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L32P_A17_M1A8 (E-TXD6) +NET "phy_txd<7>" LOC = "K12" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L34P_A13_M1WE (E-TXD7) +NET "phy_tx_en" LOC = "H15" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L37P_A7_M1A0 (E-TXEN) +NET "phy_tx_er" LOC = "G18" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # IO_L38N_A4_M1CLKN (E-TXER) +# GMII Receive +NET "phy_rx_clk" LOC = "K15" | IOSTANDARD=LVCMOS25 | TNM_NET = "clk_rx_local"; # IO_L41P_GCLK9_IRDY1_M1RASN (E-RXCLK) +NET "phy_rxd<0>" LOC = "G16" | IOSTANDARD=LVCMOS25; # IO_L38P_A5_M1CLK (E-RXD0) +NET "phy_rxd<1>" LOC = "H14" | IOSTANDARD=LVCMOS25; # IO_L36N_A8_M1BA1 (E-RXD1) +NET "phy_rxd<2>" LOC = "E16" | IOSTANDARD=LVCMOS25; # IO_L33P_A15_M1A10 (E-RXD2) +NET "phy_rxd<3>" LOC = "F15" | IOSTANDARD=LVCMOS25; # IO_L1P_A25 (E-RXD3) +NET "phy_rxd<4>" LOC = "F14" | IOSTANDARD=LVCMOS25; # IO_L30P_A21_M1RESET (E-RXD4) +NET "phy_rxd<5>" LOC = "E18" | IOSTANDARD=LVCMOS25; # IO_L33N_A14_M1A4 (E-RXD5) +NET "phy_rxd<6>" LOC = "D18" | IOSTANDARD=LVCMOS25; # IO_L31N_A18_M1A12 (E-RXD6) +NET "phy_rxd<7>" LOC = "D17" | IOSTANDARD=LVCMOS25; # IO_L31P_A19_M1CKE (E-RXD7) +NET "phy_rx_dv" LOC = "F17" | IOSTANDARD=LVCMOS25; # IO_L35P_A11_M1A7 (E-RXDV) +NET "phy_rx_er" LOC = "F18" | IOSTANDARD=LVCMOS25; # IO_L35N_A10_M1A2 (E-RXER) + +# Timing constraints for Ethernet PHY +TIMESPEC "TS_rx_clk_root" = PERIOD "clk_rx_local" 8000 ps HIGH 50 %; +# WARNING: Receiving Ethernet frames will not work without these constraints. +# WARNING: Meeting these constraints will require instantiating an IODELAY2 primitive. +#INST "phy_rxd" TNM = IN_GMII; +#INST "phy_rx_er" TNM = IN_GMII; +#INST "phy_rx_dv" TNM = IN_GMII; +#TIMEGRP "IN_GMII" OFFSET = IN 2.4 ns VALID 2.8 ns BEFORE "phy_rx_clk125"; + +# PMOD Connector (FPGA Bank 2) +# +# FPGA | Atlys +# ------------------------------------ +# T3 | IO_L62N_D6 | JA1 (TOP) +# R3 | IO_L62P_D5 | JA2 (TOP) +# P6 | IO_L64N_D9 | JA3 (TOP) +# N5 | IO_L64P_D8 | JA4 (TOP) +# | GND | JA5 (TOP) +# | Vcc | JA6 (TOP) +# V9 | IO_L32N_GCLK28 | JA7 (BOTTOM) +# T9 | IO_L32P_GCLK29 | JA8 (BOTTOM) +# V4 | IO_L63N | JA9 (BOTTOM) +# T4 | IO_L63P | JA10 (BOTTOM) +# | GND | JA11 (BOTTOM) +# | Vcc | JA12 (BOTTOM) +# +#NET "ready<1>" LOC = "T3" | IOSTANDARD=LVCMOS33 | PULLDOWN; +#NET "trigger<1>" LOC = "V9" | IOSTANDARD=LVCMOS33 | SLEW=QUIETIO | DRIVE=2; +#NET "spi_mosi<1>" LOC = "R3" | IOSTANDARD=LVCMOS33 | SLEW=QUIETIO | DRIVE=2; +#NET "spi_cs_n<1>" LOC = "T9" | IOSTANDARD=LVCMOS33 | SLEW=QUIETIO | DRIVE=2; +#NET "spi_clk<1>" LOC = "P6" | IOSTANDARD=LVCMOS33 | SLEW=QUIETIO | DRIVE=2; +#NET "spi_gnd<1>" LOC = "V4" | IOSTANDARD=LVCMOS33 | SLEW=QUIETIO | DRIVE=2; + +# VHDCI Connector (FPGA Bank 2) +# Note: Channnel 1 connects to P signals, Channel 2 to N signals +# +# FPGA | Atlys | VmodMIB +# ----------------------------------------------------------------------------- +# U16 | IO_L2P_CMPCLK | EXP-IO1_P | JC.1 | JC-CLK_P | JC8 (BOTTOM) +# U15 | *IO_L5P | EXP-IO2_P | JC.3 | JC-D0_P | JC2 (TOP) +# U13 | IO_L14P_D11 | EXP-IO3_P | JC.4 | JC-D1_P | JC10 (BOTTOM) +# M11 | *IO_L15P | EXP-IO4_P | JC.6 | JC-D2_P | JC4 (TOP) +# R11 | IO_L16P | EXP-IO5_P | JC.7 | JA-D0_P | JA2 (TOP) +# T12 | *IO_L19P | EXP-IO6_P | JC.9 | JA-D1_P | JA10 (BOTTOM) +# N10 | *IO_L20P | EXP-IO7_P | JC.10 | JA-D2_P | JA4 (TOP) +# M10 | *IO_L22P | EXP-IO8_P | JC.12 | JB-D0_P | JB2 (TOP) +# U11 | IO_L23P | EXP-IO9_P | JC.13 | JB-D1_P | JB10 (BOTTOM) +# R10 | IO_L29P_GCLK3 | EXP-IO10_P | JC.15 | JA-CLK_P | JA8 (BOTTOM) +# U10 | IO_L30P_GCLK1_D13 | EXP-IO11_P | JC.20 | JB-CLK_P | JB8 (BOTTOM) +# R8 | IO_L31P_GCLK31_D14 | EXP-IO12_P | JC.22 | JB-D2_P | JB4 (TOP) +# M8 | *IO_L40P | EXP-IO13_P | JC.23 | JE8 | JE8 (BOTTOM) +# U8 | IO_L41P | EXP-IO14_P | JC.25 | JE2 | JE2 (TOP) +# U7 | IO_L43P | EXP-IO15_P | JC.26 | JE10 | JE10 (BOTTOM) +# N7 | *IO_L44P | EXP-IO16_P | JC.28 | JE4 | JE4 (TOP) +# T6 | IO_L45P | EXP-IO17_P | JC.29 | JD-CLK_P | JD8 (BOTTOM) +# R7 | IO_L46P | EXP-IO18_P | JC.31 | JD-D0_P | JD2 (TOP) +# N6 | *IO_L47P | EXP-IO19_P | JC.32 | JD-D1_P | JD10 (BOTTOM) +# U5 | IO_49P_D3 | EXP-IO20_P | JC.34 | JD-D2_P | JD4 (TOP) +# V16 | IO_L2N_CMPMOSI | EXP-IO1_N | JC.35 | JC-CLK_N | JC7 (BOTTOM) +# V15 | *IO_L5N | EXP-IO2_N | JC.37 | JC-D0_N | JC1 (TOP) +# V13 | IO_L14N_D12 | EXP-IO3_N | JC.38 | JC-D1_N | JC9 (BOTTOM) +# N11 | *IO_L15N | EXP-IO4_N | JC.40 | JC-D2_N | JC3 (TOP) +# T11 | IO_L16N_VREF | EXP-IO5_N | JC.41 | JA-D0_N | JA1 (TOP) +# V12 | *IO_L19N | EXP-IO6_N | JC.43 | JA-D1_N | JA9 (BOTTOM) +# P11 | *IO_L20N | EXP-IO7_N | JC.44 | JA-D2_N | JA3 (TOP) +# N9 | *IO_L22N | EXP-IO8_N | JC.46 | JB-D0_N | JB1 (TOP) +# V11 | IO_L23N | EXP-IO9_N | JC.47 | JB-D1_N | JB9 (BOTTOM) +# T10 | IO_L29N_GCLK2 | EXP-IO10_N | JC.49 | JA-CLK_N | JA7 (BOTTOM) +# V10 | IO_L30N_GCLK0_USERCCLK | EXP-IO11_N | JC.54 | JB-CLK_N | JB7 (BOTTOM) +# T8 | IO_L31N_GCLK30_D15 | EXP-IO12_N | JC.56 | JB-D2_N | JB3 (TOP) +# N8 | *IO_L40N | EXP-IO13_N | JC.57 | JE7 | JE7 (BOTTOM) +# V8 | IO_L41N_VREF | EXP-IO14_N | JC.59 | JE1 | JE1 (TOP) +# V7 | IO_L43N | EXP-IO15_N | JC.60 | JE9 | JE9 (BOTTOM) +# P8 | *IO_L44N | EXP-IO16_N | JC.62 | JE3 | JE3 (TOP) +# V6 | IO_L45N | EXP-IO17_N | JC.63 | JD-CLK_N | JD7 (BOTTOM) +# T7 | IO_L46N | EXP-IO18_N | JC.65 | JD-D0_N | JD1 (TOP) +# P7 | *IO_L47N | EXP-IO19_N | JC.66 | JD-D1_N | JD9 (BOTTOM) +# V5 | IO_49N_D4 | EXP-IO20_N | JC.68 | JD-D2_N | JD3 (TOP) + +#NET "" LOC = "U16" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO1_P +#NET "" LOC = "U15" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO2_P +#NET "" LOC = "U13" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO3_P +#NET "" LOC = "M11" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO4_P +#NET "" LOC = "R11" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO5_P +#NET "" LOC = "T12" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO6_P +#NET "" LOC = "N10" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO7_P +#NET "" LOC = "M10" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO8_P +#NET "" LOC = "U11" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO9_P +#NET "" LOC = "R10" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO10_P +#NET "" LOC = "U10" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO11_P +#NET "" LOC = "R8" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO12_P +#NET "" LOC = "M8" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO13_P +#NET "" LOC = "U8" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO14_P +#NET "" LOC = "U7" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO15_P +#NET "" LOC = "N7" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO16_P +#NET "" LOC = "T6" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO17_P +#NET "" LOC = "R7" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO18_P +#NET "" LOC = "N6" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO19_P +#NET "" LOC = "U5" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO20_P +#NET "" LOC = "V16" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO1_N +#NET "" LOC = "V15" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO2_N +#NET "" LOC = "V13" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO3_N +#NET "" LOC = "N11" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO4_N +#NET "" LOC = "T11" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=8; # EXP-IO5_N +#NET "" LOC = "V12" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO6_N +#NET "" LOC = "P11" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO7_N +#NET "" LOC = "N9" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO8_N +#NET "" LOC = "V11" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO9_N +#NET "" LOC = "T10" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO10_N +#NET "" LOC = "V10" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO11_N +#NET "" LOC = "T8" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO12_N +#NET "" LOC = "N8" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO13_N +#NET "" LOC = "V8" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO14_N +#NET "" LOC = "V7" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO15_N +#NET "" LOC = "P8" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO16_N +#NET "" LOC = "V6" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO17_N +#NET "" LOC = "T7" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO18_N +#NET "" LOC = "P7" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO19_N +#NET "" LOC = "V5" | IOSTANDARD=LVCMOS33 | SLEW=FAST | DRIVE=2; # EXP-IO20_N + +# Exar UART: I/O Bank 0 +NET "uart_rxd" LOC = "A16" | IOSTANDARD=LVCMOS33; # IO_L66N_SCP0 (USBB-RXD) +NET "uart_txd" LOC = "B16" | IOSTANDARD=LVCMOS33; # IO_L66P_SCP1 (USBB-TXD) diff --git a/fpga/lib/eth/example/ATLYS/fpga/fpga/Makefile b/fpga/lib/eth/example/ATLYS/fpga/fpga/Makefile new file mode 100644 index 000000000..956dac9d9 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/fpga/Makefile @@ -0,0 +1,65 @@ + +# FPGA settings +FPGA_PART = xc6slx45-2csg324 +FPGA_TOP = fpga +FPGA_ARCH = spartan6 + +# PROM settings +#PROM = xc18v04 +#SPI_PROM_SIZE = (in bytes) + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.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_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 += coregen/dcm_i100_o125/dcm_i100_o125.v + +# UCF files +UCF_FILES = fpga.ucf +UCF_FILES += clock.ucf + +# NGC paths for ngdbuild +#NGC_PATHS = coregen/dcm_i100_o125 + +# Bitgen options +BITGEN_OPTIONS = -g StartupClk:Cclk -g ConfigRate:26 + +include ../common/xilinx.mk + +program: $(FPGA_TOP).bit + djtgcfg prog -d Atlys --index 0 --file $(FPGA_TOP).bit + diff --git a/fpga/lib/eth/example/ATLYS/fpga/lib/eth b/fpga/lib/eth/example/ATLYS/fpga/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/ATLYS/fpga/rtl/debounce_switch.v b/fpga/lib/eth/example/ATLYS/fpga/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/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 + +`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/fpga/lib/eth/example/ATLYS/fpga/rtl/fpga.v b/fpga/lib/eth/example/ATLYS/fpga/rtl/fpga.v new file mode 100644 index 000000000..eeeb96cb5 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/rtl/fpga.v @@ -0,0 +1,221 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 100MHz + * Reset: Push button, active low + */ + input wire clk, + input wire reset_n, + + /* + * 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 GMII + */ + input wire phy_rx_clk, + input wire [7:0] phy_rxd, + input wire phy_rx_dv, + input wire phy_rx_er, + output wire phy_gtx_clk, + input wire phy_tx_clk, + output wire [7:0] phy_txd, + output wire phy_tx_en, + output wire phy_tx_er, + output wire phy_reset_n, + + /* + * UART: 500000 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd +); + +// Clock and reset + +wire clk_ibufg; +wire clk_bufg; +wire clk_dcm_out; + +// Internal 125 MHz clock +wire clk_int; +wire rst_int; + +wire dcm_rst; +wire [7:0] dcm_status; +wire dcm_locked; +wire dcm_clkfx_stopped = dcm_status[2]; + +assign dcm_rst = ~reset_n | (dcm_clkfx_stopped & ~dcm_locked); + +IBUFG +clk_ibufg_inst( + .I(clk), + .O(clk_ibufg) +); + +DCM_SP #( + .CLKIN_PERIOD(10), + .CLK_FEEDBACK("NONE"), + .CLKDV_DIVIDE(2.0), + .CLKFX_MULTIPLY(5.0), + .CLKFX_DIVIDE(4.0), + .PHASE_SHIFT(0), + .CLKOUT_PHASE_SHIFT("NONE"), + .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), + .STARTUP_WAIT("FALSE"), + .CLKIN_DIVIDE_BY_2("FALSE") +) +clk_dcm_inst ( + .CLKIN(clk_ibufg), + .CLKFB(1'b0), + .RST(dcm_rst), + .PSEN(1'b0), + .PSINCDEC(1'b0), + .PSCLK(1'b0), + .CLK0(), + .CLK90(), + .CLK180(), + .CLK270(), + .CLK2X(), + .CLK2X180(), + .CLKDV(), + .CLKFX(clk_dcm_out), + .CLKFX180(), + .STATUS(dcm_status), + .LOCKED(dcm_locked), + .PSDONE() +); + +BUFG +clk_bufg_inst ( + .I(clk_dcm_out), + .O(clk_int) +); + +sync_reset #( + .N(4) +) +sync_reset_inst ( + .clk(clk_int), + .rst(~dcm_locked), + .sync_reset_out(rst_int) +); + +// GPIO +wire btnu_int; +wire btnl_int; +wire btnd_int; +wire btnr_int; +wire btnc_int; +wire [7:0] sw_int; + +debounce_switch #( + .WIDTH(13), + .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}) +); + +sync_signal #( + .WIDTH(1), + .N(2) +) +sync_signal_inst ( + .clk(clk_int), + .in({uart_rxd}), + .out({uart_rxd_int}) +); + +fpga_core +core_inst ( + /* + * Clock: 125MHz + * Synchronous reset + */ + .clk(clk_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 GMII + */ + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_dv(phy_rx_dv), + .phy_rx_er(phy_rx_er), + .phy_gtx_clk(phy_gtx_clk), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_en(phy_tx_en), + .phy_tx_er(phy_tx_er), + .phy_reset_n(phy_reset_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd) +); + +endmodule diff --git a/fpga/lib/eth/example/ATLYS/fpga/rtl/fpga_core.v b/fpga/lib/eth/example/ATLYS/fpga/rtl/fpga_core.v new file mode 100644 index 000000000..2fb71402a --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/rtl/fpga_core.v @@ -0,0 +1,583 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * Clock: 125MHz + * Synchronous reset + */ + input wire clk, + 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 GMII + */ + input wire phy_rx_clk, + input wire [7:0] phy_rxd, + input wire phy_rx_dv, + input wire phy_rx_er, + output wire phy_gtx_clk, + input wire phy_tx_clk, + output wire [7:0] phy_txd, + output wire phy_tx_en, + output wire phy_tx_er, + output wire phy_reset_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_gmii_fifo #( + .TARGET(TARGET), + .IODDR_STYLE("IODDR2"), + .CLOCK_INPUT_STYLE("BUFIO2"), + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_inst ( + .gtx_clk(clk), + .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), + + .gmii_rx_clk(phy_rx_clk), + .gmii_rxd(phy_rxd), + .gmii_rx_dv(phy_rx_dv), + .gmii_rx_er(phy_rx_er), + .gmii_tx_clk(phy_gtx_clk), + .mii_tx_clk(phy_tx_clk), + .gmii_txd(phy_txd), + .gmii_tx_en(phy_tx_en), + .gmii_tx_er(phy_tx_er), + + .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 #( + .ADDR_WIDTH(12), + .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/fpga/lib/eth/example/ATLYS/fpga/rtl/sync_reset.v b/fpga/lib/eth/example/ATLYS/fpga/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/ATLYS/fpga/rtl/sync_signal.v b/fpga/lib/eth/example/ATLYS/fpga/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/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/fpga/lib/eth/example/ATLYS/fpga/tb/arp_ep.py b/fpga/lib/eth/example/ATLYS/fpga/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ATLYS/fpga/tb/axis_ep.py b/fpga/lib/eth/example/ATLYS/fpga/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ATLYS/fpga/tb/eth_ep.py b/fpga/lib/eth/example/ATLYS/fpga/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ATLYS/fpga/tb/gmii_ep.py b/fpga/lib/eth/example/ATLYS/fpga/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ATLYS/fpga/tb/ip_ep.py b/fpga/lib/eth/example/ATLYS/fpga/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ATLYS/fpga/tb/test_fpga_core.py b/fpga/lib/eth/example/ATLYS/fpga/tb/test_fpga_core.py new file mode 100755 index 000000000..081281ea2 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/tb/test_fpga_core.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import gmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/iddr.v") +srcs.append("../lib/eth/rtl/oddr.v") +srcs.append("../lib/eth/rtl/ssio_sdr_in.v") +srcs.append("../lib/eth/rtl/ssio_sdr_out.v") +srcs.append("../lib/eth/rtl/gmii_phy_if.v") +srcs.append("../lib/eth/rtl/eth_mac_1g_gmii_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g_gmii.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/udp_complete.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen.v") +srcs.append("../lib/eth/rtl/udp.v") +srcs.append("../lib/eth/rtl/udp_ip_rx.v") +srcs.append("../lib/eth/rtl/udp_ip_tx.v") +srcs.append("../lib/eth/rtl/ip_complete.v") +srcs.append("../lib/eth/rtl/ip.v") +srcs.append("../lib/eth/rtl/ip_eth_rx.v") +srcs.append("../lib/eth/rtl/ip_eth_tx.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx.v") +srcs.append("../lib/eth/rtl/arp_eth_tx.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[8:]) + phy_rx_clk = Signal(bool(0)) + phy_rxd = Signal(intbv(0)[8:]) + phy_rx_dv = Signal(bool(0)) + phy_rx_er = Signal(bool(0)) + phy_tx_clk = Signal(bool(0)) + uart_rxd = Signal(bool(0)) + + # Outputs + led = Signal(intbv(0)[8:]) + phy_gtx_clk = Signal(bool(0)) + phy_txd = Signal(intbv(0)[8:]) + phy_tx_en = Signal(bool(0)) + phy_tx_er = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_txd = Signal(bool(0)) + + # sources and sinks + mii_select = Signal(bool(0)) + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + phy_rx_clk, + rst, + txd=phy_rxd, + tx_en=phy_rx_dv, + tx_er=phy_rx_er, + mii_select=mii_select, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + phy_tx_clk, + rst, + rxd=phy_txd, + rx_dv=phy_tx_en, + rx_er=phy_tx_er, + mii_select=mii_select, + name='gmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + led=led, + + phy_rx_clk=phy_rx_clk, + phy_rxd=phy_rxd, + phy_rx_dv=phy_rx_dv, + phy_rx_er=phy_rx_er, + phy_gtx_clk=phy_gtx_clk, + phy_tx_clk=phy_tx_clk, + phy_txd=phy_txd, + phy_tx_en=phy_tx_en, + phy_tx_er=phy_tx_er, + phy_reset_n=phy_reset_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + phy_rx_clk.next = not phy_rx_clk + phy_tx_clk.next = not phy_tx_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/ATLYS/fpga/tb/test_fpga_core.v b/fpga/lib/eth/example/ATLYS/fpga/tb/test_fpga_core.v new file mode 100644 index 000000000..bbd8df6a4 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/tb/test_fpga_core.v @@ -0,0 +1,125 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters +parameter TARGET = "SIM"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [7:0] sw = 0; +reg phy_rx_clk = 0; +reg [7:0] phy_rxd = 0; +reg phy_rx_dv = 0; +reg phy_rx_er = 0; +reg phy_tx_clk = 0; +reg uart_rxd = 0; + +// Outputs +wire [7:0] led; +wire phy_gtx_clk; +wire [7:0] phy_txd; +wire phy_tx_en; +wire phy_tx_er; +wire phy_reset_n; +wire uart_txd; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + phy_rx_clk, + phy_rxd, + phy_rx_dv, + phy_rx_er, + phy_tx_clk, + uart_rxd + ); + $to_myhdl( + led, + phy_gtx_clk, + phy_txd, + phy_tx_en, + phy_tx_er, + phy_reset_n, + uart_txd + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core #( + .TARGET(TARGET) +) +UUT ( + .clk(clk), + .rst(rst), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .led(led), + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_dv(phy_rx_dv), + .phy_rx_er(phy_rx_er), + .phy_gtx_clk(phy_gtx_clk), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_en(phy_tx_en), + .phy_tx_er(phy_tx_er), + .phy_reset_n(phy_reset_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd) +); + +endmodule diff --git a/fpga/lib/eth/example/ATLYS/fpga/tb/udp_ep.py b/fpga/lib/eth/example/ATLYS/fpga/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/ATLYS/fpga/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/Arty/fpga/Makefile b/fpga/lib/eth/example/Arty/fpga/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/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/fpga/lib/eth/example/Arty/fpga/README.md b/fpga/lib/eth/example/Arty/fpga/README.md new file mode 100644 index 000000000..75c852333 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/README.md @@ -0,0 +1,25 @@ +# Verilog Ethernet Arty Example Design + +## Introduction + +This example design targets the Digilent Arty 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: XC7A35TICSG324-1L +PHY: TI DP83848J + +## 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 Arty 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. + + diff --git a/fpga/lib/eth/example/Arty/fpga/common/vivado.mk b/fpga/lib/eth/example/Arty/fpga/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/Arty/fpga/fpga.xdc b/fpga/lib/eth/example/Arty/fpga/fpga.xdc new file mode 100644 index 000000000..c00c158cc --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/fpga.xdc @@ -0,0 +1,113 @@ +# XDC constraints for the Digilent Arty board +# part: xc7a35t-csg324-1 + +# General configuration +set_property CFGBVS VCCO [current_design] +set_property CONFIG_VOLTAGE 3.3 [current_design] +set_property BITSTREAM.GENERAL.COMPRESS true [current_design] +set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design] +set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design] + +# 100 MHz clock +set_property -dict {LOC E3 IOSTANDARD LVCMOS33} [get_ports clk] +create_clock -period 10.000 -name clk [get_ports clk] + +# LEDs +set_property -dict {LOC G6 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led0_r] +set_property -dict {LOC F6 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led0_g] +set_property -dict {LOC E1 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led0_b] +set_property -dict {LOC G3 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led1_r] +set_property -dict {LOC J4 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led1_g] +set_property -dict {LOC G4 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led1_b] +set_property -dict {LOC J3 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led2_r] +set_property -dict {LOC J2 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led2_g] +set_property -dict {LOC H4 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led2_b] +set_property -dict {LOC K1 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led3_r] +set_property -dict {LOC H6 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led3_g] +set_property -dict {LOC K2 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led3_b] +set_property -dict {LOC H5 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led4] +set_property -dict {LOC J5 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led5] +set_property -dict {LOC T9 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led6] +set_property -dict {LOC T10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports led7] + +# Reset button +set_property -dict {LOC C2 IOSTANDARD LVCMOS33} [get_ports reset_n] + +# Push buttons +set_property -dict {LOC D9 IOSTANDARD LVCMOS33} [get_ports {btn[0]}] +set_property -dict {LOC C9 IOSTANDARD LVCMOS33} [get_ports {btn[1]}] +set_property -dict {LOC B9 IOSTANDARD LVCMOS33} [get_ports {btn[2]}] +set_property -dict {LOC B8 IOSTANDARD LVCMOS33} [get_ports {btn[3]}] + +# Toggle switches +set_property -dict {LOC A8 IOSTANDARD LVCMOS33} [get_ports {sw[0]}] +set_property -dict {LOC C11 IOSTANDARD LVCMOS33} [get_ports {sw[1]}] +set_property -dict {LOC C10 IOSTANDARD LVCMOS33} [get_ports {sw[2]}] +set_property -dict {LOC A10 IOSTANDARD LVCMOS33} [get_ports {sw[3]}] + +# GPIO +# PMOD JA +#set_property -dict {LOC G13 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_ja1}] ;# PMOD JA pin 1 +#set_property -dict {LOC B11 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_ja2}] ;# PMOD JA pin 2 +#set_property -dict {LOC A11 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_ja3}] ;# PMOD JA pin 3 +#set_property -dict {LOC D12 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_ja4}] ;# PMOD JA pin 4 +#set_property -dict {LOC D13 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_ja7}] ;# PMOD JA pin 7 +#set_property -dict {LOC B18 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_ja8}] ;# PMOD JA pin 8 +#set_property -dict {LOC A18 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_ja9}] ;# PMOD JA pin 9 +#set_property -dict {LOC K16 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_ja10}] ;# PMOD JA pin 10 +# PMOD JB +#set_property -dict {LOC E15 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jb1}] ;# PMOD JB pin 1 +#set_property -dict {LOC E16 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jb2}] ;# PMOD JB pin 2 +#set_property -dict {LOC D15 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jb3}] ;# PMOD JB pin 3 +#set_property -dict {LOC C15 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jb4}] ;# PMOD JB pin 4 +#set_property -dict {LOC J17 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jb7}] ;# PMOD JB pin 7 +#set_property -dict {LOC J18 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jb8}] ;# PMOD JB pin 8 +#set_property -dict {LOC K15 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jb9}] ;# PMOD JB pin 9 +#set_property -dict {LOC J15 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jb10}] ;# PMOD JB pin 10 +# PMOD JC +#set_property -dict {LOC U12 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jc1}] ;# PMOD JC pin 1 +#set_property -dict {LOC V12 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jc2}] ;# PMOD JC pin 2 +#set_property -dict {LOC V10 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jc3}] ;# PMOD JC pin 3 +#set_property -dict {LOC V11 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jc4}] ;# PMOD JC pin 4 +#set_property -dict {LOC U14 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jc7}] ;# PMOD JC pin 7 +#set_property -dict {LOC V14 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jc8}] ;# PMOD JC pin 8 +#set_property -dict {LOC T13 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jc9}] ;# PMOD JC pin 9 +#set_property -dict {LOC U13 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jc10}] ;# PMOD JC pin 10 +# PMOD JD +#set_property -dict {LOC D4 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jd1}] ;# PMOD JD pin 1 +#set_property -dict {LOC D3 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jd2}] ;# PMOD JD pin 2 +#set_property -dict {LOC F4 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jd3}] ;# PMOD JD pin 3 +#set_property -dict {LOC F3 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jd4}] ;# PMOD JD pin 4 +#set_property -dict {LOC E2 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jd7}] ;# PMOD JD pin 7 +#set_property -dict {LOC D2 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jd8}] ;# PMOD JD pin 8 +#set_property -dict {LOC H2 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jd9}] ;# PMOD JD pin 9 +#set_property -dict {LOC G2 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {gpio_jd10}] ;# PMOD JD pin 10 + +# UART +set_property -dict {LOC D10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports uart_txd] +set_property -dict {LOC A9 IOSTANDARD LVCMOS33} [get_ports uart_rxd] + +# Ethernet MII PHY +set_property -dict {LOC F15 IOSTANDARD LVCMOS33} [get_ports phy_rx_clk] +set_property -dict {LOC D18 IOSTANDARD LVCMOS33} [get_ports {phy_rxd[0]}] +set_property -dict {LOC E17 IOSTANDARD LVCMOS33} [get_ports {phy_rxd[1]}] +set_property -dict {LOC E18 IOSTANDARD LVCMOS33} [get_ports {phy_rxd[2]}] +set_property -dict {LOC G17 IOSTANDARD LVCMOS33} [get_ports {phy_rxd[3]}] +set_property -dict {LOC G16 IOSTANDARD LVCMOS33} [get_ports phy_rx_dv] +set_property -dict {LOC C17 IOSTANDARD LVCMOS33} [get_ports phy_rx_er] +set_property -dict {LOC H16 IOSTANDARD LVCMOS33} [get_ports phy_tx_clk] +set_property -dict {LOC H14 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {phy_txd[0]}] +set_property -dict {LOC J14 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {phy_txd[1]}] +set_property -dict {LOC J13 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {phy_txd[2]}] +set_property -dict {LOC H17 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports {phy_txd[3]}] +set_property -dict {LOC H15 IOSTANDARD LVCMOS33 SLEW FAST DRIVE 12} [get_ports phy_tx_en] +set_property -dict {LOC D17 IOSTANDARD LVCMOS33} [get_ports phy_col] +set_property -dict {LOC G14 IOSTANDARD LVCMOS33} [get_ports phy_crs] +set_property -dict {LOC G18 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports phy_ref_clk] +set_property -dict {LOC C16 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports phy_reset_n] +#set_property -dict {LOC K13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports phy_mdio] +#set_property -dict {LOC F16 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports phy_mdc] + +create_clock -period 40.000 -name phy_rx_clk [get_ports phy_rx_clk] +create_clock -period 40.000 -name phy_tx_clk [get_ports phy_tx_clk] + diff --git a/fpga/lib/eth/example/Arty/fpga/fpga/Makefile b/fpga/lib/eth/example/Arty/fpga/fpga/Makefile new file mode 100644 index 000000000..36d5be2e6 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/fpga/Makefile @@ -0,0 +1,98 @@ + +# FPGA settings +FPGA_PART = xc7a35t-csg324-1 +FPGA_TOP = fpga +FPGA_ARCH = artix7 + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += lib/eth/rtl/ssio_sdr_in.v +SYN_FILES += lib/eth/rtl/mii_phy_if.v +SYN_FILES += lib/eth/rtl/eth_mac_mii_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_mii.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 + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += lib/eth/syn/mii_phy_if.tcl +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.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 + +%.mcs %.prm: %.bit + echo "write_cfgmem -force -format mcs -size 16 -interface SPIx4 -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 .mcs .prm; \ + do cp $*$$x rev/$*_rev$$COUNT$$x; \ + echo "Output: rev/$*_rev$$COUNT$$x"; done; + +flash: $(FPGA_TOP).mcs $(FPGA_TOP).prm + echo "open_hw" > 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 {mt25ql128-spi-x1_x2_x4}] 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 \"$(FPGA_TOP).mcs\"] [current_hw_cfgmem]" >> flash.tcl + echo "set_property PROGRAM.PRM_FILES [list \"$(FPGA_TOP).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 + diff --git a/fpga/lib/eth/example/Arty/fpga/lib/eth b/fpga/lib/eth/example/Arty/fpga/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/Arty/fpga/rtl/debounce_switch.v b/fpga/lib/eth/example/Arty/fpga/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/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 + +`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/fpga/lib/eth/example/Arty/fpga/rtl/fpga.v b/fpga/lib/eth/example/Arty/fpga/rtl/fpga.v new file mode 100644 index 000000000..c64b841c4 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/rtl/fpga.v @@ -0,0 +1,267 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 100MHz + * Reset: Push button, active low + */ + input wire clk, + input wire reset_n, + + /* + * GPIO + */ + input wire [3:0] sw, + input wire [3:0] btn, + output wire led0_r, + output wire led0_g, + output wire led0_b, + output wire led1_r, + output wire led1_g, + output wire led1_b, + output wire led2_r, + output wire led2_g, + output wire led2_b, + output wire led3_r, + output wire led3_g, + output wire led3_b, + output wire led4, + output wire led5, + output wire led6, + output wire led7, + + /* + * Ethernet: 100BASE-T MII + */ + output wire phy_ref_clk, + input wire phy_rx_clk, + input wire [3:0] phy_rxd, + input wire phy_rx_dv, + input wire phy_rx_er, + input wire phy_tx_clk, + output wire [3:0] phy_txd, + output wire phy_tx_en, + input wire phy_col, + input wire phy_crs, + output wire phy_reset_n, + + /* + * UART: 500000 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd +); + +// Clock and reset + +wire clk_ibufg; +wire clk_bufg; +wire clk_mmcm_out; + +// Internal 125 MHz clock +wire clk_int; +wire rst_int; + +wire mmcm_rst = ~reset_n; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFG +clk_ibufg_inst( + .I(clk), + .O(clk_ibufg) +); + +wire clk_25mhz_mmcm_out; +wire clk_25mhz_int; + +// MMCM instance +// 100 MHz in, 125 MHz out +// PFD range: 10 MHz to 550 MHz +// VCO range: 600 MHz to 1200 MHz +// M = 10, D = 1 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +// Divide by 40 to get output frequency of 25 MHz +// 1000 / 5 = 200 MHz +MMCME2_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(8), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(40), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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(10), + .CLKFBOUT_PHASE(0), + .DIVCLK_DIVIDE(1), + .REF_JITTER1(0.010), + .CLKIN1_PERIOD(10.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(clk_25mhz_mmcm_out), + .CLKOUT1B(), + .CLKOUT2(), + .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 +clk_25mhz_bufg_inst ( + .I(clk_25mhz_mmcm_out), + .O(clk_25mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_inst ( + .clk(clk_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_int) +); + +// GPIO +wire [3:0] btn_int; +wire [3:0] sw_int; + +debounce_switch #( + .WIDTH(8), + .N(4), + .RATE(125000) +) +debounce_switch_inst ( + .clk(clk_int), + .rst(rst_int), + .in({btn, + sw}), + .out({btn_int, + sw_int}) +); + +sync_signal #( + .WIDTH(1), + .N(2) +) +sync_signal_inst ( + .clk(clk_int), + .in({uart_rxd}), + .out({uart_rxd_int}) +); + +assign phy_ref_clk = clk_25mhz_int; + +fpga_core +core_inst ( + /* + * Clock: 125MHz + * Synchronous reset + */ + .clk(clk_int), + .rst(rst_int), + /* + * GPIO + */ + .btn(btn_int), + .sw(sw_int), + .led0_r(led0_r), + .led0_g(led0_g), + .led0_b(led0_b), + .led1_r(led1_r), + .led1_g(led1_g), + .led1_b(led1_b), + .led2_r(led2_r), + .led2_g(led2_g), + .led2_b(led2_b), + .led3_r(led3_r), + .led3_g(led3_g), + .led3_b(led3_b), + .led4(led4), + .led5(led5), + .led6(led6), + .led7(led7), + /* + * Ethernet: 100BASE-T MII + */ + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_dv(phy_rx_dv), + .phy_rx_er(phy_rx_er), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_en(phy_tx_en), + .phy_col(phy_col), + .phy_crs(phy_crs), + .phy_reset_n(phy_reset_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd) +); + +endmodule diff --git a/fpga/lib/eth/example/Arty/fpga/rtl/fpga_core.v b/fpga/lib/eth/example/Arty/fpga/rtl/fpga_core.v new file mode 100644 index 000000000..f13193e96 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/rtl/fpga_core.v @@ -0,0 +1,590 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * Clock: 125MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + + /* + * GPIO + */ + input wire [3:0] btn, + input wire [3:0] sw, + output wire led0_r, + output wire led0_g, + output wire led0_b, + output wire led1_r, + output wire led1_g, + output wire led1_b, + output wire led2_r, + output wire led2_g, + output wire led2_b, + output wire led3_r, + output wire led3_g, + output wire led3_b, + output wire led4, + output wire led5, + output wire led6, + output wire led7, + + /* + * Ethernet: 100BASE-T MII + */ + input wire phy_rx_clk, + input wire [3:0] phy_rxd, + input wire phy_rx_dv, + input wire phy_rx_er, + input wire phy_tx_clk, + output wire [3:0] phy_txd, + output wire phy_tx_en, + input wire phy_col, + input wire phy_crs, + output wire phy_reset_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 {led0_g, led1_g, led2_g, led3_g, led4, led5, led6, led7} = led_reg; +assign phy_reset_n = !rst; + +assign uart_txd = 0; + +eth_mac_mii_fifo #( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE("BUFR"), + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_inst ( + .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), + + .mii_rx_clk(phy_rx_clk), + .mii_rxd(phy_rxd), + .mii_rx_dv(phy_rx_dv), + .mii_rx_er(phy_rx_er), + .mii_tx_clk(phy_tx_clk), + .mii_txd(phy_txd), + .mii_tx_en(phy_tx_en), + .mii_tx_er(), + + .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(), + + .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 #( + .ADDR_WIDTH(12), + .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/fpga/lib/eth/example/Arty/fpga/rtl/sync_reset.v b/fpga/lib/eth/example/Arty/fpga/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/Arty/fpga/rtl/sync_signal.v b/fpga/lib/eth/example/Arty/fpga/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/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/fpga/lib/eth/example/Arty/fpga/tb/arp_ep.py b/fpga/lib/eth/example/Arty/fpga/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/Arty/fpga/tb/axis_ep.py b/fpga/lib/eth/example/Arty/fpga/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/Arty/fpga/tb/eth_ep.py b/fpga/lib/eth/example/Arty/fpga/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/Arty/fpga/tb/ip_ep.py b/fpga/lib/eth/example/Arty/fpga/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/Arty/fpga/tb/mii_ep.py b/fpga/lib/eth/example/Arty/fpga/tb/mii_ep.py new file mode 120000 index 000000000..e1b3a9129 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/tb/mii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/mii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/Arty/fpga/tb/test_fpga_core.py b/fpga/lib/eth/example/Arty/fpga/tb/test_fpga_core.py new file mode 100755 index 000000000..07e9f84ca --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/tb/test_fpga_core.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import mii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/ssio_sdr_in.v") +srcs.append("../lib/eth/rtl/mii_phy_if.v") +srcs.append("../lib/eth/rtl/eth_mac_mii_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_mii.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/udp_complete.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen.v") +srcs.append("../lib/eth/rtl/udp.v") +srcs.append("../lib/eth/rtl/udp_ip_rx.v") +srcs.append("../lib/eth/rtl/udp_ip_tx.v") +srcs.append("../lib/eth/rtl/ip_complete.v") +srcs.append("../lib/eth/rtl/ip.v") +srcs.append("../lib/eth/rtl/ip_eth_rx.v") +srcs.append("../lib/eth/rtl/ip_eth_tx.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx.v") +srcs.append("../lib/eth/rtl/arp_eth_tx.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btn = Signal(intbv(0)[4:]) + sw = Signal(intbv(0)[4:]) + phy_rx_clk = Signal(bool(0)) + phy_rxd = Signal(intbv(0)[4:]) + phy_rx_dv = Signal(bool(0)) + phy_rx_er = Signal(bool(0)) + phy_col = Signal(bool(0)) + phy_crs = Signal(bool(0)) + uart_rxd = Signal(bool(0)) + + # Outputs + led0_r = Signal(bool(0)) + led0_g = Signal(bool(0)) + led0_b = Signal(bool(0)) + led1_r = Signal(bool(0)) + led1_g = Signal(bool(0)) + led1_b = Signal(bool(0)) + led2_r = Signal(bool(0)) + led2_g = Signal(bool(0)) + led2_b = Signal(bool(0)) + led3_r = Signal(bool(0)) + led3_g = Signal(bool(0)) + led3_b = Signal(bool(0)) + led4 = Signal(bool(0)) + led5 = Signal(bool(0)) + led6 = Signal(bool(0)) + led7 = Signal(bool(0)) + phy_tx_clk = Signal(bool(0)) + phy_txd = Signal(intbv(0)[4:]) + phy_tx_en = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_txd = Signal(bool(0)) + + # sources and sinks + mii_source = mii_ep.MIISource() + + mii_source_logic = mii_source.create_logic( + phy_rx_clk, + rst, + txd=phy_rxd, + tx_en=phy_rx_dv, + tx_er=phy_rx_er, + name='mii_source' + ) + + mii_sink = mii_ep.MIISink() + + mii_sink_logic = mii_sink.create_logic( + phy_tx_clk, + rst, + rxd=phy_txd, + rx_dv=phy_tx_en, + rx_er=False, + name='mii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + btn=btn, + sw=sw, + led0_r=led0_r, + led0_g=led0_g, + led0_b=led0_b, + led1_r=led1_r, + led1_g=led1_g, + led1_b=led1_b, + led2_r=led2_r, + led2_g=led2_g, + led2_b=led2_b, + led3_r=led3_r, + led3_g=led3_g, + led3_b=led3_b, + led4=led4, + led5=led5, + led6=led6, + led7=led7, + + phy_rx_clk=phy_rx_clk, + phy_rxd=phy_rxd, + phy_rx_dv=phy_rx_dv, + phy_rx_er=phy_rx_er, + phy_tx_clk=phy_tx_clk, + phy_txd=phy_txd, + phy_tx_en=phy_tx_en, + phy_col=phy_col, + phy_crs=phy_crs, + phy_reset_n=phy_reset_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + phy_clk_hp = Signal(int(20)) + + @instance + def phy_clk_gen(): + while True: + yield delay(int(phy_clk_hp)) + phy_rx_clk.next = not phy_rx_clk + phy_tx_clk.next = not phy_tx_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + mii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while mii_sink.empty(): + yield clk.posedge + + rx_frame = mii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + mii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while mii_sink.empty(): + yield clk.posedge + + rx_frame = mii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert mii_source.empty() + assert mii_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/Arty/fpga/tb/test_fpga_core.v b/fpga/lib/eth/example/Arty/fpga/tb/test_fpga_core.v new file mode 100644 index 000000000..3ceea0953 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/tb/test_fpga_core.v @@ -0,0 +1,158 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters +parameter TARGET = "SIM"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [3:0] btn = 0; +reg [3:0] sw = 0; +reg phy_rx_clk = 0; +reg [3:0] phy_rxd = 0; +reg phy_rx_dv = 0; +reg phy_rx_er = 0; +reg phy_col = 0; +reg phy_crs = 0; +reg phy_tx_clk = 0; +reg uart_rxd = 0; + +// Outputs +wire led0_r; +wire led0_g; +wire led0_b; +wire led1_r; +wire led1_g; +wire led1_b; +wire led2_r; +wire led2_g; +wire led2_b; +wire led3_r; +wire led3_g; +wire led3_b; +wire led4; +wire led5; +wire led6; +wire led7; +wire [3:0] phy_txd; +wire phy_tx_en; +wire phy_reset_n; +wire uart_txd; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + btn, + sw, + phy_rx_clk, + phy_rxd, + phy_rx_dv, + phy_rx_er, + phy_tx_clk, + phy_col, + phy_crs, + uart_rxd + ); + $to_myhdl( + led0_r, + led0_g, + led0_b, + led1_r, + led1_g, + led1_b, + led2_r, + led2_g, + led2_b, + led3_r, + led3_g, + led3_b, + led4, + led5, + led6, + led7, + phy_txd, + phy_tx_en, + phy_reset_n, + uart_txd + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core #( + .TARGET(TARGET) +) +UUT ( + .clk(clk), + .rst(rst), + .btn(btn), + .sw(sw), + .led0_r(led0_r), + .led0_g(led0_g), + .led0_b(led0_b), + .led1_r(led1_r), + .led1_g(led1_g), + .led1_b(led1_b), + .led2_r(led2_r), + .led2_g(led2_g), + .led2_b(led2_b), + .led3_r(led3_r), + .led3_g(led3_g), + .led3_b(led3_b), + .led4(led4), + .led5(led5), + .led6(led6), + .led7(led7), + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_dv(phy_rx_dv), + .phy_rx_er(phy_rx_er), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_en(phy_tx_en), + .phy_col(phy_col), + .phy_crs(phy_crs), + .phy_reset_n(phy_reset_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd) +); + +endmodule diff --git a/fpga/lib/eth/example/Arty/fpga/tb/udp_ep.py b/fpga/lib/eth/example/Arty/fpga/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/Arty/fpga/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/DE5-Net/fpga/Makefile b/fpga/lib/eth/example/DE5-Net/fpga/Makefile new file mode 100644 index 000000000..c0cd7fe7b --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/Makefile @@ -0,0 +1,25 @@ +# Targets +TARGETS:= + +# Subdirectories +SUBDIRS = cores 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: + #program commands diff --git a/fpga/lib/eth/example/DE5-Net/fpga/README.md b/fpga/lib/eth/example/DE5-Net/fpga/README.md new file mode 100644 index 000000000..07c43c16e --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/README.md @@ -0,0 +1,25 @@ +# Verilog Ethernet DE5-Net Example Design + +## Introduction + +This example design targets the Terasic DE5-Net 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: 5SGXEA7N2F45C2 +PHY: 10G BASE-R PHY MegaCore + +## How to build + +Run make to build. Ensure that the Altera Quartus toolchain components are +in PATH. + +## How to test + +Run make program to program the DE5-Net board with the Altera software. 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. + + diff --git a/fpga/lib/eth/example/DE5-Net/fpga/common/altera.mk b/fpga/lib/eth/example/DE5-Net/fpga/common/altera.mk new file mode 100644 index 000000000..e06d66313 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/common/altera.mk @@ -0,0 +1,141 @@ +################################################################### +# +# Altera FPGA Makefile +# +# Alex Forencich +# +################################################################### +# +# Parameters: +# FPGA_TOP - Top module name +# FPGA_FAMILY - FPGA family (e.g. Stratix V) +# FPGA_DEVICE - FPGA device (e.g. 5SGXEA7N2F45C2) +# SYN_FILES - space-separated list of source files +# QSF_FILES - space-separated list of settings files +# SDC_FILES - space-separated list of timing constraint files +# +# Example: +# +# FPGA_TOP = fpga +# FPGA_FAMILY = "Stratix V" +# FPGA_DEVICE = 5SGXEA7N2F45C2 +# SYN_FILES = rtl/fpga.v rtl/clocks.v +# QSF_FILES = fpga.qsf +# SDC_FILES = fpga.sdc +# include ../common/altera.mk +# +################################################################### + +# phony targets +.PHONY: clean fpga + +# output files to hang on to +.PRECIOUS: %.sof %.map.rpt %.fit.rpt %.asm.rpt %.sta.rpt + +# any project specific settings +-include ../config.mk + +SYN_FILES_REL = $(patsubst %, ../%, $(SYN_FILES)) + +ifdef QSF_FILES + QSF_FILES_REL = $(patsubst %, ../%, $(QSF_FILES)) +else + QSF_FILES_REL = ../$(FPGA_TOP).qsf +endif + +SDC_FILES_REL = $(patsubst %, ../%, $(SDC_FILES)) + +ASSIGNMENT_FILES = $(FPGA_TOP).qpf $(FPGA_TOP).qsf + +################################################################### +# Main Targets +# +# all: build everything +# clean: remove output files and database +################################################################### + +all: fpga + +fpga: $(FPGA_TOP).sof + +clean: + rm -rf *.rpt *.summary *.smsg *.chg smart.log *.htm *.eqn *.pin *.sof *.pof *.qsf *.qpf *.jdi *.sld *.txt db incremental_db reconfig_mif + +map: smart.log $(PROJECT).map.rpt +fit: smart.log $(PROJECT).fit.rpt +asm: smart.log $(PROJECT).asm.rpt +sta: smart.log $(PROJECT).sta.rpt +smart: smart.log + +################################################################### +# Executable Configuration +################################################################### + +MAP_ARGS = --family=$(FPGA_FAMILY) +FIT_ARGS = --part=$(FPGA_DEVICE) +ASM_ARGS = +STA_ARGS = + +################################################################### +# Target implementations +################################################################### + +STAMP = echo done > + +%.map.rpt: map.chg $(SYN_FILES_REL) + quartus_map $(MAP_ARGS) $(FPGA_TOP) + +%.fit.rpt: fit.chg %.map.rpt + quartus_fit $(FIT_ARGS) $(FPGA_TOP) + +%.sta.rpt: sta.chg %.fit.rpt + quartus_sta $(STA_ARGS) $(FPGA_TOP) + +%.asm.rpt: asm.chg %.sta.rpt + quartus_asm $(ASM_ARGS) $(FPGA_TOP) + mkdir -p rev + EXT=sof; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $*.$$EXT rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + +%.sof: smart.log %.asm.rpt + + +smart.log: $(ASSIGNMENT_FILES) + quartus_sh --determine_smart_action $(FPGA_TOP) > smart.log + +################################################################### +# Project initialization +################################################################### + +$(ASSIGNMENT_FILES): $(QSF_FILES_REL) $(SYN_FILES_REL) + rm -f $(FPGA_TOP).qsf + quartus_sh --prepare -f $(FPGA_FAMILY) -d $(FPGA_DEVICE) -t $(FPGA_TOP) $(FPGA_TOP) + echo >> $(FPGA_TOP).qsf + echo >> $(FPGA_TOP).qsf + echo "# Source files" >> $(FPGA_TOP).qsf + for x in $(SYN_FILES_REL); do \ + case $${x##*.} in \ + v|V) echo set_global_assignment -name VERILOG_FILE $$x >> $(FPGA_TOP).qsf ;;\ + vhd|VHD) echo set_global_assignment -name VHDL_FILE $$x >> $(FPGA_TOP).qsf ;;\ + qip|QIP) echo set_global_assignment -name QIP_FILE $$x >> $(FPGA_TOP).qsf ;;\ + *) echo set_global_assignment -name SOURCE_FILE $$x >> $(FPGA_TOP).qsf ;;\ + esac; \ + done + echo >> $(FPGA_TOP).qsf + echo "# SDC files" >> $(FPGA_TOP).qsf + for x in $(SDC_FILES_REL); do echo set_global_assignment -name SDC_FILE $$x >> $(FPGA_TOP).qsf; done + for x in $(QSF_FILES_REL); do printf "\n#\n# Included QSF file $$x\n#\n" >> $(FPGA_TOP).qsf; cat $$x >> $(FPGA_TOP).qsf; done + +map.chg: + $(STAMP) map.chg +fit.chg: + $(STAMP) fit.chg +sta.chg: + $(STAMP) sta.chg +asm.chg: + $(STAMP) asm.chg + + diff --git a/fpga/lib/eth/example/DE5-Net/fpga/cores/Makefile b/fpga/lib/eth/example/DE5-Net/fpga/cores/Makefile new file mode 100644 index 000000000..9124fae06 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/cores/Makefile @@ -0,0 +1,23 @@ +# Tools +QMEGAWIZ=qmegawiz + +# Sources +QMWSRC=phy.v +QMWSRC+=phy_reconfig.v + +# Targets +TARGETS=$(QMWSRC:.v=) + +# Rules +.PHONY: all +all: $(TARGETS) + +.PHONY: clean +clean: + -rm -rf $(TARGETS) + +%: %.v + mkdir -p $@ + cp -a $< $@ + cd $@ && $(QMEGAWIZ) -silent $< + diff --git a/fpga/lib/eth/example/DE5-Net/fpga/cores/phy.v b/fpga/lib/eth/example/DE5-Net/fpga/cores/phy.v new file mode 100644 index 000000000..73ba98c79 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/cores/phy.v @@ -0,0 +1,192 @@ +// megafunction wizard: %10GBASE-R PHY v15.0% +// GENERATION: XML +// phy.v + +// Generated using ACDS version 15.0 153 + +`timescale 1 ps / 1 ps +module phy ( + input wire pll_ref_clk, // pll_ref_clk.clk + output wire xgmii_rx_clk, // xgmii_rx_clk.clk + output wire pll_locked, // pll_locked.export + output wire tx_ready, // tx_ready.export + input wire xgmii_tx_clk, // xgmii_tx_clk.clk + output wire rx_ready, // rx_ready.export + output wire [3:0] rx_data_ready, // rx_data_ready.export + output wire [71:0] xgmii_rx_dc_0, // xgmii_rx_dc_0.data + input wire rx_serial_data_0, // rx_serial_data_0.export + output wire [71:0] xgmii_rx_dc_1, // xgmii_rx_dc_1.data + input wire rx_serial_data_1, // rx_serial_data_1.export + output wire [71:0] xgmii_rx_dc_2, // xgmii_rx_dc_2.data + input wire rx_serial_data_2, // rx_serial_data_2.export + output wire [71:0] xgmii_rx_dc_3, // xgmii_rx_dc_3.data + input wire rx_serial_data_3, // rx_serial_data_3.export + input wire [71:0] xgmii_tx_dc_0, // xgmii_tx_dc_0.data + output wire [0:0] tx_serial_data_0, // tx_serial_data_0.export + input wire [71:0] xgmii_tx_dc_1, // xgmii_tx_dc_1.data + output wire [0:0] tx_serial_data_1, // tx_serial_data_1.export + input wire [71:0] xgmii_tx_dc_2, // xgmii_tx_dc_2.data + output wire [0:0] tx_serial_data_2, // tx_serial_data_2.export + input wire [71:0] xgmii_tx_dc_3, // xgmii_tx_dc_3.data + output wire [0:0] tx_serial_data_3, // tx_serial_data_3.export + output wire [367:0] reconfig_from_xcvr, // reconfig_from_xcvr.reconfig_from_xcvr + input wire [559:0] reconfig_to_xcvr, // reconfig_to_xcvr.reconfig_to_xcvr + input wire phy_mgmt_clk, // phy_mgmt_clk.clk + input wire phy_mgmt_clk_reset, // phy_mgmt_clk_reset.reset + input wire [8:0] phy_mgmt_address, // phy_mgmt.address + input wire phy_mgmt_read, // .read + output wire [31:0] phy_mgmt_readdata, // .readdata + input wire phy_mgmt_write, // .write + input wire [31:0] phy_mgmt_writedata, // .writedata + output wire phy_mgmt_waitrequest // .waitrequest + ); + + wire [3:0] phy_inst_tx_serial_data; // port fragment + wire [287:0] phy_inst_xgmii_rx_dc; // port fragment + + altera_xcvr_10gbaser #( + .device_family ("Stratix V"), + .num_channels (4), + .operation_mode ("duplex"), + .external_pma_ctrl_config (0), + .control_pin_out (0), + .recovered_clk_out (0), + .pll_locked_out (1), + .ref_clk_freq ("644.53125 MHz"), + .pma_mode (40), + .pll_type ("CMU"), + .starting_channel_number (0), + .reconfig_interfaces (8), + .rx_use_coreclk (0), + .embedded_reset (1), + .latadj (0), + .high_precision_latadj (1), + .tx_termination ("OCT_100_OHMS"), + .tx_vod_selection (7), + .tx_preemp_pretap (0), + .tx_preemp_pretap_inv (0), + .tx_preemp_tap_1 (15), + .tx_preemp_tap_2 (0), + .tx_preemp_tap_2_inv (0), + .rx_common_mode ("0.82v"), + .rx_termination ("OCT_100_OHMS"), + .rx_eq_dc_gain (0), + .rx_eq_ctrl (0), + .mgmt_clk_in_mhz (150) + ) phy_inst ( + .pll_ref_clk (pll_ref_clk), // pll_ref_clk.clk + .xgmii_rx_clk (xgmii_rx_clk), // xgmii_rx_clk.clk + .pll_locked (pll_locked), // pll_locked.export + .tx_ready (tx_ready), // tx_ready.export + .xgmii_tx_clk (xgmii_tx_clk), // xgmii_tx_clk.clk + .rx_ready (rx_ready), // rx_ready.export + .rx_data_ready (rx_data_ready), // rx_data_ready.export + .xgmii_rx_dc (phy_inst_xgmii_rx_dc), // xgmii_rx_dc_0.data + .rx_serial_data ({rx_serial_data_3,rx_serial_data_2,rx_serial_data_1,rx_serial_data_0}), // rx_serial_data_0.export + .xgmii_tx_dc ({xgmii_tx_dc_3[71:0],xgmii_tx_dc_2[71:0],xgmii_tx_dc_1[71:0],xgmii_tx_dc_0[71:0]}), // xgmii_tx_dc_0.data + .tx_serial_data (phy_inst_tx_serial_data), // tx_serial_data_0.export + .reconfig_from_xcvr (reconfig_from_xcvr), // reconfig_from_xcvr.reconfig_from_xcvr + .reconfig_to_xcvr (reconfig_to_xcvr), // reconfig_to_xcvr.reconfig_to_xcvr + .phy_mgmt_clk (phy_mgmt_clk), // phy_mgmt_clk.clk + .phy_mgmt_clk_reset (phy_mgmt_clk_reset), // phy_mgmt_clk_reset.reset + .phy_mgmt_address (phy_mgmt_address), // phy_mgmt.address + .phy_mgmt_read (phy_mgmt_read), // .read + .phy_mgmt_readdata (phy_mgmt_readdata), // .readdata + .phy_mgmt_write (phy_mgmt_write), // .write + .phy_mgmt_writedata (phy_mgmt_writedata), // .writedata + .phy_mgmt_waitrequest (phy_mgmt_waitrequest), // .waitrequest + .rx_block_lock (), // (terminated) + .rx_hi_ber (), // (terminated) + .rx_recovered_clk (), // (terminated) + .rx_coreclkin (1'b0), // (terminated) + .gxb_pdn (1'b0), // (terminated) + .pll_pdn (1'b0), // (terminated) + .cal_blk_pdn (1'b0), // (terminated) + .cal_blk_clk (1'b0), // (terminated) + .tx_digitalreset (4'b0000), // (terminated) + .tx_analogreset (4'b0000), // (terminated) + .tx_cal_busy (), // (terminated) + .pll_powerdown (4'b0000), // (terminated) + .rx_digitalreset (4'b0000), // (terminated) + .rx_analogreset (4'b0000), // (terminated) + .rx_cal_busy (), // (terminated) + .rx_is_lockedtodata (), // (terminated) + .rx_latency_adj (), // (terminated) + .tx_latency_adj () // (terminated) + ); + + assign tx_serial_data_0 = { phy_inst_tx_serial_data[0:0] }; + + assign xgmii_rx_dc_0 = { phy_inst_xgmii_rx_dc[71:0] }; + + assign tx_serial_data_1 = { phy_inst_tx_serial_data[1:1] }; + + assign xgmii_rx_dc_1 = { phy_inst_xgmii_rx_dc[143:72] }; + + assign xgmii_rx_dc_2 = { phy_inst_xgmii_rx_dc[215:144] }; + + assign xgmii_rx_dc_3 = { phy_inst_xgmii_rx_dc[287:216] }; + + assign tx_serial_data_2 = { phy_inst_tx_serial_data[2:2] }; + + assign tx_serial_data_3 = { phy_inst_tx_serial_data[3:3] }; + +endmodule +// Retrieval info: +// +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// IPFS_FILES : phy.vo +// RELATED_FILES: phy.v, altera_xcvr_functions.sv, alt_reset_ctrl_lego.sv, alt_reset_ctrl_tgx_cdrauto.sv, alt_xcvr_resync.sv, alt_xcvr_csr_common_h.sv, alt_xcvr_csr_common.sv, alt_xcvr_csr_pcs8g_h.sv, alt_xcvr_csr_pcs8g.sv, alt_xcvr_csr_selector.sv, alt_xcvr_mgmt2dec.sv, altera_wait_generate.v, altera_10gbaser_phy_handshake_clock_crosser.v, altera_10gbaser_phy_clock_crosser.v, altera_10gbaser_phy_pipeline_stage.sv, altera_10gbaser_phy_pipeline_base.v, csr_pcs10gbaser_h.sv, csr_pcs10gbaser.sv, sv_pcs.sv, sv_pcs_ch.sv, sv_pma.sv, sv_reconfig_bundle_to_xcvr.sv, sv_reconfig_bundle_to_ip.sv, sv_reconfig_bundle_merger.sv, sv_rx_pma.sv, sv_tx_pma.sv, sv_tx_pma_ch.sv, sv_xcvr_h.sv, sv_xcvr_avmm_csr.sv, sv_xcvr_avmm_dcd.sv, sv_xcvr_avmm.sv, sv_xcvr_data_adapter.sv, sv_xcvr_native.sv, sv_xcvr_plls.sv, sv_hssi_10g_rx_pcs_rbc.sv, sv_hssi_10g_tx_pcs_rbc.sv, sv_hssi_8g_rx_pcs_rbc.sv, sv_hssi_8g_tx_pcs_rbc.sv, sv_hssi_8g_pcs_aggregate_rbc.sv, sv_hssi_common_pcs_pma_interface_rbc.sv, sv_hssi_common_pld_pcs_interface_rbc.sv, sv_hssi_pipe_gen1_2_rbc.sv, sv_hssi_pipe_gen3_rbc.sv, sv_hssi_rx_pcs_pma_interface_rbc.sv, sv_hssi_rx_pld_pcs_interface_rbc.sv, sv_hssi_tx_pcs_pma_interface_rbc.sv, sv_hssi_tx_pld_pcs_interface_rbc.sv, sv_xcvr_10gbaser_nr.sv, sv_xcvr_10gbaser_native.sv, altera_xcvr_10gbaser.sv, altera_xcvr_reset_control.sv, alt_xcvr_reset_counter.sv, alt_xcvr_arbiter.sv, alt_xcvr_m2s.sv diff --git a/fpga/lib/eth/example/DE5-Net/fpga/cores/phy_reconfig.v b/fpga/lib/eth/example/DE5-Net/fpga/cores/phy_reconfig.v new file mode 100644 index 000000000..c1236c321 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/cores/phy_reconfig.v @@ -0,0 +1,101 @@ +// megafunction wizard: %Transceiver Reconfiguration Controller v15.0% +// GENERATION: XML +// phy_reconfig.v + +// Generated using ACDS version 15.0 153 + +`timescale 1 ps / 1 ps +module phy_reconfig ( + output wire reconfig_busy, // reconfig_busy.reconfig_busy + input wire mgmt_clk_clk, // mgmt_clk_clk.clk + input wire mgmt_rst_reset, // mgmt_rst_reset.reset + input wire [6:0] reconfig_mgmt_address, // reconfig_mgmt.address + input wire reconfig_mgmt_read, // .read + output wire [31:0] reconfig_mgmt_readdata, // .readdata + output wire reconfig_mgmt_waitrequest, // .waitrequest + input wire reconfig_mgmt_write, // .write + input wire [31:0] reconfig_mgmt_writedata, // .writedata + output wire [559:0] reconfig_to_xcvr, // reconfig_to_xcvr.reconfig_to_xcvr + input wire [367:0] reconfig_from_xcvr // reconfig_from_xcvr.reconfig_from_xcvr + ); + + alt_xcvr_reconfig #( + .device_family ("Stratix V"), + .number_of_reconfig_interfaces (8), + .enable_offset (1), + .enable_lc (1), + .enable_dcd (0), + .enable_dcd_power_up (1), + .enable_analog (1), + .enable_eyemon (0), + .enable_ber (0), + .enable_dfe (0), + .enable_adce (0), + .enable_mif (0), + .enable_pll (0) + ) phy_reconfig_inst ( + .reconfig_busy (reconfig_busy), // reconfig_busy.reconfig_busy + .mgmt_clk_clk (mgmt_clk_clk), // mgmt_clk_clk.clk + .mgmt_rst_reset (mgmt_rst_reset), // mgmt_rst_reset.reset + .reconfig_mgmt_address (reconfig_mgmt_address), // reconfig_mgmt.address + .reconfig_mgmt_read (reconfig_mgmt_read), // .read + .reconfig_mgmt_readdata (reconfig_mgmt_readdata), // .readdata + .reconfig_mgmt_waitrequest (reconfig_mgmt_waitrequest), // .waitrequest + .reconfig_mgmt_write (reconfig_mgmt_write), // .write + .reconfig_mgmt_writedata (reconfig_mgmt_writedata), // .writedata + .reconfig_to_xcvr (reconfig_to_xcvr), // reconfig_to_xcvr.reconfig_to_xcvr + .reconfig_from_xcvr (reconfig_from_xcvr), // reconfig_from_xcvr.reconfig_from_xcvr + .tx_cal_busy (), // (terminated) + .rx_cal_busy (), // (terminated) + .cal_busy_in (1'b0), // (terminated) + .reconfig_mif_address (), // (terminated) + .reconfig_mif_read (), // (terminated) + .reconfig_mif_readdata (16'b0000000000000000), // (terminated) + .reconfig_mif_waitrequest (1'b0) // (terminated) + ); + +endmodule +// Retrieval info: +// +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// Retrieval info: +// IPFS_FILES : phy_reconfig.vo +// RELATED_FILES: phy_reconfig.v, altera_xcvr_functions.sv, sv_xcvr_h.sv, alt_xcvr_resync.sv, alt_xcvr_reconfig_h.sv, sv_xcvr_dfe_cal_sweep_h.sv, alt_xcvr_reconfig.sv, alt_xcvr_reconfig_sv.sv, alt_xcvr_reconfig_cal_seq.sv, alt_xreconf_cif.sv, alt_xreconf_uif.sv, alt_xreconf_basic_acq.sv, alt_xcvr_reconfig_analog.sv, alt_xcvr_reconfig_analog_sv.sv, alt_xreconf_analog_datactrl.sv, alt_xreconf_analog_rmw.sv, alt_xreconf_analog_ctrlsm.sv, alt_xcvr_reconfig_offset_cancellation.sv, alt_xcvr_reconfig_offset_cancellation_sv.sv, alt_xcvr_reconfig_eyemon.sv, alt_xcvr_reconfig_eyemon_sv.sv, alt_xcvr_reconfig_eyemon_ctrl_sv.sv, alt_xcvr_reconfig_eyemon_ber_sv.sv, ber_reader_dcfifo.v, step_to_mon_sv.sv, mon_to_step_sv.sv, alt_xcvr_reconfig_dfe.sv, alt_xcvr_reconfig_dfe_sv.sv, alt_xcvr_reconfig_dfe_reg_sv.sv, alt_xcvr_reconfig_dfe_cal_sv.sv, alt_xcvr_reconfig_dfe_cal_sweep_sv.sv, alt_xcvr_reconfig_dfe_cal_sweep_datapath_sv.sv, alt_xcvr_reconfig_dfe_oc_cal_sv.sv, alt_xcvr_reconfig_dfe_pi_phase_sv.sv, alt_xcvr_reconfig_dfe_step_to_mon_en_sv.sv, alt_xcvr_reconfig_dfe_adapt_tap_sv.sv, alt_xcvr_reconfig_dfe_ctrl_mux_sv.sv, alt_xcvr_reconfig_dfe_local_reset_sv.sv, alt_xcvr_reconfig_dfe_cal_sim_sv.sv, alt_xcvr_reconfig_dfe_adapt_tap_sim_sv.sv, alt_xcvr_reconfig_adce.sv, alt_xcvr_reconfig_adce_sv.sv, alt_xcvr_reconfig_adce_datactrl_sv.sv, alt_xcvr_reconfig_dcd.sv, alt_xcvr_reconfig_dcd_sv.sv, alt_xcvr_reconfig_dcd_cal.sv, alt_xcvr_reconfig_dcd_control.sv, alt_xcvr_reconfig_dcd_datapath.sv, alt_xcvr_reconfig_dcd_pll_reset.sv, alt_xcvr_reconfig_dcd_eye_width.sv, alt_xcvr_reconfig_dcd_align_clk.sv, alt_xcvr_reconfig_dcd_get_sum.sv, alt_xcvr_reconfig_dcd_cal_sim_model.sv, alt_xcvr_reconfig_mif.sv, sv_xcvr_reconfig_mif.sv, sv_xcvr_reconfig_mif_ctrl.sv, sv_xcvr_reconfig_mif_avmm.sv, alt_xcvr_reconfig_pll.sv, sv_xcvr_reconfig_pll.sv, sv_xcvr_reconfig_pll_ctrl.sv, alt_xcvr_reconfig_soc.sv, alt_xcvr_reconfig_cpu_ram.sv, alt_xcvr_reconfig_direct.sv, sv_xrbasic_l2p_addr.sv, sv_xrbasic_l2p_ch.sv, sv_xrbasic_l2p_rom.sv, sv_xrbasic_lif_csr.sv, sv_xrbasic_lif.sv, sv_xcvr_reconfig_basic.sv, alt_arbiter_acq.sv, alt_xcvr_reconfig_basic.sv, alt_xcvr_arbiter.sv, alt_xcvr_m2s.sv, altera_wait_generate.v, alt_xcvr_csr_selector.sv, sv_reconfig_bundle_to_basic.sv, alt_xcvr_reconfig_cpu.v, alt_xcvr_reconfig_cpu_reconfig_cpu.v, alt_xcvr_reconfig_cpu_reconfig_cpu_test_bench.v, alt_xcvr_reconfig_cpu_mm_interconnect_0.v, alt_xcvr_reconfig_cpu_irq_mapper.sv, altera_reset_controller.v, altera_reset_synchronizer.v, altera_merlin_master_translator.sv, altera_merlin_slave_translator.sv, altera_merlin_master_agent.sv, altera_merlin_slave_agent.sv, altera_merlin_burst_uncompressor.sv, altera_avalon_sc_fifo.v, alt_xcvr_reconfig_cpu_mm_interconnect_0_router.sv, alt_xcvr_reconfig_cpu_mm_interconnect_0_router_001.sv, alt_xcvr_reconfig_cpu_mm_interconnect_0_router_002.sv, alt_xcvr_reconfig_cpu_mm_interconnect_0_router_003.sv, alt_xcvr_reconfig_cpu_mm_interconnect_0_cmd_demux.sv, alt_xcvr_reconfig_cpu_mm_interconnect_0_cmd_demux_001.sv, altera_merlin_arbitrator.sv, alt_xcvr_reconfig_cpu_mm_interconnect_0_cmd_mux.sv, alt_xcvr_reconfig_cpu_mm_interconnect_0_cmd_mux_001.sv, alt_xcvr_reconfig_cpu_mm_interconnect_0_rsp_mux.sv, alt_xcvr_reconfig_cpu_mm_interconnect_0_rsp_mux_001.sv, alt_xcvr_reconfig_cpu_mm_interconnect_0_avalon_st_adapter.v, alt_xcvr_reconfig_cpu_mm_interconnect_0_avalon_st_adapter_error_adapter_0.sv diff --git a/fpga/lib/eth/example/DE5-Net/fpga/fpga.qsf b/fpga/lib/eth/example/DE5-Net/fpga/fpga.qsf new file mode 100644 index 000000000..4e91c5cef --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/fpga.qsf @@ -0,0 +1,1646 @@ +#============================================================ +# BUTTON +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to BUTTON[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to BUTTON[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to BUTTON[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to BUTTON[3] +#============================================================ +# CLOCK +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to CLOCK_SCL +set_instance_assignment -name IO_STANDARD "2.5 V" -to CLOCK_SDA +#============================================================ +# CPU +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to CPU_RESET_n +#============================================================ +# DDR3A +#============================================================ +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[2] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[3] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[4] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[5] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[6] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[7] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[8] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[9] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[10] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[11] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[12] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[13] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[14] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_A[15] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_BA[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_BA[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_BA[2] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_CAS_n +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_CK[0] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_CK[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_CKE[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_CKE[1] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_CK_n[0] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_CK_n[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_CS_n[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_CS_n[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DM[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DM[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DM[2] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DM[3] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DM[4] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DM[5] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DM[6] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DM[7] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[2] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[3] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[4] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[5] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[6] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[7] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[8] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[9] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[10] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[11] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[12] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[13] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[14] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[15] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[16] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[17] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[18] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[19] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[20] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[21] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[22] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[23] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[24] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[25] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[26] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[27] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[28] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[29] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[30] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[31] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[32] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[33] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[34] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[35] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[36] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[37] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[38] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[39] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[40] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[41] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[42] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[43] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[44] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[45] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[46] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[47] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[48] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[49] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[50] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[51] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[52] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[53] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[54] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[55] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[56] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[57] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[58] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[59] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[60] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[61] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[62] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_DQ[63] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS[0] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS[1] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS[2] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS[3] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS[4] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS[5] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS[6] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS[7] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS_n[0] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS_n[1] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS_n[2] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS_n[3] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS_n[4] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS_n[5] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS_n[6] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3A_DQS_n[7] +set_instance_assignment -name IO_STANDARD "1.5 V" -to DDR3A_EVENT_n +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_ODT[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_ODT[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_RAS_n +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_RESET_n +set_instance_assignment -name IO_STANDARD "1.5 V" -to DDR3A_SCL +set_instance_assignment -name IO_STANDARD "1.5 V" -to DDR3A_SDA +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3A_WE_n +#============================================================ +# DDR3B +#============================================================ +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[2] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[3] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[4] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[5] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[6] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[7] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[8] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[9] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[10] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[11] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[12] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[13] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[14] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_A[15] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_BA[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_BA[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_BA[2] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_CAS_n +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_CK[0] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_CK[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_CKE[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_CKE[1] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_CK_n[0] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_CK_n[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_CS_n[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_CS_n[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DM[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DM[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DM[2] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DM[3] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DM[4] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DM[5] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DM[6] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DM[7] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[2] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[3] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[4] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[5] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[6] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[7] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[8] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[9] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[10] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[11] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[12] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[13] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[14] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[15] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[16] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[17] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[18] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[19] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[20] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[21] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[22] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[23] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[24] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[25] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[26] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[27] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[28] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[29] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[30] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[31] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[32] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[33] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[34] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[35] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[36] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[37] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[38] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[39] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[40] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[41] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[42] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[43] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[44] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[45] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[46] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[47] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[48] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[49] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[50] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[51] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[52] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[53] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[54] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[55] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[56] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[57] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[58] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[59] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[60] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[61] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[62] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_DQ[63] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS[0] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS[1] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS[2] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS[3] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS[4] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS[5] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS[6] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS[7] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS_n[0] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS_n[1] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS_n[2] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS_n[3] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS_n[4] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS_n[5] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS_n[6] +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.5-V SSTL CLASS I" -to DDR3B_DQS_n[7] +set_instance_assignment -name IO_STANDARD "1.5 V" -to DDR3B_EVENT_n +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_ODT[0] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_ODT[1] +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_RAS_n +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_RESET_n +set_instance_assignment -name IO_STANDARD "1.5 V" -to DDR3B_SCL +set_instance_assignment -name IO_STANDARD "1.5 V" -to DDR3B_SDA +set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to DDR3B_WE_n +#============================================================ +# FAN +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to FAN_CTRL +#============================================================ +# FLASH +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to FLASH_ADV_n +set_instance_assignment -name IO_STANDARD "2.5 V" -to FLASH_CE_n[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FLASH_CE_n[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FLASH_CLK +set_instance_assignment -name IO_STANDARD "2.5 V" -to FLASH_OE_n +set_instance_assignment -name IO_STANDARD "2.5 V" -to FLASH_RDY_BSY_n[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FLASH_RDY_BSY_n[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FLASH_RESET_n +set_instance_assignment -name IO_STANDARD "2.5 V" -to FLASH_WE_n +#============================================================ +# FSM +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[6] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[8] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[9] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[10] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[11] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[12] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[13] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[14] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[15] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[16] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[17] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[18] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[19] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[20] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[21] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[22] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[23] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[24] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[25] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_A[26] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[6] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[8] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[9] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[10] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[11] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[12] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[13] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[14] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[15] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[16] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[17] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[18] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[19] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[20] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[21] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[22] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[23] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[24] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[25] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[26] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[27] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[28] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[29] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[30] +set_instance_assignment -name IO_STANDARD "2.5 V" -to FSM_D[31] +#============================================================ +# HEX0 +#============================================================ +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX0_D[0] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX0_D[1] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX0_D[2] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX0_D[3] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX0_D[4] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX0_D[5] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX0_D[6] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX0_DP +#============================================================ +# HEX1 +#============================================================ +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX1_D[0] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX1_D[1] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX1_D[2] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX1_D[3] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX1_D[4] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX1_D[5] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX1_D[6] +set_instance_assignment -name IO_STANDARD "1.5 V" -to HEX1_DP +#============================================================ +# LED +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to LED[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LED[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LED[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LED[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LED_BRACKET[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LED_BRACKET[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LED_BRACKET[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LED_BRACKET[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LED_RJ45_L +set_instance_assignment -name IO_STANDARD "2.5 V" -to LED_RJ45_R +#============================================================ +# MAX2 +#============================================================ +#============================================================ +# OSC +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to OSC_50_B3B +set_instance_assignment -name IO_STANDARD "1.8 V" -to OSC_50_B3D +set_instance_assignment -name IO_STANDARD "1.8 V" -to OSC_50_B4A +set_instance_assignment -name IO_STANDARD "1.8 V" -to OSC_50_B4D +set_instance_assignment -name IO_STANDARD "1.5 V" -to OSC_50_B7A +set_instance_assignment -name IO_STANDARD "1.5 V" -to OSC_50_B7D +set_instance_assignment -name IO_STANDARD "1.5 V" -to OSC_50_B8A +set_instance_assignment -name IO_STANDARD "1.8 V" -to OSC_50_B8D +#============================================================ +# PCIE +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to PCIE_PERST_n +set_instance_assignment -name IO_STANDARD HCSL -to PCIE_REFCLK_p +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_RX_p[0] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_RX_p[1] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_RX_p[2] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_RX_p[3] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_RX_p[4] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_RX_p[5] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_RX_p[6] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_RX_p[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to PCIE_SMBCLK +set_instance_assignment -name IO_STANDARD "2.5 V" -to PCIE_SMBDAT +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_TX_p[0] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_TX_p[1] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_TX_p[2] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_TX_p[3] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_TX_p[4] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_TX_p[5] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_TX_p[6] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to PCIE_TX_p[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to PCIE_WAKE_n +#============================================================ +# QDRIIA +#============================================================ +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[18] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[19] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_A[20] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_BWS_n[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_BWS_n[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_CQ_n +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_CQ_p +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_D[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_DOFF_n +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.8-V HSTL CLASS I" -to QDRIIA_K_n +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.8-V HSTL CLASS I" -to QDRIIA_K_p +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_ODT +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_Q[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_QVLD +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_RPS_n +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIA_WPS_n +#============================================================ +# QDRIIB +#============================================================ +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[18] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[19] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_A[20] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_BWS_n[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_BWS_n[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_CQ_n +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_CQ_p +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_D[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_DOFF_n +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.8-V HSTL CLASS I" -to QDRIIB_K_n +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.8-V HSTL CLASS I" -to QDRIIB_K_p +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_ODT +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_Q[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_QVLD +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_RPS_n +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIB_WPS_n +#============================================================ +# QDRIIC +#============================================================ +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[18] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[19] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_A[20] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_BWS_n[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_BWS_n[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_CQ_n +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_CQ_p +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_D[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_DOFF_n +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.8-V HSTL CLASS I" -to QDRIIC_K_n +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.8-V HSTL CLASS I" -to QDRIIC_K_p +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_ODT +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_Q[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_QVLD +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_RPS_n +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIIC_WPS_n +#============================================================ +# QDRIID +#============================================================ +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[18] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[19] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_A[20] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_BWS_n[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_BWS_n[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_CQ_n +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_CQ_p +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_D[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_DOFF_n +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.8-V HSTL CLASS I" -to QDRIID_K_n +set_instance_assignment -name IO_STANDARD "DIFFERENTIAL 1.8-V HSTL CLASS I" -to QDRIID_K_p +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_ODT +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[0] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[1] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[2] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[3] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[4] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[5] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[6] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[7] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[8] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[9] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[10] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[11] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[12] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[13] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[14] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[15] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[16] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_Q[17] +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_QVLD +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_RPS_n +set_instance_assignment -name IO_STANDARD "1.8-V HSTL CLASS I" -to QDRIID_WPS_n +#============================================================ +# RS422 +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to RS422_DE +set_instance_assignment -name IO_STANDARD "2.5 V" -to RS422_DIN +set_instance_assignment -name IO_STANDARD "2.5 V" -to RS422_DOUT +set_instance_assignment -name IO_STANDARD "2.5 V" -to RS422_RE_n +set_instance_assignment -name IO_STANDARD "2.5 V" -to RS422_TE +#============================================================ +# RZQ +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to RZQ_0 +set_instance_assignment -name IO_STANDARD "1.8 V" -to RZQ_1 +set_instance_assignment -name IO_STANDARD "1.5 V" -to RZQ_4 +set_instance_assignment -name IO_STANDARD "1.5 V" -to RZQ_5 +#============================================================ +# SATA +#============================================================ +#============================================================ +# SFP10G +#============================================================ +#============================================================ +# SFP1G +#============================================================ +set_instance_assignment -name IO_STANDARD HCSL -to SFP1G_REFCLK_p +#============================================================ +# SFPA +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPA_LOS +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPA_MOD0_PRSNT_n +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPA_MOD1_SCL +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPA_MOD2_SDA +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPA_RATESEL[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPA_RATESEL[1] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SFPA_RX_p +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPA_TXDISABLE +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPA_TXFAULT +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SFPA_TX_p +#============================================================ +# SFPB +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPB_LOS +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPB_MOD0_PRSNT_n +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPB_MOD1_SCL +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPB_MOD2_SDA +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPB_RATESEL[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPB_RATESEL[1] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SFPB_RX_p +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPB_TXDISABLE +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPB_TXFAULT +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SFPB_TX_p +#============================================================ +# SFPC +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPC_LOS +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPC_MOD0_PRSNT_n +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPC_MOD1_SCL +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPC_MOD2_SDA +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPC_RATESEL[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPC_RATESEL[1] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SFPC_RX_p +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPC_TXDISABLE +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPC_TXFAULT +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SFPC_TX_p +#============================================================ +# SFPD +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPD_LOS +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPD_MOD0_PRSNT_n +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPD_MOD1_SCL +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPD_MOD2_SDA +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPD_RATESEL[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPD_RATESEL[1] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SFPD_RX_p +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPD_TXDISABLE +set_instance_assignment -name IO_STANDARD "2.5 V" -to SFPD_TXFAULT +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SFPD_TX_p +set_instance_assignment -name IO_STANDARD HCSL -to SFP_REFCLK_p +#============================================================ +# SMA +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to SMA_CLKIN +set_instance_assignment -name IO_STANDARD "2.5 V" -to SMA_CLKOUT +#============================================================ +# SW +#============================================================ +set_instance_assignment -name IO_STANDARD "1.8 V" -to SW[0] +set_instance_assignment -name IO_STANDARD "1.8 V" -to SW[1] +set_instance_assignment -name IO_STANDARD "1.8 V" -to SW[2] +set_instance_assignment -name IO_STANDARD "1.8 V" -to SW[3] +#============================================================ +# TEMP +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to TEMP_CLK +set_instance_assignment -name IO_STANDARD "2.5 V" -to TEMP_DATA +set_instance_assignment -name IO_STANDARD "2.5 V" -to TEMP_INT_n +set_instance_assignment -name IO_STANDARD "2.5 V" -to TEMP_OVERT_n + +#============================================================ +# SATA +#============================================================ +set_instance_assignment -name IO_STANDARD HCSL -to SATA_HOST_REFCLK_p +set_instance_assignment -name IO_STANDARD HCSL -to SATA_DEVICE_REFCLK_p +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SATA_DEVICE_RX_p[0] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SATA_DEVICE_RX_p[1] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SATA_DEVICE_TX_p[0] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SATA_DEVICE_TX_p[1] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SATA_HOST_RX_p[0] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SATA_HOST_RX_p[1] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SATA_HOST_TX_p[0] +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to SATA_HOST_TX_p[1] + +#============================================================ +# SATA +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to PLL_SCL +set_location_assignment PIN_AF32 -to PLL_SCL +set_instance_assignment -name IO_STANDARD "2.5 V" -to PLL_SDA +set_location_assignment PIN_AG32 -to PLL_SDA + +#============================================================ +# End of pin assignments by Terasic System Builder +#============================================================ + + +set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO" +set_location_assignment PIN_AK15 -to BUTTON[0] +set_location_assignment PIN_AK14 -to BUTTON[1] +set_location_assignment PIN_AL14 -to BUTTON[2] +set_location_assignment PIN_AL15 -to BUTTON[3] +set_location_assignment PIN_AE15 -to CLOCK_SCL +set_location_assignment PIN_AE16 -to CLOCK_SDA +set_location_assignment PIN_BC37 -to CPU_RESET_n +set_location_assignment PIN_M39 -to DDR3A_A[0] +set_location_assignment PIN_L35 -to DDR3A_A[1] +set_location_assignment PIN_N38 -to DDR3A_A[2] +set_location_assignment PIN_L36 -to DDR3A_A[3] +set_location_assignment PIN_H36 -to DDR3A_A[4] +set_location_assignment PIN_K29 -to DDR3A_A[5] +set_location_assignment PIN_D37 -to DDR3A_A[6] +set_location_assignment PIN_K35 -to DDR3A_A[7] +set_location_assignment PIN_K32 -to DDR3A_A[8] +set_location_assignment PIN_K37 -to DDR3A_A[9] +set_location_assignment PIN_M38 -to DDR3A_A[10] +set_location_assignment PIN_C37 -to DDR3A_A[11] +set_location_assignment PIN_K36 -to DDR3A_A[12] +set_location_assignment PIN_M33 -to DDR3A_A[13] +set_location_assignment PIN_K34 -to DDR3A_A[14] +set_location_assignment PIN_B38 -to DDR3A_A[15] +set_location_assignment PIN_M37 -to DDR3A_BA[0] +set_location_assignment PIN_P39 -to DDR3A_BA[1] +set_location_assignment PIN_J36 -to DDR3A_BA[2] +set_location_assignment PIN_M36 -to DDR3A_CAS_n +set_location_assignment PIN_G37 -to DDR3A_CK[0] +set_location_assignment PIN_J37 -to DDR3A_CK[1] +set_location_assignment PIN_E36 -to DDR3A_CKE[0] +set_location_assignment PIN_B35 -to DDR3A_CKE[1] +set_location_assignment PIN_F36 -to DDR3A_CK_n[0] +set_location_assignment PIN_H37 -to DDR3A_CK_n[1] +set_location_assignment PIN_P36 -to DDR3A_CS_n[0] +set_location_assignment PIN_R28 -to DDR3A_CS_n[1] +set_location_assignment PIN_C36 -to DDR3A_DM[0] +set_location_assignment PIN_E32 -to DDR3A_DM[1] +set_location_assignment PIN_H34 -to DDR3A_DM[2] +set_location_assignment PIN_L32 -to DDR3A_DM[3] +set_location_assignment PIN_N32 -to DDR3A_DM[4] +set_location_assignment PIN_W32 -to DDR3A_DM[5] +set_location_assignment PIN_K30 -to DDR3A_DM[6] +set_location_assignment PIN_T28 -to DDR3A_DM[7] +set_location_assignment PIN_A35 -to DDR3A_DQ[0] +set_location_assignment PIN_A34 -to DDR3A_DQ[1] +set_location_assignment PIN_D36 -to DDR3A_DQ[2] +set_location_assignment PIN_C33 -to DDR3A_DQ[3] +set_location_assignment PIN_B32 -to DDR3A_DQ[4] +set_location_assignment PIN_D35 -to DDR3A_DQ[5] +set_location_assignment PIN_D33 -to DDR3A_DQ[6] +set_location_assignment PIN_E33 -to DDR3A_DQ[7] +set_location_assignment PIN_A32 -to DDR3A_DQ[8] +set_location_assignment PIN_A31 -to DDR3A_DQ[9] +set_location_assignment PIN_C30 -to DDR3A_DQ[10] +set_location_assignment PIN_D30 -to DDR3A_DQ[11] +set_location_assignment PIN_B29 -to DDR3A_DQ[12] +set_location_assignment PIN_E30 -to DDR3A_DQ[13] +set_location_assignment PIN_F31 -to DDR3A_DQ[14] +set_location_assignment PIN_G31 -to DDR3A_DQ[15] +set_location_assignment PIN_F35 -to DDR3A_DQ[16] +set_location_assignment PIN_G34 -to DDR3A_DQ[17] +set_location_assignment PIN_J33 -to DDR3A_DQ[18] +set_location_assignment PIN_J34 -to DDR3A_DQ[19] +set_location_assignment PIN_F34 -to DDR3A_DQ[20] +set_location_assignment PIN_E35 -to DDR3A_DQ[21] +set_location_assignment PIN_J31 -to DDR3A_DQ[22] +set_location_assignment PIN_K31 -to DDR3A_DQ[23] +set_location_assignment PIN_P34 -to DDR3A_DQ[24] +set_location_assignment PIN_R33 -to DDR3A_DQ[25] +set_location_assignment PIN_M34 -to DDR3A_DQ[26] +set_location_assignment PIN_L33 -to DDR3A_DQ[27] +set_location_assignment PIN_R34 -to DDR3A_DQ[28] +set_location_assignment PIN_T34 -to DDR3A_DQ[29] +set_location_assignment PIN_W34 -to DDR3A_DQ[30] +set_location_assignment PIN_V35 -to DDR3A_DQ[31] +set_location_assignment PIN_P33 -to DDR3A_DQ[32] +set_location_assignment PIN_P32 -to DDR3A_DQ[33] +set_location_assignment PIN_V33 -to DDR3A_DQ[34] +set_location_assignment PIN_V34 -to DDR3A_DQ[35] +set_location_assignment PIN_N31 -to DDR3A_DQ[36] +set_location_assignment PIN_M31 -to DDR3A_DQ[37] +set_location_assignment PIN_U32 -to DDR3A_DQ[38] +set_location_assignment PIN_U33 -to DDR3A_DQ[39] +set_location_assignment PIN_R31 -to DDR3A_DQ[40] +set_location_assignment PIN_W31 -to DDR3A_DQ[41] +set_location_assignment PIN_U30 -to DDR3A_DQ[42] +set_location_assignment PIN_P31 -to DDR3A_DQ[43] +set_location_assignment PIN_T31 -to DDR3A_DQ[44] +set_location_assignment PIN_Y32 -to DDR3A_DQ[45] +set_location_assignment PIN_T29 -to DDR3A_DQ[46] +set_location_assignment PIN_P30 -to DDR3A_DQ[47] +set_location_assignment PIN_H32 -to DDR3A_DQ[48] +set_location_assignment PIN_H31 -to DDR3A_DQ[49] +set_location_assignment PIN_L30 -to DDR3A_DQ[50] +set_location_assignment PIN_L29 -to DDR3A_DQ[51] +set_location_assignment PIN_F32 -to DDR3A_DQ[52] +set_location_assignment PIN_G32 -to DDR3A_DQ[53] +set_location_assignment PIN_M30 -to DDR3A_DQ[54] +set_location_assignment PIN_N29 -to DDR3A_DQ[55] +set_location_assignment PIN_U29 -to DDR3A_DQ[56] +set_location_assignment PIN_V28 -to DDR3A_DQ[57] +set_location_assignment PIN_Y28 -to DDR3A_DQ[58] +set_location_assignment PIN_W29 -to DDR3A_DQ[59] +set_location_assignment PIN_V30 -to DDR3A_DQ[60] +set_location_assignment PIN_V29 -to DDR3A_DQ[61] +set_location_assignment PIN_W28 -to DDR3A_DQ[62] +set_location_assignment PIN_Y27 -to DDR3A_DQ[63] +set_location_assignment PIN_C34 -to DDR3A_DQS[0] +set_location_assignment PIN_C31 -to DDR3A_DQS[1] +set_location_assignment PIN_H35 -to DDR3A_DQS[2] +set_location_assignment PIN_U35 -to DDR3A_DQS[3] +set_location_assignment PIN_T33 -to DDR3A_DQS[4] +set_location_assignment PIN_T30 -to DDR3A_DQS[5] +set_location_assignment PIN_J30 -to DDR3A_DQS[6] +set_location_assignment PIN_Y30 -to DDR3A_DQS[7] +set_location_assignment PIN_B34 -to DDR3A_DQS_n[0] +set_location_assignment PIN_B31 -to DDR3A_DQS_n[1] +set_location_assignment PIN_G35 -to DDR3A_DQS_n[2] +set_location_assignment PIN_T35 -to DDR3A_DQS_n[3] +set_location_assignment PIN_T32 -to DDR3A_DQS_n[4] +set_location_assignment PIN_R30 -to DDR3A_DQS_n[5] +set_location_assignment PIN_H30 -to DDR3A_DQS_n[6] +set_location_assignment PIN_Y29 -to DDR3A_DQS_n[7] +set_location_assignment PIN_K19 -to DDR3A_EVENT_n +set_location_assignment PIN_V36 -to DDR3A_ODT[0] +set_location_assignment PIN_W35 -to DDR3A_ODT[1] +set_location_assignment PIN_P38 -to DDR3A_RAS_n +set_location_assignment PIN_H33 -to DDR3A_RESET_n +set_location_assignment PIN_C15 -to DDR3A_SCL +set_location_assignment PIN_P15 -to DDR3A_SDA +set_location_assignment PIN_N37 -to DDR3A_WE_n +set_location_assignment PIN_G17 -to DDR3B_A[0] +set_location_assignment PIN_F17 -to DDR3B_A[1] +set_location_assignment PIN_N17 -to DDR3B_A[2] +set_location_assignment PIN_F19 -to DDR3B_A[3] +set_location_assignment PIN_N19 -to DDR3B_A[4] +set_location_assignment PIN_H16 -to DDR3B_A[5] +set_location_assignment PIN_M17 -to DDR3B_A[6] +set_location_assignment PIN_T18 -to DDR3B_A[7] +set_location_assignment PIN_H17 -to DDR3B_A[8] +set_location_assignment PIN_J19 -to DDR3B_A[9] +set_location_assignment PIN_C19 -to DDR3B_A[10] +set_location_assignment PIN_R18 -to DDR3B_A[11] +set_location_assignment PIN_K18 -to DDR3B_A[12] +set_location_assignment PIN_E18 -to DDR3B_A[13] +set_location_assignment PIN_T19 -to DDR3B_A[14] +set_location_assignment PIN_R19 -to DDR3B_A[15] +set_location_assignment PIN_C18 -to DDR3B_BA[0] +set_location_assignment PIN_G19 -to DDR3B_BA[1] +set_location_assignment PIN_M20 -to DDR3B_BA[2] +set_location_assignment PIN_A17 -to DDR3B_CAS_n +set_location_assignment PIN_B16 -to DDR3B_CK[0] +set_location_assignment PIN_E17 -to DDR3B_CK[1] +set_location_assignment PIN_P17 -to DDR3B_CKE[0] +set_location_assignment PIN_V18 -to DDR3B_CKE[1] +set_location_assignment PIN_A16 -to DDR3B_CK_n[0] +set_location_assignment PIN_D17 -to DDR3B_CK_n[1] +set_location_assignment PIN_B19 -to DDR3B_CS_n[0] +set_location_assignment PIN_B17 -to DDR3B_CS_n[1] +set_location_assignment PIN_R15 -to DDR3B_DM[0] +set_location_assignment PIN_K15 -to DDR3B_DM[1] +set_location_assignment PIN_V12 -to DDR3B_DM[2] +set_location_assignment PIN_G10 -to DDR3B_DM[3] +set_location_assignment PIN_T12 -to DDR3B_DM[4] +set_location_assignment PIN_C16 -to DDR3B_DM[5] +set_location_assignment PIN_H15 -to DDR3B_DM[6] +set_location_assignment PIN_B11 -to DDR3B_DM[7] +set_location_assignment PIN_Y17 -to DDR3B_DQ[0] +set_location_assignment PIN_W17 -to DDR3B_DQ[1] +set_location_assignment PIN_V15 -to DDR3B_DQ[2] +set_location_assignment PIN_T15 -to DDR3B_DQ[3] +set_location_assignment PIN_V13 -to DDR3B_DQ[4] +set_location_assignment PIN_V16 -to DDR3B_DQ[5] +set_location_assignment PIN_W14 -to DDR3B_DQ[6] +set_location_assignment PIN_U15 -to DDR3B_DQ[7] +set_location_assignment PIN_T17 -to DDR3B_DQ[8] +set_location_assignment PIN_T16 -to DDR3B_DQ[9] +set_location_assignment PIN_R16 -to DDR3B_DQ[10] +set_location_assignment PIN_P16 -to DDR3B_DQ[11] +set_location_assignment PIN_N16 -to DDR3B_DQ[12] +set_location_assignment PIN_M15 -to DDR3B_DQ[13] +set_location_assignment PIN_M14 -to DDR3B_DQ[14] +set_location_assignment PIN_L14 -to DDR3B_DQ[15] +set_location_assignment PIN_T14 -to DDR3B_DQ[16] +set_location_assignment PIN_U14 -to DDR3B_DQ[17] +set_location_assignment PIN_U11 -to DDR3B_DQ[18] +set_location_assignment PIN_T13 -to DDR3B_DQ[19] +set_location_assignment PIN_U12 -to DDR3B_DQ[20] +set_location_assignment PIN_R13 -to DDR3B_DQ[21] +set_location_assignment PIN_P13 -to DDR3B_DQ[22] +set_location_assignment PIN_N13 -to DDR3B_DQ[23] +set_location_assignment PIN_K12 -to DDR3B_DQ[24] +set_location_assignment PIN_J12 -to DDR3B_DQ[25] +set_location_assignment PIN_J10 -to DDR3B_DQ[26] +set_location_assignment PIN_H12 -to DDR3B_DQ[27] +set_location_assignment PIN_N11 -to DDR3B_DQ[28] +set_location_assignment PIN_M11 -to DDR3B_DQ[29] +set_location_assignment PIN_H10 -to DDR3B_DQ[30] +set_location_assignment PIN_H11 -to DDR3B_DQ[31] +set_location_assignment PIN_T10 -to DDR3B_DQ[32] +set_location_assignment PIN_R10 -to DDR3B_DQ[33] +set_location_assignment PIN_M12 -to DDR3B_DQ[34] +set_location_assignment PIN_L12 -to DDR3B_DQ[35] +set_location_assignment PIN_V10 -to DDR3B_DQ[36] +set_location_assignment PIN_V9 -to DDR3B_DQ[37] +set_location_assignment PIN_R12 -to DDR3B_DQ[38] +set_location_assignment PIN_P12 -to DDR3B_DQ[39] +set_location_assignment PIN_D14 -to DDR3B_DQ[40] +set_location_assignment PIN_C13 -to DDR3B_DQ[41] +set_location_assignment PIN_B14 -to DDR3B_DQ[42] +set_location_assignment PIN_B13 -to DDR3B_DQ[43] +set_location_assignment PIN_E14 -to DDR3B_DQ[44] +set_location_assignment PIN_F14 -to DDR3B_DQ[45] +set_location_assignment PIN_A14 -to DDR3B_DQ[46] +set_location_assignment PIN_A13 -to DDR3B_DQ[47] +set_location_assignment PIN_K13 -to DDR3B_DQ[48] +set_location_assignment PIN_K16 -to DDR3B_DQ[49] +set_location_assignment PIN_H13 -to DDR3B_DQ[50] +set_location_assignment PIN_H14 -to DDR3B_DQ[51] +set_location_assignment PIN_J13 -to DDR3B_DQ[52] +set_location_assignment PIN_J16 -to DDR3B_DQ[53] +set_location_assignment PIN_G13 -to DDR3B_DQ[54] +set_location_assignment PIN_F13 -to DDR3B_DQ[55] +set_location_assignment PIN_D11 -to DDR3B_DQ[56] +set_location_assignment PIN_C10 -to DDR3B_DQ[57] +set_location_assignment PIN_A10 -to DDR3B_DQ[58] +set_location_assignment PIN_B10 -to DDR3B_DQ[59] +set_location_assignment PIN_G11 -to DDR3B_DQ[60] +set_location_assignment PIN_F11 -to DDR3B_DQ[61] +set_location_assignment PIN_E11 -to DDR3B_DQ[62] +set_location_assignment PIN_E12 -to DDR3B_DQ[63] +set_location_assignment PIN_Y16 -to DDR3B_DQS[0] +set_location_assignment PIN_V17 -to DDR3B_DQS[1] +set_location_assignment PIN_P14 -to DDR3B_DQS[2] +set_location_assignment PIN_K11 -to DDR3B_DQS[3] +set_location_assignment PIN_U9 -to DDR3B_DQS[4] +set_location_assignment PIN_E15 -to DDR3B_DQS[5] +set_location_assignment PIN_L15 -to DDR3B_DQS[6] +set_location_assignment PIN_D12 -to DDR3B_DQS[7] +set_location_assignment PIN_W16 -to DDR3B_DQS_n[0] +set_location_assignment PIN_U17 -to DDR3B_DQS_n[1] +set_location_assignment PIN_N14 -to DDR3B_DQS_n[2] +set_location_assignment PIN_L11 -to DDR3B_DQS_n[3] +set_location_assignment PIN_T9 -to DDR3B_DQS_n[4] +set_location_assignment PIN_D15 -to DDR3B_DQS_n[5] +set_location_assignment PIN_K14 -to DDR3B_DQS_n[6] +set_location_assignment PIN_C12 -to DDR3B_DQS_n[7] +set_location_assignment PIN_K17 -to DDR3B_EVENT_n +set_location_assignment PIN_M18 -to DDR3B_ODT[0] +set_location_assignment PIN_A19 -to DDR3B_ODT[1] +set_location_assignment PIN_H19 -to DDR3B_RAS_n +set_location_assignment PIN_T20 -to DDR3B_RESET_n +set_location_assignment PIN_P18 -to DDR3B_SCL +set_location_assignment PIN_P19 -to DDR3B_SDA +set_location_assignment PIN_D18 -to DDR3B_WE_n +set_location_assignment PIN_AR32 -to FAN_CTRL +set_location_assignment PIN_AK29 -to FLASH_ADV_n +set_location_assignment PIN_AE27 -to FLASH_CE_n[0] +set_location_assignment PIN_BA31 -to FLASH_CE_n[1] +set_location_assignment PIN_AL29 -to FLASH_CLK +set_location_assignment PIN_AY30 -to FLASH_OE_n +set_location_assignment PIN_BA29 -to FLASH_RDY_BSY_n[0] +set_location_assignment PIN_BB32 -to FLASH_RDY_BSY_n[1] +set_location_assignment PIN_AE28 -to FLASH_RESET_n +set_location_assignment PIN_AR31 -to FLASH_WE_n +set_location_assignment PIN_AU32 -to FSM_A[0] +set_location_assignment PIN_AH30 -to FSM_A[1] +set_location_assignment PIN_AJ30 -to FSM_A[2] +set_location_assignment PIN_AH31 -to FSM_A[3] +set_location_assignment PIN_AK30 -to FSM_A[4] +set_location_assignment PIN_AJ32 -to FSM_A[5] +set_location_assignment PIN_AG33 -to FSM_A[6] +set_location_assignment PIN_AL30 -to FSM_A[7] +set_location_assignment PIN_AK33 -to FSM_A[8] +set_location_assignment PIN_AJ33 -to FSM_A[9] +set_location_assignment PIN_AN30 -to FSM_A[10] +set_location_assignment PIN_AH33 -to FSM_A[11] +set_location_assignment PIN_AK32 -to FSM_A[12] +set_location_assignment PIN_AM32 -to FSM_A[13] +set_location_assignment PIN_AM31 -to FSM_A[14] +set_location_assignment PIN_AL31 -to FSM_A[15] +set_location_assignment PIN_AN33 -to FSM_A[16] +set_location_assignment PIN_AP33 -to FSM_A[17] +set_location_assignment PIN_AT32 -to FSM_A[18] +set_location_assignment PIN_AT29 -to FSM_A[19] +set_location_assignment PIN_AP31 -to FSM_A[20] +set_location_assignment PIN_AR30 -to FSM_A[21] +set_location_assignment PIN_AU30 -to FSM_A[22] +set_location_assignment PIN_AJ31 -to FSM_A[23] +set_location_assignment PIN_AP30 -to FSM_A[24] +set_location_assignment PIN_AN31 -to FSM_A[25] +set_location_assignment PIN_AT30 -to FSM_A[26] +set_location_assignment PIN_AG26 -to FSM_D[0] +set_location_assignment PIN_AD33 -to FSM_D[1] +set_location_assignment PIN_AE34 -to FSM_D[2] +set_location_assignment PIN_AF31 -to FSM_D[3] +set_location_assignment PIN_AG28 -to FSM_D[4] +set_location_assignment PIN_AG30 -to FSM_D[5] +set_location_assignment PIN_AF29 -to FSM_D[6] +set_location_assignment PIN_AE29 -to FSM_D[7] +set_location_assignment PIN_AG25 -to FSM_D[8] +set_location_assignment PIN_AF34 -to FSM_D[9] +set_location_assignment PIN_AE33 -to FSM_D[10] +set_location_assignment PIN_AE31 -to FSM_D[11] +set_location_assignment PIN_AF28 -to FSM_D[12] +set_location_assignment PIN_AE30 -to FSM_D[13] +set_location_assignment PIN_AG29 -to FSM_D[14] +set_location_assignment PIN_AG27 -to FSM_D[15] +set_location_assignment PIN_AP28 -to FSM_D[16] +set_location_assignment PIN_AN28 -to FSM_D[17] +set_location_assignment PIN_AU31 -to FSM_D[18] +set_location_assignment PIN_AW32 -to FSM_D[19] +set_location_assignment PIN_BD32 -to FSM_D[20] +set_location_assignment PIN_AY31 -to FSM_D[21] +set_location_assignment PIN_BA30 -to FSM_D[22] +set_location_assignment PIN_BB30 -to FSM_D[23] +set_location_assignment PIN_AM29 -to FSM_D[24] +set_location_assignment PIN_AR29 -to FSM_D[25] +set_location_assignment PIN_AV31 -to FSM_D[26] +set_location_assignment PIN_AV32 -to FSM_D[27] +set_location_assignment PIN_BC31 -to FSM_D[28] +set_location_assignment PIN_AW30 -to FSM_D[29] +set_location_assignment PIN_BC32 -to FSM_D[30] +set_location_assignment PIN_BD31 -to FSM_D[31] +set_location_assignment PIN_G8 -to HEX0_D[0] +set_location_assignment PIN_H8 -to HEX0_D[1] +set_location_assignment PIN_J9 -to HEX0_D[2] +set_location_assignment PIN_K10 -to HEX0_D[3] +set_location_assignment PIN_K8 -to HEX0_D[4] +set_location_assignment PIN_K9 -to HEX0_D[5] +set_location_assignment PIN_N8 -to HEX0_D[6] +set_location_assignment PIN_P8 -to HEX0_DP +set_location_assignment PIN_H18 -to HEX1_D[0] +set_location_assignment PIN_G16 -to HEX1_D[1] +set_location_assignment PIN_F16 -to HEX1_D[2] +set_location_assignment PIN_A7 -to HEX1_D[3] +set_location_assignment PIN_B7 -to HEX1_D[4] +set_location_assignment PIN_C9 -to HEX1_D[5] +set_location_assignment PIN_D10 -to HEX1_D[6] +set_location_assignment PIN_E9 -to HEX1_DP +set_location_assignment PIN_AW37 -to LED[0] +set_location_assignment PIN_AV37 -to LED[1] +set_location_assignment PIN_BB36 -to LED[2] +set_location_assignment PIN_BB39 -to LED[3] +set_location_assignment PIN_AH15 -to LED_BRACKET[0] +set_location_assignment PIN_AH13 -to LED_BRACKET[1] +set_location_assignment PIN_AJ13 -to LED_BRACKET[2] +set_location_assignment PIN_AJ14 -to LED_BRACKET[3] +set_location_assignment PIN_AG15 -to LED_RJ45_L +set_location_assignment PIN_AG16 -to LED_RJ45_R +set_location_assignment PIN_AW35 -to OSC_50_B3B +set_location_assignment PIN_BC28 -to OSC_50_B3D +set_location_assignment PIN_AP10 -to OSC_50_B4A +set_location_assignment PIN_AY18 -to OSC_50_B4D +set_location_assignment PIN_M8 -to OSC_50_B7A +set_location_assignment PIN_J18 -to OSC_50_B7D +set_location_assignment PIN_R36 -to OSC_50_B8A +set_location_assignment PIN_R25 -to OSC_50_B8D +set_location_assignment PIN_AU33 -to PCIE_PERST_n +set_location_assignment PIN_AK38 -to PCIE_REFCLK_p +set_location_assignment PIN_BB43 -to PCIE_RX_p[0] +set_location_assignment PIN_BA41 -to PCIE_RX_p[1] +set_location_assignment PIN_AW41 -to PCIE_RX_p[2] +set_location_assignment PIN_AY43 -to PCIE_RX_p[3] +set_location_assignment PIN_AT43 -to PCIE_RX_p[4] +set_location_assignment PIN_AP43 -to PCIE_RX_p[5] +set_location_assignment PIN_AM43 -to PCIE_RX_p[6] +set_location_assignment PIN_AK43 -to PCIE_RX_p[7] +set_location_assignment PIN_BD34 -to PCIE_SMBCLK +set_location_assignment PIN_AT33 -to PCIE_SMBDAT +set_location_assignment PIN_AY39 -to PCIE_TX_p[0] +set_location_assignment PIN_AV39 -to PCIE_TX_p[1] +set_location_assignment PIN_AT39 -to PCIE_TX_p[2] +set_location_assignment PIN_AU41 -to PCIE_TX_p[3] +set_location_assignment PIN_AN41 -to PCIE_TX_p[4] +set_location_assignment PIN_AL41 -to PCIE_TX_p[5] +set_location_assignment PIN_AJ41 -to PCIE_TX_p[6] +set_location_assignment PIN_AG41 -to PCIE_TX_p[7] +set_location_assignment PIN_BD35 -to PCIE_WAKE_n +set_location_assignment PIN_AU29 -to QDRIIA_A[0] +set_location_assignment PIN_BA28 -to QDRIIA_A[1] +set_location_assignment PIN_AP27 -to QDRIIA_A[2] +set_location_assignment PIN_AK27 -to QDRIIA_A[3] +set_location_assignment PIN_AN27 -to QDRIIA_A[4] +set_location_assignment PIN_AM28 -to QDRIIA_A[5] +set_location_assignment PIN_AV28 -to QDRIIA_A[6] +set_location_assignment PIN_AY27 -to QDRIIA_A[7] +set_location_assignment PIN_BC29 -to QDRIIA_A[8] +set_location_assignment PIN_AU28 -to QDRIIA_A[9] +set_location_assignment PIN_AW27 -to QDRIIA_A[10] +set_location_assignment PIN_AY28 -to QDRIIA_A[11] +set_location_assignment PIN_BD28 -to QDRIIA_A[12] +set_location_assignment PIN_AV29 -to QDRIIA_A[13] +set_location_assignment PIN_AW29 -to QDRIIA_A[14] +set_location_assignment PIN_BB29 -to QDRIIA_A[15] +set_location_assignment PIN_BD29 -to QDRIIA_A[16] +set_location_assignment PIN_AL27 -to QDRIIA_A[17] +set_location_assignment PIN_AR27 -to QDRIIA_A[18] +set_location_assignment PIN_AL28 -to QDRIIA_A[19] +set_location_assignment PIN_AR28 -to QDRIIA_A[20] +set_location_assignment PIN_AJ24 -to QDRIIA_BWS_n[0] +set_location_assignment PIN_AT27 -to QDRIIA_BWS_n[1] +set_location_assignment PIN_BA25 -to QDRIIA_CQ_n +set_location_assignment PIN_AH22 -to QDRIIA_CQ_p +set_location_assignment PIN_AH28 -to QDRIIA_D[0] +set_location_assignment PIN_AH27 -to QDRIIA_D[1] +set_location_assignment PIN_AH25 -to QDRIIA_D[2] +set_location_assignment PIN_AJ28 -to QDRIIA_D[3] +set_location_assignment PIN_AJ27 -to QDRIIA_D[4] +set_location_assignment PIN_AJ26 -to QDRIIA_D[5] +set_location_assignment PIN_AJ25 -to QDRIIA_D[6] +set_location_assignment PIN_AL25 -to QDRIIA_D[7] +set_location_assignment PIN_AH24 -to QDRIIA_D[8] +set_location_assignment PIN_AN25 -to QDRIIA_D[9] +set_location_assignment PIN_AM26 -to QDRIIA_D[10] +set_location_assignment PIN_AM25 -to QDRIIA_D[11] +set_location_assignment PIN_AL26 -to QDRIIA_D[12] +set_location_assignment PIN_AK26 -to QDRIIA_D[13] +set_location_assignment PIN_AU27 -to QDRIIA_D[14] +set_location_assignment PIN_AU26 -to QDRIIA_D[15] +set_location_assignment PIN_AV26 -to QDRIIA_D[16] +set_location_assignment PIN_AW26 -to QDRIIA_D[17] +set_location_assignment PIN_AR23 -to QDRIIA_DOFF_n +set_location_assignment PIN_AR26 -to QDRIIA_K_n +set_location_assignment PIN_AP25 -to QDRIIA_K_p +set_location_assignment PIN_AN23 -to QDRIIA_ODT +set_location_assignment PIN_AK23 -to QDRIIA_Q[0] +set_location_assignment PIN_BB26 -to QDRIIA_Q[1] +set_location_assignment PIN_BD26 -to QDRIIA_Q[2] +set_location_assignment PIN_BA24 -to QDRIIA_Q[3] +set_location_assignment PIN_AL23 -to QDRIIA_Q[4] +set_location_assignment PIN_AJ23 -to QDRIIA_Q[5] +set_location_assignment PIN_AL21 -to QDRIIA_Q[6] +set_location_assignment PIN_AK21 -to QDRIIA_Q[7] +set_location_assignment PIN_AJ22 -to QDRIIA_Q[8] +set_location_assignment PIN_AW24 -to QDRIIA_Q[9] +set_location_assignment PIN_BC26 -to QDRIIA_Q[10] +set_location_assignment PIN_AY25 -to QDRIIA_Q[11] +set_location_assignment PIN_AU24 -to QDRIIA_Q[12] +set_location_assignment PIN_AV25 -to QDRIIA_Q[13] +set_location_assignment PIN_AU25 -to QDRIIA_Q[14] +set_location_assignment PIN_AR25 -to QDRIIA_Q[15] +set_location_assignment PIN_AP24 -to QDRIIA_Q[16] +set_location_assignment PIN_AL24 -to QDRIIA_Q[17] +set_location_assignment PIN_AM23 -to QDRIIA_QVLD +set_location_assignment PIN_AT26 -to QDRIIA_RPS_n +set_location_assignment PIN_AK24 -to QDRIIA_WPS_n +set_location_assignment PIN_AR24 -to QDRIIB_A[0] +set_location_assignment PIN_BB23 -to QDRIIB_A[1] +set_location_assignment PIN_AK20 -to QDRIIB_A[2] +set_location_assignment PIN_AJ19 -to QDRIIB_A[3] +set_location_assignment PIN_AL20 -to QDRIIB_A[4] +set_location_assignment PIN_AG19 -to QDRIIB_A[5] +set_location_assignment PIN_AT23 -to QDRIIB_A[6] +set_location_assignment PIN_AU23 -to QDRIIB_A[7] +set_location_assignment PIN_AV23 -to QDRIIB_A[8] +set_location_assignment PIN_AM22 -to QDRIIB_A[9] +set_location_assignment PIN_AJ20 -to QDRIIB_A[10] +set_location_assignment PIN_AG20 -to QDRIIB_A[11] +set_location_assignment PIN_AW23 -to QDRIIB_A[12] +set_location_assignment PIN_BB24 -to QDRIIB_A[13] +set_location_assignment PIN_AY24 -to QDRIIB_A[14] +set_location_assignment PIN_BD23 -to QDRIIB_A[15] +set_location_assignment PIN_BC23 -to QDRIIB_A[16] +set_location_assignment PIN_AG21 -to QDRIIB_A[17] +set_location_assignment PIN_AM20 -to QDRIIB_A[18] +set_location_assignment PIN_AK18 -to QDRIIB_A[19] +set_location_assignment PIN_AN22 -to QDRIIB_A[20] +set_location_assignment PIN_AV20 -to QDRIIB_BWS_n[0] +set_location_assignment PIN_AU21 -to QDRIIB_BWS_n[1] +set_location_assignment PIN_AP18 -to QDRIIB_CQ_n +set_location_assignment PIN_AJ15 -to QDRIIB_CQ_p +set_location_assignment PIN_BB21 -to QDRIIB_D[0] +set_location_assignment PIN_BD20 -to QDRIIB_D[1] +set_location_assignment PIN_BC20 -to QDRIIB_D[2] +set_location_assignment PIN_AR22 -to QDRIIB_D[3] +set_location_assignment PIN_BB20 -to QDRIIB_D[4] +set_location_assignment PIN_AU22 -to QDRIIB_D[5] +set_location_assignment PIN_BA21 -to QDRIIB_D[6] +set_location_assignment PIN_AY21 -to QDRIIB_D[7] +set_location_assignment PIN_AW21 -to QDRIIB_D[8] +set_location_assignment PIN_AT21 -to QDRIIB_D[9] +set_location_assignment PIN_AR21 -to QDRIIB_D[10] +set_location_assignment PIN_AP21 -to QDRIIB_D[11] +set_location_assignment PIN_BD22 -to QDRIIB_D[12] +set_location_assignment PIN_BC22 -to QDRIIB_D[13] +set_location_assignment PIN_BA22 -to QDRIIB_D[14] +set_location_assignment PIN_AV22 -to QDRIIB_D[15] +set_location_assignment PIN_AY22 -to QDRIIB_D[16] +set_location_assignment PIN_AW22 -to QDRIIB_D[17] +set_location_assignment PIN_AH19 -to QDRIIB_DOFF_n +set_location_assignment PIN_AT20 -to QDRIIB_K_n +set_location_assignment PIN_AR20 -to QDRIIB_K_p +set_location_assignment PIN_AH18 -to QDRIIB_ODT +set_location_assignment PIN_AR19 -to QDRIIB_Q[0] +set_location_assignment PIN_AM19 -to QDRIIB_Q[1] +set_location_assignment PIN_AL19 -to QDRIIB_Q[2] +set_location_assignment PIN_AM17 -to QDRIIB_Q[3] +set_location_assignment PIN_AL18 -to QDRIIB_Q[4] +set_location_assignment PIN_AN19 -to QDRIIB_Q[5] +set_location_assignment PIN_AU18 -to QDRIIB_Q[6] +set_location_assignment PIN_AK17 -to QDRIIB_Q[7] +set_location_assignment PIN_AL17 -to QDRIIB_Q[8] +set_location_assignment PIN_AG17 -to QDRIIB_Q[9] +set_location_assignment PIN_AJ18 -to QDRIIB_Q[10] +set_location_assignment PIN_AJ17 -to QDRIIB_Q[11] +set_location_assignment PIN_AG18 -to QDRIIB_Q[12] +set_location_assignment PIN_AU19 -to QDRIIB_Q[13] +set_location_assignment PIN_AW19 -to QDRIIB_Q[14] +set_location_assignment PIN_AV19 -to QDRIIB_Q[15] +set_location_assignment PIN_AP19 -to QDRIIB_Q[16] +set_location_assignment PIN_AN20 -to QDRIIB_Q[17] +set_location_assignment PIN_AJ16 -to QDRIIB_QVLD +set_location_assignment PIN_AW20 -to QDRIIB_RPS_n +set_location_assignment PIN_AU20 -to QDRIIB_WPS_n +set_location_assignment PIN_AV16 -to QDRIIC_A[0] +set_location_assignment PIN_AW16 -to QDRIIC_A[1] +set_location_assignment PIN_AP16 -to QDRIIC_A[2] +set_location_assignment PIN_AW9 -to QDRIIC_A[3] +set_location_assignment PIN_BD7 -to QDRIIC_A[4] +set_location_assignment PIN_BC7 -to QDRIIC_A[5] +set_location_assignment PIN_AR17 -to QDRIIC_A[6] +set_location_assignment PIN_AR18 -to QDRIIC_A[7] +set_location_assignment PIN_AT17 -to QDRIIC_A[8] +set_location_assignment PIN_BB9 -to QDRIIC_A[9] +set_location_assignment PIN_AH21 -to QDRIIC_A[10] +set_location_assignment PIN_AU17 -to QDRIIC_A[11] +set_location_assignment PIN_AU16 -to QDRIIC_A[12] +set_location_assignment PIN_BB8 -to QDRIIC_A[13] +set_location_assignment PIN_AT18 -to QDRIIC_A[14] +set_location_assignment PIN_AW17 -to QDRIIC_A[15] +set_location_assignment PIN_AV17 -to QDRIIC_A[16] +set_location_assignment PIN_AU8 -to QDRIIC_A[17] +set_location_assignment PIN_AT9 -to QDRIIC_A[18] +set_location_assignment PIN_AV8 -to QDRIIC_A[19] +set_location_assignment PIN_AN17 -to QDRIIC_A[20] +set_location_assignment PIN_AJ11 -to QDRIIC_BWS_n[0] +set_location_assignment PIN_AJ10 -to QDRIIC_BWS_n[1] +set_location_assignment PIN_AF13 -to QDRIIC_CQ_n +set_location_assignment PIN_BC11 -to QDRIIC_CQ_p +set_location_assignment PIN_AG9 -to QDRIIC_D[0] +set_location_assignment PIN_AG10 -to QDRIIC_D[1] +set_location_assignment PIN_AG12 -to QDRIIC_D[2] +set_location_assignment PIN_AG11 -to QDRIIC_D[3] +set_location_assignment PIN_AV10 -to QDRIIC_D[4] +set_location_assignment PIN_AH12 -to QDRIIC_D[5] +set_location_assignment PIN_AK12 -to QDRIIC_D[6] +set_location_assignment PIN_AL12 -to QDRIIC_D[7] +set_location_assignment PIN_AJ12 -to QDRIIC_D[8] +set_location_assignment PIN_AN12 -to QDRIIC_D[9] +set_location_assignment PIN_AM13 -to QDRIIC_D[10] +set_location_assignment PIN_AR12 -to QDRIIC_D[11] +set_location_assignment PIN_AR13 -to QDRIIC_D[12] +set_location_assignment PIN_AU9 -to QDRIIC_D[13] +set_location_assignment PIN_AU10 -to QDRIIC_D[14] +set_location_assignment PIN_AU11 -to QDRIIC_D[15] +set_location_assignment PIN_AV11 -to QDRIIC_D[16] +set_location_assignment PIN_AT12 -to QDRIIC_D[17] +set_location_assignment PIN_AE14 -to QDRIIC_DOFF_n +set_location_assignment PIN_AP13 -to QDRIIC_K_n +set_location_assignment PIN_AP12 -to QDRIIC_K_p +set_location_assignment PIN_BD10 -to QDRIIC_ODT +set_location_assignment PIN_BA12 -to QDRIIC_Q[0] +set_location_assignment PIN_AF14 -to QDRIIC_Q[1] +set_location_assignment PIN_AE13 -to QDRIIC_Q[2] +set_location_assignment PIN_AD14 -to QDRIIC_Q[3] +set_location_assignment PIN_AE12 -to QDRIIC_Q[4] +set_location_assignment PIN_AF11 -to QDRIIC_Q[5] +set_location_assignment PIN_AE11 -to QDRIIC_Q[6] +set_location_assignment PIN_AE10 -to QDRIIC_Q[7] +set_location_assignment PIN_AE9 -to QDRIIC_Q[8] +set_location_assignment PIN_BB11 -to QDRIIC_Q[9] +set_location_assignment PIN_AW11 -to QDRIIC_Q[10] +set_location_assignment PIN_AF10 -to QDRIIC_Q[11] +set_location_assignment PIN_AY12 -to QDRIIC_Q[12] +set_location_assignment PIN_AW10 -to QDRIIC_Q[13] +set_location_assignment PIN_AY10 -to QDRIIC_Q[14] +set_location_assignment PIN_BB12 -to QDRIIC_Q[15] +set_location_assignment PIN_BC10 -to QDRIIC_Q[16] +set_location_assignment PIN_BA10 -to QDRIIC_Q[17] +set_location_assignment PIN_BD11 -to QDRIIC_QVLD +set_location_assignment PIN_AH10 -to QDRIIC_RPS_n +set_location_assignment PIN_AL11 -to QDRIIC_WPS_n +set_location_assignment PIN_N26 -to QDRIID_A[0] +set_location_assignment PIN_P28 -to QDRIID_A[1] +set_location_assignment PIN_N28 -to QDRIID_A[2] +set_location_assignment PIN_L26 -to QDRIID_A[3] +set_location_assignment PIN_K27 -to QDRIID_A[4] +set_location_assignment PIN_L27 -to QDRIID_A[5] +set_location_assignment PIN_U26 -to QDRIID_A[6] +set_location_assignment PIN_T26 -to QDRIID_A[7] +set_location_assignment PIN_T27 -to QDRIID_A[8] +set_location_assignment PIN_V27 -to QDRIID_A[9] +set_location_assignment PIN_U27 -to QDRIID_A[10] +set_location_assignment PIN_R27 -to QDRIID_A[11] +set_location_assignment PIN_P27 -to QDRIID_A[12] +set_location_assignment PIN_V25 -to QDRIID_A[13] +set_location_assignment PIN_V26 -to QDRIID_A[14] +set_location_assignment PIN_T25 -to QDRIID_A[15] +set_location_assignment PIN_P26 -to QDRIID_A[16] +set_location_assignment PIN_M27 -to QDRIID_A[17] +set_location_assignment PIN_M28 -to QDRIID_A[18] +set_location_assignment PIN_P29 -to QDRIID_A[19] +set_location_assignment PIN_D29 -to QDRIID_A[20] +set_location_assignment PIN_E26 -to QDRIID_BWS_n[0] +set_location_assignment PIN_K26 -to QDRIID_BWS_n[1] +set_location_assignment PIN_H27 -to QDRIID_CQ_n +set_location_assignment PIN_E29 -to QDRIID_CQ_p +set_location_assignment PIN_H25 -to QDRIID_D[0] +set_location_assignment PIN_H24 -to QDRIID_D[1] +set_location_assignment PIN_H23 -to QDRIID_D[2] +set_location_assignment PIN_J25 -to QDRIID_D[3] +set_location_assignment PIN_J24 -to QDRIID_D[4] +set_location_assignment PIN_K25 -to QDRIID_D[5] +set_location_assignment PIN_D26 -to QDRIID_D[6] +set_location_assignment PIN_F25 -to QDRIID_D[7] +set_location_assignment PIN_G25 -to QDRIID_D[8] +set_location_assignment PIN_N23 -to QDRIID_D[9] +set_location_assignment PIN_P24 -to QDRIID_D[10] +set_location_assignment PIN_P23 -to QDRIID_D[11] +set_location_assignment PIN_L24 -to QDRIID_D[12] +set_location_assignment PIN_R24 -to QDRIID_D[13] +set_location_assignment PIN_U23 -to QDRIID_D[14] +set_location_assignment PIN_U24 -to QDRIID_D[15] +set_location_assignment PIN_T24 -to QDRIID_D[16] +set_location_assignment PIN_T23 -to QDRIID_D[17] +set_location_assignment PIN_E27 -to QDRIID_DOFF_n +set_location_assignment PIN_K24 -to QDRIID_K_n +set_location_assignment PIN_L23 -to QDRIID_K_p +set_location_assignment PIN_H26 -to QDRIID_ODT +set_location_assignment PIN_C27 -to QDRIID_Q[0] +set_location_assignment PIN_A26 -to QDRIID_Q[1] +set_location_assignment PIN_B26 -to QDRIID_Q[2] +set_location_assignment PIN_F26 -to QDRIID_Q[3] +set_location_assignment PIN_G26 -to QDRIID_Q[4] +set_location_assignment PIN_C28 -to QDRIID_Q[5] +set_location_assignment PIN_A29 -to QDRIID_Q[6] +set_location_assignment PIN_A28 -to QDRIID_Q[7] +set_location_assignment PIN_B28 -to QDRIID_Q[8] +set_location_assignment PIN_G28 -to QDRIID_Q[9] +set_location_assignment PIN_F28 -to QDRIID_Q[10] +set_location_assignment PIN_D27 -to QDRIID_Q[11] +set_location_assignment PIN_G29 -to QDRIID_Q[12] +set_location_assignment PIN_F29 -to QDRIID_Q[13] +set_location_assignment PIN_H28 -to QDRIID_Q[14] +set_location_assignment PIN_K28 -to QDRIID_Q[15] +set_location_assignment PIN_J28 -to QDRIID_Q[16] +set_location_assignment PIN_H29 -to QDRIID_Q[17] +set_location_assignment PIN_J27 -to QDRIID_QVLD +set_location_assignment PIN_F24 -to QDRIID_RPS_n +set_location_assignment PIN_M23 -to QDRIID_WPS_n +set_location_assignment PIN_AG14 -to RS422_DE +set_location_assignment PIN_AE18 -to RS422_DIN +set_location_assignment PIN_AE17 -to RS422_DOUT +set_location_assignment PIN_AF17 -to RS422_RE_n +set_location_assignment PIN_AF16 -to RS422_TE +set_location_assignment PIN_BA36 -to RZQ_0 +set_location_assignment PIN_AR8 -to RZQ_1 +set_location_assignment PIN_H9 -to RZQ_4 +set_location_assignment PIN_P35 -to RZQ_5 +set_location_assignment PIN_AH6 -to SFP1G_REFCLK_p +set_location_assignment PIN_F22 -to SFPA_LOS +set_location_assignment PIN_E21 -to SFPA_MOD0_PRSNT_n +set_location_assignment PIN_B20 -to SFPA_MOD1_SCL +set_location_assignment PIN_A20 -to SFPA_MOD2_SDA +set_location_assignment PIN_E20 -to SFPA_RATESEL[0] +set_location_assignment PIN_G22 -to SFPA_RATESEL[1] +set_location_assignment PIN_AK2 -to SFPA_RX_p +set_location_assignment PIN_B22 -to SFPA_TXDISABLE +set_location_assignment PIN_A22 -to SFPA_TXFAULT +set_location_assignment PIN_AG4 -to SFPA_TX_p +set_location_assignment PIN_R22 -to SFPB_LOS +set_location_assignment PIN_K22 -to SFPB_MOD0_PRSNT_n +set_location_assignment PIN_K21 -to SFPB_MOD1_SCL +set_location_assignment PIN_K20 -to SFPB_MOD2_SDA +set_location_assignment PIN_R21 -to SFPB_RATESEL[0] +set_location_assignment PIN_T22 -to SFPB_RATESEL[1] +set_location_assignment PIN_AP2 -to SFPB_RX_p +set_location_assignment PIN_H22 -to SFPB_TXDISABLE +set_location_assignment PIN_H20 -to SFPB_TXFAULT +set_location_assignment PIN_AL4 -to SFPB_TX_p +set_location_assignment PIN_L21 -to SFPC_LOS +set_location_assignment PIN_J21 -to SFPC_MOD0_PRSNT_n +set_location_assignment PIN_H21 -to SFPC_MOD1_SCL +set_location_assignment PIN_G20 -to SFPC_MOD2_SDA +set_location_assignment PIN_J22 -to SFPC_RATESEL[0] +set_location_assignment PIN_P21 -to SFPC_RATESEL[1] +set_location_assignment PIN_AW4 -to SFPC_RX_p +set_location_assignment PIN_F21 -to SFPC_TXDISABLE +set_location_assignment PIN_F20 -to SFPC_TXFAULT +set_location_assignment PIN_AT6 -to SFPC_TX_p +set_location_assignment PIN_N22 -to SFPD_LOS +set_location_assignment PIN_V20 -to SFPD_MOD0_PRSNT_n +set_location_assignment PIN_U21 -to SFPD_MOD1_SCL +set_location_assignment PIN_V19 -to SFPD_MOD2_SDA +set_location_assignment PIN_V21 -to SFPD_RATESEL[0] +set_location_assignment PIN_M22 -to SFPD_RATESEL[1] +set_location_assignment PIN_BB2 -to SFPD_RX_p +set_location_assignment PIN_U20 -to SFPD_TXDISABLE +set_location_assignment PIN_T21 -to SFPD_TXFAULT +set_location_assignment PIN_AY6 -to SFPD_TX_p +set_location_assignment PIN_BB33 -to SMA_CLKIN +set_location_assignment PIN_B25 -to SW[0] +set_location_assignment PIN_A25 -to SW[1] +set_location_assignment PIN_B23 -to SW[2] +set_location_assignment PIN_A23 -to SW[3] +set_location_assignment PIN_D21 -to TEMP_CLK +set_location_assignment PIN_D20 -to TEMP_DATA +set_location_assignment PIN_C21 -to TEMP_INT_n +set_location_assignment PIN_C22 -to TEMP_OVERT_n +set_location_assignment PIN_AV34 -to SMA_CLKOUT +set_location_assignment PIN_V39 -to SATA_DEVICE_REFCLK_p +set_location_assignment PIN_V6 -to SATA_HOST_REFCLK_p +set_location_assignment PIN_V5 -to "SATA_HOST_REFCLK_p(n)" +set_location_assignment PIN_V40 -to "SATA_DEVICE_REFCLK_p(n)" +set_location_assignment PIN_K43 -to SATA_DEVICE_RX_p[0] +set_location_assignment PIN_H43 -to SATA_DEVICE_RX_p[1] +set_location_assignment PIN_K39 -to SATA_DEVICE_TX_p[0] +set_location_assignment PIN_H39 -to SATA_DEVICE_TX_p[1] +set_location_assignment PIN_K44 -to "SATA_DEVICE_RX_p[0](n)" +set_location_assignment PIN_H44 -to "SATA_DEVICE_RX_p[1](n)" +set_location_assignment PIN_K40 -to "SATA_DEVICE_TX_p[0](n)" +set_location_assignment PIN_H40 -to "SATA_DEVICE_TX_p[1](n)" +set_location_assignment PIN_K2 -to SATA_HOST_RX_p[0] +set_location_assignment PIN_K1 -to "SATA_HOST_RX_p[0](n)" +set_location_assignment PIN_H2 -to SATA_HOST_RX_p[1] +set_location_assignment PIN_H1 -to "SATA_HOST_RX_p[1](n)" +set_location_assignment PIN_K6 -to SATA_HOST_TX_p[0] +set_location_assignment PIN_K5 -to "SATA_HOST_TX_p[0](n)" +set_location_assignment PIN_H6 -to SATA_HOST_TX_p[1] +set_location_assignment PIN_H5 -to "SATA_HOST_TX_p[1](n)" +set_location_assignment PIN_AK7 -to SFP_REFCLK_p + +#============================================================ + + + +set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top +set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top +set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top +set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V" +set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 +set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 +set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW" +set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" + +set_location_assignment PIN_PIN -to NETName +set_location_assignment PIN_AK39 -to "PCIE_REFCLK_p(n)" +set_instance_assignment -name IO_STANDARD HCSL -to "PCIE_REFCLK_p(n)" +set_location_assignment PIN_BB44 -to "PCIE_RX_p[0](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_RX_p[0](n)" +set_location_assignment PIN_BA42 -to "PCIE_RX_p[1](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_RX_p[1](n)" +set_location_assignment PIN_AW42 -to "PCIE_RX_p[2](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_RX_p[2](n)" +set_location_assignment PIN_AY44 -to "PCIE_RX_p[3](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_RX_p[3](n)" +set_location_assignment PIN_AT44 -to "PCIE_RX_p[4](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_RX_p[4](n)" +set_location_assignment PIN_AP44 -to "PCIE_RX_p[5](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_RX_p[5](n)" +set_location_assignment PIN_AM44 -to "PCIE_RX_p[6](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_RX_p[6](n)" +set_location_assignment PIN_AK44 -to "PCIE_RX_p[7](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_RX_p[7](n)" +set_location_assignment PIN_AY40 -to "PCIE_TX_p[0](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_TX_p[0](n)" +set_location_assignment PIN_AV40 -to "PCIE_TX_p[1](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_TX_p[1](n)" +set_location_assignment PIN_AT40 -to "PCIE_TX_p[2](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_TX_p[2](n)" +set_location_assignment PIN_AU42 -to "PCIE_TX_p[3](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_TX_p[3](n)" +set_location_assignment PIN_AN42 -to "PCIE_TX_p[4](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_TX_p[4](n)" +set_location_assignment PIN_AL42 -to "PCIE_TX_p[5](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_TX_p[5](n)" +set_location_assignment PIN_AJ42 -to "PCIE_TX_p[6](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_TX_p[6](n)" +set_location_assignment PIN_AG42 -to "PCIE_TX_p[7](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "PCIE_TX_p[7](n)" +set_instance_assignment -name IO_STANDARD HCSL -to "SATA_DEVICE_REFCLK_p(n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SATA_DEVICE_RX_p[0](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SATA_DEVICE_RX_p[1](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SATA_DEVICE_TX_p[0](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SATA_DEVICE_TX_p[1](n)" +set_instance_assignment -name IO_STANDARD HCSL -to "SATA_HOST_REFCLK_p(n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SATA_HOST_RX_p[0](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SATA_HOST_RX_p[1](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SATA_HOST_TX_p[0](n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SATA_HOST_TX_p[1](n)" +set_location_assignment PIN_AH5 -to "SFP1G_REFCLK_p(n)" +set_instance_assignment -name IO_STANDARD HCSL -to "SFP1G_REFCLK_p(n)" +set_location_assignment PIN_AK1 -to "SFPA_RX_p(n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SFPA_RX_p(n)" +set_location_assignment PIN_AG3 -to "SFPA_TX_p(n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SFPA_TX_p(n)" +set_location_assignment PIN_AP1 -to "SFPB_RX_p(n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SFPB_RX_p(n)" +set_location_assignment PIN_AL3 -to "SFPB_TX_p(n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SFPB_TX_p(n)" +set_location_assignment PIN_AW3 -to "SFPC_RX_p(n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SFPC_RX_p(n)" +set_location_assignment PIN_AT5 -to "SFPC_TX_p(n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SFPC_TX_p(n)" +set_location_assignment PIN_BB1 -to "SFPD_RX_p(n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SFPD_RX_p(n)" +set_location_assignment PIN_AY5 -to "SFPD_TX_p(n)" +set_instance_assignment -name IO_STANDARD "1.4-V PCML" -to "SFPD_TX_p(n)" +set_location_assignment PIN_AK7 -to SFP_REFCLK_P +set_instance_assignment -name IO_STANDARD HCSL -to SFP_REFCLK_P +set_location_assignment PIN_AK6 -to "SFP_REFCLK_P(n)" +set_instance_assignment -name IO_STANDARD HCSL -to "SFP_REFCLK_P(n)" +set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top + diff --git a/fpga/lib/eth/example/DE5-Net/fpga/fpga.sdc b/fpga/lib/eth/example/DE5-Net/fpga/fpga.sdc new file mode 100644 index 000000000..7d6348f81 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/fpga.sdc @@ -0,0 +1,98 @@ + +#************************************************************** +# Create Clock +#************************************************************** + +create_clock -period 20 [get_ports OSC_50_B3B] +create_clock -period 20 [get_ports OSC_50_B3D] +create_clock -period 20 [get_ports OSC_50_B4A] +create_clock -period 20 [get_ports OSC_50_B4D] + +create_clock -period 20 [get_ports OSC_50_B7A] +create_clock -period 20 [get_ports OSC_50_B7D] +create_clock -period 20 [get_ports OSC_50_B8A] +create_clock -period 20 [get_ports OSC_50_B8D] + +create_clock -period 1.5515 [get_ports SFP_REFCLK_P] + + + + +#************************************************************** +# Create Generated Clock +#************************************************************** +derive_pll_clocks + + + + + + +#************************************************************** +# Set Clock Latency +#************************************************************** + + +#************************************************************** +# Set Clock Uncertainty +#************************************************************** +derive_clock_uncertainty + + + +#************************************************************** +# Set Input Delay +#************************************************************** + + + +#************************************************************** +# Set Output Delay +#************************************************************** + + + +#************************************************************** +# Set Clock Groups +#************************************************************** + + + +#************************************************************** +# Set False Path +#************************************************************** + + + +#************************************************************** +# Set Multicycle Path +#************************************************************** + + + +#************************************************************** +# Set Maximum Delay +#************************************************************** + + + +#************************************************************** +# Set Minimum Delay +#************************************************************** + + + +#************************************************************** +# Set Input Transition +#************************************************************** + + + +#************************************************************** +# Set Load +#************************************************************** + + + + + diff --git a/fpga/lib/eth/example/DE5-Net/fpga/fpga/Makefile b/fpga/lib/eth/example/DE5-Net/fpga/fpga/Makefile new file mode 100644 index 000000000..4f8808924 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/fpga/Makefile @@ -0,0 +1,60 @@ + +# FPGA settings +FPGA_TOP = fpga +FPGA_FAMILY = "Stratix V" +FPGA_DEVICE = 5SGXEA7N2F45C2 + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += rtl/i2c_master.v +SYN_FILES += rtl/si570_i2c_init.v +SYN_FILES += lib/eth/rtl/eth_mac_10g_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_10g.v +SYN_FILES += lib/eth/rtl/axis_xgmii_rx_64.v +SYN_FILES += lib/eth/rtl/axis_xgmii_tx_64.v +SYN_FILES += lib/eth/rtl/lfsr.v +SYN_FILES += lib/eth/rtl/eth_axis_rx_64.v +SYN_FILES += lib/eth/rtl/eth_axis_tx_64.v +SYN_FILES += lib/eth/rtl/udp_complete_64.v +SYN_FILES += lib/eth/rtl/udp_checksum_gen_64.v +SYN_FILES += lib/eth/rtl/udp_64.v +SYN_FILES += lib/eth/rtl/udp_ip_rx_64.v +SYN_FILES += lib/eth/rtl/udp_ip_tx_64.v +SYN_FILES += lib/eth/rtl/ip_complete_64.v +SYN_FILES += lib/eth/rtl/ip_64.v +SYN_FILES += lib/eth/rtl/ip_eth_rx_64.v +SYN_FILES += lib/eth/rtl/ip_eth_tx_64.v +SYN_FILES += lib/eth/rtl/ip_arb_mux.v +SYN_FILES += lib/eth/rtl/arp_64.v +SYN_FILES += lib/eth/rtl/arp_cache.v +SYN_FILES += lib/eth/rtl/arp_eth_rx_64.v +SYN_FILES += lib/eth/rtl/arp_eth_tx_64.v +SYN_FILES += lib/eth/rtl/eth_arb_mux.v +SYN_FILES += lib/eth/rtl/xgmii_interleave.v +SYN_FILES += lib/eth/rtl/xgmii_deinterleave.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 += cores/phy/phy.qip +SYN_FILES += cores/phy_reconfig/phy_reconfig.qip + +# QSF files +QSF_FILES = fpga.qsf + +# SDC files +SDC_FILES = fpga.sdc + +include ../common/altera.mk + +program: fpga + quartus_pgm --no_banner --mode=jtag -o "P;$(FPGA_TOP).sof" + +# program with 'factory default' parallel flash loader design (or something else with a NIOS2 that can see the flash chips) +program_flash_pfl: fpga + nios2_command_shell.sh sof2flash --input="$(FPGA_TOP).sof" --output="$(FPGA_TOP).flash" --offset=0x20C0000 --pfl --optionbit=0x00030000 --programmingmode=PS + nios2_command_shell.sh nios2-flash-programmer --base=0x0 "$(FPGA_TOP).flash" diff --git a/fpga/lib/eth/example/DE5-Net/fpga/lib/eth b/fpga/lib/eth/example/DE5-Net/fpga/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/DE5-Net/fpga/rtl/debounce_switch.v b/fpga/lib/eth/example/DE5-Net/fpga/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/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 + +`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/fpga/lib/eth/example/DE5-Net/fpga/rtl/fpga.v b/fpga/lib/eth/example/DE5-Net/fpga/rtl/fpga.v new file mode 100644 index 000000000..404f3cf94 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/rtl/fpga.v @@ -0,0 +1,470 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + // CPU reset button + input wire CPU_RESET_n, + // buttons + input wire [3:0] BUTTON, + input wire [3:0] SW, + // LEDs + output wire [6:0] HEX0_D, + output wire HEX0_DP, + output wire [6:0] HEX1_D, + output wire HEX1_DP, + output wire [3:0] LED, + output wire [3:0] LED_BRACKET, + output wire LED_RJ45_L, + output wire LED_RJ45_R, + // Temperature control + //inout wire TEMP_CLK, + //inout wire TEMP_DATA, + //input wire TEMP_INT_n, + //input wire TEMP_OVERT_n, + output wire FAN_CTRL, + // 50 MHz clock inputs + input wire OSC_50_B3B, + input wire OSC_50_B3D, + input wire OSC_50_B4A, + input wire OSC_50_B4D, + input wire OSC_50_B7A, + input wire OSC_50_B7D, + input wire OSC_50_B8A, + input wire OSC_50_B8D, + // PCIe interface + //input wire PCIE_PERST_n, + //input wire PCIE_REFCLK_p, + //input wire [7:0] PCIE_RX_p, + //output wire [7:0] PCIE_TX_p, + //input wire PCIE_WAKE_n, + //inout wire PCIE_SMBCLK, + //inout wire PCIE_SMBDAT, + // Si570 + inout wire CLOCK_SCL, + inout wire CLOCK_SDA, + // 10G Ethernet + input wire SFPA_LOS, + input wire SFPA_TXFAULT, + input wire SFPA_MOD0_PRESNT_n, + inout wire SFPA_MOD1_SCL, + inout wire SFPA_MOD2_SDA, + output wire SFPA_TXDISABLE, + output wire [1:0] SPFA_RATESEL, + input wire SFPA_RX_p, + output wire SFPA_TX_p, + input wire SFPB_LOS, + input wire SFPB_TXFAULT, + input wire SFPB_MOD0_PRESNT_n, + inout wire SFPB_MOD1_SCL, + inout wire SFPB_MOD2_SDA, + output wire SFPB_TXDISABLE, + output wire [1:0] SPFB_RATESEL, + input wire SFPB_RX_p, + output wire SFPB_TX_p, + input wire SFPC_LOS, + input wire SFPC_TXFAULT, + input wire SFPC_MOD0_PRESNT_n, + inout wire SFPC_MOD1_SCL, + inout wire SFPC_MOD2_SDA, + output wire SFPC_TXDISABLE, + output wire [1:0] SPFC_RATESEL, + input wire SFPC_RX_p, + output wire SFPC_TX_p, + input wire SFPD_LOS, + input wire SFPD_TXFAULT, + input wire SFPD_MOD0_PRESNT_n, + inout wire SFPD_MOD1_SCL, + inout wire SFPD_MOD2_SDA, + output wire SFPD_TXDISABLE, + output wire [1:0] SPFD_RATESEL, + input wire SFPD_RX_p, + output wire SFPD_TX_p, + input wire SFP_REFCLK_P +); + +// Clock and reset + +wire clk_50mhz = OSC_50_B3B; +wire rst_50mhz; + +sync_reset #( + .N(4) +) +sync_reset_50mhz_inst ( + .clk(clk_50mhz), + .rst(~CPU_RESET_n), + .sync_reset_out(rst_50mhz) +); + +wire clk_156mhz; +wire rst_156mhz; + +wire phy_pll_locked; + +sync_reset #( + .N(4) +) +sync_reset_156mhz_inst ( + .clk(clk_156mhz), + .rst(rst_50mhz | ~phy_pll_locked), + .sync_reset_out(rst_156mhz) +); + +// GPIO + +wire [3:0] btn_int; +wire [3:0] sw_int; +wire [3:0] led_int; +wire [3:0] led_bkt_int; +wire [6:0] led_hex0_d_int; +wire led_hex0_dp_int; +wire [6:0] led_hex1_d_int; +wire led_hex1_dp_int; + +debounce_switch #( + .WIDTH(8), + .N(4), + .RATE(156250) +) +debounce_switch_inst ( + .clk(clk_156mhz), + .rst(rst_156mhz), + .in({BUTTON, + SW}), + .out({btn_int, + sw_int}) +); + +assign LED = ~led_int; +assign LED_BRACKET = ~led_bkt_int; +assign HEX0_D = ~led_hex0_d_int; +assign HEX0_DP = ~led_hex0_dp_int; +assign HEX1_D = ~led_hex1_d_int; +assign HEX1_DP = ~led_hex1_dp_int; + +assign FAN_CTRL = 1; + +// Si570 oscillator I2C init + +wire si570_scl_i; +wire si570_scl_o; +wire si570_scl_t; +wire si570_sda_i; +wire si570_sda_o; +wire si570_sda_t; + +assign si570_sda_i = CLOCK_SDA; +assign CLOCK_SDA = si570_sda_t ? 1'bz : si570_sda_o; +assign si570_scl_i = CLOCK_SCL; +assign CLOCK_SCL = si570_scl_t ? 1'bz : si570_scl_o; + +wire [6:0] si570_i2c_cmd_address; +wire si570_i2c_cmd_start; +wire si570_i2c_cmd_read; +wire si570_i2c_cmd_write; +wire si570_i2c_cmd_write_multiple; +wire si570_i2c_cmd_stop; +wire si570_i2c_cmd_valid; +wire si570_i2c_cmd_ready; + +wire [7:0] si570_i2c_data; +wire si570_i2c_data_valid; +wire si570_i2c_data_ready; +wire si570_i2c_data_last; + +si570_i2c_init +si570_i2c_init_inst ( + .clk(clk_50mhz), + .rst(rst_50mhz), + .cmd_address(si570_i2c_cmd_address), + .cmd_start(si570_i2c_cmd_start), + .cmd_read(si570_i2c_cmd_read), + .cmd_write(si570_i2c_cmd_write), + .cmd_write_multiple(si570_i2c_cmd_write_multiple), + .cmd_stop(si570_i2c_cmd_stop), + .cmd_valid(si570_i2c_cmd_valid), + .cmd_ready(si570_i2c_cmd_ready), + .data_out(si570_i2c_data), + .data_out_valid(si570_i2c_data_valid), + .data_out_ready(si570_i2c_data_ready), + .data_out_last(si570_i2c_data_last), + .busy(), + .start(1) +); + +i2c_master +si570_i2c_master_inst ( + .clk(clk_50mhz), + .rst(rst_50mhz), + .cmd_address(si570_i2c_cmd_address), + .cmd_start(si570_i2c_cmd_start), + .cmd_read(si570_i2c_cmd_read), + .cmd_write(si570_i2c_cmd_write), + .cmd_write_multiple(si570_i2c_cmd_write_multiple), + .cmd_stop(si570_i2c_cmd_stop), + .cmd_valid(si570_i2c_cmd_valid), + .cmd_ready(si570_i2c_cmd_ready), + .data_in(si570_i2c_data), + .data_in_valid(si570_i2c_data_valid), + .data_in_ready(si570_i2c_data_ready), + .data_in_last(si570_i2c_data_last), + .data_out(), + .data_out_valid(), + .data_out_ready(1), + .data_out_last(), + .scl_i(si570_scl_i), + .scl_o(si570_scl_o), + .scl_t(si570_scl_t), + .sda_i(si570_sda_i), + .sda_o(si570_sda_o), + .sda_t(si570_sda_t), + .busy(), + .bus_control(), + .bus_active(), + .missed_ack(), + .prescale(312), + .stop_on_idle(1) +); + +// 10G Ethernet PHY + +wire [71:0] sfp_a_tx_dc; +wire [71:0] sfp_a_rx_dc; +wire [71:0] sfp_b_tx_dc; +wire [71:0] sfp_b_rx_dc; +wire [71:0] sfp_c_tx_dc; +wire [71:0] sfp_c_rx_dc; +wire [71:0] sfp_d_tx_dc; +wire [71:0] sfp_d_rx_dc; + +wire [367:0] phy_reconfig_from_xcvr; +wire [559:0] phy_reconfig_to_xcvr; + +assign SFPA_MOD1_SCL = 1'bz; +assign SFPA_MOD2_SDA = 1'bz; +assign SFPA_TXDISABLE = 1'b0; +assign SPFA_RATESEL = 2'b00; + +assign SFPB_MOD1_SCL = 1'bz; +assign SFPB_MOD2_SDA = 1'bz; +assign SFPB_TXDISABLE = 1'b0; +assign SPFB_RATESEL = 2'b00; + +assign SFPC_MOD1_SCL = 1'bz; +assign SFPC_MOD2_SDA = 1'bz; +assign SFPC_TXDISABLE = 1'b0; +assign SPFC_RATESEL = 2'b00; + +assign SFPD_MOD1_SCL = 1'bz; +assign SFPD_MOD2_SDA = 1'bz; +assign SFPD_TXDISABLE = 1'b0; +assign SPFD_RATESEL = 2'b00; + +phy +phy_inst ( + .pll_ref_clk(SFP_REFCLK_P), + .pll_locked(phy_pll_locked), + + .tx_serial_data_0(SFPA_TX_p), + .rx_serial_data_0(SFPA_RX_p), + .tx_serial_data_1(SFPB_TX_p), + .rx_serial_data_1(SFPB_RX_p), + .tx_serial_data_2(SFPC_TX_p), + .rx_serial_data_2(SFPC_RX_p), + .tx_serial_data_3(SFPD_TX_p), + .rx_serial_data_3(SFPD_RX_p), + + .xgmii_tx_dc_0(sfp_a_tx_dc), + .xgmii_rx_dc_0(sfp_a_rx_dc), + .xgmii_tx_dc_1(sfp_b_tx_dc), + .xgmii_rx_dc_1(sfp_b_rx_dc), + .xgmii_tx_dc_2(sfp_c_tx_dc), + .xgmii_rx_dc_2(sfp_c_rx_dc), + .xgmii_tx_dc_3(sfp_d_tx_dc), + .xgmii_rx_dc_3(sfp_d_rx_dc), + + .xgmii_rx_clk(clk_156mhz), + .xgmii_tx_clk(clk_156mhz), + + .tx_ready(~rst_156mhz), + .rx_ready(), + + .rx_data_ready(), + + .phy_mgmt_clk(clk_50mhz), + .phy_mgmt_clk_reset(rst_50mhz), + .phy_mgmt_address(9'd0), + .phy_mgmt_read(1'b0), + .phy_mgmt_readdata(), + .phy_mgmt_waitrequest(), + .phy_mgmt_write(1'b0), + .phy_mgmt_writedata(32'd0), + + .reconfig_from_xcvr(phy_reconfig_from_xcvr), + .reconfig_to_xcvr(phy_reconfig_to_xcvr) +); + +phy_reconfig +phy_reconfig_inst ( + .reconfig_busy(), + + .mgmt_clk_clk(clk_50mhz), + .mgmt_rst_reset(rst_50mhz), + + .reconfig_mgmt_address(7'd0), + .reconfig_mgmt_read(1'b0), + .reconfig_mgmt_readdata(), + .reconfig_mgmt_waitrequest(), + .reconfig_mgmt_write(1'b0), + .reconfig_mgmt_writedata(32'd0), + + .reconfig_to_xcvr(phy_reconfig_to_xcvr), + .reconfig_from_xcvr(phy_reconfig_from_xcvr) +); + +// Convert XGMII interfaces + +wire [63:0] sfp_a_txd_int; +wire [7:0] sfp_a_txc_int; +wire [63:0] sfp_a_rxd_int; +wire [7:0] sfp_a_rxc_int; +wire [63:0] sfp_b_txd_int; +wire [7:0] sfp_b_txc_int; +wire [63:0] sfp_b_rxd_int; +wire [7:0] sfp_b_rxc_int; +wire [63:0] sfp_c_txd_int; +wire [7:0] sfp_c_txc_int; +wire [63:0] sfp_c_rxd_int; +wire [7:0] sfp_c_rxc_int; +wire [63:0] sfp_d_txd_int; +wire [7:0] sfp_d_txc_int; +wire [63:0] sfp_d_rxd_int; +wire [7:0] sfp_d_rxc_int; + +xgmii_interleave +xgmii_interleave_inst_a ( + .input_xgmii_d(sfp_a_txd_int), + .input_xgmii_c(sfp_a_txc_int), + .output_xgmii_dc(sfp_a_tx_dc) +); + +xgmii_deinterleave +xgmii_deinterleave_inst_a ( + .input_xgmii_dc(sfp_a_rx_dc), + .output_xgmii_d(sfp_a_rxd_int), + .output_xgmii_c(sfp_a_rxc_int) +); + +xgmii_interleave +xgmii_interleave_inst_b ( + .input_xgmii_d(sfp_b_txd_int), + .input_xgmii_c(sfp_b_txc_int), + .output_xgmii_dc(sfp_b_tx_dc) +); + +xgmii_deinterleave +xgmii_deinterleave_inst_b ( + .input_xgmii_dc(sfp_b_rx_dc), + .output_xgmii_d(sfp_b_rxd_int), + .output_xgmii_c(sfp_b_rxc_int) +); + +xgmii_interleave +xgmii_interleave_inst_c ( + .input_xgmii_d(sfp_c_txd_int), + .input_xgmii_c(sfp_c_txc_int), + .output_xgmii_dc(sfp_c_tx_dc) +); + +xgmii_deinterleave +xgmii_deinterleave_inst_c ( + .input_xgmii_dc(sfp_c_rx_dc), + .output_xgmii_d(sfp_c_rxd_int), + .output_xgmii_c(sfp_c_rxc_int) +); + +xgmii_interleave +xgmii_interleave_inst_d ( + .input_xgmii_d(sfp_d_txd_int), + .input_xgmii_c(sfp_d_txc_int), + .output_xgmii_dc(sfp_d_tx_dc) +); + +xgmii_deinterleave +xgmii_deinterleave_inst_d ( + .input_xgmii_dc(sfp_d_rx_dc), + .output_xgmii_d(sfp_d_rxd_int), + .output_xgmii_c(sfp_d_rxc_int) +); + +// Core logic + +fpga_core +core_inst ( + /* + * Clock: 156.25MHz + * Synchronous reset + */ + .clk(clk_156mhz), + .rst(rst_156mhz), + /* + * GPIO + */ + .btn(btn_int), + .sw(sw_int), + .led(led_int), + .led_bkt(led_bkt_int), + .led_hex0_d(led_hex0_d_int), + .led_hex0_dp(led_hex0_dp_int), + .led_hex1_d(led_hex1_d_int), + .led_hex1_dp(led_hex1_dp_int), + /* + * 10G Ethernet + */ + .sfp_a_txd(sfp_a_txd_int), + .sfp_a_txc(sfp_a_txc_int), + .sfp_a_rxd(sfp_a_rxd_int), + .sfp_a_rxc(sfp_a_rxc_int), + .sfp_b_txd(sfp_b_txd_int), + .sfp_b_txc(sfp_b_txc_int), + .sfp_b_rxd(sfp_b_rxd_int), + .sfp_b_rxc(sfp_b_rxc_int), + .sfp_c_txd(sfp_c_txd_int), + .sfp_c_txc(sfp_c_txc_int), + .sfp_c_rxd(sfp_c_rxd_int), + .sfp_c_rxc(sfp_c_rxc_int), + .sfp_d_txd(sfp_d_txd_int), + .sfp_d_txc(sfp_d_txc_int), + .sfp_d_rxd(sfp_d_rxd_int), + .sfp_d_rxc(sfp_d_rxc_int) +); + +endmodule diff --git a/fpga/lib/eth/example/DE5-Net/fpga/rtl/fpga_core.v b/fpga/lib/eth/example/DE5-Net/fpga/rtl/fpga_core.v new file mode 100644 index 000000000..56a15431d --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/rtl/fpga_core.v @@ -0,0 +1,610 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core +( + /* + * Clock: 156.25MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + + /* + * GPIO + */ + input wire [3:0] btn, + input wire [3:0] sw, + output wire [3:0] led, + output wire [3:0] led_bkt, + output wire [6:0] led_hex0_d, + output wire led_hex0_dp, + output wire [6:0] led_hex1_d, + output wire led_hex1_dp, + + /* + * 10G Ethernet + */ + output wire [63:0] sfp_a_txd, + output wire [7:0] sfp_a_txc, + input wire [63:0] sfp_a_rxd, + input wire [7:0] sfp_a_rxc, + output wire [63:0] sfp_b_txd, + output wire [7:0] sfp_b_txc, + input wire [63:0] sfp_b_rxd, + input wire [7:0] sfp_b_rxc, + output wire [63:0] sfp_c_txd, + output wire [7:0] sfp_c_txc, + input wire [63:0] sfp_c_rxd, + input wire [7:0] sfp_c_rxc, + output wire [63:0] sfp_d_txd, + output wire [7:0] sfp_d_txc, + input wire [63:0] sfp_d_rxd, + input wire [7:0] sfp_d_rxc +); + +// AXI between MAC and Ethernet modules +wire [63:0] rx_axis_tdata; +wire [7:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tready; +wire rx_axis_tlast; +wire rx_axis_tuser; + +wire [63:0] tx_axis_tdata; +wire [7:0] tx_axis_tkeep; +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 [63:0] rx_eth_payload_axis_tdata; +wire [7:0] rx_eth_payload_axis_tkeep; +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 [63:0] tx_eth_payload_axis_tdata; +wire [7:0] tx_eth_payload_axis_tkeep; +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 [63:0] rx_ip_payload_axis_tdata; +wire [7:0] rx_ip_payload_axis_tkeep; +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 [63:0] tx_ip_payload_axis_tdata; +wire [7:0] tx_ip_payload_axis_tkeep; +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 [63:0] rx_udp_payload_axis_tdata; +wire [7:0] rx_udp_payload_axis_tkeep; +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 [63:0] tx_udp_payload_axis_tdata; +wire [7:0] tx_udp_payload_axis_tkeep; +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 [63:0] rx_fifo_udp_payload_axis_tdata; +wire [7:0] rx_fifo_udp_payload_axis_tkeep; +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 [63:0] tx_fifo_udp_payload_axis_tdata; +wire [7:0] tx_fifo_udp_payload_axis_tkeep; +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_tkeep = 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_tkeep = tx_fifo_udp_payload_axis_tkeep; +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_tkeep = rx_udp_payload_axis_tkeep; +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 led_bkt = led_reg; +assign led_hex0_d = 7'h00; +assign led_hex0_dp = 1'b0; +assign led_hex1_d = 7'h00; +assign led_hex1_dp = 1'b0; + +assign sfp_b_txd = 64'h0707070707070707; +assign sfp_b_txc = 8'hff; +assign sfp_c_txd = 64'h0707070707070707; +assign sfp_c_txc = 8'hff; +assign sfp_d_txd = 64'h0707070707070707; +assign sfp_d_txc = 8'hff; + +eth_mac_10g_fifo #( + .ENABLE_PADDING(1), + .ENABLE_DIC(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(9), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(9), + .RX_FRAME_FIFO(1) +) +eth_mac_10g_fifo_inst ( + .rx_clk(clk), + .rx_rst(rst), + .tx_clk(clk), + .tx_rst(rst), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + + .xgmii_rxd(sfp_a_rxd), + .xgmii_rxc(sfp_a_rxc), + .xgmii_txd(sfp_a_txd), + .xgmii_txc(sfp_a_txc), + + .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(), + + .ifg_delay(8'd12) +); + +eth_axis_rx_64 +eth_axis_rx_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(rx_axis_tdata), + .s_axis_tkeep(rx_axis_tkeep), + .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_tkeep(rx_eth_payload_axis_tkeep), + .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_64 +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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_axis_tkeep), + .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_64 +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_tkeep(rx_eth_payload_axis_tkeep), + .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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_ip_payload_axis_tkeep), + .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_tkeep(rx_ip_payload_axis_tkeep), + .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_tkeep(tx_udp_payload_axis_tkeep), + .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_tkeep(rx_udp_payload_axis_tkeep), + .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(1'b0) +); + +axis_fifo #( + .ADDR_WIDTH(10), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .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(rx_fifo_udp_payload_axis_tkeep), + .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(tx_fifo_udp_payload_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/fpga/lib/eth/example/DE5-Net/fpga/rtl/i2c_master.v b/fpga/lib/eth/example/DE5-Net/fpga/rtl/i2c_master.v new file mode 100644 index 000000000..95d3a5212 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/rtl/i2c_master.v @@ -0,0 +1,895 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * I2C master + */ +module i2c_master ( + input wire clk, + input wire rst, + + /* + * Host interface + */ + input wire [6:0] cmd_address, + input wire cmd_start, + input wire cmd_read, + input wire cmd_write, + input wire cmd_write_multiple, + input wire cmd_stop, + input wire cmd_valid, + output wire cmd_ready, + + input wire [7:0] data_in, + input wire data_in_valid, + output wire data_in_ready, + input wire data_in_last, + + output wire [7:0] data_out, + output wire data_out_valid, + input wire data_out_ready, + output wire data_out_last, + + /* + * I2C interface + */ + input wire scl_i, + output wire scl_o, + output wire scl_t, + input wire sda_i, + output wire sda_o, + output wire sda_t, + + /* + * Status + */ + output wire busy, + output wire bus_control, + output wire bus_active, + output wire missed_ack, + + /* + * Configuration + */ + input wire [15:0] prescale, + input wire stop_on_idle +); + +/* + +I2C + +Read + __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __ +sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_\_R___A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A____/ + ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____ +scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP + +Write + __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __ +sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_/_W_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_/_N_\__/ + ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____ +scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP + +Commands: + +read + read data byte + set start to force generation of a start condition + start is implied when bus is inactive or active with write or different address + set stop to issue a stop condition after reading current byte + if stop is set with read command, then data_out_last will be set + +write + write data byte + set start to force generation of a start condition + start is implied when bus is inactive or active with read or different address + set stop to issue a stop condition after writing current byte + +write multiple + write multiple data bytes (until data_in_last) + set start to force generation of a start condition + start is implied when bus is inactive or active with read or different address + set stop to issue a stop condition after writing block + +stop + issue stop condition if bus is active + +Status: + +busy + module is communicating over the bus + +bus_control + module has control of bus in active state + +bus_active + bus is active, not necessarily controlled by this module + +missed_ack + strobed when a slave ack is missed + +Parameters: + +prescale + set prescale to 1/4 of the minimum clock period in units + of input clk cycles (prescale = Fclk / (FI2Cclk * 4)) + +stop_on_idle + automatically issue stop when command input is not valid + +Example of interfacing with tristate pins: +(this will work for any tristate bus) + +assign scl_i = scl_pin; +assign scl_pin = scl_t ? 1'bz : scl_o; +assign sda_i = sda_pin; +assign sda_pin = sda_t ? 1'bz : sda_o; + +Equivalent code that does not use *_t connections: +(we can get away with this because I2C is open-drain) + +assign scl_i = scl_pin; +assign scl_pin = scl_o ? 1'bz : 1'b0; +assign sda_i = sda_pin; +assign sda_pin = sda_o ? 1'bz : 1'b0; + +Example of two interconnected I2C devices: + +assign scl_1_i = scl_1_o & scl_2_o; +assign scl_2_i = scl_1_o & scl_2_o; +assign sda_1_i = sda_1_o & sda_2_o; +assign sda_2_i = sda_1_o & sda_2_o; + +Example of two I2C devices sharing the same pins: + +assign scl_1_i = scl_pin; +assign scl_2_i = scl_pin; +assign scl_pin = (scl_1_o & scl_2_o) ? 1'bz : 1'b0; +assign sda_1_i = sda_pin; +assign sda_2_i = sda_pin; +assign sda_pin = (sda_1_o & sda_2_o) ? 1'bz : 1'b0; + +Notes: + +scl_o should not be connected directly to scl_i, only via AND logic or a tristate +I/O pin. This would prevent devices from stretching the clock period. + +*/ + +localparam [4:0] + STATE_IDLE = 4'd0, + STATE_ACTIVE_WRITE = 4'd1, + STATE_ACTIVE_READ = 4'd2, + STATE_START_WAIT = 4'd3, + STATE_START = 4'd4, + STATE_ADDRESS_1 = 4'd5, + STATE_ADDRESS_2 = 4'd6, + STATE_WRITE_1 = 4'd7, + STATE_WRITE_2 = 4'd8, + STATE_WRITE_3 = 4'd9, + STATE_READ = 4'd10, + STATE_STOP = 4'd11; + +reg [4:0] state_reg = STATE_IDLE, state_next; + +localparam [4:0] + PHY_STATE_IDLE = 5'd0, + PHY_STATE_ACTIVE = 5'd1, + PHY_STATE_REPEATED_START_1 = 5'd2, + PHY_STATE_REPEATED_START_2 = 5'd3, + PHY_STATE_START_1 = 5'd4, + PHY_STATE_START_2 = 5'd5, + PHY_STATE_WRITE_BIT_1 = 5'd6, + PHY_STATE_WRITE_BIT_2 = 5'd7, + PHY_STATE_WRITE_BIT_3 = 5'd8, + PHY_STATE_READ_BIT_1 = 5'd9, + PHY_STATE_READ_BIT_2 = 5'd10, + PHY_STATE_READ_BIT_3 = 5'd11, + PHY_STATE_READ_BIT_4 = 5'd12, + PHY_STATE_STOP_1 = 5'd13, + PHY_STATE_STOP_2 = 5'd14, + PHY_STATE_STOP_3 = 5'd15; + +reg [4:0] phy_state_reg = STATE_IDLE, phy_state_next; + +reg phy_start_bit; +reg phy_stop_bit; +reg phy_write_bit; +reg phy_read_bit; +reg phy_release_bus; + +reg phy_tx_data; + +reg phy_rx_data_reg = 1'b0, phy_rx_data_next; + +reg [6:0] addr_reg = 7'd0, addr_next; +reg [7:0] data_reg = 8'd0, data_next; +reg last_reg = 1'b0, last_next; + +reg mode_read_reg = 1'b0, mode_read_next; +reg mode_write_multiple_reg = 1'b0, mode_write_multiple_next; +reg mode_stop_reg = 1'b0, mode_stop_next; + +reg [16:0] delay_reg = 16'd0, delay_next; +reg delay_scl_reg = 1'b0, delay_scl_next; +reg delay_sda_reg = 1'b0, delay_sda_next; + +reg [3:0] bit_count_reg = 4'd0, bit_count_next; + +reg cmd_ready_reg = 1'b0, cmd_ready_next; + +reg data_in_ready_reg = 1'b0, data_in_ready_next; + +reg [7:0] data_out_reg = 8'd0, data_out_next; +reg data_out_valid_reg = 1'b0, data_out_valid_next; +reg data_out_last_reg = 1'b0, data_out_last_next; + +reg scl_i_reg = 1'b1; +reg sda_i_reg = 1'b1; + +reg scl_o_reg = 1'b1, scl_o_next; +reg sda_o_reg = 1'b1, sda_o_next; + +reg last_scl_i_reg = 1'b1; +reg last_sda_i_reg = 1'b1; + +reg busy_reg = 1'b0; +reg bus_active_reg = 1'b0; +reg bus_control_reg = 1'b0, bus_control_next; +reg missed_ack_reg = 1'b0, missed_ack_next; + +assign cmd_ready = cmd_ready_reg; + +assign data_in_ready = data_in_ready_reg; + +assign data_out = data_out_reg; +assign data_out_valid = data_out_valid_reg; +assign data_out_last = data_out_last_reg; + +assign scl_o = scl_o_reg; +assign scl_t = scl_o_reg; +assign sda_o = sda_o_reg; +assign sda_t = sda_o_reg; + +assign busy = busy_reg; +assign bus_active = bus_active_reg; +assign bus_control = bus_control_reg; +assign missed_ack = missed_ack_reg; + +wire scl_posedge = scl_i_reg & ~last_scl_i_reg; +wire scl_negedge = ~scl_i_reg & last_scl_i_reg; +wire sda_posedge = sda_i_reg & ~last_sda_i_reg; +wire sda_negedge = ~sda_i_reg & last_sda_i_reg; + +wire start_bit = sda_negedge & scl_i_reg; +wire stop_bit = sda_posedge & scl_i_reg; + +always @* begin + state_next = STATE_IDLE; + + phy_start_bit = 1'b0; + phy_stop_bit = 1'b0; + phy_write_bit = 1'b0; + phy_read_bit = 1'b0; + phy_tx_data = 1'b0; + phy_release_bus = 1'b0; + + addr_next = addr_reg; + data_next = data_reg; + last_next = last_reg; + + mode_read_next = mode_read_reg; + mode_write_multiple_next = mode_write_multiple_reg; + mode_stop_next = mode_stop_reg; + + bit_count_next = bit_count_reg; + + cmd_ready_next = 1'b0; + + data_in_ready_next = 1'b0; + + data_out_next = data_out_reg; + data_out_valid_next = data_out_valid_reg & ~data_out_ready; + data_out_last_next = data_out_last_reg; + + missed_ack_next = 1'b0; + + // generate delays + if (phy_state_reg != PHY_STATE_IDLE && phy_state_reg != PHY_STATE_ACTIVE) begin + // wait for phy operation + state_next = state_reg; + end else begin + // process states + case (state_reg) + STATE_IDLE: begin + // line idle + cmd_ready_next = 1'b1; + + if (cmd_ready & cmd_valid) begin + // command valid + if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin + // read or write command + addr_next = cmd_address; + mode_read_next = cmd_read; + mode_write_multiple_next = cmd_write_multiple; + mode_stop_next = cmd_stop; + + cmd_ready_next = 1'b0; + + // start bit + if (bus_active) begin + state_next = STATE_START_WAIT; + end else begin + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end + end else begin + // invalid or unspecified - ignore + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_ACTIVE_WRITE: begin + // line active with current address and read/write mode + cmd_ready_next = 1'b1; + + if (cmd_ready & cmd_valid) begin + // command valid + if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin + // read or write command + addr_next = cmd_address; + mode_read_next = cmd_read; + mode_write_multiple_next = cmd_write_multiple; + mode_stop_next = cmd_stop; + + cmd_ready_next = 1'b0; + + if (cmd_start || cmd_address != addr_reg || cmd_read) begin + // address or mode mismatch or forced start - repeated start + + // repeated start bit + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end else begin + // address and mode match + + // start write + data_in_ready_next = 1'b1; + state_next = STATE_WRITE_1; + end + end else if (cmd_stop && !(cmd_read || cmd_write || cmd_write_multiple)) begin + // stop command + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end else begin + // invalid or unspecified - ignore + state_next = STATE_ACTIVE_WRITE; + end + end else begin + if (stop_on_idle & cmd_ready & ~cmd_valid) begin + // no waiting command and stop_on_idle selected, issue stop condition + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_ACTIVE_WRITE; + end + end + end + STATE_ACTIVE_READ: begin + // line active to current address + cmd_ready_next = ~data_out_valid; + + if (cmd_ready & cmd_valid) begin + // command valid + if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin + // read or write command + addr_next = cmd_address; + mode_read_next = cmd_read; + mode_write_multiple_next = cmd_write_multiple; + mode_stop_next = cmd_stop; + + cmd_ready_next = 1'b0; + + if (cmd_start || cmd_address != addr_reg || cmd_write) begin + // address or mode mismatch or forced start - repeated start + + // write nack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + // repeated start bit + state_next = STATE_START; + end else begin + // address and mode match + + // write ack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b0; + // start next read + bit_count_next = 4'd8; + data_next = 8'd0; + state_next = STATE_READ; + end + end else if (cmd_stop && !(cmd_read || cmd_write || cmd_write_multiple)) begin + // stop command + // write nack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + // send stop bit + state_next = STATE_STOP; + end else begin + // invalid or unspecified - ignore + state_next = STATE_ACTIVE_READ; + end + end else begin + if (stop_on_idle & cmd_ready & ~cmd_valid) begin + // no waiting command and stop_on_idle selected, issue stop condition + // write ack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + // send stop bit + state_next = STATE_STOP; + end else begin + state_next = STATE_ACTIVE_READ; + end + end + end + STATE_START_WAIT: begin + // wait for bus idle + + if (bus_active) begin + state_next = STATE_START_WAIT; + end else begin + // bus is idle, take control + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end + end + STATE_START: begin + // send start bit + + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end + STATE_ADDRESS_1: begin + // send address + bit_count_next = bit_count_reg - 1; + if (bit_count_reg > 1) begin + // send address + phy_write_bit = 1'b1; + phy_tx_data = addr_reg[bit_count_reg-2]; + state_next = STATE_ADDRESS_1; + end else if (bit_count_reg > 0) begin + // send read/write bit + phy_write_bit = 1'b1; + phy_tx_data = mode_read_reg; + state_next = STATE_ADDRESS_1; + end else begin + // read ack bit + phy_read_bit = 1'b1; + state_next = STATE_ADDRESS_2; + end + end + STATE_ADDRESS_2: begin + // read ack bit + missed_ack_next = phy_rx_data_reg; + + if (mode_read_reg) begin + // start read + bit_count_next = 4'd8; + data_next = 1'b0; + state_next = STATE_READ; + end else begin + // start write + data_in_ready_next = 1'b1; + state_next = STATE_WRITE_1; + end + end + STATE_WRITE_1: begin + data_in_ready_next = 1'b1; + + if (data_in_ready & data_in_valid) begin + // got data, start write + data_next = data_in; + last_next = data_in_last; + bit_count_next = 4'd8; + data_in_ready_next = 1'b0; + state_next = STATE_WRITE_2; + end else begin + // wait for data + state_next = STATE_WRITE_1; + end + end + STATE_WRITE_2: begin + // send data + bit_count_next = bit_count_reg - 1; + if (bit_count_reg > 0) begin + // write data bit + phy_write_bit = 1'b1; + phy_tx_data = data_reg[bit_count_reg-1]; + state_next = STATE_WRITE_2; + end else begin + // read ack bit + phy_read_bit = 1'b1; + state_next = STATE_WRITE_3; + end + end + STATE_WRITE_3: begin + // read ack bit + missed_ack_next = phy_rx_data_reg; + + if (mode_write_multiple_reg && !last_reg) begin + // more to write + state_next = STATE_WRITE_1; + end else if (mode_stop_reg) begin + // last cycle and stop selected + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end else begin + // otherwise, return to bus active state + state_next = STATE_ACTIVE_WRITE; + end + end + STATE_READ: begin + // read data + + bit_count_next = bit_count_reg - 1; + data_next = {data_reg[6:0], phy_rx_data_reg}; + if (bit_count_reg > 0) begin + // read next bit + phy_read_bit = 1'b1; + state_next = STATE_READ; + end else begin + // output data word + data_out_next = data_next; + data_out_valid_next = 1'b1; + data_out_last_next = 1'b0; + if (mode_stop_reg) begin + // send nack and stop + data_out_last_next = 1'b1; + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + state_next = STATE_STOP; + end else begin + // return to bus active state + state_next = STATE_ACTIVE_READ; + end + end + end + STATE_STOP: begin + // send stop bit + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end + endcase + end +end + +always @* begin + phy_state_next = PHY_STATE_IDLE; + + phy_rx_data_next = phy_rx_data_reg; + + delay_next = delay_reg; + delay_scl_next = delay_scl_reg; + delay_sda_next = delay_sda_reg; + + scl_o_next = scl_o_reg; + sda_o_next = sda_o_reg; + + bus_control_next = bus_control_reg; + + if (phy_release_bus) begin + // release bus and return to idle state + sda_o_next = 1'b1; + scl_o_next = 1'b1; + delay_scl_next = 1'b0; + delay_sda_next = 1'b0; + delay_next = 1'b0; + phy_state_next = PHY_STATE_IDLE; + end else if (delay_scl_reg) begin + // wait for SCL to match command + delay_scl_next = scl_o_reg & ~scl_i_reg; + phy_state_next = phy_state_reg; + end else if (delay_sda_reg) begin + // wait for SDA to match command + delay_sda_next = sda_o_reg & ~sda_i_reg; + phy_state_next = phy_state_reg; + end else if (delay_reg > 0) begin + // time delay + delay_next = delay_reg - 1; + phy_state_next = phy_state_reg; + end else begin + case (phy_state_reg) + PHY_STATE_IDLE: begin + // bus idle - wait for start command + sda_o_next = 1'b1; + scl_o_next = 1'b1; + if (phy_start_bit) begin + sda_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_START_1; + end else begin + phy_state_next = PHY_STATE_IDLE; + end + end + PHY_STATE_ACTIVE: begin + // bus active + if (phy_start_bit) begin + sda_o_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_REPEATED_START_1; + end else if (phy_write_bit) begin + sda_o_next = phy_tx_data; + delay_next = prescale; + phy_state_next = PHY_STATE_WRITE_BIT_1; + end else if (phy_read_bit) begin + sda_o_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_1; + end else if (phy_stop_bit) begin + sda_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_STOP_1; + end else begin + phy_state_next = PHY_STATE_ACTIVE; + end + end + PHY_STATE_REPEATED_START_1: begin + // generate repeated start bit + // ______ + // sda XXX/ \_______ + // _______ + // scl ______/ \___ + // + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_REPEATED_START_2; + end + PHY_STATE_REPEATED_START_2: begin + // generate repeated start bit + // ______ + // sda XXX/ \_______ + // _______ + // scl ______/ \___ + // + + sda_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_START_1; + end + PHY_STATE_START_1: begin + // generate start bit + // ___ + // sda \_______ + // _______ + // scl \___ + // + + scl_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_START_2; + end + PHY_STATE_START_2: begin + // generate start bit + // ___ + // sda \_______ + // _______ + // scl \___ + // + + bus_control_next = 1'b1; + phy_state_next = PHY_STATE_ACTIVE; + end + PHY_STATE_WRITE_BIT_1: begin + // write bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale << 1; + phy_state_next = PHY_STATE_WRITE_BIT_2; + end + PHY_STATE_WRITE_BIT_2: begin + // write bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_WRITE_BIT_3; + end + PHY_STATE_WRITE_BIT_3: begin + // write bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + phy_state_next = PHY_STATE_ACTIVE; + end + PHY_STATE_READ_BIT_1: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_2; + end + PHY_STATE_READ_BIT_2: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + phy_rx_data_next = sda_i_reg; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_3; + end + PHY_STATE_READ_BIT_3: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_4; + end + PHY_STATE_READ_BIT_4: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + phy_state_next = PHY_STATE_ACTIVE; + end + PHY_STATE_STOP_1: begin + // stop bit + // ___ + // sda XXX\_______/ + // _______ + // scl _______/ + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_STOP_2; + end + PHY_STATE_STOP_2: begin + // stop bit + // ___ + // sda XXX\_______/ + // _______ + // scl _______/ + + sda_o_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_STOP_3; + end + PHY_STATE_STOP_3: begin + // stop bit + // ___ + // sda XXX\_______/ + // _______ + // scl _______/ + + bus_control_next = 1'b0; + phy_state_next = PHY_STATE_IDLE; + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + phy_state_reg <= PHY_STATE_IDLE; + delay_reg <= 16'd0; + delay_scl_reg <= 1'b0; + delay_sda_reg <= 1'b0; + cmd_ready_reg <= 1'b0; + data_in_ready_reg <= 1'b0; + data_out_valid_reg <= 1'b0; + scl_o_reg <= 1'b1; + sda_o_reg <= 1'b1; + busy_reg <= 1'b0; + bus_active_reg <= 1'b0; + bus_control_reg <= 1'b0; + missed_ack_reg <= 1'b0; + end else begin + state_reg <= state_next; + phy_state_reg <= phy_state_next; + + delay_reg <= delay_next; + delay_scl_reg <= delay_scl_next; + delay_sda_reg <= delay_sda_next; + + cmd_ready_reg <= cmd_ready_next; + data_in_ready_reg <= data_in_ready_next; + data_out_valid_reg <= data_out_valid_next; + + scl_o_reg <= scl_o_next; + sda_o_reg <= sda_o_next; + + busy_reg <= !(state_reg == STATE_IDLE || state_reg == STATE_ACTIVE_WRITE || state_reg == STATE_ACTIVE_READ); + + if (start_bit) begin + bus_active_reg <= 1'b1; + end else if (stop_bit) begin + bus_active_reg <= 1'b0; + end else begin + bus_active_reg <= bus_active_reg; + end + + bus_control_reg <= bus_control_next; + missed_ack_reg <= missed_ack_next; + end + + phy_rx_data_reg <= phy_rx_data_next; + + addr_reg <= addr_next; + data_reg <= data_next; + last_reg <= last_next; + + mode_read_reg <= mode_read_next; + mode_write_multiple_reg <= mode_write_multiple_next; + mode_stop_reg <= mode_stop_next; + + bit_count_reg <= bit_count_next; + + data_out_reg <= data_out_next; + data_out_last_reg <= data_out_last_next; + + scl_i_reg <= scl_i; + sda_i_reg <= sda_i; + last_scl_i_reg <= scl_i_reg; + last_sda_i_reg <= sda_i_reg; +end + +endmodule diff --git a/fpga/lib/eth/example/DE5-Net/fpga/rtl/si570_i2c_init.v b/fpga/lib/eth/example/DE5-Net/fpga/rtl/si570_i2c_init.v new file mode 100644 index 000000000..e4dc5625b --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/rtl/si570_i2c_init.v @@ -0,0 +1,455 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * si570_i2c_init + */ +module si570_i2c_init ( + input wire clk, + input wire rst, + + /* + * I2C master interface + */ + output wire [6:0] cmd_address, + output wire cmd_start, + output wire cmd_read, + output wire cmd_write, + output wire cmd_write_multiple, + output wire cmd_stop, + output wire cmd_valid, + input wire cmd_ready, + + output wire [7:0] data_out, + output wire data_out_valid, + input wire data_out_ready, + output wire data_out_last, + + /* + * Status + */ + output wire busy, + + /* + * Configuration + */ + input wire start +); + +/* + +Generic module for I2C bus initialization. Good for use when multiple devices +on an I2C bus must be initialized on system start without intervention of a +general-purpose processor. + +Copy this file and change init_data and INIT_DATA_LEN as needed. + +This module can be used in two modes: simple device initalization, or multiple +device initialization. In multiple device mode, the same initialization sequence +can be performed on multiple different device addresses. + +To use single device mode, only use the start write to address and write data commands. +The module will generate the I2C commands in sequential order. Terminate the list +with a 0 entry. + +To use the multiple device mode, use the start data and start address block commands +to set up lists of initialization data and device addresses. The module enters +multiple device mode upon seeing a start data block command. The module stores the +offset of the start of the data block and then skips ahead until it reaches a start +address block command. The module will store the offset to the address block and +read the first address in the block. Then it will jump back to the data block +and execute it, substituting the stored address for each current address write +command. Upon reaching the start address block command, the module will read out the +next address and start again at the top of the data block. If the module encounters +a start data block command while looking for an address, then it will store a new data +offset and then look for a start address block command. Terminate the list with a 0 +entry. Normal address commands will operate normally inside a data block. + +Commands: + +00 0000000 : stop +00 0000001 : exit multiple device mode +00 0000011 : start write to current address +00 0001000 : start address block +00 0001001 : start data block +01 aaaaaaa : start write to address +1 dddddddd : write 8-bit data + +Examples + +write 0x11223344 to register 0x0004 on device at 0x50 + +01 1010000 start write to 0x50 +1 00000000 write address 0x0004 +1 00000100 +1 00010001 write data 0x11223344 +1 00100010 +1 00110011 +1 01000100 +0 00000000 stop + +write 0x11223344 to register 0x0004 on devices at 0x50, 0x51, 0x52, and 0x53 + +00 0001001 start data block +00 0000011 start write to current address +1 00000000 write address 0x0004 +1 00000100 +1 00010001 write data 0x11223344 +1 00100010 +1 00110011 +1 01000100 +00 0001000 start address block +01 1010000 address 0x50 +01 1010001 address 0x51 +01 1010010 address 0x52 +01 1010011 address 0x53 +00 0000000 stop + +*/ + +// init_data ROM +localparam INIT_DATA_LEN = 18; + +reg [8:0] init_data [INIT_DATA_LEN-1:0]; + +initial begin + // Set Si570 to generate 644.53125 MHz + init_data[0] = {2'b01, 7'h00}; // start write to address 0x00 + init_data[1] = {1'b1, 8'd137}; // write address 137 + init_data[2] = {1'b1, 8'h10}; // write data 0x10 (freeze DCO) + init_data[3] = {2'b01, 7'h00}; // start write to address 0x00 + init_data[4] = {1'b1, 8'd7}; // write address 7 + init_data[5] = {1'b1, {3'b000, 5'b000000}}; // write data (address 7) (HS_DIV = 3'b000) + init_data[6] = {1'b1, {2'b01, 6'h2}}; // write data (address 8) (N1 = 7'b000001) + init_data[7] = {1'b1, 8'hD1}; // write data (address 9) + init_data[8] = {1'b1, 8'hE1}; // write data (address 10) + init_data[9] = {1'b1, 8'h27}; // write data (address 11) + init_data[10] = {1'b1, 8'hAF}; // write data (address 12) (RFREQ = 38'h2D1E127AF) + init_data[11] = {2'b01, 7'h00}; // start write to address 0x00 + init_data[12] = {1'b1, 8'd137}; // write address 137 + init_data[13] = {1'b1, 8'h00}; // write data 0x00 (un-freeze DCO) + init_data[14] = {2'b01, 7'h00}; // start write to address 0x00 + init_data[15] = {1'b1, 8'd135}; // write address 135 + init_data[16] = {1'b1, 8'h40}; // write data 0x40 (new frequency applied) + init_data[17] = 9'd0; // stop +end + +localparam [3:0] + STATE_IDLE = 3'd0, + STATE_RUN = 3'd1, + STATE_TABLE_1 = 3'd2, + STATE_TABLE_2 = 3'd3, + STATE_TABLE_3 = 3'd4; + +reg [4:0] state_reg = STATE_IDLE, state_next; + +parameter AW = $clog2(INIT_DATA_LEN); + +reg [8:0] init_data_reg = 9'd0; + +reg [AW-1:0] address_reg = {AW{1'b0}}, address_next; +reg [AW-1:0] address_ptr_reg = {AW{1'b0}}, address_ptr_next; +reg [AW-1:0] data_ptr_reg = {AW{1'b0}}, data_ptr_next; + +reg [6:0] cur_address_reg = 7'd0, cur_address_next; + +reg [6:0] cmd_address_reg = 7'd0, cmd_address_next; +reg cmd_start_reg = 1'b0, cmd_start_next; +reg cmd_write_reg = 1'b0, cmd_write_next; +reg cmd_stop_reg = 1'b0, cmd_stop_next; +reg cmd_valid_reg = 1'b0, cmd_valid_next; + +reg [7:0] data_out_reg = 8'd0, data_out_next; +reg data_out_valid_reg = 1'b0, data_out_valid_next; + +reg start_flag_reg = 1'b0, start_flag_next; + +reg busy_reg = 1'b0; + +assign cmd_address = cmd_address_reg; +assign cmd_start = cmd_start_reg; +assign cmd_read = 1'b0; +assign cmd_write = cmd_write_reg; +assign cmd_write_multiple = 1'b0; +assign cmd_stop = cmd_stop_reg; +assign cmd_valid = cmd_valid_reg; + +assign data_out = data_out_reg; +assign data_out_valid = data_out_valid_reg; +assign data_out_last = 1'b1; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + address_next = address_reg; + address_ptr_next = address_ptr_reg; + data_ptr_next = data_ptr_reg; + + cur_address_next = cur_address_reg; + + cmd_address_next = cmd_address_reg; + cmd_start_next = cmd_start_reg & ~(cmd_valid & cmd_ready); + cmd_write_next = cmd_write_reg & ~(cmd_valid & cmd_ready); + cmd_stop_next = cmd_stop_reg & ~(cmd_valid & cmd_ready); + cmd_valid_next = cmd_valid_reg & ~cmd_ready; + + data_out_next = data_out_reg; + data_out_valid_next = data_out_valid_reg & ~data_out_ready; + + start_flag_next = start_flag_reg; + + if (cmd_valid | data_out_valid) begin + // wait for output registers to clear + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // wait for start signal + if (~start_flag_reg & start) begin + address_next = {AW{1'b0}}; + start_flag_next = 1'b1; + state_next = STATE_RUN; + end else begin + state_next = STATE_IDLE; + end + end + STATE_RUN: begin + // process commands + if (init_data_reg[8] == 1'b1) begin + // write data + cmd_write_next = 1'b1; + cmd_stop_next = 1'b0; + cmd_valid_next = 1'b1; + + data_out_next = init_data_reg[7:0]; + data_out_valid_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_RUN; + end else if (init_data_reg[8:7] == 2'b01) begin + // write address + cmd_address_next = init_data_reg[6:0]; + cmd_start_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_RUN; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_RUN; + end + end + STATE_TABLE_1: begin + // find address table start + if (init_data_reg == 9'b000001000) begin + // address table start + address_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_2; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 1) begin + // exit mode + address_next = address_reg + 1; + state_next = STATE_RUN; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end + end + STATE_TABLE_2: begin + // find next address + if (init_data_reg[8:7] == 2'b01) begin + // write address command + // store address and move to data table + cur_address_next = init_data_reg[6:0]; + address_ptr_next = address_reg + 1; + address_next = data_ptr_reg; + state_next = STATE_TABLE_3; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'd1) begin + // exit mode + address_next = address_reg + 1; + state_next = STATE_RUN; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_TABLE_2; + end + end + STATE_TABLE_3: begin + // process data table with selected address + if (init_data_reg[8] == 1'b1) begin + // write data + cmd_write_next = 1'b1; + cmd_stop_next = 1'b0; + cmd_valid_next = 1'b1; + + data_out_next = init_data_reg[7:0]; + data_out_valid_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_TABLE_3; + end else if (init_data_reg[8:7] == 2'b01) begin + // write address + cmd_address_next = init_data_reg[6:0]; + cmd_start_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_TABLE_3; + end else if (init_data_reg == 9'b000000011) begin + // write current address + cmd_address_next = cur_address_reg; + cmd_start_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_TABLE_3; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'b000001000) begin + // address table start + address_next = address_ptr_reg; + state_next = STATE_TABLE_2; + end else if (init_data_reg == 9'd1) begin + // exit mode + address_next = address_reg + 1; + state_next = STATE_RUN; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_TABLE_3; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + init_data_reg <= 9'd0; + + address_reg <= {AW{1'b0}}; + address_ptr_reg <= {AW{1'b0}}; + data_ptr_reg <= {AW{1'b0}}; + + cur_address_reg <= 7'd0; + + cmd_valid_reg <= 1'b0; + + data_out_valid_reg <= 1'b0; + + start_flag_reg <= 1'b0; + + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + // read init_data ROM + init_data_reg <= init_data[address_next]; + + address_reg <= address_next; + address_ptr_reg <= address_ptr_next; + data_ptr_reg <= data_ptr_next; + + cur_address_reg <= cur_address_next; + + cmd_valid_reg <= cmd_valid_next; + + data_out_valid_reg <= data_out_valid_next; + + start_flag_reg <= start & start_flag_next; + + busy_reg <= (state_reg != STATE_IDLE); + end + + cmd_address_reg <= cmd_address_next; + cmd_start_reg <= cmd_start_next; + cmd_write_reg <= cmd_write_next; + cmd_stop_reg <= cmd_stop_next; + + data_out_reg <= data_out_next; +end + +endmodule diff --git a/fpga/lib/eth/example/DE5-Net/fpga/rtl/sync_reset.v b/fpga/lib/eth/example/DE5-Net/fpga/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/DE5-Net/fpga/rtl/sync_signal.v b/fpga/lib/eth/example/DE5-Net/fpga/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/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/fpga/lib/eth/example/DE5-Net/fpga/tb/arp_ep.py b/fpga/lib/eth/example/DE5-Net/fpga/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/DE5-Net/fpga/tb/axis_ep.py b/fpga/lib/eth/example/DE5-Net/fpga/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/DE5-Net/fpga/tb/eth_ep.py b/fpga/lib/eth/example/DE5-Net/fpga/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/DE5-Net/fpga/tb/ip_ep.py b/fpga/lib/eth/example/DE5-Net/fpga/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/DE5-Net/fpga/tb/test_fpga_core.py b/fpga/lib/eth/example/DE5-Net/fpga/tb/test_fpga_core.py new file mode 100755 index 000000000..5a4a121d7 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/tb/test_fpga_core.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import xgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_10g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_10g.v") +srcs.append("../lib/eth/rtl/axis_xgmii_rx_64.v") +srcs.append("../lib/eth/rtl/axis_xgmii_tx_64.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx_64.v") +srcs.append("../lib/eth/rtl/eth_axis_tx_64.v") +srcs.append("../lib/eth/rtl/udp_complete_64.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen_64.v") +srcs.append("../lib/eth/rtl/udp_64.v") +srcs.append("../lib/eth/rtl/udp_ip_rx_64.v") +srcs.append("../lib/eth/rtl/udp_ip_tx_64.v") +srcs.append("../lib/eth/rtl/ip_complete_64.v") +srcs.append("../lib/eth/rtl/ip_64.v") +srcs.append("../lib/eth/rtl/ip_eth_rx_64.v") +srcs.append("../lib/eth/rtl/ip_eth_tx_64.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp_64.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx_64.v") +srcs.append("../lib/eth/rtl/arp_eth_tx_64.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btn = Signal(intbv(0)[4:]) + sw = Signal(intbv(0)[4:]) + sfp_a_rxd = Signal(intbv(0)[64:]) + sfp_a_rxc = Signal(intbv(0)[8:]) + sfp_b_rxd = Signal(intbv(0)[64:]) + sfp_b_rxc = Signal(intbv(0)[8:]) + sfp_c_rxd = Signal(intbv(0)[64:]) + sfp_c_rxc = Signal(intbv(0)[8:]) + sfp_d_rxd = Signal(intbv(0)[64:]) + sfp_d_rxc = Signal(intbv(0)[8:]) + + # Outputs + led = Signal(intbv(0)[4:]) + led_bkt = Signal(intbv(0)[4:]) + led_hex0_d = Signal(intbv(0)[7:]) + led_hex0_dp = Signal(bool(0)) + led_hex1_d = Signal(intbv(0)[7:]) + led_hex1_dp = Signal(bool(0)) + sfp_a_txd = Signal(intbv(0)[64:]) + sfp_a_txc = Signal(intbv(0)[8:]) + sfp_b_txd = Signal(intbv(0)[64:]) + sfp_b_txc = Signal(intbv(0)[8:]) + sfp_c_txd = Signal(intbv(0)[64:]) + sfp_c_txc = Signal(intbv(0)[8:]) + sfp_d_txd = Signal(intbv(0)[64:]) + sfp_d_txc = Signal(intbv(0)[8:]) + + # sources and sinks + sfp_a_source = xgmii_ep.XGMIISource() + sfp_a_source_logic = sfp_a_source.create_logic(clk, rst, txd=sfp_a_rxd, txc=sfp_a_rxc, name='sfp_a_source') + + sfp_a_sink = xgmii_ep.XGMIISink() + sfp_a_sink_logic = sfp_a_sink.create_logic(clk, rst, rxd=sfp_a_txd, rxc=sfp_a_txc, name='sfp_a_sink') + + sfp_b_source = xgmii_ep.XGMIISource() + sfp_b_source_logic = sfp_b_source.create_logic(clk, rst, txd=sfp_b_rxd, txc=sfp_b_rxc, name='sfp_b_source') + + sfp_b_sink = xgmii_ep.XGMIISink() + sfp_b_sink_logic = sfp_b_sink.create_logic(clk, rst, rxd=sfp_b_txd, rxc=sfp_b_txc, name='sfp_b_sink') + + sfp_c_source = xgmii_ep.XGMIISource() + sfp_c_source_logic = sfp_c_source.create_logic(clk, rst, txd=sfp_c_rxd, txc=sfp_c_rxc, name='sfp_c_source') + + sfp_c_sink = xgmii_ep.XGMIISink() + sfp_c_sink_logic = sfp_c_sink.create_logic(clk, rst, rxd=sfp_c_txd, rxc=sfp_c_txc, name='sfp_c_sink') + + sfp_d_source = xgmii_ep.XGMIISource() + sfp_d_source_logic = sfp_d_source.create_logic(clk, rst, txd=sfp_d_rxd, txc=sfp_d_rxc, name='sfp_d_source') + + sfp_d_sink = xgmii_ep.XGMIISink() + sfp_d_sink_logic = sfp_d_sink.create_logic(clk, rst, rxd=sfp_d_txd, rxc=sfp_d_txc, name='sfp_d_sink') + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + btn=btn, + sw=sw, + led=led, + led_bkt=led_bkt, + led_hex0_d=led_hex0_d, + led_hex0_dp=led_hex0_dp, + led_hex1_d=led_hex1_d, + led_hex1_dp=led_hex1_dp, + + sfp_a_txd=sfp_a_txd, + sfp_a_txc=sfp_a_txc, + sfp_a_rxd=sfp_a_rxd, + sfp_a_rxc=sfp_a_rxc, + sfp_b_txd=sfp_b_txd, + sfp_b_txc=sfp_b_txc, + sfp_b_rxd=sfp_b_rxd, + sfp_b_rxc=sfp_b_rxc, + sfp_c_txd=sfp_c_txd, + sfp_c_txc=sfp_c_txc, + sfp_c_rxd=sfp_c_rxd, + sfp_c_rxc=sfp_c_rxc, + sfp_d_txd=sfp_d_txd, + sfp_d_txc=sfp_d_txc, + sfp_d_rxd=sfp_d_rxd, + sfp_d_rxc=sfp_d_rxc + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + sfp_a_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while sfp_a_sink.empty(): + yield clk.posedge + + rx_frame = sfp_a_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + sfp_a_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while sfp_a_sink.empty(): + yield clk.posedge + + rx_frame = sfp_a_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert sfp_a_source.empty() + assert sfp_a_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/DE5-Net/fpga/tb/test_fpga_core.v b/fpga/lib/eth/example/DE5-Net/fpga/tb/test_fpga_core.v new file mode 100644 index 000000000..d441f0cce --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/tb/test_fpga_core.v @@ -0,0 +1,137 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [3:0] btn = 0; +reg [3:0] sw = 0; +reg [63:0] sfp_a_rxd = 0; +reg [7:0] sfp_a_rxc = 0; +reg [63:0] sfp_b_rxd = 0; +reg [7:0] sfp_b_rxc = 0; +reg [63:0] sfp_c_rxd = 0; +reg [7:0] sfp_c_rxc = 0; +reg [63:0] sfp_d_rxd = 0; +reg [7:0] sfp_d_rxc = 0; + +// Outputs +wire [3:0] led; +wire [3:0] led_bkt; +wire [6:0] led_hex0_d; +wire led_hex0_dp; +wire [6:0] led_hex1_d; +wire led_hex1_dp; +wire [63:0] sfp_a_txd; +wire [7:0] sfp_a_txc; +wire [63:0] sfp_b_txd; +wire [7:0] sfp_b_txc; +wire [63:0] sfp_c_txd; +wire [7:0] sfp_c_txc; +wire [63:0] sfp_d_txd; +wire [7:0] sfp_d_txc; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + btn, + sw, + sfp_a_rxd, + sfp_a_rxc, + sfp_b_rxd, + sfp_b_rxc, + sfp_c_rxd, + sfp_c_rxc, + sfp_d_rxd, + sfp_d_rxc + ); + $to_myhdl( + led, + led_bkt, + led_hex0_d, + led_hex0_dp, + led_hex1_d, + led_hex1_dp, + sfp_a_txd, + sfp_a_txc, + sfp_b_txd, + sfp_b_txc, + sfp_c_txd, + sfp_c_txc, + sfp_d_txd, + sfp_d_txc + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .btn(btn), + .sw(sw), + .led(led), + .led_bkt(led_bkt), + .led_hex0_d(led_hex0_d), + .led_hex0_dp(led_hex0_dp), + .led_hex1_d(led_hex1_d), + .led_hex1_dp(led_hex1_dp), + .sfp_a_txd(sfp_a_txd), + .sfp_a_txc(sfp_a_txc), + .sfp_a_rxd(sfp_a_rxd), + .sfp_a_rxc(sfp_a_rxc), + .sfp_b_txd(sfp_b_txd), + .sfp_b_txc(sfp_b_txc), + .sfp_b_rxd(sfp_b_rxd), + .sfp_b_rxc(sfp_b_rxc), + .sfp_c_txd(sfp_c_txd), + .sfp_c_txc(sfp_c_txc), + .sfp_c_rxd(sfp_c_rxd), + .sfp_c_rxc(sfp_c_rxc), + .sfp_d_txd(sfp_d_txd), + .sfp_d_txc(sfp_d_txc), + .sfp_d_rxd(sfp_d_rxd), + .sfp_d_rxc(sfp_d_rxc) +); + +endmodule diff --git a/fpga/lib/eth/example/DE5-Net/fpga/tb/udp_ep.py b/fpga/lib/eth/example/DE5-Net/fpga/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/DE5-Net/fpga/tb/xgmii_ep.py b/fpga/lib/eth/example/DE5-Net/fpga/tb/xgmii_ep.py new file mode 120000 index 000000000..63b6d3567 --- /dev/null +++ b/fpga/lib/eth/example/DE5-Net/fpga/tb/xgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/xgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/Makefile b/fpga/lib/eth/example/ExaNIC_X10/fpga/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/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/fpga/lib/eth/example/ExaNIC_X10/fpga/README.md b/fpga/lib/eth/example/ExaNIC_X10/fpga/README.md new file mode 100644 index 000000000..c11efb3d7 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/README.md @@ -0,0 +1,25 @@ +# Verilog Ethernet ExaNIC X10 Example Design + +## Introduction + +This example design targets the Exablaze ExaNIC X10 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: xcku035-fbva676-2-c +PHY: 10G BASE-R PHY IP core and internal GTH transceiver + +## 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 ExaNIC X10 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. + + diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/common/vivado.mk b/fpga/lib/eth/example/ExaNIC_X10/fpga/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/ExaNIC_X10/fpga/fpga.xdc b/fpga/lib/eth/example/ExaNIC_X10/fpga/fpga.xdc new file mode 100644 index 000000000..b65411da0 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/fpga.xdc @@ -0,0 +1,152 @@ +# XDC constraints for the ExaNIC X10 +# part: xcku035-fbva676-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.UNUSEDPIN Pullup [current_design] +set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design] +set_property BITSTREAM.CONFIG.BPI_SYNC_MODE Type2 [current_design] +set_property CONFIG_MODE BPI16 [current_design] + +# 100 MHz system clock +set_property -dict {LOC D18 IOSTANDARD LVDS} [get_ports clk_100mhz_p] +set_property -dict {LOC C18 IOSTANDARD LVDS} [get_ports clk_100mhz_n] +create_clock -period 10 -name clk_100mhz [get_ports clk_100mhz_p] + +# LEDs +set_property -dict {LOC A25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_1_led[0]}] +set_property -dict {LOC A24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_1_led[1]}] +set_property -dict {LOC E23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_2_led[0]}] +set_property -dict {LOC D26 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_2_led[1]}] +set_property -dict {LOC C23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sma_led[0]}] +set_property -dict {LOC D23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sma_led[1]}] + +# GPIO +#set_property -dict {LOC W26 IOSTANDARD LVCMOS18} [get_ports gpio[0]] +#set_property -dict {LOC Y26 IOSTANDARD LVCMOS18} [get_ports gpio[1]] +#set_property -dict {LOC AB26 IOSTANDARD LVCMOS18} [get_ports gpio[2]] +#set_property -dict {LOC AC26 IOSTANDARD LVCMOS18} [get_ports gpio[3]] + +# SMA +#set_property -dict {LOC B17 IOSTANDARD LVCMOS18} [get_ports sma_in] +#set_property -dict {LOC B16 IOSTANDARD LVCMOS18} [get_ports sma_out] +#set_property -dict {LOC B19 IOSTANDARD LVCMOS18} [get_ports sma_out_en] +#set_property -dict {LOC C16 IOSTANDARD LVCMOS18} [get_ports sma_term_en] + +# SFP+ Interface +set_property -dict {LOC D2 } [get_ports sfp_1_rx_p] ;# MGTHTXN0_227 GTHE3_CHANNEL_X0Y12 / GTHE3_COMMON_X0Y3 +#set_property -dict {LOC D1 } [get_ports sfp_1_rx_n] ;# MGTHTXP0_227 GTHE3_CHANNEL_X0Y12 / GTHE3_COMMON_X0Y3 +set_property -dict {LOC E4 } [get_ports sfp_1_tx_p] ;# MGTHTXN0_227 GTHE3_CHANNEL_X0Y12 / GTHE3_COMMON_X0Y3 +#set_property -dict {LOC E3 } [get_ports sfp_1_tx_n] ;# MGTHTXP0_227 GTHE3_CHANNEL_X0Y12 / GTHE3_COMMON_X0Y3 +set_property -dict {LOC C4 } [get_ports sfp_2_rx_p] ;# MGTHTXN1_227 GTHE3_CHANNEL_X0Y13 / GTHE3_COMMON_X0Y3 +#set_property -dict {LOC C3 } [get_ports sfp_2_rx_n] ;# MGTHTXP1_227 GTHE3_CHANNEL_X0Y13 / GTHE3_COMMON_X0Y3 +set_property -dict {LOC D6 } [get_ports sfp_2_tx_p] ;# MGTHTXN1_227 GTHE3_CHANNEL_X0Y13 / GTHE3_COMMON_X0Y3 +#set_property -dict {LOC D5 } [get_ports sfp_2_tx_n] ;# MGTHTXP1_227 GTHE3_CHANNEL_X0Y13 / GTHE3_COMMON_X0Y3 +set_property -dict {LOC H6 } [get_ports sfp_mgt_refclk_p] ;# MGTREFCLK0P_227 from X2 +#set_property -dict {LOC H5 } [get_ports sfp_mgt_refclk_n] ;# MGTREFCLK0N_227 from X2 +set_property -dict {LOC AA12 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports sfp_1_tx_disable] +set_property -dict {LOC W14 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports sfp_2_tx_disable] +set_property -dict {LOC C24 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_1_npres] +set_property -dict {LOC D24 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_2_npres] +set_property -dict {LOC W13 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_1_los] +set_property -dict {LOC AB12 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_2_los] +set_property -dict {LOC B25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports sfp_1_rs] +set_property -dict {LOC D25 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports sfp_2_rs] +#set_property -dict {LOC W11 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports sfp_i2c_scl] +#set_property -dict {LOC Y11 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports sfp_1_i2c_sda] +#set_property -dict {LOC Y13 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports sfp_2_i2c_sda] + +# 161.1328125 MHz MGT reference clock +create_clock -period 6.206 -name sfp_mgt_refclk [get_ports sfp_mgt_refclk_p] + +# I2C interface +#set_property -dict {LOC B26 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports eeprom_i2c_scl] +#set_property -dict {LOC C26 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports eeprom_i2c_sda] + +# PCIe Interface +#set_property -dict {LOC AF2 } [get_ports {pcie_rx_p[7]}] ;# MGTHTXN0_224 GTHE3_CHANNEL_X0Y0 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AF1 } [get_ports {pcie_rx_n[7]}] ;# MGTHTXP0_224 GTHE3_CHANNEL_X0Y0 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AF6 } [get_ports {pcie_tx_p[7]}] ;# MGTHTXN0_224 GTHE3_CHANNEL_X0Y0 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AF5 } [get_ports {pcie_tx_n[7]}] ;# MGTHTXP0_224 GTHE3_CHANNEL_X0Y0 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AE4 } [get_ports {pcie_rx_p[6]}] ;# MGTHTXN1_224 GTHE3_CHANNEL_X0Y1 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AE3 } [get_ports {pcie_rx_n[6]}] ;# MGTHTXP1_224 GTHE3_CHANNEL_X0Y1 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AD6 } [get_ports {pcie_tx_p[6]}] ;# MGTHTXN1_224 GTHE3_CHANNEL_X0Y1 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AD5 } [get_ports {pcie_tx_n[6]}] ;# MGTHTXP1_224 GTHE3_CHANNEL_X0Y1 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AD2 } [get_ports {pcie_rx_p[5]}] ;# MGTHTXN2_224 GTHE3_CHANNEL_X0Y2 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AD1 } [get_ports {pcie_rx_n[5]}] ;# MGTHTXP2_224 GTHE3_CHANNEL_X0Y2 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AC4 } [get_ports {pcie_tx_p[5]}] ;# MGTHTXN2_224 GTHE3_CHANNEL_X0Y2 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AC3 } [get_ports {pcie_tx_n[5]}] ;# MGTHTXP2_224 GTHE3_CHANNEL_X0Y2 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AB2 } [get_ports {pcie_rx_p[4]}] ;# MGTHTXN3_224 GTHE3_CHANNEL_X0Y3 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AB1 } [get_ports {pcie_rx_n[4]}] ;# MGTHTXP3_224 GTHE3_CHANNEL_X0Y3 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AB6 } [get_ports {pcie_tx_p[4]}] ;# MGTHTXN3_224 GTHE3_CHANNEL_X0Y3 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC AB5 } [get_ports {pcie_tx_n[4]}] ;# MGTHTXP3_224 GTHE3_CHANNEL_X0Y3 / GTHE3_COMMON_X0Y0 +#set_property -dict {LOC Y2 } [get_ports {pcie_rx_p[3]}] ;# MGTHTXN0_225 GTHE3_CHANNEL_X0Y4 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC Y1 } [get_ports {pcie_rx_n[3]}] ;# MGTHTXP0_225 GTHE3_CHANNEL_X0Y4 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC AA4 } [get_ports {pcie_tx_p[3]}] ;# MGTHTXN0_225 GTHE3_CHANNEL_X0Y4 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC AA3 } [get_ports {pcie_tx_n[3]}] ;# MGTHTXP0_225 GTHE3_CHANNEL_X0Y4 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC V2 } [get_ports {pcie_rx_p[2]}] ;# MGTHTXN1_225 GTHE3_CHANNEL_X0Y5 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC V1 } [get_ports {pcie_rx_n[2]}] ;# MGTHTXP1_225 GTHE3_CHANNEL_X0Y5 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC W4 } [get_ports {pcie_tx_p[2]}] ;# MGTHTXN1_225 GTHE3_CHANNEL_X0Y5 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC W3 } [get_ports {pcie_tx_n[2]}] ;# MGTHTXP1_225 GTHE3_CHANNEL_X0Y5 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC T2 } [get_ports {pcie_rx_p[1]}] ;# MGTHTXN2_225 GTHE3_CHANNEL_X0Y6 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC T1 } [get_ports {pcie_rx_n[1]}] ;# MGTHTXP2_225 GTHE3_CHANNEL_X0Y6 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC U4 } [get_ports {pcie_tx_p[1]}] ;# MGTHTXN2_225 GTHE3_CHANNEL_X0Y6 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC U3 } [get_ports {pcie_tx_n[1]}] ;# MGTHTXP2_225 GTHE3_CHANNEL_X0Y6 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC P2 } [get_ports {pcie_rx_p[0]}] ;# MGTHTXN3_225 GTHE3_CHANNEL_X0Y7 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC P1 } [get_ports {pcie_rx_n[0]}] ;# MGTHTXP3_225 GTHE3_CHANNEL_X0Y7 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC R4 } [get_ports {pcie_tx_p[0]}] ;# MGTHTXN3_225 GTHE3_CHANNEL_X0Y7 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC R3 } [get_ports {pcie_tx_n[0]}] ;# MGTHTXP3_225 GTHE3_CHANNEL_X0Y7 / GTHE3_COMMON_X0Y1 +#set_property -dict {LOC T6 } [get_ports pcie_mgt_refclk_p] ;# MGTREFCLK0P_225 +#set_property -dict {LOC T5 } [get_ports pcie_mgt_refclk_n] ;# MGTREFCLK0N_225 +#set_property -dict {LOC AC22 IOSTANDARD LVCMOS18 PULLUP true} [get_ports pcie_reset_n] + +# 100 MHz MGT reference clock +#create_clock -period 10 -name pcie_mgt_refclk [get_ports pcie_mgt_refclk_p] + +# Flash +#set_property -dict {LOC AE10 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[0]}] +#set_property -dict {LOC AC8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[1]}] +#set_property -dict {LOC AD10 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[2]}] +#set_property -dict {LOC AD9 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[3]}] +#set_property -dict {LOC AC11 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[4]}] +#set_property -dict {LOC AF10 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[5]}] +#set_property -dict {LOC AF14 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[6]}] +#set_property -dict {LOC AE12 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[7]}] +#set_property -dict {LOC AD14 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[8]}] +#set_property -dict {LOC AF13 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[9]}] +#set_property -dict {LOC AE13 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[10]}] +#set_property -dict {LOC AD8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[11]}] +#set_property -dict {LOC AC13 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[12]}] +#set_property -dict {LOC AD13 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[13]}] +#set_property -dict {LOC AA14 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[14]}] +#set_property -dict {LOC AB15 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_dq[15]}] +#set_property -dict {LOC AD11 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[0]}] +#set_property -dict {LOC AE11 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[1]}] +#set_property -dict {LOC AF12 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[2]}] +#set_property -dict {LOC AB11 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[3]}] +#set_property -dict {LOC AB9 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[4]}] +#set_property -dict {LOC AB14 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[5]}] +#set_property -dict {LOC AA10 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[6]}] +#set_property -dict {LOC AA9 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[7]}] +#set_property -dict {LOC W10 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[8]}] +#set_property -dict {LOC AA13 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[9]}] +#set_property -dict {LOC Y15 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[10]}] +#set_property -dict {LOC AC12 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[11]}] +#set_property -dict {LOC V12 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[12]}] +#set_property -dict {LOC V11 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[13]}] +#set_property -dict {LOC Y12 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[14]}] +#set_property -dict {LOC W9 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[15]}] +#set_property -dict {LOC Y8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[16]}] +#set_property -dict {LOC W8 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[17]}] +#set_property -dict {LOC W15 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[18]}] +#set_property -dict {LOC AA15 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[19]}] +#set_property -dict {LOC AE16 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[20]}] +#set_property -dict {LOC AF15 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[21]}] +#set_property -dict {LOC AE15 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_addr[22]}] +#set_property -dict {LOC AD15 IOSTANDARD LVCMOS18 PULLUP true} [get_ports {flash_region}] +#set_property -dict {LOC AC9 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_ce_n}] +#set_property -dict {LOC AC14 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_oe_n}] +#set_property -dict {LOC AB10 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_we_n}] +#set_property -dict {LOC Y10 IOSTANDARD LVCMOS18 DRIVE 16} [get_ports {flash_adv_n}] diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/fpga/Makefile b/fpga/lib/eth/example/ExaNIC_X10/fpga/fpga/Makefile new file mode 100644 index 000000000..5533ee52a --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/fpga/Makefile @@ -0,0 +1,106 @@ + +# FPGA settings +FPGA_PART = xcku035-fbva676-2-e +FPGA_TOP = fpga +FPGA_ARCH = kintexu + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += lib/eth/rtl/eth_mac_10g_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_10g.v +SYN_FILES += lib/eth/rtl/axis_xgmii_rx_64.v +SYN_FILES += lib/eth/rtl/axis_xgmii_tx_64.v +SYN_FILES += lib/eth/rtl/eth_phy_10g.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_if.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_frame_sync.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_ber_mon.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx_if.v +SYN_FILES += lib/eth/rtl/xgmii_baser_dec_64.v +SYN_FILES += lib/eth/rtl/xgmii_baser_enc_64.v +SYN_FILES += lib/eth/rtl/lfsr.v +SYN_FILES += lib/eth/rtl/eth_axis_rx_64.v +SYN_FILES += lib/eth/rtl/eth_axis_tx_64.v +SYN_FILES += lib/eth/rtl/udp_complete_64.v +SYN_FILES += lib/eth/rtl/udp_checksum_gen_64.v +SYN_FILES += lib/eth/rtl/udp_64.v +SYN_FILES += lib/eth/rtl/udp_ip_rx_64.v +SYN_FILES += lib/eth/rtl/udp_ip_tx_64.v +SYN_FILES += lib/eth/rtl/ip_complete_64.v +SYN_FILES += lib/eth/rtl/ip_64.v +SYN_FILES += lib/eth/rtl/ip_eth_rx_64.v +SYN_FILES += lib/eth/rtl/ip_eth_tx_64.v +SYN_FILES += lib/eth/rtl/ip_arb_mux.v +SYN_FILES += lib/eth/rtl/arp_64.v +SYN_FILES += lib/eth/rtl/arp_cache.v +SYN_FILES += lib/eth/rtl/arp_eth_rx_64.v +SYN_FILES += lib/eth/rtl/arp_eth_tx_64.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 + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl + +# IP +XCI_FILES = ip/gtwizard_ultrascale_0.xci + +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 + +%.mcs %.prm: %.bit + echo "write_cfgmem -force -format mcs -size 32 -interface BPIx16 -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 .mcs .prm; \ + do cp $*$$x rev/$*_rev$$COUNT$$x; \ + echo "Output: rev/$*_rev$$COUNT$$x"; done; + +flash: $(FPGA_TOP).mcs $(FPGA_TOP).prm + echo "open_hw" > 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 {28f256p30t-bpi-x16}] 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 \"$(FPGA_TOP).mcs\"] [current_hw_cfgmem]" >> flash.tcl + echo "set_property PROGRAM.PRM_FILES [list \"$(FPGA_TOP).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.BPI_RS_PINS {none} [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 + diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/ip/gtwizard_ultrascale_0.xci b/fpga/lib/eth/example/ExaNIC_X10/fpga/ip/gtwizard_ultrascale_0.xci new file mode 100644 index 000000000..3380b154b --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/ip/gtwizard_ultrascale_0.xci @@ -0,0 +1,1406 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gtwizard_ultrascale_0 + + + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000" + 1 + 2578.125 + 0 + 0 + 125 + 17 + 0 + 2 + 0 + 2 + 0 + 0 + 1 + 0 + 1 + 1 + 250 + 0 + 0 + 0 + 0 + 0 + 1 + "00000000" + "00000000" + 1 + 1 + 0 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000" + 0 + "00000000" + 1 + 0 + 5000 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + 0 + "1010000011" + 0 + "0101111100" + 4 + 1 + 32 + 10.3125 + 12 + 1 + 312.5000000 + 4 + 0 + 0x000000000000000000000000000000000000000000000000 + 161.1328125 + 0 + 0 + 0 + 1 + 2 + 0 + 64 + 156.25 + 312.5000000 + 0 + 257.8125 + 0 + 2 + 1 + 0 + 0 + 0 + 312.5 + 0 + 0 + 1 + 4 + 1 + 32 + 10.3125 + 12 + 1 + 312.5000000 + 4 + 0 + 161.1328125 + 0 + 0 + 1 + 2 + 0 + 64 + 156.25 + 312.5000000 + 0 + X0Y13 X0Y12 + gtwizard_ultrascale_0 + 0 + 0 + rxpolarity_in txpolarity_in + 125 + BOTH + 0 + GTH + 2 + 20 + 96 + 1 + gthe3 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + -1 + -1 + -1 + -1 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + -1 + 0 + -1 + -1 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + -1 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + -1 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + 1 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + 1 + 1 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 0 + 0 + -1 + -1 + -1 + 0 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 13 + 0 + 10GBASE-R + 4 + 312.5000000 + 2 + 1 + 312.5000000 + false + CORE + NONE + CORE + CORE + EXAMPLE_DESIGN + CORE + EXAMPLE_DESIGN + EXAMPLE_DESIGN + false + NAME + false + 250 + false + false + 250 + GTH-10GBASE-R + 0 + MULTI + 1 + ENABLE + DISABLE + ENABLE + 00000000 + false + false + false + false + false + false + false + false + 00000000 + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 1 + 1 + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + false + false + false + false + false + false + false + false + 00000000 + DISABLE + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 0 + 5000 + ENABLE + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 1 + false + 0000000000 + false + 1010000011 + NONE + false + 0101111100 + true + 0 + AC + 64B66B_ASYNC + true + AUTO + 32 + 6.1862627 + -20 + 10.3125 + X0Y12 + RXPROGDIVCLK + QPLL0 + 200 + 0 + + 161.1328125 + + OFF + 0 + PROGRAMMABLE + 800 + 64 + 15 + false + 0 + 10.3125 + 257.8125 + 0 + false + QPLL0 + 312.5 + 1 + ENABLE + 64B66B_ASYNC + CUSTOM + true + 32 + 10.3125 + X0Y12 + TXPROGDIVCLK + QPLL0 + 0 + 161.1328125 + + 64 + false + 1 + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + true + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + true + true + false + true + true + true + false + true + true + true + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + kintexu + + + xcku035 + fbva676 + VERILOG + + MIXED + -2 + + E + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/lib/eth b/fpga/lib/eth/example/ExaNIC_X10/fpga/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/fpga.v b/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/fpga.v new file mode 100644 index 000000000..8392a87d6 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/fpga.v @@ -0,0 +1,497 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 100MHz LVDS + */ + input wire clk_100mhz_p, + input wire clk_100mhz_n, + + /* + * GPIO + */ + output wire [1:0] sfp_1_led, + output wire [1:0] sfp_2_led, + output wire [1:0] sma_led, + + /* + * Ethernet: SFP+ + */ + input wire sfp_1_rx_p, + input wire sfp_1_rx_n, + output wire sfp_1_tx_p, + output wire sfp_1_tx_n, + input wire sfp_2_rx_p, + input wire sfp_2_rx_n, + output wire sfp_2_tx_p, + output wire sfp_2_tx_n, + input wire sfp_mgt_refclk_p, + input wire sfp_mgt_refclk_n, + output wire sfp_1_tx_disable, + output wire sfp_2_tx_disable, + input wire sfp_1_npres, + input wire sfp_2_npres, + input wire sfp_1_los, + input wire sfp_2_los, + output wire sfp_1_rs, + output wire sfp_2_rs +); + +// Clock and reset + +wire clk_100mhz_ibufg; +wire clk_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire rst_125mhz_int; + +// Internal 156.25 MHz clock +wire clk_156mhz_int; +wire rst_156mhz_int; + +wire mmcm_rst = 1'b0; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS #( + .DIFF_TERM("FALSE"), + .IBUF_LOW_PWR("FALSE") +) +clk_100mhz_ibufg_inst ( + .O (clk_100mhz_ibufg), + .I (clk_100mhz_p), + .IB (clk_100mhz_n) +); + +// MMCM instance +// 100 MHz in, 125 MHz out +// PFD range: 10 MHz to 500 MHz +// VCO range: 600 MHz to 1440 MHz +// M = 10, D = 1 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +MMCME3_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(8), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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(10), + .CLKFBOUT_PHASE(0), + .DIVCLK_DIVIDE(1), + .REF_JITTER1(0.010), + .CLKIN1_PERIOD(10.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_100mhz_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_125mhz_bufg_inst ( + .I(clk_125mhz_mmcm_out), + .O(clk_125mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_int) +); + +// GPIO +wire [1:0] sfp_1_led_int; +wire [1:0] sfp_2_led_int; +wire [1:0] sma_led_int; + +// XGMII 10G PHY + +assign sfp_1_tx_disable = 1'b0; +assign sfp_2_tx_disable = 1'b0; +assign sfp_1_rs = 1'b1; +assign sfp_2_rs = 1'b1; + +wire sfp_1_tx_clk_int; +wire sfp_1_tx_rst_int; +wire [63:0] sfp_1_txd_int; +wire [7:0] sfp_1_txc_int; +wire sfp_1_rx_clk_int; +wire sfp_1_rx_rst_int; +wire [63:0] sfp_1_rxd_int; +wire [7:0] sfp_1_rxc_int; +wire sfp_2_tx_clk_int; +wire sfp_2_tx_rst_int; +wire [63:0] sfp_2_txd_int; +wire [7:0] sfp_2_txc_int; +wire sfp_2_rx_clk_int; +wire sfp_2_rx_rst_int; +wire [63:0] sfp_2_rxd_int; +wire [7:0] sfp_2_rxc_int; + +wire sfp_1_rx_block_lock; +wire sfp_2_rx_block_lock; + +wire sfp_mgt_refclk; + +wire [1:0] gt_txclkout; +wire gt_txusrclk; +wire gt_txusrclk2; + +wire [1:0] gt_rxclkout; +wire [1:0] gt_rxusrclk; +wire [1:0] gt_rxusrclk2; + +wire gt_reset_tx_done; +wire gt_reset_rx_done; + +wire [1:0] gt_txprgdivresetdone; +wire [1:0] gt_txpmaresetdone; +wire [1:0] gt_rxprgdivresetdone; +wire [1:0] gt_rxpmaresetdone; + +wire gt_tx_reset = ~((>_txprgdivresetdone) & (>_txpmaresetdone)); +wire gt_rx_reset = ~>_rxpmaresetdone; + +reg gt_userclk_tx_active = 1'b0; +reg [1:0] gt_userclk_rx_active = 1'b0; + +IBUFDS_GTE3 ibufds_gte3_sfp_mgt_refclk_inst ( + .I (sfp_mgt_refclk_p), + .IB (sfp_mgt_refclk_n), + .CEB (1'b0), + .O (sfp_mgt_refclk), + .ODIV2 () +); + +BUFG_GT bufg_gt_tx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_tx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_txclkout[0]), + .O (gt_txusrclk) +); + +BUFG_GT bufg_gt_tx_usrclk2_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_tx_reset), + .CLRMASK (1'b0), + .DIV (3'd1), + .I (gt_txclkout[0]), + .O (gt_txusrclk2) +); + +assign clk_156mhz_int = gt_txusrclk2; + +always @(posedge gt_txusrclk, posedge gt_tx_reset) begin + if (gt_tx_reset) begin + gt_userclk_tx_active <= 1'b0; + end else begin + gt_userclk_tx_active <= 1'b1; + end +end + +genvar n; + +generate + +for (n = 0 ; n < 2; n = n + 1) begin + + BUFG_GT bufg_gt_rx_usrclk_0_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_rx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_rxclkout[n]), + .O (gt_rxusrclk[n]) + ); + + BUFG_GT bufg_gt_rx_usrclk2_0_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_rx_reset), + .CLRMASK (1'b0), + .DIV (3'd1), + .I (gt_rxclkout[n]), + .O (gt_rxusrclk2[n]) + ); + + always @(posedge gt_rxusrclk[n], posedge gt_rx_reset) begin + if (gt_rx_reset) begin + gt_userclk_rx_active[n] <= 1'b0; + end else begin + gt_userclk_rx_active[n] <= 1'b1; + end + end + +end + +endgenerate + +sync_reset #( + .N(4) +) +sync_reset_156mhz_inst ( + .clk(clk_156mhz_int), + .rst(~gt_reset_tx_done), + .sync_reset_out(rst_156mhz_int) +); + +wire [5:0] sfp_1_gt_txheader; +wire [127:0] sfp_1_gt_txdata; +wire sfp_1_gt_rxgearboxslip; +wire [5:0] sfp_1_gt_rxheader; +wire [1:0] sfp_1_gt_rxheadervalid; +wire [127:0] sfp_1_gt_rxdata; +wire [1:0] sfp_1_gt_rxdatavalid; + +wire [5:0] sfp_2_gt_txheader; +wire [127:0] sfp_2_gt_txdata; +wire sfp_2_gt_rxgearboxslip; +wire [5:0] sfp_2_gt_rxheader; +wire [1:0] sfp_2_gt_rxheadervalid; +wire [127:0] sfp_2_gt_rxdata; +wire [1:0] sfp_2_gt_rxdatavalid; + +gtwizard_ultrascale_0 +sfp_gth_inst ( + .gtwiz_userclk_tx_active_in(>_userclk_tx_active), + .gtwiz_userclk_rx_active_in(>_userclk_rx_active), + + .gtwiz_reset_clk_freerun_in(clk_125mhz_int), + .gtwiz_reset_all_in(rst_125mhz_int), + + .gtwiz_reset_tx_pll_and_datapath_in(1'b0), + .gtwiz_reset_tx_datapath_in(1'b0), + + .gtwiz_reset_rx_pll_and_datapath_in(1'b0), + .gtwiz_reset_rx_datapath_in(1'b0), + + .gtwiz_reset_rx_cdr_stable_out(), + + .gtwiz_reset_tx_done_out(gt_reset_tx_done), + .gtwiz_reset_rx_done_out(gt_reset_rx_done), + + .gtrefclk00_in(sfp_mgt_refclk), + + .qpll0outclk_out(), + .qpll0outrefclk_out(), + + .gthrxn_in({sfp_2_rx_n, sfp_1_rx_n}), + .gthrxp_in({sfp_2_rx_p, sfp_1_rx_p}), + + .rxusrclk_in(gt_rxusrclk), + .rxusrclk2_in(gt_rxusrclk2), + + .txdata_in({sfp_2_gt_txdata, sfp_1_gt_txdata}), + .txheader_in({sfp_2_gt_txheader, sfp_1_gt_txheader}), + .txsequence_in({2{7'b0}}), + + .txusrclk_in({2{gt_txusrclk}}), + .txusrclk2_in({2{gt_txusrclk2}}), + + .gtpowergood_out(), + + .gthtxn_out({sfp_2_tx_n, sfp_1_tx_n}), + .gthtxp_out({sfp_2_tx_p, sfp_1_tx_p}), + + .txpolarity_in(2'b11), + .rxpolarity_in(2'b00), + + .rxgearboxslip_in({sfp_2_gt_rxgearboxslip, sfp_1_gt_rxgearboxslip}), + .rxdata_out({sfp_2_gt_rxdata, sfp_1_gt_rxdata}), + .rxdatavalid_out({sfp_2_gt_rxdatavalid, sfp_1_gt_rxdatavalid}), + .rxheader_out({sfp_2_gt_rxheader, sfp_1_gt_rxheader}), + .rxheadervalid_out({sfp_2_gt_rxheadervalid, sfp_1_gt_rxheadervalid}), + .rxoutclk_out(gt_rxclkout), + .rxpmaresetdone_out(gt_rxpmaresetdone), + .rxprgdivresetdone_out(gt_rxprgdivresetdone), + .rxstartofseq_out(), + + .txoutclk_out(gt_txclkout), + .txpmaresetdone_out(gt_txpmaresetdone), + .txprgdivresetdone_out(gt_txprgdivresetdone) +); + +assign sfp_1_tx_clk_int = clk_156mhz_int; +assign sfp_1_tx_rst_int = rst_156mhz_int; + +assign sfp_1_rx_clk_int = gt_rxusrclk2[0]; + +sync_reset #( + .N(4) +) +sfp_1_rx_rst_reset_sync_inst ( + .clk(sfp_1_rx_clk_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(sfp_1_rx_rst_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +sfp_1_phy_inst ( + .tx_clk(sfp_1_tx_clk_int), + .tx_rst(sfp_1_tx_rst_int), + .rx_clk(sfp_1_rx_clk_int), + .rx_rst(sfp_1_rx_rst_int), + .xgmii_txd(sfp_1_txd_int), + .xgmii_txc(sfp_1_txc_int), + .xgmii_rxd(sfp_1_rxd_int), + .xgmii_rxc(sfp_1_rxc_int), + .serdes_tx_data(sfp_1_gt_txdata), + .serdes_tx_hdr(sfp_1_gt_txheader), + .serdes_rx_data(sfp_1_gt_rxdata), + .serdes_rx_hdr(sfp_1_gt_rxheader), + .serdes_rx_bitslip(sfp_1_gt_rxgearboxslip), + .rx_block_lock(sfp_1_rx_block_lock), + .rx_high_ber() +); + +assign sfp_2_tx_clk_int = clk_156mhz_int; +assign sfp_2_tx_rst_int = rst_156mhz_int; + +assign sfp_2_rx_clk_int = gt_rxusrclk2[1]; + +sync_reset #( + .N(4) +) +sfp_2_rx_rst_reset_sync_inst ( + .clk(sfp_2_rx_clk_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(sfp_2_rx_rst_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +sfp_2_phy_inst ( + .tx_clk(sfp_2_tx_clk_int), + .tx_rst(sfp_2_tx_rst_int), + .rx_clk(sfp_2_rx_clk_int), + .rx_rst(sfp_2_rx_rst_int), + .xgmii_txd(sfp_2_txd_int), + .xgmii_txc(sfp_2_txc_int), + .xgmii_rxd(sfp_2_rxd_int), + .xgmii_rxc(sfp_2_rxc_int), + .serdes_tx_data(sfp_2_gt_txdata), + .serdes_tx_hdr(sfp_2_gt_txheader), + .serdes_rx_data(sfp_2_gt_rxdata), + .serdes_rx_hdr(sfp_2_gt_rxheader), + .serdes_rx_bitslip(sfp_2_gt_rxgearboxslip), + .rx_block_lock(sfp_2_rx_block_lock), + .rx_high_ber() +); + +assign sfp_1_led[0] = sfp_1_rx_block_lock; +assign sfp_1_led[1] = 1'b0; +assign sfp_2_led[0] = sfp_2_rx_block_lock; +assign sfp_2_led[1] = 1'b0; +assign sma_led = sma_led_int; + +fpga_core +core_inst ( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + .clk(clk_156mhz_int), + .rst(rst_156mhz_int), + /* + * GPIO + */ + .sfp_1_led(sfp_1_led_int), + .sfp_2_led(sfp_2_led_int), + .sma_led(sma_led_int), + /* + * Ethernet: SFP+ + */ + .sfp_1_tx_clk(sfp_1_tx_clk_int), + .sfp_1_tx_rst(sfp_1_tx_rst_int), + .sfp_1_txd(sfp_1_txd_int), + .sfp_1_txc(sfp_1_txc_int), + .sfp_1_rx_clk(sfp_1_rx_clk_int), + .sfp_1_rx_rst(sfp_1_rx_rst_int), + .sfp_1_rxd(sfp_1_rxd_int), + .sfp_1_rxc(sfp_1_rxc_int), + .sfp_2_tx_clk(sfp_2_tx_clk_int), + .sfp_2_tx_rst(sfp_2_tx_rst_int), + .sfp_2_txd(sfp_2_txd_int), + .sfp_2_txc(sfp_2_txc_int), + .sfp_2_rx_clk(sfp_2_rx_clk_int), + .sfp_2_rx_rst(sfp_2_rx_rst_int), + .sfp_2_rxd(sfp_2_rxd_int), + .sfp_2_rxc(sfp_2_rxc_int) +); + +endmodule diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/fpga_core.v b/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/fpga_core.v new file mode 100644 index 000000000..952c0f399 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/fpga_core.v @@ -0,0 +1,588 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core +( + /* + * Clock: 156.25MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + + /* + * GPIO + */ + output wire [1:0] sfp_1_led, + output wire [1:0] sfp_2_led, + output wire [1:0] sma_led, + + /* + * Ethernet: SFP+ + */ + input wire sfp_1_tx_clk, + input wire sfp_1_tx_rst, + output wire [63:0] sfp_1_txd, + output wire [7:0] sfp_1_txc, + input wire sfp_1_rx_clk, + input wire sfp_1_rx_rst, + input wire [63:0] sfp_1_rxd, + input wire [7:0] sfp_1_rxc, + input wire sfp_2_tx_clk, + input wire sfp_2_tx_rst, + output wire [63:0] sfp_2_txd, + output wire [7:0] sfp_2_txc, + input wire sfp_2_rx_clk, + input wire sfp_2_rx_rst, + input wire [63:0] sfp_2_rxd, + input wire [7:0] sfp_2_rxc +); + +// AXI between MAC and Ethernet modules +wire [63:0] rx_axis_tdata; +wire [7:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tready; +wire rx_axis_tlast; +wire rx_axis_tuser; + +wire [63:0] tx_axis_tdata; +wire [7:0] tx_axis_tkeep; +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 [63:0] rx_eth_payload_axis_tdata; +wire [7:0] rx_eth_payload_axis_tkeep; +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 [63:0] tx_eth_payload_axis_tdata; +wire [7:0] tx_eth_payload_axis_tkeep; +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 [63:0] rx_ip_payload_axis_tdata; +wire [7:0] rx_ip_payload_axis_tkeep; +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 [63:0] tx_ip_payload_axis_tdata; +wire [7:0] tx_ip_payload_axis_tkeep; +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 [63:0] rx_udp_payload_axis_tdata; +wire [7:0] rx_udp_payload_axis_tkeep; +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 [63:0] tx_udp_payload_axis_tdata; +wire [7:0] tx_udp_payload_axis_tkeep; +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 [63:0] rx_fifo_udp_payload_axis_tdata; +wire [7:0] rx_fifo_udp_payload_axis_tkeep; +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 [63:0] tx_fifo_udp_payload_axis_tdata; +wire [7:0] tx_fifo_udp_payload_axis_tkeep; +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_tkeep = 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_tkeep = tx_fifo_udp_payload_axis_tkeep; +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_tkeep = rx_udp_payload_axis_tkeep; +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 + valid_last <= tx_udp_payload_axis_tvalid; + if (tx_udp_payload_axis_tvalid & ~valid_last) begin + led_reg <= tx_udp_payload_axis_tdata; + end + end +end + +assign led = led_reg; + +assign sfp_2_txd = 64'h0707070707070707; +assign sfp_2_txc = 8'hff; + +eth_mac_10g_fifo #( + .ENABLE_PADDING(1), + .ENABLE_DIC(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(9), + .RX_FIFO_ADDR_WIDTH(9) +) +eth_mac_10g_fifo_inst ( + .rx_clk(sfp_1_rx_clk), + .rx_rst(sfp_1_rx_rst), + .tx_clk(sfp_1_tx_clk), + .tx_rst(sfp_1_tx_rst), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + + .xgmii_rxd(sfp_1_rxd), + .xgmii_rxc(sfp_1_rxc), + .xgmii_txd(sfp_1_txd), + .xgmii_txc(sfp_1_txc), + + .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(), + + .ifg_delay(8'd12) +); + +eth_axis_rx_64 +eth_axis_rx_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(rx_axis_tdata), + .s_axis_tkeep(rx_axis_tkeep), + .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_tkeep(rx_eth_payload_axis_tkeep), + .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_64 +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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_axis_tkeep), + .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_64 +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_tkeep(rx_eth_payload_axis_tkeep), + .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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_ip_payload_axis_tkeep), + .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_tkeep(rx_ip_payload_axis_tkeep), + .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_tkeep(tx_udp_payload_axis_tkeep), + .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_tkeep(rx_udp_payload_axis_tkeep), + .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(1'b0) +); + +axis_fifo #( + .ADDR_WIDTH(10), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .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(rx_fifo_udp_payload_axis_tkeep), + .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(tx_fifo_udp_payload_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/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/sync_reset.v b/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/sync_signal.v b/fpga/lib/eth/example/ExaNIC_X10/fpga/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/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/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/arp_ep.py b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/axis_ep.py b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/eth_ep.py b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/ip_ep.py b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/test_fpga_core.py b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/test_fpga_core.py new file mode 100755 index 000000000..c4e47a956 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/test_fpga_core.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import xgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_10g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_10g.v") +srcs.append("../lib/eth/rtl/axis_xgmii_rx_64.v") +srcs.append("../lib/eth/rtl/axis_xgmii_tx_64.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/eth_axis_rx_64.v") +srcs.append("../lib/eth/rtl/eth_axis_tx_64.v") +srcs.append("../lib/eth/rtl/udp_complete_64.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen_64.v") +srcs.append("../lib/eth/rtl/udp_64.v") +srcs.append("../lib/eth/rtl/udp_ip_rx_64.v") +srcs.append("../lib/eth/rtl/udp_ip_tx_64.v") +srcs.append("../lib/eth/rtl/ip_complete_64.v") +srcs.append("../lib/eth/rtl/ip_64.v") +srcs.append("../lib/eth/rtl/ip_eth_rx_64.v") +srcs.append("../lib/eth/rtl/ip_eth_tx_64.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp_64.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx_64.v") +srcs.append("../lib/eth/rtl/arp_eth_tx_64.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + sfp_1_tx_clk = Signal(bool(0)) + sfp_1_tx_rst = Signal(bool(0)) + sfp_1_rx_clk = Signal(bool(0)) + sfp_1_rx_rst = Signal(bool(0)) + sfp_1_rxd = Signal(intbv(0)[64:]) + sfp_1_rxc = Signal(intbv(0)[8:]) + sfp_2_tx_clk = Signal(bool(0)) + sfp_2_tx_rst = Signal(bool(0)) + sfp_2_rx_clk = Signal(bool(0)) + sfp_2_rx_rst = Signal(bool(0)) + sfp_2_rxd = Signal(intbv(0)[64:]) + sfp_2_rxc = Signal(intbv(0)[8:]) + + # Outputs + sfp_1_led = Signal(intbv(0)[2:]) + sfp_2_led = Signal(intbv(0)[2:]) + sma_led = Signal(intbv(0)[2:]) + sfp_1_txd = Signal(intbv(0)[64:]) + sfp_1_txc = Signal(intbv(0)[8:]) + sfp_2_txd = Signal(intbv(0)[64:]) + sfp_2_txc = Signal(intbv(0)[8:]) + + # sources and sinks + sfp_1_source = xgmii_ep.XGMIISource() + sfp_1_source_logic = sfp_1_source.create_logic(sfp_1_rx_clk, sfp_1_rx_rst, txd=sfp_1_rxd, txc=sfp_1_rxc, name='sfp_1_source') + + sfp_1_sink = xgmii_ep.XGMIISink() + sfp_1_sink_logic = sfp_1_sink.create_logic(sfp_1_tx_clk, sfp_1_tx_rst, rxd=sfp_1_txd, rxc=sfp_1_txc, name='sfp_1_sink') + + sfp_2_source = xgmii_ep.XGMIISource() + sfp_2_source_logic = sfp_2_source.create_logic(sfp_2_rx_clk, sfp_2_rx_rst, txd=sfp_2_rxd, txc=sfp_2_rxc, name='sfp_2_source') + + sfp_2_sink = xgmii_ep.XGMIISink() + sfp_2_sink_logic = sfp_2_sink.create_logic(sfp_2_tx_clk, sfp_2_tx_rst, rxd=sfp_2_txd, rxc=sfp_2_txc, name='sfp_2_sink') + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + sfp_1_led=sfp_1_led, + sfp_2_led=sfp_2_led, + sma_led=sma_led, + + sfp_1_tx_clk=sfp_1_tx_clk, + sfp_1_tx_rst=sfp_1_tx_rst, + sfp_1_txd=sfp_1_txd, + sfp_1_txc=sfp_1_txc, + sfp_1_rx_clk=sfp_1_rx_clk, + sfp_1_rx_rst=sfp_1_rx_rst, + sfp_1_rxd=sfp_1_rxd, + sfp_1_rxc=sfp_1_rxc, + sfp_2_tx_clk=sfp_2_tx_clk, + sfp_2_tx_rst=sfp_2_tx_rst, + sfp_2_txd=sfp_2_txd, + sfp_2_txc=sfp_2_txc, + sfp_2_rx_clk=sfp_2_rx_clk, + sfp_2_rx_rst=sfp_2_rx_rst, + sfp_2_rxd=sfp_2_rxd, + sfp_2_rxc=sfp_2_rxc + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @always_comb + def clk_logic(): + sfp_1_tx_clk.next = clk + sfp_1_tx_rst.next = rst + sfp_1_rx_clk.next = clk + sfp_1_rx_rst.next = rst + sfp_2_tx_clk.next = clk + sfp_2_tx_rst.next = rst + sfp_2_rx_clk.next = clk + sfp_2_rx_rst.next = rst + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + sfp_1_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while sfp_1_sink.empty(): + yield clk.posedge + + rx_frame = sfp_1_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + sfp_1_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while sfp_1_sink.empty(): + yield clk.posedge + + rx_frame = sfp_1_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert sfp_1_source.empty() + assert sfp_1_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/test_fpga_core.v b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/test_fpga_core.v new file mode 100644 index 000000000..790c5ced7 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/test_fpga_core.v @@ -0,0 +1,122 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg sfp_1_tx_clk = 0; +reg sfp_1_tx_rst = 0; +reg sfp_1_rx_clk = 0; +reg sfp_1_rx_rst = 0; +reg [63:0] sfp_1_rxd = 0; +reg [7:0] sfp_1_rxc = 0; +reg sfp_2_tx_clk = 0; +reg sfp_2_tx_rst = 0; +reg sfp_2_rx_clk = 0; +reg sfp_2_rx_rst = 0; +reg [63:0] sfp_2_rxd = 0; +reg [7:0] sfp_2_rxc = 0; + +// Outputs +wire [1:0] sfp_1_led; +wire [1:0] sfp_2_led; +wire [1:0] sma_led; +wire [63:0] sfp_1_txd; +wire [7:0] sfp_1_txc; +wire [63:0] sfp_2_txd; +wire [7:0] sfp_2_txc; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + sfp_1_tx_clk, + sfp_1_tx_rst, + sfp_1_rx_clk, + sfp_1_rx_rst, + sfp_1_rxd, + sfp_1_rxc, + sfp_2_tx_clk, + sfp_2_tx_rst, + sfp_2_rx_clk, + sfp_2_rx_rst, + sfp_2_rxd, + sfp_2_rxc + ); + $to_myhdl( + sfp_1_led, + sfp_2_led, + sma_led, + sfp_1_txd, + sfp_1_txc, + sfp_2_txd, + sfp_2_txc + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .sfp_1_led(sfp_1_led), + .sfp_2_led(sfp_2_led), + .sma_led(sma_led), + .sfp_1_tx_clk(sfp_1_tx_clk), + .sfp_1_tx_rst(sfp_1_tx_rst), + .sfp_1_txd(sfp_1_txd), + .sfp_1_txc(sfp_1_txc), + .sfp_1_rx_clk(sfp_1_rx_clk), + .sfp_1_rx_rst(sfp_1_rx_rst), + .sfp_1_rxd(sfp_1_rxd), + .sfp_1_rxc(sfp_1_rxc), + .sfp_2_tx_clk(sfp_2_tx_clk), + .sfp_2_tx_rst(sfp_2_tx_rst), + .sfp_2_txd(sfp_2_txd), + .sfp_2_txc(sfp_2_txc), + .sfp_2_rx_clk(sfp_2_rx_clk), + .sfp_2_rx_rst(sfp_2_rx_rst), + .sfp_2_rxd(sfp_2_rxd), + .sfp_2_rxc(sfp_2_rxc) +); + +endmodule diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/udp_ep.py b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/xgmii_ep.py b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/xgmii_ep.py new file mode 120000 index 000000000..63b6d3567 --- /dev/null +++ b/fpga/lib/eth/example/ExaNIC_X10/fpga/tb/xgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/xgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga/Makefile b/fpga/lib/eth/example/HXT100G/fpga/Makefile new file mode 100644 index 000000000..9f8bd4048 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/Makefile @@ -0,0 +1,25 @@ +# Targets +TARGETS:= + +# Subdirectories +SUBDIRS = coregen 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/fpga/lib/eth/example/HXT100G/fpga/README.md b/fpga/lib/eth/example/HXT100G/fpga/README.md new file mode 100644 index 000000000..9da6d1a32 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/README.md @@ -0,0 +1,25 @@ +# Verilog Ethernet HXT100G Example Design + +## Introduction + +This example design targets the HiTech Global HXT100G 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: XC6VHX565T-2FFG1923 +PHY: 10G BASE-R PHY IP core and internal GTH transceiver + +## How to build + +Run make to build. Ensure that the Xilinx ISE toolchain components are +in PATH. + +## How to test + +Run make program to program the HXT100G board with the Xilinx Impact software. +Then run netcat -u 192.168.1.128 1234 to open a UDP connection to port 1234. +Anyntext entered into netcat will be echoed back after pressing enter. + + diff --git a/fpga/lib/eth/example/HXT100G/fpga/common/xilinx.mk b/fpga/lib/eth/example/HXT100G/fpga/common/xilinx.mk new file mode 100644 index 000000000..f10a45f8e --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/common/xilinx.mk @@ -0,0 +1,191 @@ +############################################################################# +# Author: Lane Brooks/Keith Fife +# Date: 04/28/2006 +# License: GPL +# Desc: This is a Makefile intended to take a verilog rtl design +# through the Xilinx ISE tools to generate configuration files for +# Xilinx FPGAs. This file is generic and just a template. As such +# all design specific options such as synthesis files, fpga part type, +# prom part type, etc should be set in the top Makefile prior to +# including this file. Alternatively, all parameters can be passed +# in from the command line as well. +# +############################################################################## +# +# Parameter: +# SYN_FILES - Space seperated list of files to be synthesized +# PART - FPGA part (see Xilinx documentation) +# PROM - PROM part +# NGC_PATHS - Space seperated list of any dirs with pre-compiled ngc files. +# UCF_FILES - Space seperated list of user constraint files. Defaults to xilinx/$(FPGA_TOP).ucf +# +# +# Example Calling Makefile: +# +# SYN_FILES = fpga.v fifo.v clks.v +# PART = xc3s1000 +# FPGA_TOP = fpga +# PROM = xc18v04 +# NGC_PATH = ipLib1 ipLib2 +# FPGA_ARCH = spartan6 +# SPI_PROM_SIZE = (in bytes) +# include xilinx.mk +############################################################################# +# +# Command Line Example: +# make -f xilinx.mk PART=xc3s1000-4fg320 SYN_FILES="fpga.v test.v" FPGA_TOP=fpga +# +############################################################################## +# +# Required Setup: +# +# %.ucf - user constraint file. Needed by ngdbuild +# +# Optional Files: +# %.xcf - user constraint file. Needed by xst. +# %.ut - File for pin states needed by bitgen + + +.PHONY: clean bit prom fpga spi + + +# Mark the intermediate files as PRECIOUS to prevent make from +# deleting them (see make manual section 10.4). +.PRECIOUS: %.ngc %.ngd %_map.ncd %.ncd %.twr %.bit %_timesim.v + +# include the local Makefile for project for any project specific targets +CONFIG ?= config.mk +-include ../$(CONFIG) + +SYN_FILES_REL = $(patsubst %, ../%, $(SYN_FILES)) +INC_FILES_REL = $(patsubst %, ../%, $(INC_FILES)) +INC_PATHS_REL = $(patsubst %, ../%, $(INC_PATHS)) +NGC_PATHS_REL = $(patsubst %, ../%, $(NGC_PATHS)) + +ifdef UCF_FILES + UCF_FILES_REL = $(patsubst %, ../%, $(UCF_FILES)) +else + UCF_FILES_REL = $(FPGA_TOP).ucf +endif + + + +fpga: $(FPGA_TOP).bit + +mcs: $(FPGA_TOP).mcs + +prom: $(FPGA_TOP).spi + +spi: $(FPGA_TOP).spi + +fpgasim: $(FPGA_TOP)_sim.v + + +########################### XST TEMPLATES ############################ +# There are 2 files that XST uses for synthesis that we auto generate. +# The first is a project file which is just a list of all the verilog +# files. The second is the src file which passes XST all the options. +# See XST user manual for XST options. +%.ngc: $(SYN_FILES_REL) $(INC_FILES_REL) + rm -rf xst $*.prj $*.xst defines.v + touch defines.v + mkdir -p xst/tmp + for x in $(DEFS); do echo '`define' $$x >> defines.v; done + echo verilog work defines.v > $*.prj + for x in $(SYN_FILES_REL); do echo verilog work $$x >> $*.prj; done + @echo "set -tmpdir ./xst/tmp" >> $*.xst + @echo "set -xsthdpdir ./xst" >> $*.xst + @echo "run" >> $*.xst + @echo "-ifn $*.prj" >> $*.xst + @echo "-ifmt mixed" >> $*.xst + @echo "-top $*" >> $*.xst + @echo "-ofn $*" >> $*.xst + @echo "-ofmt NGC" >> $*.xst + @echo "-opt_mode Speed" >> $*.xst + @echo "-opt_level 1" >> $*.xst + # @echo "-verilog2001 YES" >> $*.xst + @echo "-keep_hierarchy NO" >> $*.xst + @echo "-p $(FPGA_PART)" >> $*.xst + xst -ifn $*.xst -ofn $*.log + + +########################### ISE TRANSLATE ############################ +# ngdbuild will automatically use a ucf called %.ucf if one is found. +# We setup the dependancy such that %.ucf file is required. If any +# pre-compiled ncd files are needed, set the NGC_PATH variable as a space +# seperated list of directories that include the pre-compiled ngc files. +%.ngd: %.ngc $(UCF_FILES_REL) + ngdbuild -dd ngdbuild $(patsubst %,-sd %, $(NGC_PATHS_REL)) $(patsubst %,-uc %, $(UCF_FILES_REL)) -p $(FPGA_PART) $< $@ + + +########################### ISE MAP ################################### +ifeq ($(FPGA_ARCH),spartan6) + MAP_OPTS= -register_duplication on -timing -xe n +else + MAP_OPTS= -cm speed -register_duplication on -timing -xe n -pr b +endif + +%_map.ncd: %.ngd + map -p $(FPGA_PART) $(MAP_OPTS) -w -o $@ $< $*.pcf + +# map -p $(FPGA_PART) -cm area -pr b -k 4 -c 100 -o $@ $< $*.pcf + + +########################### ISE PnR ################################### +%.ncd: %_map.ncd + par -w -ol high $< $@ $*.pcf + +# par -w -ol std -t 1 $< $@ $*.pcf + + +##################### ISE Static Timing Analysis ##################### +%.twr: %.ncd + -trce -e 3 -l 3 -u -xml $* $< -o $@ $*.pcf + +%_sim.v: %.ncd + netgen -s 4 -pcf $*.pcf -sdf_anno true -ism -sdf_path netgen -w -dir . -ofmt verilog -sim $< $@ + +# netgen -ise "/home/lane/Second/xilinx/Second/Second" -intstyle ise -s 4 -pcf Second.pcf -sdf_anno true -sdf_path netgen/par -w -dir netgen/par -ofmt verilog -sim Second.ncd Second_timesim.v + + +########################### ISE Bitgen ############################# +%.bit: %.twr + bitgen $(BITGEN_OPTIONS) -w $*.ncd $*.bit + mkdir -p rev + EXT=bit; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +########################### ISE Promgen ############################# +%.mcs: %.bit + promgen -spi -w -p mcs -s $(SPI_PROM_SIZE) -o $@ -u 0 $< + # promgen -w -p mcs -c FF -o $@ -u 0 $< -x $(PROM) + mkdir -p rev + EXT=mcs; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +%.spi: %.mcs + objcopy -I ihex -O binary $< $@ + EXT=spi; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + + +tmpclean: + -rm -rf xst ngdbuild *_map.* *.ncd *.ngc *.log *.xst *.prj *.lso *~ *.pcf *.bld *.ngd *.xpi *_pad.* *.unroutes *.twx *.par *.twr *.pad *.drc *.bgn *.prm *.sig netgen *.v *.nlf *.xml + +clean: tmpclean + -rm -rf *.bit *.mcs + +# clean everything +distclean: clean + -rm -rf rev + diff --git a/fpga/lib/eth/example/HXT100G/fpga/coregen/Makefile b/fpga/lib/eth/example/HXT100G/fpga/coregen/Makefile new file mode 100644 index 000000000..09ec2cdf4 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/coregen/Makefile @@ -0,0 +1,33 @@ +# Tools +COREGEN:=coregen +XAW2VERILOG:=xaw2verilog + +# Source +XCO:=ten_gig_eth_pcs_pma_v2_6.xco ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper.xco +XAW:= + +# Targets +TARGETS += $(XCO:.xco=) +TARGETS += $(XAW:.xaw=) + +# Rules +.PHONY: all +all: $(TARGETS) + +.PHONY: clean +clean: + -rm -rf $(TARGETS) + +%: %.xco + $(eval $@_TMP := $(shell mktemp -d)) + cp -a coregen.cgp $($@_TMP) + cp -a $< $($@_TMP) + cd $($@_TMP) && $(COREGEN) -p coregen.cgp -b $(notdir $<) + mv $($@_TMP) $@ + +%: %.xaw + $(eval $@_TMP := $(shell mktemp -d)) + cp -a coregen.cgp $($@_TMP) + cp -a $< $($@_TMP) + cd $($@_TMP) && $(XAW2VERILOG) -st $(notdir $<) $(notdir $*) + mv $($@_TMP) $@ diff --git a/fpga/lib/eth/example/HXT100G/fpga/coregen/coregen.cgp b/fpga/lib/eth/example/HXT100G/fpga/coregen/coregen.cgp new file mode 100644 index 000000000..dbe49f4c0 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/coregen/coregen.cgp @@ -0,0 +1,22 @@ +# Date: Wed Apr 1 17:38:18 2015 + +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6vhx565t +SET devicefamily = virtex6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = ff1923 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -2 +SET verilogsim = true +SET vhdlsim = false +SET workingdirectory = ./tmp/ + +# CRC: 3d2f7d04 diff --git a/fpga/lib/eth/example/HXT100G/fpga/coregen/ten_gig_eth_pcs_pma_v2_6.xco b/fpga/lib/eth/example/HXT100G/fpga/coregen/ten_gig_eth_pcs_pma_v2_6.xco new file mode 100644 index 000000000..2d141c5b0 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/coregen/ten_gig_eth_pcs_pma_v2_6.xco @@ -0,0 +1,53 @@ +############################################################## +# +# Xilinx Core Generator version 14.7 +# Date: Wed Apr 1 17:39:05 2015 +# +############################################################## +# +# This file contains the customisation parameters for a +# Xilinx CORE Generator IP GUI. It is strongly recommended +# that you do not manually alter this file as it may cause +# unexpected and unsupported behavior. +# +############################################################## +# +# Generated from component: xilinx.com:ip:ten_gig_eth_pcs_pma:2.6 +# +############################################################## +# +# BEGIN Project Options +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6vhx565t +SET devicefamily = virtex6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = ff1923 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -2 +SET verilogsim = true +SET vhdlsim = false +# END Project Options +# BEGIN Select +SELECT Ten_Gigabit_Ethernet_PCS/PMA_(10GBASE-R/KR) xilinx.com:ip:ten_gig_eth_pcs_pma:2.6 +# END Select +# BEGIN Parameters +CSET autonegotiation=false +CSET base_kr=BASE-R +CSET component_name=ten_gig_eth_pcs_pma_v2_6 +CSET fec=false +CSET ieee_1588=None +CSET mdio_management=false +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2012-10-08T14:58:05Z +# END Extra information +GENERATE +# CRC: d3dbdcf5 diff --git a/fpga/lib/eth/example/HXT100G/fpga/coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper.xco b/fpga/lib/eth/example/HXT100G/fpga/coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper.xco new file mode 100644 index 000000000..3c380f101 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper.xco @@ -0,0 +1,185 @@ +############################################################## +# +# Xilinx Core Generator version 14.7 +# Date: Wed Apr 1 18:19:03 2015 +# +############################################################## +# +# This file contains the customisation parameters for a +# Xilinx CORE Generator IP GUI. It is strongly recommended +# that you do not manually alter this file as it may cause +# unexpected and unsupported behavior. +# +############################################################## +# +# Generated from component: xilinx.com:ip:v6_gthwizard:1.11 +# +############################################################## +# +# BEGIN Project Options +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6vhx565t +SET devicefamily = virtex6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = ff1923 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -2 +SET verilogsim = true +SET vhdlsim = false +# END Project Options +# BEGIN Select +SELECT Virtex-6_FPGA_GTH_Transceiver_Wizard xilinx.com:ip:v6_gthwizard:1.11 +# END Select +# BEGIN Parameters +CSET component_name=ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper +CSET config_identical=true +CSET drp_clock=50.00 +CSET gth0_datapath_width=64 +CSET gth0_encoding=10GbE_64B/66B +CSET gth0_line_rate=10.3125 +CSET gth0_postcursor_emphasis=0 +CSET gth0_precursor_emphasis=0 +CSET gth0_protocol_file=10GBASE-R +CSET gth0_rxeqmix=1000 +CSET gth0_tx_swing=800 +CSET gth0_use_dfetrainctrl=false +CSET gth0_use_lat_msr=false +CSET gth0_use_port_powerdown=true +CSET gth0_use_port_rxbufreset=true +CSET gth0_use_port_rxcodeerr=true +CSET gth0_use_port_rxctrl=true +CSET gth0_use_port_rxdisperr=false +CSET gth0_use_port_rxencommadet=false +CSET gth0_use_port_rxpolarity=false +CSET gth0_use_port_rxpowerdown=true +CSET gth0_use_port_rxslip=false +CSET gth0_use_port_rxvalid=false +CSET gth0_use_port_txbufreset=true +CSET gth0_use_port_txctrl=true +CSET gth0_use_port_txpowerdown=true +CSET gth1_datapath_width=64 +CSET gth1_encoding=10GbE_64B/66B +CSET gth1_line_rate=10.3125 +CSET gth1_postcursor_emphasis=0 +CSET gth1_precursor_emphasis=0 +CSET gth1_protocol_file=10GBASE-R +CSET gth1_rxeqmix=1000 +CSET gth1_tx_swing=800 +CSET gth1_use_dfetrainctrl=false +CSET gth1_use_lat_msr=false +CSET gth1_use_port_powerdown=true +CSET gth1_use_port_rxbufreset=true +CSET gth1_use_port_rxcodeerr=true +CSET gth1_use_port_rxctrl=true +CSET gth1_use_port_rxdisperr=false +CSET gth1_use_port_rxencommadet=false +CSET gth1_use_port_rxpolarity=false +CSET gth1_use_port_rxpowerdown=true +CSET gth1_use_port_rxslip=false +CSET gth1_use_port_rxvalid=false +CSET gth1_use_port_txbufreset=true +CSET gth1_use_port_txctrl=true +CSET gth1_use_port_txpowerdown=true +CSET gth2_datapath_width=64 +CSET gth2_encoding=10GbE_64B/66B +CSET gth2_line_rate=10.3125 +CSET gth2_postcursor_emphasis=0 +CSET gth2_precursor_emphasis=0 +CSET gth2_protocol_file=10GBASE-R +CSET gth2_rxeqmix=1000 +CSET gth2_tx_swing=800 +CSET gth2_use_dfetrainctrl=false +CSET gth2_use_lat_msr=false +CSET gth2_use_port_powerdown=true +CSET gth2_use_port_rxbufreset=true +CSET gth2_use_port_rxcodeerr=true +CSET gth2_use_port_rxctrl=true +CSET gth2_use_port_rxdisperr=false +CSET gth2_use_port_rxencommadet=false +CSET gth2_use_port_rxpolarity=false +CSET gth2_use_port_rxpowerdown=true +CSET gth2_use_port_rxslip=false +CSET gth2_use_port_rxvalid=false +CSET gth2_use_port_txbufreset=true +CSET gth2_use_port_txctrl=true +CSET gth2_use_port_txpowerdown=true +CSET gth3_datapath_width=64 +CSET gth3_encoding=10GbE_64B/66B +CSET gth3_line_rate=10.3125 +CSET gth3_postcursor_emphasis=0 +CSET gth3_precursor_emphasis=0 +CSET gth3_protocol_file=10GBASE-R +CSET gth3_rxeqmix=1000 +CSET gth3_tx_swing=800 +CSET gth3_use_dfetrainctrl=false +CSET gth3_use_lat_msr=false +CSET gth3_use_port_powerdown=true +CSET gth3_use_port_rxbufreset=true +CSET gth3_use_port_rxcodeerr=true +CSET gth3_use_port_rxctrl=true +CSET gth3_use_port_rxdisperr=false +CSET gth3_use_port_rxencommadet=false +CSET gth3_use_port_rxpolarity=false +CSET gth3_use_port_rxpowerdown=true +CSET gth3_use_port_rxslip=false +CSET gth3_use_port_rxvalid=false +CSET gth3_use_port_txbufreset=true +CSET gth3_use_port_txctrl=true +CSET gth3_use_port_txpowerdown=true +CSET gth_column=Right_Column_(X1) +CSET gthx4lane=false +CSET protocol_template=10GBASE-R +CSET refclk_x0y0=CLK_Y0 +CSET refclk_x0y1=CLK_Y1 +CSET refclk_x0y2=CLK_Y2 +CSET refclk_x1y0=CLK_Y0 +CSET refclk_x1y1=CLK_Y1 +CSET refclk_x1y2=CLK_Y2 +CSET reference_clock=156.25 +CSET target_line_rate=10.3125 +CSET use_gth0_x0y0=true +CSET use_gth0_x0y1=true +CSET use_gth0_x0y2=true +CSET use_gth0_x1y0=true +CSET use_gth0_x1y1=true +CSET use_gth0_x1y2=true +CSET use_gth1_x0y0=true +CSET use_gth1_x0y1=true +CSET use_gth1_x0y2=true +CSET use_gth1_x1y0=true +CSET use_gth1_x1y1=true +CSET use_gth1_x1y2=true +CSET use_gth2_x0y0=true +CSET use_gth2_x0y1=true +CSET use_gth2_x0y2=true +CSET use_gth2_x1y0=true +CSET use_gth2_x1y1=true +CSET use_gth2_x1y2=true +CSET use_gth3_x0y0=true +CSET use_gth3_x0y1=true +CSET use_gth3_x0y2=true +CSET use_gth3_x1y0=true +CSET use_gth3_x1y1=true +CSET use_gth3_x1y2=true +CSET use_gth_quad_x0y0=false +CSET use_gth_quad_x0y1=false +CSET use_gth_quad_x0y2=false +CSET use_gth_quad_x1y0=true +CSET use_gth_quad_x1y1=false +CSET use_gth_quad_x1y2=false +CSET use_no_rx=false +CSET use_no_tx=false +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2011-04-05T17:48:34Z +# END Extra information +GENERATE +# CRC: 75800c49 diff --git a/fpga/lib/eth/example/HXT100G/fpga/fpga.ucf b/fpga/lib/eth/example/HXT100G/fpga/fpga.ucf new file mode 100644 index 000000000..784dcb369 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/fpga.ucf @@ -0,0 +1,269 @@ +# User Constraints File for the HTG-V6HXT-100GIG FPGA board + +CONFIG PART = xc6vhx565t-2ff1923; + +# 50 MHz clock +NET "sys_clk" LOC = "AP33" | IOSTANDARD=LVCMOS25; # SYS_CLK, 50MHz +NET "sys_clk" TNM_NET = "sys_clk"; +TIMESPEC "TS_sys_clk" = PERIOD "sys_clk" 20.000 ns HIGH 50% INPUT_JITTER 200.0ps; + +# 156.25 MHz transceiver clock +NET "*txclk156" TNM_NET = "txclk156"; +TIMESPEC "TS_txclk156" = PERIOD "txclk156" 6400 ps; + +NET "*rx_clk_0" TNM_NET = "rx_clk"; +NET "*rx_clk_1" TNM_NET = "rx_clk"; +NET "*rx_clk_2" TNM_NET = "rx_clk"; +NET "*rx_clk_3" TNM_NET = "rx_clk"; +TIMESPEC "TS_rx_clk" = PERIOD "rx_clk" 6400 ps; + +# clock crossing constraints +TIMESPEC "TS_txclk156_to_sys_clk" = FROM "txclk156" TO "sys_clk" 10 ns; +TIMESPEC "TS_sys_clk_to_txclk156" = FROM "sys_clk" TO "txclk156" 10 ns; +TIMESPEC "TS_sys_clk_to_rx_clk" = FROM "sys_clk" TO "rx_clk" 10 ns; +TIMESPEC "TS_rx_clk_to_sys_clk" = FROM "rx_clk" TO "sys_clk" 10 ns; + +# PHY elastic buffer constraints +NET "*elastic_buffer_i*rd_truegray" MAXDELAY = 6.0 ns; +NET "*elastic_buffer_i?can_insert_wra" TIG; +NET "*wr_gray*" MAXDELAY = 6.0 ns; +NET "*rd_lastgray*" MAXDELAY = 6.0 ns; + +TIMESPEC "TS_txclk156_to_rx_clk" = FROM "txclk156" TO "rx_clk" 10 ns; +TIMESPEC "TS_rx_clk_to_txclk156" = FROM "rx_clk" TO "txclk156" 10 ns; + +# 200 MHz DDR3 clock +#NET "clk_ddr3_p" LOC = "AL13" | IOSTANDARD=LVDS_25; +#NET "clk_ddr3_n" LOC = "AL12" | IOSTANDARD=LVDS_25; +#NET "clk_ddr3_p" TNM_NET = "clk_ddr3"; +#TIMESPEC "TS_clk_ddr3" = PERIOD "clk_ddr3" 5.000 ns HIGH 50% INPUT_JITTER 20.0ps; + +# User clock +#NET "clk_usr_p" LOC = "J33" | IOSTANDARD=LVDS_25; +#NET "clk_usr_n" LOC = "H33" | IOSTANDARD=LVDS_25; +#NET "clk_usr_p" TNM_NET = "clk_usr"; +#TIMESPEC "TS_clk_usr" = PERIOD "clk_usr" 5.000 ns HIGH 50% INPUT_JITTER 20.0ps; + +# Button +NET "reset_n" LOC = "AY12" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (S7) + +# DIP switches +NET "sw<0>" LOC = "BB32" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (USER_SW0) +NET "sw<1>" LOC = "AT30" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (USER_SW1) + +# Jumpers +NET "jp<0>" LOC = "AW34" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (IO0, JP18) +NET "jp<1>" LOC = "AY35" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (IO1, JP19) +NET "jp<2>" LOC = "AW35" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (IO2, JP20) +NET "jp<3>" LOC = "AV33" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (IO3, JP21) + +# LEDs +NET "led<0>" LOC = "AY33" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L52N_M1DQ15 (D20) +NET "led<1>" LOC = "AW33" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L52N_M1DQ15 (D23) +NET "led<2>" LOC = "AK35" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L52N_M1DQ15 (D26) +NET "led<3>" LOC = "AL35" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L52N_M1DQ15 (D31) + +# Silicon Labs CP2102 +NET "uart_rst" LOC = "D10" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_suspend" LOC = "E12" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_ri" LOC = "B12" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_dcd" LOC = "C11" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_dtr" LOC = "B11" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_dsr" LOC = "D11" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_rxd" LOC = "E11" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # IO_L66N_SCP0 +NET "uart_txd" LOC = "B9" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # IO_L66P_SCP1 +NET "uart_rts" LOC = "A9" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_cts" LOC = "F12" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # + +# Clock muxes +NET "clk_gth_scl" LOC = "M11"; +NET "clk_gth_sda" LOC = "L10"; +NET "clk_gth_rst_n" LOC = "AR7"; +NET "clk_gthl_alm" LOC = "M34"; +NET "clk_gthr_alm" LOC = "R13"; +NET "clk_gthl_lol" LOC = "L34"; +NET "clk_gthr_lol" LOC = "L12"; + +# AirMax I/O +#NET "amh_right_io<0>" LOC="N33"; # AMH1R_IO0 J6.TX[12]_P mdc +#NET "amh_right_io<1>" LOC="J34"; # AMH1R_IO1 J6.TX[12]_N mdio +#NET "amh_right_io<2>" LOC="F34"; # AMH1R_IO2 J6.TX[13]_P +#NET "amh_right_io<3>" LOC="G34"; # AMH1R_IO3 J6.TX[13]_N +#NET "amh_right_io<4>" LOC="K33"; # AMH1R_IO4 J6.TX[14]_P +#NET "amh_right_io<5>" LOC="L33"; # AMH1R_IO5 J6.TX[14]_N +#NET "amh_right_io<6>" LOC="N34"; # AMH1R_IO6 J6.TX[15]_P +#NET "amh_right_io<7>" LOC="H34"; # AMH1R_IO7 J6.TX[15]_N reset_n + +#NET "amh_left_io<0>" LOC="E35"; # AMH1L_IO0 J3.TX[12]_P mdc +#NET "amh_left_io<1>" LOC="B37"; # AMH1L_IO1 J3.TX[12]_N mdio +#NET "amh_left_io<2>" LOC="A35"; # AMH1L_IO2 J3.TX[13]_P +#NET "amh_left_io<3>" LOC="B35"; # AMH1L_IO3 J3.TX[13]_N +#NET "amh_left_io<4>" LOC="G35"; # AMH1L_IO4 J3.TX[14]_P +#NET "amh_left_io<5>" LOC="F35"; # AMH1L_IO5 J3.TX[14]_N +#NET "amh_left_io<6>" LOC="A37"; # AMH1L_IO6 J3.TX[15]_P +#NET "amh_left_io<7>" LOC="B36"; # AMH1L_IO7 J3.TX[15]_N reset_n + +NET "amh_right_mdc" LOC="N33"; # AMH1R_IO0 J6.TX[12]_P mdc +NET "amh_right_mdio" LOC="J34"; # AMH1R_IO1 J6.TX[12]_N mdio +NET "amh_right_phy_rst_n" LOC="H34" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; + +NET "amh_left_mdc" LOC="E35"; # AMH1L_IO0 J3.TX[12]_P mdc +NET "amh_left_mdio" LOC="B37"; # AMH1L_IO1 J3.TX[12]_N mdio +NET "amh_left_phy_rst_n" LOC="B36" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; + +# 10G Ethernet interfaces +# Quad A X0Y0 +# Ports 11, 9, 10, 8 (right) +# SFP 0, 2, 4, 6 +NET "gth_quad_A_refclk_p" LOC = "R41"; +NET "gth_quad_A_refclk_n" LOC = "R42"; + +NET "gth_quad_A_txp_0" LOC = "T43"; +NET "gth_quad_A_txn_0" LOC = "T44"; +NET "gth_quad_A_rxp_0" LOC = "U41"; +NET "gth_quad_A_rxn_0" LOC = "U42"; + +NET "gth_quad_A_txp_1" LOC = "P43"; +NET "gth_quad_A_txn_1" LOC = "P44"; +NET "gth_quad_A_rxp_1" LOC = "T39"; +NET "gth_quad_A_rxn_1" LOC = "T40"; + +NET "gth_quad_A_txp_2" LOC = "M43"; +NET "gth_quad_A_txn_2" LOC = "M44"; +NET "gth_quad_A_rxp_2" LOC = "N37"; +NET "gth_quad_A_rxn_2" LOC = "N38"; + +NET "gth_quad_A_txp_3" LOC = "N41"; +NET "gth_quad_A_txn_3" LOC = "N42"; +NET "gth_quad_A_rxp_3" LOC = "M39"; +NET "gth_quad_A_rxn_3" LOC = "M40"; + +# Quad B X0Y1 +# Ports 4, 6, 5, 7 (right) +# SFP 3, 5, 1, 7 +NET "gth_quad_B_refclk_p" LOC = "J41"; +NET "gth_quad_B_refclk_n" LOC = "J42"; + +NET "gth_quad_B_txp_0" LOC = "L41"; +NET "gth_quad_B_txn_0" LOC = "L42"; +NET "gth_quad_B_rxp_0" LOC = "K39"; +NET "gth_quad_B_rxn_0" LOC = "K40"; + +NET "gth_quad_B_txp_1" LOC = "K43"; +NET "gth_quad_B_txn_1" LOC = "K44"; +NET "gth_quad_B_rxp_1" LOC = "L37"; +NET "gth_quad_B_rxn_1" LOC = "L38"; + +NET "gth_quad_B_txp_2" LOC = "G41"; +NET "gth_quad_B_txn_2" LOC = "G42"; +NET "gth_quad_B_rxp_2" LOC = "H39"; +NET "gth_quad_B_rxn_2" LOC = "H40"; + +NET "gth_quad_B_txp_3" LOC = "H43"; +NET "gth_quad_B_txn_3" LOC = "H44"; +NET "gth_quad_B_rxp_3" LOC = "J37"; +NET "gth_quad_B_rxn_3" LOC = "J38"; + +# Quad C X0Y2 +# Ports 1, 0, 3, 2 (right) +# SFP 8, 9, 11, 10 +NET "gth_quad_C_refclk_p" LOC = "E41"; +NET "gth_quad_C_refclk_n" LOC = "E42"; + +NET "gth_quad_C_txp_0" LOC = "F43"; +NET "gth_quad_C_txn_0" LOC = "F44"; +NET "gth_quad_C_rxp_0" LOC = "G37"; +NET "gth_quad_C_rxn_0" LOC = "G38"; + +NET "gth_quad_C_txp_1" LOC = "D43"; +NET "gth_quad_C_txn_1" LOC = "D44"; +NET "gth_quad_C_rxp_1" LOC = "F39"; +NET "gth_quad_C_rxn_1" LOC = "F40"; + +NET "gth_quad_C_txp_2" LOC = "A41"; +NET "gth_quad_C_txn_2" LOC = "A42"; +NET "gth_quad_C_rxp_2" LOC = "B39"; +NET "gth_quad_C_rxn_2" LOC = "B40"; + +NET "gth_quad_C_txp_3" LOC = "C41"; +NET "gth_quad_C_txn_3" LOC = "C42"; +NET "gth_quad_C_rxp_3" LOC = "D39"; +NET "gth_quad_C_rxn_3" LOC = "D40"; + +# Quad D X1Y0 +# Ports 11, 9, 10, 8 (left) +# SFP 0, 2, 4, 6 +NET "gth_quad_D_refclk_p" LOC = "R4"; +NET "gth_quad_D_refclk_n" LOC = "R3"; + +NET "gth_quad_D_txp_0" LOC = "T2"; +NET "gth_quad_D_txn_0" LOC = "T1"; +NET "gth_quad_D_rxp_0" LOC = "U4"; +NET "gth_quad_D_rxn_0" LOC = "U3"; + +NET "gth_quad_D_txp_1" LOC = "P2"; +NET "gth_quad_D_txn_1" LOC = "P1"; +NET "gth_quad_D_rxp_1" LOC = "T6"; +NET "gth_quad_D_rxn_1" LOC = "T5"; + +NET "gth_quad_D_txp_2" LOC = "M2"; +NET "gth_quad_D_txn_2" LOC = "M1"; +NET "gth_quad_D_rxp_2" LOC = "N8"; +NET "gth_quad_D_rxn_2" LOC = "N7"; + +NET "gth_quad_D_txp_3" LOC = "N4"; +NET "gth_quad_D_txn_3" LOC = "N3"; +NET "gth_quad_D_rxp_3" LOC = "M6"; +NET "gth_quad_D_rxn_3" LOC = "M5"; + +# Quad E X1Y1 +# Ports 4, 6, 5, 7 (left) +# SFP 3, 5, 1, 7 +NET "gth_quad_E_refclk_p" LOC = "J4"; +NET "gth_quad_E_refclk_n" LOC = "J3"; + +NET "gth_quad_E_txp_0" LOC = "L4"; +NET "gth_quad_E_txn_0" LOC = "L3"; +NET "gth_quad_E_rxp_0" LOC = "K6"; +NET "gth_quad_E_rxn_0" LOC = "K5"; + +NET "gth_quad_E_txp_1" LOC = "K2"; +NET "gth_quad_E_txn_1" LOC = "K1"; +NET "gth_quad_E_rxp_1" LOC = "L8"; +NET "gth_quad_E_rxn_1" LOC = "L7"; + +NET "gth_quad_E_txp_2" LOC = "G4"; +NET "gth_quad_E_txn_2" LOC = "G3"; +NET "gth_quad_E_rxp_2" LOC = "H6"; +NET "gth_quad_E_rxn_2" LOC = "H5"; + +NET "gth_quad_E_txp_3" LOC = "H2"; +NET "gth_quad_E_txn_3" LOC = "H1"; +NET "gth_quad_E_rxp_3" LOC = "J8"; +NET "gth_quad_E_rxn_3" LOC = "J7"; + +# Quad F X1Y2 +# Ports 1, 0, 3, 2 (left) +# SFP 8, 9, 11, 10 +NET "gth_quad_F_refclk_p" LOC = "E4"; +NET "gth_quad_F_refclk_n" LOC = "E3"; + +NET "gth_quad_F_txp_0" LOC = "F2"; +NET "gth_quad_F_txn_0" LOC = "F1"; +NET "gth_quad_F_rxp_0" LOC = "G8"; +NET "gth_quad_F_rxn_0" LOC = "G7"; + +NET "gth_quad_F_txp_1" LOC = "D2"; +NET "gth_quad_F_txn_1" LOC = "D1"; +NET "gth_quad_F_rxp_1" LOC = "F6"; +NET "gth_quad_F_rxn_1" LOC = "F5"; + +NET "gth_quad_F_txp_2" LOC = "A4"; +NET "gth_quad_F_txn_2" LOC = "A3"; +NET "gth_quad_F_rxp_2" LOC = "B6"; +NET "gth_quad_F_rxn_2" LOC = "B5"; + +NET "gth_quad_F_txp_3" LOC = "C4"; +NET "gth_quad_F_txn_3" LOC = "C3"; +NET "gth_quad_F_rxp_3" LOC = "D6"; +NET "gth_quad_F_rxn_3" LOC = "D5"; diff --git a/fpga/lib/eth/example/HXT100G/fpga/fpga/Makefile b/fpga/lib/eth/example/HXT100G/fpga/fpga/Makefile new file mode 100644 index 000000000..d1c3e8b5a --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/fpga/Makefile @@ -0,0 +1,83 @@ + +# FPGA settings +FPGA_PART = xc6vhx565t-2ff1923 +FPGA_TOP = fpga +FPGA_ARCH = spartan6 + +# PROM settings +#PROM = xc18v04 +#SPI_PROM_SIZE = (in bytes) + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += rtl/i2c_master.v +SYN_FILES += rtl/gth_i2c_init.v +SYN_FILES += rtl/eth_gth_phy_quad.v +SYN_FILES += lib/eth/rtl/eth_mac_10g_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_10g.v +SYN_FILES += lib/eth/rtl/axis_xgmii_rx_64.v +SYN_FILES += lib/eth/rtl/axis_xgmii_tx_64.v +SYN_FILES += lib/eth/rtl/lfsr.v +SYN_FILES += lib/eth/rtl/eth_axis_rx_64.v +SYN_FILES += lib/eth/rtl/eth_axis_tx_64.v +SYN_FILES += lib/eth/rtl/udp_complete_64.v +SYN_FILES += lib/eth/rtl/udp_checksum_gen_64.v +SYN_FILES += lib/eth/rtl/udp_64.v +SYN_FILES += lib/eth/rtl/udp_ip_rx_64.v +SYN_FILES += lib/eth/rtl/udp_ip_tx_64.v +SYN_FILES += lib/eth/rtl/ip_complete_64.v +SYN_FILES += lib/eth/rtl/ip_64.v +SYN_FILES += lib/eth/rtl/ip_eth_rx_64.v +SYN_FILES += lib/eth/rtl/ip_eth_tx_64.v +SYN_FILES += lib/eth/rtl/ip_arb_mux.v +SYN_FILES += lib/eth/rtl/arp_64.v +SYN_FILES += lib/eth/rtl/arp_cache.v +SYN_FILES += lib/eth/rtl/arp_eth_rx_64.v +SYN_FILES += lib/eth/rtl/arp_eth_tx_64.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 += coregen/ten_gig_eth_pcs_pma_v2_6/ten_gig_eth_pcs_pma_v2_6.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6/ten_gig_eth_pcs_pma_v2_6/example_design/ten_gig_eth_pcs_pma_v2_6_management_arbiter.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_quad.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_gth_reset.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_gth_init.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_gth_tx_pcs_reset.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_gth_rx_pcs_cdr_reset.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_pulse_synchronizer.v + +# UCF files +UCF_FILES = fpga.ucf + +# NGC paths for ngdbuild +NGC_PATHS = coregen/ten_gig_eth_pcs_pma_v2_6 + +# Bitgen options +BITGEN_OPTIONS = -g StartupClk:Cclk -g ConfigRate:26 + +include ../common/xilinx.mk + +program: $(FPGA_TOP).bit + echo "setmode -bscan" > program.cmd + echo "setcable -p auto" >> program.cmd + echo "identify" >> program.cmd + echo "assignfile -p 1 -file $(FPGA_TOP).bit" >> program.cmd + echo "program -p 1" >> program.cmd + echo "quit" >> program.cmd + impact -batch program.cmd + +program_flash: $(FPGA_TOP).mcs + echo "setmode -bscan" > program.cmd + echo "setcable -p auto" >> program.cmd + echo "identify" >> program.cmd + echo "assignfile -p 1 -file $(FPGA_TOP).mcs" >> program.cmd + echo "program -p 1 -e" >> program.cmd + echo "quit" >> program.cmd + impact -batch program.cmd + diff --git a/fpga/lib/eth/example/HXT100G/fpga/lib/eth b/fpga/lib/eth/example/HXT100G/fpga/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga/rtl/debounce_switch.v b/fpga/lib/eth/example/HXT100G/fpga/rtl/debounce_switch.v new file mode 100644 index 000000000..69f30d706 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/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 + +`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) 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/fpga/lib/eth/example/HXT100G/fpga/rtl/eth_gth_phy_quad.v b/fpga/lib/eth/example/HXT100G/fpga/rtl/eth_gth_phy_quad.v new file mode 100644 index 000000000..60ab0f137 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/rtl/eth_gth_phy_quad.v @@ -0,0 +1,569 @@ +/* + +Copyright (c) 2015-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. + +*/ + +module eth_gth_phy_quad ( + /* + * Clock and reset + */ + input wire clk156, + input wire rst156, + input wire dclk, + input wire dclk_reset, + + output wire txclk156, + + input wire gth_reset, + output wire gth_reset_done, + + /* + * Transciever pins + */ + input wire refclk_p, + input wire refclk_n, + output wire txp_0, + output wire txn_0, + input wire rxp_0, + input wire rxn_0, + output wire txp_1, + output wire txn_1, + input wire rxp_1, + input wire rxn_1, + output wire txp_2, + output wire txn_2, + input wire rxp_2, + input wire rxn_2, + output wire txp_3, + output wire txn_3, + input wire rxp_3, + input wire rxn_3, + + /* + * XGMII interfaces + */ + input wire [63:0] xgmii_txd_0, + input wire [7:0] xgmii_txc_0, + output wire [63:0] xgmii_rxd_0, + output wire [7:0] xgmii_rxc_0, + input wire [63:0] xgmii_txd_1, + input wire [7:0] xgmii_txc_1, + output wire [63:0] xgmii_rxd_1, + output wire [7:0] xgmii_rxc_1, + input wire [63:0] xgmii_txd_2, + input wire [7:0] xgmii_txc_2, + output wire [63:0] xgmii_rxd_2, + output wire [7:0] xgmii_rxc_2, + input wire [63:0] xgmii_txd_3, + input wire [7:0] xgmii_txc_3, + output wire [63:0] xgmii_rxd_3, + output wire [7:0] xgmii_rxc_3, + + /* + * Control + */ + input wire tx_powerdown_0, + input wire rx_powerdown_0, + input wire tx_powerdown_1, + input wire rx_powerdown_1, + input wire tx_powerdown_2, + input wire rx_powerdown_2, + input wire tx_powerdown_3, + input wire rx_powerdown_3 +); + +wire [63:0] gth_txd_0; +wire [7:0] gth_txc_0; +wire [63:0] gth_rxd_0; +wire [7:0] gth_rxc_0; +wire [63:0] gth_txd_1; +wire [7:0] gth_txc_1; +wire [63:0] gth_rxd_1; +wire [7:0] gth_rxc_1; +wire [63:0] gth_txd_2; +wire [7:0] gth_txc_2; +wire [63:0] gth_rxd_2; +wire [7:0] gth_rxc_2; +wire [63:0] gth_txd_3; +wire [7:0] gth_txc_3; +wire [63:0] gth_rxd_3; +wire [7:0] gth_rxc_3; + +wire mgmt_rd; +wire mgmt_wr; +wire mgmt_rdack; +wire [20:0] mgmt_addr; +wire [15:0] mgmt_rddata; +wire [15:0] mgmt_wrdata; + +wire mgmt_rd0; +wire mgmt_wr0; +wire [20:0] mgmt_addr0; +wire [15:0] mgmt_wrdata0; + +wire mgmt_req0; +wire mgmt_gnt0; + +wire mgmt_rd1; +wire mgmt_wr1; +wire [20:0] mgmt_addr1; +wire [15:0] mgmt_wrdata1; + +wire mgmt_req1; +wire mgmt_gnt1; + +wire mgmt_rd2; +wire mgmt_wr2; +wire [20:0] mgmt_addr2; +wire [15:0] mgmt_wrdata2; + +wire mgmt_req2; +wire mgmt_gnt2; + +wire mgmt_rd3; +wire mgmt_wr3; +wire [20:0] mgmt_addr3; +wire [15:0] mgmt_wrdata3; + +wire mgmt_req3; +wire mgmt_gnt3; + +// clocking +wire refclk; + +IBUFDS_GTHE1 refclk_ibufds_inst +( + .I(refclk_p), + .IB(refclk_n), + .O(refclk) +); + +wire rx_clk_0; +wire rx_clk_0_buf; +wire rx_clk_1; +wire rx_clk_1_buf; +wire rx_clk_2; +wire rx_clk_2_buf; +wire rx_clk_3; +wire rx_clk_3_buf; + +BUFR #( + .SIM_DEVICE("VIRTEX6") +) +rx_clk_0_buf_inst +( + .CE(1'b1), + .CLR(1'b0), + .I(rx_clk_0), + .O(rx_clk_0_buf) +); + +BUFR #( + .SIM_DEVICE("VIRTEX6") +) +rx_clk_1_buf_inst +( + .CE(1'b1), + .CLR(1'b0), + .I(rx_clk_1), + .O(rx_clk_1_buf) +); + +BUFG rx_clk_2_buf_inst +( + .I(rx_clk_2), + .O(rx_clk_2_buf) +); + +BUFG rx_clk_3_buf_inst +( + .I(rx_clk_3), + .O(rx_clk_3_buf) +); + +wire tx_resetdone_0; +wire rx_resetdone_0; +wire tx_resetdone_1; +wire rx_resetdone_1; +wire tx_resetdone_2; +wire rx_resetdone_2; +wire tx_resetdone_3; +wire rx_resetdone_3; + +wire resetdone_0 = tx_resetdone_0 & rx_resetdone_0; +wire resetdone_1 = tx_resetdone_1 & rx_resetdone_1; +wire resetdone_2 = tx_resetdone_2 & rx_resetdone_2; +wire resetdone_3 = tx_resetdone_3 & rx_resetdone_3; + +reg gth_reset_done_reg = 0; + +assign gth_reset_done = gth_reset_done_reg; + +// register overall reset done output +always @(posedge clk156) begin + gth_reset_done_reg <= resetdone_0 & resetdone_1 & resetdone_2 & resetdone_3; +end + +wire disable_drp_mgmt; + +wire disable_drp = gth_reset_done & disable_drp_mgmt; + +wire lane_sel = {mgmt_gnt3, mgmt_gnt2, mgmt_gnt1, mgmt_gnt0}; + +// GTH quad wrapper +ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_QUAD # +( + // Simulation attributes + .QUAD_SIM_GTHRESET_SPEEDUP(0) +) +gth_inst +( + //------------------------------- Resetdone -------------------------------- + .TX_PCS_RESETDONE0_OUT (tx_resetdone_0), + .RX_PCS_CDR_RESETDONE0_OUT (rx_resetdone_0), + .TX_PCS_RESETDONE1_OUT (tx_resetdone_1), + .RX_PCS_CDR_RESETDONE1_OUT (rx_resetdone_1), + .TX_PCS_RESETDONE2_OUT (tx_resetdone_2), + .RX_PCS_CDR_RESETDONE2_OUT (rx_resetdone_2), + .TX_PCS_RESETDONE3_OUT (tx_resetdone_3), + .RX_PCS_CDR_RESETDONE3_OUT (rx_resetdone_3), + //------------------------------- Initdone --------------------------------- + .GTHINITDONE_OUT (), + //------------------------------- PCS Resets ------------------------------- + .TX_PCS_RESET0_IN (1'b0), + .RX_PCS_CDR_RESET0_IN (1'b0), + .TX_PCS_RESET1_IN (1'b0), + .RX_PCS_CDR_RESET1_IN (1'b0), + .TX_PCS_RESET2_IN (1'b0), + .RX_PCS_CDR_RESET2_IN (1'b0), + .TX_PCS_RESET3_IN (1'b0), + .RX_PCS_CDR_RESET3_IN (1'b0), + //------------------------------- Powerdown -------------------------------- + .POWERDOWN0_IN (1'b0), + .POWERDOWN1_IN (1'b0), + .POWERDOWN2_IN (1'b0), + .POWERDOWN3_IN (1'b0), + .RXPOWERDOWN0_IN ({rx_powerdown_0 | (~gth_reset_done), 1'b0}), + .RXPOWERDOWN1_IN ({rx_powerdown_1 | (~gth_reset_done), 1'b0}), + .RXPOWERDOWN2_IN ({rx_powerdown_2 | (~gth_reset_done), 1'b0}), + .RXPOWERDOWN3_IN ({rx_powerdown_3 | (~gth_reset_done), 1'b0}), + .TXPOWERDOWN0_IN ({tx_powerdown_0 | (~gth_reset_done), 1'b0}), + .TXPOWERDOWN1_IN ({tx_powerdown_1 | (~gth_reset_done), 1'b0}), + .TXPOWERDOWN2_IN ({tx_powerdown_2 | (~gth_reset_done), 1'b0}), + .TXPOWERDOWN3_IN ({tx_powerdown_3 | (~gth_reset_done), 1'b0}), + //----------------------------- Receive Ports ------------------------------ + .RXBUFRESET0_IN (1'b0), + .RXBUFRESET1_IN (1'b0), + .RXBUFRESET2_IN (1'b0), + .RXBUFRESET3_IN (1'b0), + .RXCODEERR0_OUT (), + .RXCODEERR1_OUT (), + .RXCODEERR2_OUT (), + .RXCODEERR3_OUT (), + .RXCTRL0_OUT (gth_rxc_0), + .RXCTRL1_OUT (gth_rxc_1), + .RXCTRL2_OUT (gth_rxc_2), + .RXCTRL3_OUT (gth_rxc_3), + .RXCTRLACK0_OUT (), + .RXCTRLACK1_OUT (), + .RXCTRLACK2_OUT (), + .RXCTRLACK3_OUT (), + .RXDATA0_OUT (gth_rxd_0), + .RXDATA1_OUT (gth_rxd_1), + .RXDATA2_OUT (gth_rxd_2), + .RXDATA3_OUT (gth_rxd_3), + .RXN0_IN (rxn_0), + .RXN1_IN (rxn_1), + .RXN2_IN (rxn_2), + .RXN3_IN (rxn_3), + .RXP0_IN (rxp_0), + .RXP1_IN (rxp_1), + .RXP2_IN (rxp_2), + .RXP3_IN (rxp_3), + .RXUSERCLKIN0_IN (rx_clk_0_buf), + .RXUSERCLKIN1_IN (rx_clk_1_buf), + .RXUSERCLKIN2_IN (rx_clk_2_buf), + .RXUSERCLKIN3_IN (rx_clk_3_buf), + .RXUSERCLKOUT0_OUT (rx_clk_0), + .RXUSERCLKOUT1_OUT (rx_clk_1), + .RXUSERCLKOUT2_OUT (rx_clk_2), + .RXUSERCLKOUT3_OUT (rx_clk_3), + //----------- Shared Ports - Dynamic Reconfiguration Port () ------------ + .DADDR_IN (16'h0000), + .DCLK_IN (dclk), + .DEN_IN (1'b0), + .DI_IN (16'h0000), + .DISABLEDRP_IN (disable_drp), + .DRDY_OUT (), + .DRPDO_OUT (), + .DWE_IN (1'b0), + //-------------------------- Shared Ports - Other -------------------------- + .TSTREFCLKFAB_OUT (), + .TSTREFCLKOUT_OUT (), + .GTHINIT_IN (1'b0), + .GTHRESET_IN (gth_reset), + .MGMTPCSLANESEL_IN (lane_sel), + .MGMTPCSMMDADDR_IN (mgmt_addr[20:16]), + .MGMTPCSRDACK_OUT (mgmt_rdack), + .MGMTPCSRDDATA_OUT (mgmt_rddata), + .MGMTPCSREGADDR_IN (mgmt_addr[15:0]), + .MGMTPCSREGRD_IN (mgmt_rd), + .MGMTPCSREGWR_IN (mgmt_wr), + .MGMTPCSWRDATA_IN (mgmt_wrdata), + .REFCLK_IN (refclk), + //----------------------------- Transmit Ports ----------------------------- + .TXBUFRESET0_IN (1'b0), + .TXBUFRESET1_IN (1'b0), + .TXBUFRESET2_IN (1'b0), + .TXBUFRESET3_IN (1'b0), + .TXCTRL0_IN (gth_txc_0), + .TXCTRL1_IN (gth_txc_1), + .TXCTRL2_IN (gth_txc_2), + .TXCTRL3_IN (gth_txc_3), + .TXCTRLACK0_OUT (), + .TXCTRLACK1_OUT (), + .TXCTRLACK2_OUT (), + .TXCTRLACK3_OUT (), + .TXDATA0_IN (gth_txd_0), + .TXDATA1_IN (gth_txd_1), + .TXDATA2_IN (gth_txd_2), + .TXDATA3_IN (gth_txd_3), + .TXN0_OUT (txn_0), + .TXN1_OUT (txn_1), + .TXN2_OUT (txn_2), + .TXN3_OUT (txn_3), + .TXP0_OUT (txp_0), + .TXP1_OUT (txp_1), + .TXP2_OUT (txp_2), + .TXP3_OUT (txp_3), + .TXUSERCLKIN0_IN (clk156), + .TXUSERCLKIN1_IN (clk156), + .TXUSERCLKIN2_IN (clk156), + .TXUSERCLKIN3_IN (clk156), + .TXUSERCLKOUT0_OUT (txclk156), + .TXUSERCLKOUT1_OUT (), + .TXUSERCLKOUT2_OUT (), + .TXUSERCLKOUT3_OUT () +); + +wire [535:0] configuration_vector; + +assign configuration_vector[14:1] = 0; +assign configuration_vector[79:17] = 0; +assign configuration_vector[109:84] = 0; +assign configuration_vector[175:170] = 0; +assign configuration_vector[239:234] = 0; +assign configuration_vector[269:246] = 0; +assign configuration_vector[511:272] = 0; +assign configuration_vector[515:513] = 0; +assign configuration_vector[517:517] = 0; +assign configuration_vector[0] = 0; // pma_loopback; +assign configuration_vector[15] = 0; // pma_reset; +assign configuration_vector[16] = 0; // global_tx_disable; +assign configuration_vector[83:80] = 0; // pma_vs_loopback; +assign configuration_vector[110] = 0; // pcs_loopback; +assign configuration_vector[111] = 0; // pcs_reset; +assign configuration_vector[169:112] = 0; // test_patt_a; +assign configuration_vector[233:176] = 0; // test_patt_b; +assign configuration_vector[240] = 0; // data_patt_sel; +assign configuration_vector[241] = 0; // test_patt_sel; +assign configuration_vector[242] = 0; // rx_test_patt_en; +assign configuration_vector[243] = 0; // tx_test_patt_en; +assign configuration_vector[244] = 0; // prbs31_tx_en; +assign configuration_vector[245] = 0; // prbs31_rx_en; +assign configuration_vector[271:270] = 0; // pcs_vs_loopback; +assign configuration_vector[512] = 0; // set_pma_link_status; +assign configuration_vector[516] = 0; // set_pcs_link_status; +assign configuration_vector[518] = 0; // clear_pcs_status2; +assign configuration_vector[519] = 0; // clear_test_patt_err_count; +assign configuration_vector[535:520] = 0; + +ten_gig_eth_pcs_pma_v2_6 +ten_gig_eth_pcs_pma_core_inst_0 +( + .reset(rst156), + .clk156(clk156), + .rxclk156(rx_clk_0_buf), + .xgmii_txd(xgmii_txd_0), + .xgmii_txc(xgmii_txc_0), + .xgmii_rxd(xgmii_rxd_0), + .xgmii_rxc(xgmii_rxc_0), + .configuration_vector(configuration_vector), + .status_vector(), + .dclk(dclk), + .mgmt_req(mgmt_req0), + .mgmt_gnt(mgmt_gnt0), + .mgmt_rd_out(mgmt_rd0), + .mgmt_wr_out(mgmt_wr0), + .mgmt_addr_out(mgmt_addr0), + .mgmt_rdack_in(mgmt_rdack), + .mgmt_rddata_in(mgmt_rddata), + .mgmt_wrdata_out(mgmt_wrdata0), + .gt_txd(gth_txd_0), + .gt_txc(gth_txc_0), + .gt_rxd(gth_rxd_0), + .gt_rxc(gth_rxc_0), + .signal_detect(1'b1), + .tx_fault(1'b0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_v2_6 +ten_gig_eth_pcs_pma_core_inst_1 +( + .reset(rst156), + .clk156(clk156), + .rxclk156(rx_clk_1_buf), + .xgmii_txd(xgmii_txd_1), + .xgmii_txc(xgmii_txc_1), + .xgmii_rxd(xgmii_rxd_1), + .xgmii_rxc(xgmii_rxc_1), + .configuration_vector(configuration_vector), + .status_vector(), + .dclk(dclk), + .mgmt_req(mgmt_req1), + .mgmt_gnt(mgmt_gnt1), + .mgmt_rd_out(mgmt_rd1), + .mgmt_wr_out(mgmt_wr1), + .mgmt_addr_out(mgmt_addr1), + .mgmt_rdack_in(mgmt_rdack), + .mgmt_rddata_in(mgmt_rddata), + .mgmt_wrdata_out(mgmt_wrdata1), + .gt_txd(gth_txd_1), + .gt_txc(gth_txc_1), + .gt_rxd(gth_rxd_1), + .gt_rxc(gth_rxc_1), + .signal_detect(1'b1), + .tx_fault(1'b0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_v2_6 +ten_gig_eth_pcs_pma_core_inst_2 +( + .reset(rst156), + .clk156(clk156), + .rxclk156(rx_clk_2_buf), + .xgmii_txd(xgmii_txd_2), + .xgmii_txc(xgmii_txc_2), + .xgmii_rxd(xgmii_rxd_2), + .xgmii_rxc(xgmii_rxc_2), + .configuration_vector(configuration_vector), + .status_vector(), + .dclk(dclk), + .mgmt_req(mgmt_req2), + .mgmt_gnt(mgmt_gnt2), + .mgmt_rd_out(mgmt_rd2), + .mgmt_wr_out(mgmt_wr2), + .mgmt_addr_out(mgmt_addr2), + .mgmt_rdack_in(mgmt_rdack), + .mgmt_rddata_in(mgmt_rddata), + .mgmt_wrdata_out(mgmt_wrdata2), + .gt_txd(gth_txd_2), + .gt_txc(gth_txc_2), + .gt_rxd(gth_rxd_2), + .gt_rxc(gth_rxc_2), + .signal_detect(1'b1), + .tx_fault(1'b0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_v2_6 +ten_gig_eth_pcs_pma_core_inst_3 +( + .reset(rst156), + .clk156(clk156), + .rxclk156(rx_clk_3_buf), + .xgmii_txd(xgmii_txd_3), + .xgmii_txc(xgmii_txc_3), + .xgmii_rxd(xgmii_rxd_3), + .xgmii_rxc(xgmii_rxc_3), + .configuration_vector(configuration_vector), + .status_vector(), + .dclk(dclk), + .mgmt_req(mgmt_req3), + .mgmt_gnt(mgmt_gnt3), + .mgmt_rd_out(mgmt_rd3), + .mgmt_wr_out(mgmt_wr3), + .mgmt_addr_out(mgmt_addr3), + .mgmt_rdack_in(mgmt_rdack), + .mgmt_rddata_in(mgmt_rddata), + .mgmt_wrdata_out(mgmt_wrdata3), + .gt_txd(gth_txd_3), + .gt_txc(gth_txc_3), + .gt_rxd(gth_rxd_3), + .gt_rxc(gth_rxc_3), + .signal_detect(1'b1), + .tx_fault(1'b0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_v2_6_management_arbiter +mgmt_arb_inst +( + .dclk(dclk), + .reset(dclk_reset), + + .mgmt_rd0(mgmt_rd0), + .mgmt_wr0(mgmt_wr0), + .mgmt_addr0(mgmt_addr0), + .mgmt_wrdata0(mgmt_wrdata0), + + .mgmt_req0(mgmt_req0), + .mgmt_gnt0(mgmt_gnt0), + + .mgmt_rd1(mgmt_rd1), + .mgmt_wr1(mgmt_wr1), + .mgmt_addr1(mgmt_addr1), + .mgmt_wrdata1(mgmt_wrdata1), + + .mgmt_req1(mgmt_req1), + .mgmt_gnt1(mgmt_gnt1), + + .mgmt_rd2(mgmt_rd2), + .mgmt_wr2(mgmt_wr2), + .mgmt_addr2(mgmt_addr2), + .mgmt_wrdata2(mgmt_wrdata2), + + .mgmt_req2(mgmt_req2), + .mgmt_gnt2(mgmt_gnt2), + + .mgmt_rd3(mgmt_rd3), + .mgmt_wr3(mgmt_wr3), + .mgmt_addr3(mgmt_addr3), + .mgmt_wrdata3(mgmt_wrdata3), + + .mgmt_req3(mgmt_req3), + .mgmt_gnt3(mgmt_gnt3), + + .mgmt_rd(mgmt_rd), + .mgmt_wr(mgmt_wr), + .mgmt_addr(mgmt_addr), + .mgmt_wrdata(mgmt_wrdata), + + .drp_req(1'b0), + .drp_gnt(), + + .disable_drp(disable_drp_mgmt) +); + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga/rtl/fpga.v b/fpga/lib/eth/example/HXT100G/fpga/rtl/fpga.v new file mode 100644 index 000000000..66f1df8d7 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/rtl/fpga.v @@ -0,0 +1,1131 @@ +/* + +Copyright (c) 2016-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. + +*/ + +module fpga ( + /* + * Clock: 50MHz + */ + input wire sys_clk, + /* + * Clock: 200MHz + */ + //input wire clk_ddr3_p, + //input wire clk_ddr3_n, + /* + * Clock: User + */ + //input wire clk_usr_p, + //input wire clk_usr_pr_n, + /* + * Reset: Push button, active low + */ + input wire reset_n, + /* + * GPIO + */ + input wire [1:0] sw, + input wire [3:0] jp, + output wire [3:0] led, + /* + * Silicon Labs CP2102 USB UART + */ + output wire uart_rst, + input wire uart_suspend, + output wire uart_ri, + output wire uart_dcd, + input wire uart_dtr, + output wire uart_dsr, + input wire uart_txd, + output wire uart_rxd, + input wire uart_rts, + output wire uart_cts, + /* + * Clock muxes + */ + inout wire clk_gth_scl, + inout wire clk_gth_sda, + output wire clk_gth_rst_n, + input wire clk_gthl_alm, + input wire clk_gthl_lol, + input wire clk_gthr_alm, + input wire clk_gthr_lol, + /* + * AirMax I/O + */ + output wire amh_right_mdc, + inout wire amh_right_mdio, + output wire amh_right_phy_rst_n, + output wire amh_left_mdc, + inout wire amh_left_mdio, + output wire amh_left_phy_rst_n, + /* + * 10G Ethernet + */ + input wire gth_quad_A_refclk_p, + input wire gth_quad_A_refclk_n, + output wire gth_quad_A_txp_0, + output wire gth_quad_A_txn_0, + input wire gth_quad_A_rxp_0, + input wire gth_quad_A_rxn_0, + output wire gth_quad_A_txp_1, + output wire gth_quad_A_txn_1, + input wire gth_quad_A_rxp_1, + input wire gth_quad_A_rxn_1, + output wire gth_quad_A_txp_2, + output wire gth_quad_A_txn_2, + input wire gth_quad_A_rxp_2, + input wire gth_quad_A_rxn_2, + output wire gth_quad_A_txp_3, + output wire gth_quad_A_txn_3, + input wire gth_quad_A_rxp_3, + input wire gth_quad_A_rxn_3, + input wire gth_quad_B_refclk_p, + input wire gth_quad_B_refclk_n, + output wire gth_quad_B_txp_0, + output wire gth_quad_B_txn_0, + input wire gth_quad_B_rxp_0, + input wire gth_quad_B_rxn_0, + output wire gth_quad_B_txp_1, + output wire gth_quad_B_txn_1, + input wire gth_quad_B_rxp_1, + input wire gth_quad_B_rxn_1, + output wire gth_quad_B_txp_2, + output wire gth_quad_B_txn_2, + input wire gth_quad_B_rxp_2, + input wire gth_quad_B_rxn_2, + output wire gth_quad_B_txp_3, + output wire gth_quad_B_txn_3, + input wire gth_quad_B_rxp_3, + input wire gth_quad_B_rxn_3, + input wire gth_quad_C_refclk_p, + input wire gth_quad_C_refclk_n, + output wire gth_quad_C_txp_0, + output wire gth_quad_C_txn_0, + input wire gth_quad_C_rxp_0, + input wire gth_quad_C_rxn_0, + output wire gth_quad_C_txp_1, + output wire gth_quad_C_txn_1, + input wire gth_quad_C_rxp_1, + input wire gth_quad_C_rxn_1, + output wire gth_quad_C_txp_2, + output wire gth_quad_C_txn_2, + input wire gth_quad_C_rxp_2, + input wire gth_quad_C_rxn_2, + output wire gth_quad_C_txp_3, + output wire gth_quad_C_txn_3, + input wire gth_quad_C_rxp_3, + input wire gth_quad_C_rxn_3, + input wire gth_quad_D_refclk_p, + input wire gth_quad_D_refclk_n, + output wire gth_quad_D_txp_0, + output wire gth_quad_D_txn_0, + input wire gth_quad_D_rxp_0, + input wire gth_quad_D_rxn_0, + output wire gth_quad_D_txp_1, + output wire gth_quad_D_txn_1, + input wire gth_quad_D_rxp_1, + input wire gth_quad_D_rxn_1, + output wire gth_quad_D_txp_2, + output wire gth_quad_D_txn_2, + input wire gth_quad_D_rxp_2, + input wire gth_quad_D_rxn_2, + output wire gth_quad_D_txp_3, + output wire gth_quad_D_txn_3, + input wire gth_quad_D_rxp_3, + input wire gth_quad_D_rxn_3, + input wire gth_quad_E_refclk_p, + input wire gth_quad_E_refclk_n, + output wire gth_quad_E_txp_0, + output wire gth_quad_E_txn_0, + input wire gth_quad_E_rxp_0, + input wire gth_quad_E_rxn_0, + output wire gth_quad_E_txp_1, + output wire gth_quad_E_txn_1, + input wire gth_quad_E_rxp_1, + input wire gth_quad_E_rxn_1, + output wire gth_quad_E_txp_2, + output wire gth_quad_E_txn_2, + input wire gth_quad_E_rxp_2, + input wire gth_quad_E_rxn_2, + output wire gth_quad_E_txp_3, + output wire gth_quad_E_txn_3, + input wire gth_quad_E_rxp_3, + input wire gth_quad_E_rxn_3, + input wire gth_quad_F_refclk_p, + input wire gth_quad_F_refclk_n, + output wire gth_quad_F_txp_0, + output wire gth_quad_F_txn_0, + input wire gth_quad_F_rxp_0, + input wire gth_quad_F_rxn_0, + output wire gth_quad_F_txp_1, + output wire gth_quad_F_txn_1, + input wire gth_quad_F_rxp_1, + input wire gth_quad_F_rxn_1, + output wire gth_quad_F_txp_2, + output wire gth_quad_F_txn_2, + input wire gth_quad_F_rxp_2, + input wire gth_quad_F_rxn_2, + output wire gth_quad_F_txp_3, + output wire gth_quad_F_txn_3, + input wire gth_quad_F_rxp_3, + input wire gth_quad_F_rxn_3 +); + +/* + * Clock: 50MHz + */ +wire sys_clk_ibufg; +wire sys_clk_int; + +/* + * Clock: 156.25 MHz + */ +wire clk_156mhz; + +/* + * Synchronous reset + */ +wire sys_rst; +wire rst_156mhz; + +/* + * GPIO + */ +wire [1:0] sw_int; +wire [3:0] jp_int; +wire [3:0] led_int; + +/* + * Silicon Labs CP2102 USB UART + */ +wire uart_sys_rst; +wire uart_suspend_int; +wire uart_ri_int; +wire uart_dcd_int; +wire uart_dtr_int; +wire uart_dsr_int; +wire uart_txd_int; +wire uart_rxd_int; +wire uart_rts_int; +wire uart_cts_int; + +/* + * Clock muxes + */ +wire clk_gth_scl_i; +wire clk_gth_scl_o; +wire clk_gth_scl_t; +wire clk_gth_sda_i; +wire clk_gth_sda_o; +wire clk_gth_sda_t; +wire clk_gthl_alm_int; +wire clk_gthl_lol_int; +wire clk_gthr_alm_int; +wire clk_gthr_lol_int; + +/* + * AirMax I/O + */ +wire amh_right_mdc_int; +wire amh_right_mdio_i_int; +wire amh_right_mdio_o_int; +wire amh_right_mdio_t_int; +wire amh_left_mdc_int; +wire amh_left_mdio_i_int; +wire amh_left_mdio_o_int; +wire amh_left_mdio_t_int; + +/* + * 10G Ethernet + */ +wire [63:0] eth_r0_txd; +wire [7:0] eth_r0_txc; +wire [63:0] eth_r0_rxd; +wire [7:0] eth_r0_rxc; +wire [63:0] eth_r1_txd; +wire [7:0] eth_r1_txc; +wire [63:0] eth_r1_rxd; +wire [7:0] eth_r1_rxc; +wire [63:0] eth_r2_txd; +wire [7:0] eth_r2_txc; +wire [63:0] eth_r2_rxd; +wire [7:0] eth_r2_rxc; +wire [63:0] eth_r3_txd; +wire [7:0] eth_r3_txc; +wire [63:0] eth_r3_rxd; +wire [7:0] eth_r3_rxc; +wire [63:0] eth_r4_txd; +wire [7:0] eth_r4_txc; +wire [63:0] eth_r4_rxd; +wire [7:0] eth_r4_rxc; +wire [63:0] eth_r5_txd; +wire [7:0] eth_r5_txc; +wire [63:0] eth_r5_rxd; +wire [7:0] eth_r5_rxc; +wire [63:0] eth_r6_txd; +wire [7:0] eth_r6_txc; +wire [63:0] eth_r6_rxd; +wire [7:0] eth_r6_rxc; +wire [63:0] eth_r7_txd; +wire [7:0] eth_r7_txc; +wire [63:0] eth_r7_rxd; +wire [7:0] eth_r7_rxc; +wire [63:0] eth_r8_txd; +wire [7:0] eth_r8_txc; +wire [63:0] eth_r8_rxd; +wire [7:0] eth_r8_rxc; +wire [63:0] eth_r9_txd; +wire [7:0] eth_r9_txc; +wire [63:0] eth_r9_rxd; +wire [7:0] eth_r9_rxc; +wire [63:0] eth_r10_txd; +wire [7:0] eth_r10_txc; +wire [63:0] eth_r10_rxd; +wire [7:0] eth_r10_rxc; +wire [63:0] eth_r11_txd; +wire [7:0] eth_r11_txc; +wire [63:0] eth_r11_rxd; +wire [7:0] eth_r11_rxc; +wire [63:0] eth_l0_txd; +wire [7:0] eth_l0_txc; +wire [63:0] eth_l0_rxd; +wire [7:0] eth_l0_rxc; +wire [63:0] eth_l1_txd; +wire [7:0] eth_l1_txc; +wire [63:0] eth_l1_rxd; +wire [7:0] eth_l1_rxc; +wire [63:0] eth_l2_txd; +wire [7:0] eth_l2_txc; +wire [63:0] eth_l2_rxd; +wire [7:0] eth_l2_rxc; +wire [63:0] eth_l3_txd; +wire [7:0] eth_l3_txc; +wire [63:0] eth_l3_rxd; +wire [7:0] eth_l3_rxc; +wire [63:0] eth_l4_txd; +wire [7:0] eth_l4_txc; +wire [63:0] eth_l4_rxd; +wire [7:0] eth_l4_rxc; +wire [63:0] eth_l5_txd; +wire [7:0] eth_l5_txc; +wire [63:0] eth_l5_rxd; +wire [7:0] eth_l5_rxc; +wire [63:0] eth_l6_txd; +wire [7:0] eth_l6_txc; +wire [63:0] eth_l6_rxd; +wire [7:0] eth_l6_rxc; +wire [63:0] eth_l7_txd; +wire [7:0] eth_l7_txc; +wire [63:0] eth_l7_rxd; +wire [7:0] eth_l7_rxc; +wire [63:0] eth_l8_txd; +wire [7:0] eth_l8_txc; +wire [63:0] eth_l8_rxd; +wire [7:0] eth_l8_rxc; +wire [63:0] eth_l9_txd; +wire [7:0] eth_l9_txc; +wire [63:0] eth_l9_rxd; +wire [7:0] eth_l9_rxc; +wire [63:0] eth_l10_txd; +wire [7:0] eth_l10_txc; +wire [63:0] eth_l10_rxd; +wire [7:0] eth_l10_rxc; +wire [63:0] eth_l11_txd; +wire [7:0] eth_l11_txc; +wire [63:0] eth_l11_rxd; +wire [7:0] eth_l11_rxc; + +// Clock buffering for 50 MHz sys_clk +IBUFG +sys_clk_ibufg_inst ( + .I(sys_clk), + .O(sys_clk_ibufg) +); + +BUFG +sys_clk_bufg_inst ( + .I(sys_clk_ibufg), + .O(sys_clk_int) +); + +// 156.25 MHz clock from GTH +wire txclk156; + +BUFG +clk156_bufg_inst ( + .I(txclk156), + .O(clk_156mhz) +); + +// Synchronize reset signal +sync_reset #( + .N(6) +) +sync_reset_inst ( + .clk(sys_clk_int), + .rst(~reset_n), + .sync_reset_out(sys_rst) +); + +sync_signal #( + .WIDTH(4), + .N(2) +) +sync_signal_50mhz_inst ( + .clk(sys_clk_int), + .in({clk_gthl_alm, + clk_gthl_lol, + clk_gthr_alm, + clk_gthr_lol}), + .out({clk_gthl_alm_int, + clk_gthl_lol_int, + clk_gthr_alm_int, + clk_gthr_lol_int}) +); + +sync_signal #( + .WIDTH(4), + .N(2) +) +sync_signal_156mhz_inst ( + .clk(clk_156mhz), + .in({uart_suspend, + uart_dtr, + uart_txd, + uart_rts}), + .out({uart_suspend_int, + uart_dtr_int, + uart_txd_int, + uart_rts_int}) +); + +// Debounce switch inputs +debounce_switch #( + .WIDTH(6), + .N(4), + .RATE(50000) +) +debounce_switch_inst ( + .clk(sys_clk_int), + .rst(sys_rst), + .in({sw, jp}), + .out({sw_int, jp_int}) +); + +// pass through outputs +assign led = led_int; + +assign uart_rst = uart_rst_int; +assign uart_ri = uart_ri_int; +assign uart_dcd = uart_dcd_int; +assign uart_dsr = uart_dsr_int; +assign uart_rxd = uart_rxd_int; +assign uart_cts = uart_cts_int; + +// clock mux I2C +assign clk_gth_scl_i = clk_gth_scl; +assign clk_gth_scl = clk_gth_scl_t ? 1'bz : clk_gth_scl_o; +assign clk_gth_sda_i = clk_gth_sda; +assign clk_gth_sda = clk_gth_sda_t ? 1'bz : clk_gth_sda_o; + +assign clk_gth_rst_n = ~sys_rst; + +wire [6:0] clk_gth_i2c_cmd_address; +wire clk_gth_i2c_cmd_start; +wire clk_gth_i2c_cmd_read; +wire clk_gth_i2c_cmd_write; +wire clk_gth_i2c_cmd_write_multiple; +wire clk_gth_i2c_cmd_stop; +wire clk_gth_i2c_cmd_valid; +wire clk_gth_i2c_cmd_ready; + +wire [7:0] clk_gth_i2c_data; +wire clk_gth_i2c_data_valid; +wire clk_gth_i2c_data_ready; +wire clk_gth_i2c_data_last; + +gth_i2c_init +clk_gth_i2c_init ( + .clk(sys_clk_int), + .rst(sys_rst), + .cmd_address(clk_gth_i2c_cmd_address), + .cmd_start(clk_gth_i2c_cmd_start), + .cmd_read(clk_gth_i2c_cmd_read), + .cmd_write(clk_gth_i2c_cmd_write), + .cmd_write_multiple(clk_gth_i2c_cmd_write_multiple), + .cmd_stop(clk_gth_i2c_cmd_stop), + .cmd_valid(clk_gth_i2c_cmd_valid), + .cmd_ready(clk_gth_i2c_cmd_ready), + .data_out(clk_gth_i2c_data), + .data_out_valid(clk_gth_i2c_data_valid), + .data_out_ready(clk_gth_i2c_data_ready), + .data_out_last(clk_gth_i2c_data_last), + .busy(), + .start(1) +); + +i2c_master +clk_gth_i2c_master ( + .clk(sys_clk_int), + .rst(sys_rst), + .cmd_address(clk_gth_i2c_cmd_address), + .cmd_start(clk_gth_i2c_cmd_start), + .cmd_read(clk_gth_i2c_cmd_read), + .cmd_write(clk_gth_i2c_cmd_write), + .cmd_write_multiple(clk_gth_i2c_cmd_write_multiple), + .cmd_stop(clk_gth_i2c_cmd_stop), + .cmd_valid(clk_gth_i2c_cmd_valid), + .cmd_ready(clk_gth_i2c_cmd_ready), + .data_in(clk_gth_i2c_data), + .data_in_valid(clk_gth_i2c_data_valid), + .data_in_ready(clk_gth_i2c_data_ready), + .data_in_last(clk_gth_i2c_data_last), + .data_out(), + .data_out_valid(), + .data_out_ready(1), + .data_out_last(), + .scl_i(clk_gth_scl_i), + .scl_o(clk_gth_scl_o), + .scl_t(clk_gth_scl_t), + .sda_i(clk_gth_sda_i), + .sda_o(clk_gth_sda_o), + .sda_t(clk_gth_sda_t), + .busy(), + .bus_control(), + .bus_active(), + .missed_ack(), + .prescale(312), + .stop_on_idle(1) +); + +// reset logic +wire gth_reset; + +wire gth_reset_done_A; +wire gth_reset_done_B; +wire gth_reset_done_C; +wire gth_reset_done_D; +wire gth_reset_done_E; +wire gth_reset_done_F; + +wire gth_reset_done = gth_reset_done_A & gth_reset_done_B & gth_reset_done_C & gth_reset_done_D & gth_reset_done_E & gth_reset_done_F; + +wire clk_gth_ready = ~clk_gthl_lol_int & ~clk_gthr_lol_int; + +sync_reset #( + .N(6) +) +sync_reset_gth_inst ( + .clk(sys_clk_int), + .rst(sys_rst | ~clk_gth_ready), + .sync_reset_out(gth_reset) +); + +sync_reset #( + .N(6) +) +sync_reset_156mhz_inst ( + .clk(clk_156mhz), + .rst(gth_reset | ~gth_reset_done), + .sync_reset_out(rst_156mhz) +); + +assign amh_right_phy_rst_n = ~rst_156mhz; +assign amh_left_phy_rst_n = ~rst_156mhz; + +// AirMax I/O + +assign amh_right_mdc = amh_right_mdc_int; + +assign amh_right_mdio_i_int = amh_right_mdio; +assign amh_right_mdio = amh_right_mdio_t_int ? 1'bz : amh_right_mdio_o_int; + +assign amh_left_mdc = amh_left_mdc_int; + +assign amh_left_mdio_i_int = amh_left_mdio; +assign amh_left_mdio = amh_left_mdio_t_int ? 1'bz : amh_left_mdio_o_int; + +// 10G Ethernet PCS/PMA + +// Quad A X0Y0 +eth_gth_phy_quad +eth_gth_phy_quad_A_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(txclk156), // pickoff one transmit clock for 156.25 MHz core clock + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_A), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_A_refclk_p), + .refclk_n(gth_quad_A_refclk_n), + .txn_0(gth_quad_A_txn_0), + .txp_0(gth_quad_A_txp_0), + .rxn_0(gth_quad_A_rxn_0), + .rxp_0(gth_quad_A_rxp_0), + .txn_1(gth_quad_A_txn_1), + .txp_1(gth_quad_A_txp_1), + .rxn_1(gth_quad_A_rxn_1), + .rxp_1(gth_quad_A_rxp_1), + .txn_2(gth_quad_A_txn_2), + .txp_2(gth_quad_A_txp_2), + .rxn_2(gth_quad_A_rxn_2), + .rxp_2(gth_quad_A_rxp_2), + .txn_3(gth_quad_A_txn_3), + .txp_3(gth_quad_A_txp_3), + .rxn_3(gth_quad_A_rxn_3), + .rxp_3(gth_quad_A_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_r0_txd), + .xgmii_txc_0(eth_r0_txc), + .xgmii_rxd_0(eth_r0_rxd), + .xgmii_rxc_0(eth_r0_rxc), + .xgmii_txd_1(eth_r2_txd), + .xgmii_txc_1(eth_r2_txc), + .xgmii_rxd_1(eth_r2_rxd), + .xgmii_rxc_1(eth_r2_rxc), + .xgmii_txd_2(eth_r4_txd), + .xgmii_txc_2(eth_r4_txc), + .xgmii_rxd_2(eth_r4_rxd), + .xgmii_rxc_2(eth_r4_rxc), + .xgmii_txd_3(eth_r6_txd), + .xgmii_txc_3(eth_r6_txc), + .xgmii_rxd_3(eth_r6_rxd), + .xgmii_rxc_3(eth_r6_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + +// Quad B X0Y1 +eth_gth_phy_quad +eth_gth_phy_quad_B_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(), + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_B), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_B_refclk_p), + .refclk_n(gth_quad_B_refclk_n), + .txn_0(gth_quad_B_txn_0), + .txp_0(gth_quad_B_txp_0), + .rxn_0(gth_quad_B_rxn_0), + .rxp_0(gth_quad_B_rxp_0), + .txn_1(gth_quad_B_txn_1), + .txp_1(gth_quad_B_txp_1), + .rxn_1(gth_quad_B_rxn_1), + .rxp_1(gth_quad_B_rxp_1), + .txn_2(gth_quad_B_txn_2), + .txp_2(gth_quad_B_txp_2), + .rxn_2(gth_quad_B_rxn_2), + .rxp_2(gth_quad_B_rxp_2), + .txn_3(gth_quad_B_txn_3), + .txp_3(gth_quad_B_txp_3), + .rxn_3(gth_quad_B_rxn_3), + .rxp_3(gth_quad_B_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_r3_txd), + .xgmii_txc_0(eth_r3_txc), + .xgmii_rxd_0(eth_r3_rxd), + .xgmii_rxc_0(eth_r3_rxc), + .xgmii_txd_1(eth_r5_txd), + .xgmii_txc_1(eth_r5_txc), + .xgmii_rxd_1(eth_r5_rxd), + .xgmii_rxc_1(eth_r5_rxc), + .xgmii_txd_2(eth_r1_txd), + .xgmii_txc_2(eth_r1_txc), + .xgmii_rxd_2(eth_r1_rxd), + .xgmii_rxc_2(eth_r1_rxc), + .xgmii_txd_3(eth_r7_txd), + .xgmii_txc_3(eth_r7_txc), + .xgmii_rxd_3(eth_r7_rxd), + .xgmii_rxc_3(eth_r7_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + +// Quad C X0Y2 +eth_gth_phy_quad +eth_gth_phy_quad_C_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(), + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_C), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_C_refclk_p), + .refclk_n(gth_quad_C_refclk_n), + .txn_0(gth_quad_C_txn_0), + .txp_0(gth_quad_C_txp_0), + .rxn_0(gth_quad_C_rxn_0), + .rxp_0(gth_quad_C_rxp_0), + .txn_1(gth_quad_C_txn_1), + .txp_1(gth_quad_C_txp_1), + .rxn_1(gth_quad_C_rxn_1), + .rxp_1(gth_quad_C_rxp_1), + .txn_2(gth_quad_C_txn_2), + .txp_2(gth_quad_C_txp_2), + .rxn_2(gth_quad_C_rxn_2), + .rxp_2(gth_quad_C_rxp_2), + .txn_3(gth_quad_C_txn_3), + .txp_3(gth_quad_C_txp_3), + .rxn_3(gth_quad_C_rxn_3), + .rxp_3(gth_quad_C_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_r8_txd), + .xgmii_txc_0(eth_r8_txc), + .xgmii_rxd_0(eth_r8_rxd), + .xgmii_rxc_0(eth_r8_rxc), + .xgmii_txd_1(eth_r9_txd), + .xgmii_txc_1(eth_r9_txc), + .xgmii_rxd_1(eth_r9_rxd), + .xgmii_rxc_1(eth_r9_rxc), + .xgmii_txd_2(eth_r11_txd), + .xgmii_txc_2(eth_r11_txc), + .xgmii_rxd_2(eth_r11_rxd), + .xgmii_rxc_2(eth_r11_rxc), + .xgmii_txd_3(eth_r10_txd), + .xgmii_txc_3(eth_r10_txc), + .xgmii_rxd_3(eth_r10_rxd), + .xgmii_rxc_3(eth_r10_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + +// Quad D X1Y0 +eth_gth_phy_quad +eth_gth_phy_quad_D_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(), + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_D), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_D_refclk_p), + .refclk_n(gth_quad_D_refclk_n), + .txn_0(gth_quad_D_txn_0), + .txp_0(gth_quad_D_txp_0), + .rxn_0(gth_quad_D_rxn_0), + .rxp_0(gth_quad_D_rxp_0), + .txn_1(gth_quad_D_txn_1), + .txp_1(gth_quad_D_txp_1), + .rxn_1(gth_quad_D_rxn_1), + .rxp_1(gth_quad_D_rxp_1), + .txn_2(gth_quad_D_txn_2), + .txp_2(gth_quad_D_txp_2), + .rxn_2(gth_quad_D_rxn_2), + .rxp_2(gth_quad_D_rxp_2), + .txn_3(gth_quad_D_txn_3), + .txp_3(gth_quad_D_txp_3), + .rxn_3(gth_quad_D_rxn_3), + .rxp_3(gth_quad_D_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_l0_txd), + .xgmii_txc_0(eth_l0_txc), + .xgmii_rxd_0(eth_l0_rxd), + .xgmii_rxc_0(eth_l0_rxc), + .xgmii_txd_1(eth_l2_txd), + .xgmii_txc_1(eth_l2_txc), + .xgmii_rxd_1(eth_l2_rxd), + .xgmii_rxc_1(eth_l2_rxc), + .xgmii_txd_2(eth_l4_txd), + .xgmii_txc_2(eth_l4_txc), + .xgmii_rxd_2(eth_l4_rxd), + .xgmii_rxc_2(eth_l4_rxc), + .xgmii_txd_3(eth_l6_txd), + .xgmii_txc_3(eth_l6_txc), + .xgmii_rxd_3(eth_l6_rxd), + .xgmii_rxc_3(eth_l6_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + +// Quad E X1Y1 +eth_gth_phy_quad +eth_gth_phy_quad_E_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(), + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_E), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_E_refclk_p), + .refclk_n(gth_quad_E_refclk_n), + .txn_0(gth_quad_E_txn_0), + .txp_0(gth_quad_E_txp_0), + .rxn_0(gth_quad_E_rxn_0), + .rxp_0(gth_quad_E_rxp_0), + .txn_1(gth_quad_E_txn_1), + .txp_1(gth_quad_E_txp_1), + .rxn_1(gth_quad_E_rxn_1), + .rxp_1(gth_quad_E_rxp_1), + .txn_2(gth_quad_E_txn_2), + .txp_2(gth_quad_E_txp_2), + .rxn_2(gth_quad_E_rxn_2), + .rxp_2(gth_quad_E_rxp_2), + .txn_3(gth_quad_E_txn_3), + .txp_3(gth_quad_E_txp_3), + .rxn_3(gth_quad_E_rxn_3), + .rxp_3(gth_quad_E_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_l3_txd), + .xgmii_txc_0(eth_l3_txc), + .xgmii_rxd_0(eth_l3_rxd), + .xgmii_rxc_0(eth_l3_rxc), + .xgmii_txd_1(eth_l5_txd), + .xgmii_txc_1(eth_l5_txc), + .xgmii_rxd_1(eth_l5_rxd), + .xgmii_rxc_1(eth_l5_rxc), + .xgmii_txd_2(eth_l1_txd), + .xgmii_txc_2(eth_l1_txc), + .xgmii_rxd_2(eth_l1_rxd), + .xgmii_rxc_2(eth_l1_rxc), + .xgmii_txd_3(eth_l7_txd), + .xgmii_txc_3(eth_l7_txc), + .xgmii_rxd_3(eth_l7_rxd), + .xgmii_rxc_3(eth_l7_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + +// Quad F X1Y2 +eth_gth_phy_quad +eth_gth_phy_quad_F_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(), + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_F), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_F_refclk_p), + .refclk_n(gth_quad_F_refclk_n), + .txn_0(gth_quad_F_txn_0), + .txp_0(gth_quad_F_txp_0), + .rxn_0(gth_quad_F_rxn_0), + .rxp_0(gth_quad_F_rxp_0), + .txn_1(gth_quad_F_txn_1), + .txp_1(gth_quad_F_txp_1), + .rxn_1(gth_quad_F_rxn_1), + .rxp_1(gth_quad_F_rxp_1), + .txn_2(gth_quad_F_txn_2), + .txp_2(gth_quad_F_txp_2), + .rxn_2(gth_quad_F_rxn_2), + .rxp_2(gth_quad_F_rxp_2), + .txn_3(gth_quad_F_txn_3), + .txp_3(gth_quad_F_txp_3), + .rxn_3(gth_quad_F_rxn_3), + .rxp_3(gth_quad_F_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_l8_txd), + .xgmii_txc_0(eth_l8_txc), + .xgmii_rxd_0(eth_l8_rxd), + .xgmii_rxc_0(eth_l8_rxc), + .xgmii_txd_1(eth_l9_txd), + .xgmii_txc_1(eth_l9_txc), + .xgmii_rxd_1(eth_l9_rxd), + .xgmii_rxc_1(eth_l9_rxc), + .xgmii_txd_2(eth_l11_txd), + .xgmii_txc_2(eth_l11_txc), + .xgmii_rxd_2(eth_l11_rxd), + .xgmii_rxc_2(eth_l11_rxc), + .xgmii_txd_3(eth_l10_txd), + .xgmii_txc_3(eth_l10_txc), + .xgmii_rxd_3(eth_l10_rxd), + .xgmii_rxc_3(eth_l10_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + + +fpga_core +core_inst ( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + .clk(clk_156mhz), + .rst(rst_156mhz), + /* + * GPIO + */ + .sw(sw_int), + .jp(jp_int), + .led(led_int), + /* + * Silicon Labs CP2102 USB UART + */ + .uart_rst(uart_rst_int), + .uart_suspend(uart_suspend_int), + .uart_ri(uart_ri_int), + .uart_dcd(uart_dcd_int), + .uart_dtr(uart_dtr_int), + .uart_dsr(uart_dsr_int), + .uart_txd(uart_txd_int), + .uart_rxd(uart_rxd_int), + .uart_rts(uart_rts_int), + .uart_cts(uart_cts_int), + /* + * AirMax I/O + */ + .amh_right_mdc(amh_right_mdc_int), + .amh_right_mdio_i(amh_right_mdio_i_int), + .amh_right_mdio_o(amh_right_mdio_o_int), + .amh_right_mdio_t(amh_right_mdio_t_int), + .amh_left_mdc(amh_left_mdc_int), + .amh_left_mdio_i(amh_left_mdio_i_int), + .amh_left_mdio_o(amh_left_mdio_o_int), + .amh_left_mdio_t(amh_left_mdio_t_int), + /* + * 10G Ethernet XGMII + */ + .eth_r0_txd(eth_r0_txd), + .eth_r0_txc(eth_r0_txc), + .eth_r0_rxd(eth_r0_rxd), + .eth_r0_rxc(eth_r0_rxc), + .eth_r1_txd(eth_r1_txd), + .eth_r1_txc(eth_r1_txc), + .eth_r1_rxd(eth_r1_rxd), + .eth_r1_rxc(eth_r1_rxc), + .eth_r2_txd(eth_r2_txd), + .eth_r2_txc(eth_r2_txc), + .eth_r2_rxd(eth_r2_rxd), + .eth_r2_rxc(eth_r2_rxc), + .eth_r3_txd(eth_r3_txd), + .eth_r3_txc(eth_r3_txc), + .eth_r3_rxd(eth_r3_rxd), + .eth_r3_rxc(eth_r3_rxc), + .eth_r4_txd(eth_r4_txd), + .eth_r4_txc(eth_r4_txc), + .eth_r4_rxd(eth_r4_rxd), + .eth_r4_rxc(eth_r4_rxc), + .eth_r5_txd(eth_r5_txd), + .eth_r5_txc(eth_r5_txc), + .eth_r5_rxd(eth_r5_rxd), + .eth_r5_rxc(eth_r5_rxc), + .eth_r6_txd(eth_r6_txd), + .eth_r6_txc(eth_r6_txc), + .eth_r6_rxd(eth_r6_rxd), + .eth_r6_rxc(eth_r6_rxc), + .eth_r7_txd(eth_r7_txd), + .eth_r7_txc(eth_r7_txc), + .eth_r7_rxd(eth_r7_rxd), + .eth_r7_rxc(eth_r7_rxc), + .eth_r8_txd(eth_r8_txd), + .eth_r8_txc(eth_r8_txc), + .eth_r8_rxd(eth_r8_rxd), + .eth_r8_rxc(eth_r8_rxc), + .eth_r9_txd(eth_r9_txd), + .eth_r9_txc(eth_r9_txc), + .eth_r9_rxd(eth_r9_rxd), + .eth_r9_rxc(eth_r9_rxc), + .eth_r10_txd(eth_r10_txd), + .eth_r10_txc(eth_r10_txc), + .eth_r10_rxd(eth_r10_rxd), + .eth_r10_rxc(eth_r10_rxc), + .eth_r11_txd(eth_r11_txd), + .eth_r11_txc(eth_r11_txc), + .eth_r11_rxd(eth_r11_rxd), + .eth_r11_rxc(eth_r11_rxc), + .eth_l0_txd(eth_l0_txd), + .eth_l0_txc(eth_l0_txc), + .eth_l0_rxd(eth_l0_rxd), + .eth_l0_rxc(eth_l0_rxc), + .eth_l1_txd(eth_l1_txd), + .eth_l1_txc(eth_l1_txc), + .eth_l1_rxd(eth_l1_rxd), + .eth_l1_rxc(eth_l1_rxc), + .eth_l2_txd(eth_l2_txd), + .eth_l2_txc(eth_l2_txc), + .eth_l2_rxd(eth_l2_rxd), + .eth_l2_rxc(eth_l2_rxc), + .eth_l3_txd(eth_l3_txd), + .eth_l3_txc(eth_l3_txc), + .eth_l3_rxd(eth_l3_rxd), + .eth_l3_rxc(eth_l3_rxc), + .eth_l4_txd(eth_l4_txd), + .eth_l4_txc(eth_l4_txc), + .eth_l4_rxd(eth_l4_rxd), + .eth_l4_rxc(eth_l4_rxc), + .eth_l5_txd(eth_l5_txd), + .eth_l5_txc(eth_l5_txc), + .eth_l5_rxd(eth_l5_rxd), + .eth_l5_rxc(eth_l5_rxc), + .eth_l6_txd(eth_l6_txd), + .eth_l6_txc(eth_l6_txc), + .eth_l6_rxd(eth_l6_rxd), + .eth_l6_rxc(eth_l6_rxc), + .eth_l7_txd(eth_l7_txd), + .eth_l7_txc(eth_l7_txc), + .eth_l7_rxd(eth_l7_rxd), + .eth_l7_rxc(eth_l7_rxc), + .eth_l8_txd(eth_l8_txd), + .eth_l8_txc(eth_l8_txc), + .eth_l8_rxd(eth_l8_rxd), + .eth_l8_rxc(eth_l8_rxc), + .eth_l9_txd(eth_l9_txd), + .eth_l9_txc(eth_l9_txc), + .eth_l9_rxd(eth_l9_rxd), + .eth_l9_rxc(eth_l9_rxc), + .eth_l10_txd(eth_l10_txd), + .eth_l10_txc(eth_l10_txc), + .eth_l10_rxd(eth_l10_rxd), + .eth_l10_rxc(eth_l10_rxc), + .eth_l11_txd(eth_l11_txd), + .eth_l11_txc(eth_l11_txc), + .eth_l11_rxd(eth_l11_rxd), + .eth_l11_rxc(eth_l11_rxc) +); + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga/rtl/fpga_core.v b/fpga/lib/eth/example/HXT100G/fpga/rtl/fpga_core.v new file mode 100644 index 000000000..4efe34132 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/rtl/fpga_core.v @@ -0,0 +1,754 @@ +/* + +Copyright (c) 2016-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 + +module fpga_core +( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + /* + * GPIO + */ + input wire [1:0] sw, + input wire [3:0] jp, + output wire [3:0] led, + /* + * Silicon Labs CP2102 USB UART + */ + output wire uart_rst, + input wire uart_suspend, + output wire uart_ri, + output wire uart_dcd, + input wire uart_dtr, + output wire uart_dsr, + input wire uart_txd, + output wire uart_rxd, + input wire uart_rts, + output wire uart_cts, + /* + * AirMax I/O + */ + output wire amh_right_mdc, + input wire amh_right_mdio_i, + output wire amh_right_mdio_o, + output wire amh_right_mdio_t, + output wire amh_left_mdc, + input wire amh_left_mdio_i, + output wire amh_left_mdio_o, + output wire amh_left_mdio_t, + /* + * 10G Ethernet + */ + output wire [63:0] eth_r0_txd, + output wire [7:0] eth_r0_txc, + input wire [63:0] eth_r0_rxd, + input wire [7:0] eth_r0_rxc, + output wire [63:0] eth_r1_txd, + output wire [7:0] eth_r1_txc, + input wire [63:0] eth_r1_rxd, + input wire [7:0] eth_r1_rxc, + output wire [63:0] eth_r2_txd, + output wire [7:0] eth_r2_txc, + input wire [63:0] eth_r2_rxd, + input wire [7:0] eth_r2_rxc, + output wire [63:0] eth_r3_txd, + output wire [7:0] eth_r3_txc, + input wire [63:0] eth_r3_rxd, + input wire [7:0] eth_r3_rxc, + output wire [63:0] eth_r4_txd, + output wire [7:0] eth_r4_txc, + input wire [63:0] eth_r4_rxd, + input wire [7:0] eth_r4_rxc, + output wire [63:0] eth_r5_txd, + output wire [7:0] eth_r5_txc, + input wire [63:0] eth_r5_rxd, + input wire [7:0] eth_r5_rxc, + output wire [63:0] eth_r6_txd, + output wire [7:0] eth_r6_txc, + input wire [63:0] eth_r6_rxd, + input wire [7:0] eth_r6_rxc, + output wire [63:0] eth_r7_txd, + output wire [7:0] eth_r7_txc, + input wire [63:0] eth_r7_rxd, + input wire [7:0] eth_r7_rxc, + output wire [63:0] eth_r8_txd, + output wire [7:0] eth_r8_txc, + input wire [63:0] eth_r8_rxd, + input wire [7:0] eth_r8_rxc, + output wire [63:0] eth_r9_txd, + output wire [7:0] eth_r9_txc, + input wire [63:0] eth_r9_rxd, + input wire [7:0] eth_r9_rxc, + output wire [63:0] eth_r10_txd, + output wire [7:0] eth_r10_txc, + input wire [63:0] eth_r10_rxd, + input wire [7:0] eth_r10_rxc, + output wire [63:0] eth_r11_txd, + output wire [7:0] eth_r11_txc, + input wire [63:0] eth_r11_rxd, + input wire [7:0] eth_r11_rxc, + output wire [63:0] eth_l0_txd, + output wire [7:0] eth_l0_txc, + input wire [63:0] eth_l0_rxd, + input wire [7:0] eth_l0_rxc, + output wire [63:0] eth_l1_txd, + output wire [7:0] eth_l1_txc, + input wire [63:0] eth_l1_rxd, + input wire [7:0] eth_l1_rxc, + output wire [63:0] eth_l2_txd, + output wire [7:0] eth_l2_txc, + input wire [63:0] eth_l2_rxd, + input wire [7:0] eth_l2_rxc, + output wire [63:0] eth_l3_txd, + output wire [7:0] eth_l3_txc, + input wire [63:0] eth_l3_rxd, + input wire [7:0] eth_l3_rxc, + output wire [63:0] eth_l4_txd, + output wire [7:0] eth_l4_txc, + input wire [63:0] eth_l4_rxd, + input wire [7:0] eth_l4_rxc, + output wire [63:0] eth_l5_txd, + output wire [7:0] eth_l5_txc, + input wire [63:0] eth_l5_rxd, + input wire [7:0] eth_l5_rxc, + output wire [63:0] eth_l6_txd, + output wire [7:0] eth_l6_txc, + input wire [63:0] eth_l6_rxd, + input wire [7:0] eth_l6_rxc, + output wire [63:0] eth_l7_txd, + output wire [7:0] eth_l7_txc, + input wire [63:0] eth_l7_rxd, + input wire [7:0] eth_l7_rxc, + output wire [63:0] eth_l8_txd, + output wire [7:0] eth_l8_txc, + input wire [63:0] eth_l8_rxd, + input wire [7:0] eth_l8_rxc, + output wire [63:0] eth_l9_txd, + output wire [7:0] eth_l9_txc, + input wire [63:0] eth_l9_rxd, + input wire [7:0] eth_l9_rxc, + output wire [63:0] eth_l10_txd, + output wire [7:0] eth_l10_txc, + input wire [63:0] eth_l10_rxd, + input wire [7:0] eth_l10_rxc, + output wire [63:0] eth_l11_txd, + output wire [7:0] eth_l11_txc, + input wire [63:0] eth_l11_rxd, + input wire [7:0] eth_l11_rxc +); + +// UART +assign uart_rst = 1'b1; +assign uart_txd = 1'b1; + +// AirMax I/O +assign amh_right_mdc = 1'b1; +assign amh_right_mdio_o = 1'b1; +assign amh_right_mdio_t = 1'b1; +assign amh_left_mdc = 1'b1; +assign amh_left_mdio_o = 1'b1; +assign amh_left_mdio_t = 1'b1; + +// AXI between MAC and Ethernet modules +wire [63:0] rx_axis_tdata; +wire [7:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tready; +wire rx_axis_tlast; +wire rx_axis_tuser; + +wire [63:0] tx_axis_tdata; +wire [7:0] tx_axis_tkeep; +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 [63:0] rx_eth_payload_axis_tdata; +wire [7:0] rx_eth_payload_axis_tkeep; +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 [63:0] tx_eth_payload_axis_tdata; +wire [7:0] tx_eth_payload_axis_tkeep; +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 [63:0] rx_ip_payload_axis_tdata; +wire [7:0] rx_ip_payload_axis_tkeep; +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 [63:0] tx_ip_payload_axis_tdata; +wire [7:0] tx_ip_payload_axis_tkeep; +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 [63:0] rx_udp_payload_axis_tdata; +wire [7:0] rx_udp_payload_axis_tkeep; +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 [63:0] tx_udp_payload_axis_tdata; +wire [7:0] tx_udp_payload_axis_tkeep; +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 [63:0] rx_fifo_udp_payload_axis_tdata; +wire [7:0] rx_fifo_udp_payload_axis_tkeep; +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 [63:0] tx_fifo_udp_payload_axis_tdata; +wire [7:0] tx_fifo_udp_payload_axis_tkeep; +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_tkeep = 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_tkeep = tx_fifo_udp_payload_axis_tkeep; +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_tkeep = rx_udp_payload_axis_tkeep; +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 eth_r0_txd = 64'h0707070707070707; +assign eth_r0_txc = 8'hff; +assign eth_r1_txd = 64'h0707070707070707; +assign eth_r1_txc = 8'hff; +assign eth_r2_txd = 64'h0707070707070707; +assign eth_r2_txc = 8'hff; +assign eth_r3_txd = 64'h0707070707070707; +assign eth_r3_txc = 8'hff; +assign eth_r4_txd = 64'h0707070707070707; +assign eth_r4_txc = 8'hff; +assign eth_r5_txd = 64'h0707070707070707; +assign eth_r5_txc = 8'hff; +assign eth_r6_txd = 64'h0707070707070707; +assign eth_r6_txc = 8'hff; +assign eth_r7_txd = 64'h0707070707070707; +assign eth_r7_txc = 8'hff; +assign eth_r8_txd = 64'h0707070707070707; +assign eth_r8_txc = 8'hff; +assign eth_r9_txd = 64'h0707070707070707; +assign eth_r9_txc = 8'hff; +assign eth_r10_txd = 64'h0707070707070707; +assign eth_r10_txc = 8'hff; +assign eth_r11_txd = 64'h0707070707070707; +assign eth_r11_txc = 8'hff; + +//assign eth_l0_txd = 64'h0707070707070707; +//assign eth_l0_txc = 8'hff; +assign eth_l1_txd = 64'h0707070707070707; +assign eth_l1_txc = 8'hff; +assign eth_l2_txd = 64'h0707070707070707; +assign eth_l2_txc = 8'hff; +assign eth_l3_txd = 64'h0707070707070707; +assign eth_l3_txc = 8'hff; +assign eth_l4_txd = 64'h0707070707070707; +assign eth_l4_txc = 8'hff; +assign eth_l5_txd = 64'h0707070707070707; +assign eth_l5_txc = 8'hff; +assign eth_l6_txd = 64'h0707070707070707; +assign eth_l6_txc = 8'hff; +assign eth_l7_txd = 64'h0707070707070707; +assign eth_l7_txc = 8'hff; +assign eth_l8_txd = 64'h0707070707070707; +assign eth_l8_txc = 8'hff; +assign eth_l9_txd = 64'h0707070707070707; +assign eth_l9_txc = 8'hff; +assign eth_l10_txd = 64'h0707070707070707; +assign eth_l10_txc = 8'hff; +assign eth_l11_txd = 64'h0707070707070707; +assign eth_l11_txc = 8'hff; + +eth_mac_10g_fifo #( + .ENABLE_PADDING(1), + .ENABLE_DIC(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(9), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(9), + .RX_FRAME_FIFO(1) +) +eth_mac_10g_fifo_inst ( + .rx_clk(clk), + .rx_rst(rst), + .tx_clk(clk), + .tx_rst(rst), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + + .xgmii_rxd(eth_l0_rxd), + .xgmii_rxc(eth_l0_rxc), + .xgmii_txd(eth_l0_txd), + .xgmii_txc(eth_l0_txc), + + .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(), + + .ifg_delay(8'd12) +); + +eth_axis_rx_64 +eth_axis_rx_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(rx_axis_tdata), + .s_axis_tkeep(rx_axis_tkeep), + .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_tkeep(rx_eth_payload_axis_tkeep), + .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_64 +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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_axis_tkeep), + .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_64 +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_tkeep(rx_eth_payload_axis_tkeep), + .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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_ip_payload_axis_tkeep), + .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_tkeep(rx_ip_payload_axis_tkeep), + .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_tkeep(tx_udp_payload_axis_tkeep), + .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_tkeep(rx_udp_payload_axis_tkeep), + .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(1'b0) +); + +axis_fifo #( + .ADDR_WIDTH(10), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .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(rx_fifo_udp_payload_axis_tkeep), + .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(tx_fifo_udp_payload_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/fpga/lib/eth/example/HXT100G/fpga/rtl/gth_i2c_init.v b/fpga/lib/eth/example/HXT100G/fpga/rtl/gth_i2c_init.v new file mode 100644 index 000000000..a8eff170a --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/rtl/gth_i2c_init.v @@ -0,0 +1,508 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * gth_i2c_init + */ +module gth_i2c_init ( + input wire clk, + input wire rst, + + /* + * I2C master interface + */ + output wire [6:0] cmd_address, + output wire cmd_start, + output wire cmd_read, + output wire cmd_write, + output wire cmd_write_multiple, + output wire cmd_stop, + output wire cmd_valid, + input wire cmd_ready, + + output wire [7:0] data_out, + output wire data_out_valid, + input wire data_out_ready, + output wire data_out_last, + + /* + * Status + */ + output wire busy, + + /* + * Configuration + */ + input wire start +); + +/* + +Generic module for I2C bus initialization. Good for use when multiple devices +on an I2C bus must be initialized on system start without intervention of a +general-purpose processor. + +Copy this file and change init_data and INIT_DATA_LEN as needed. + +This module can be used in two modes: simple device initalization, or multiple +device initialization. In multiple device mode, the same initialization sequence +can be performed on multiple different device addresses. + +To use single device mode, only use the start write to address and write data commands. +The module will generate the I2C commands in sequential order. Terminate the list +with a 0 entry. + +To use the multiple device mode, use the start data and start address block commands +to set up lists of initialization data and device addresses. The module enters +multiple device mode upon seeing a start data block command. The module stores the +offset of the start of the data block and then skips ahead until it reaches a start +address block command. The module will store the offset to the address block and +read the first address in the block. Then it will jump back to the data block +and execute it, substituting the stored address for each current address write +command. Upon reaching the start address block command, the module will read out the +next address and start again at the top of the data block. If the module encounters +a start data block command while looking for an address, then it will store a new data +offset and then look for a start address block command. Terminate the list with a 0 +entry. Normal address commands will operate normally inside a data block. + +Commands: + +00 0000000 : stop +00 0000001 : exit multiple device mode +00 0000011 : start write to current address +00 0001000 : start address block +00 0001001 : start data block +01 aaaaaaa : start write to address +1 dddddddd : write 8-bit data + +Examples + +write 0x11223344 to register 0x0004 on device at 0x50 + +01 1010000 start write to 0x50 +1 00000000 write address 0x0004 +1 00000100 +1 00010001 write data 0x11223344 +1 00100010 +1 00110011 +1 01000100 +0 00000000 stop + +write 0x11223344 to register 0x0004 on devices at 0x50, 0x51, 0x52, and 0x53 + +00 0001001 start data block +00 0000011 start write to current address +1 00000100 +1 00010001 write data 0x11223344 +1 00100010 +1 00110011 +1 01000100 +00 0001000 start address block +01 1010000 address 0x50 +01 1010000 address 0x51 +01 1010000 address 0x52 +01 1010000 address 0x53 +00 0000000 stop + +*/ + +// init_data ROM +localparam INIT_DATA_LEN = 68; + +reg [8:0] init_data [INIT_DATA_LEN-1:0]; + +initial begin + // init clock mux registers + init_data[0] = {2'b00, 7'b0001001}; // start data block + init_data[1] = {2'b00, 7'b0000011}; // start write to current address + init_data[2] = {1'b1, 8'd2}; // select PLL bandwidth + //init_data[3] = {1'b1, 8'hA2}; + init_data[3] = {1'b1, 8'hA0}; + init_data[4] = {2'b00, 7'b0000011}; // start write to current address + init_data[5] = {1'b1, 8'd3}; // disable outputs during ICAL + //init_data[6] = {1'b1, 8'h15}; + init_data[6] = {1'b1, 8'h10}; + init_data[7] = {2'b00, 7'b0000011}; // start write to current address + init_data[8] = {1'b1, 8'd5}; // set CLKOUT1 and CLKOUT2 to LVPECL + init_data[9] = {1'b1, 8'hED}; + init_data[10] = {2'b00, 7'b0000011}; // start write to current address + init_data[11] = {1'b1, 8'd6}; // set CLKOUT3 to LVPECL and CLKOUT4 to LVDS + //init_data[12] = {1'b1, 8'h2D}; + init_data[12] = {1'b1, 8'h3D}; + init_data[13] = {2'b00, 7'b0000011}; // start write to current address + init_data[14] = {1'b1, 8'd7}; // set CLKOUT5 to to LVDS + //init_data[15] = {1'b1, 8'h0A}; + init_data[15] = {1'b1, 8'h3A}; + init_data[16] = {2'b00, 7'b0000011}; // start write to current address + init_data[17] = {1'b1, 8'd20}; // enable LOL output + init_data[18] = {1'b1, 8'h3E}; + init_data[19] = {2'b00, 7'b0000011}; // start write to current address + init_data[20] = {1'b1, 8'd25}; // N1_HS + init_data[21] = {1'b1, 8'h40}; + init_data[22] = {2'b00, 7'b0000011}; // start write to current address + init_data[23] = {1'b1, 8'd27}; // NC1_LS + init_data[24] = {1'b1, 8'h05}; + init_data[25] = {2'b00, 7'b0000011}; // start write to current address + init_data[26] = {1'b1, 8'd30}; // NC2_LS + init_data[27] = {1'b1, 8'h05}; + init_data[28] = {2'b00, 7'b0000011}; // start write to current address + init_data[29] = {1'b1, 8'd33}; // NC3_LS + init_data[30] = {1'b1, 8'h05}; + init_data[31] = {2'b00, 7'b0000011}; // start write to current address + init_data[32] = {1'b1, 8'd36}; // NC4_LS + init_data[33] = {1'b1, 8'h05}; + init_data[34] = {2'b00, 7'b0000011}; // start write to current address + init_data[35] = {1'b1, 8'd39}; // NC5_LS + init_data[36] = {1'b1, 8'h05}; + init_data[37] = {2'b00, 7'b0000011}; // start write to current address + init_data[38] = {1'b1, 8'd40}; // N2_HS + init_data[39] = {1'b1, 8'hA0}; + init_data[40] = {2'b00, 7'b0000011}; // start write to current address + init_data[41] = {1'b1, 8'd41}; // N2_LS + init_data[42] = {1'b1, 8'h01}; + init_data[43] = {2'b00, 7'b0000011}; // start write to current address + init_data[44] = {1'b1, 8'd42}; // N2_LS + init_data[45] = {1'b1, 8'h3B}; + init_data[46] = {2'b00, 7'b0000011}; // start write to current address + init_data[47] = {1'b1, 8'd45}; // N31 + init_data[48] = {1'b1, 8'h4E}; + init_data[49] = {2'b00, 7'b0000011}; // start write to current address + init_data[50] = {1'b1, 8'd48}; // N32 + init_data[51] = {1'b1, 8'h4E}; + init_data[52] = {2'b00, 7'b0000011}; // start write to current address + init_data[53] = {1'b1, 8'd51}; // N33 + init_data[54] = {1'b1, 8'h4E}; + init_data[55] = {2'b00, 7'b0000011}; // start write to current address + init_data[56] = {1'b1, 8'd54}; // N34 + init_data[57] = {1'b1, 8'h4E}; + init_data[58] = {2'b00, 7'b0000011}; // start write to current address + init_data[59] = {1'b1, 8'd141}; // Zero independent skew + init_data[60] = {1'b1, 8'h00}; + init_data[61] = {2'b00, 7'b0000011}; // start write to current address + init_data[62] = {1'b1, 8'd136}; // Soft reset + init_data[63] = {1'b1, 8'h40}; + init_data[64] = {2'b00, 7'b0001000}; // start address block + init_data[65] = {2'b01, 7'b1101001}; // first clock mux + init_data[66] = {2'b01, 7'b1101000}; // second clock mux + init_data[67] = 9'd0; // stop +end + +localparam [3:0] + STATE_IDLE = 3'd0, + STATE_RUN = 3'd1, + STATE_TABLE_1 = 3'd2, + STATE_TABLE_2 = 3'd3, + STATE_TABLE_3 = 3'd4; + +reg [4:0] state_reg = STATE_IDLE, state_next; + +parameter AW = $clog2(INIT_DATA_LEN); + +reg [8:0] init_data_reg = 9'd0; + +reg [AW-1:0] address_reg = {AW{1'b0}}, address_next; +reg [AW-1:0] address_ptr_reg = {AW{1'b0}}, address_ptr_next; +reg [AW-1:0] data_ptr_reg = {AW{1'b0}}, data_ptr_next; + +reg [6:0] cur_address_reg = 7'd0, cur_address_next; + +reg [6:0] cmd_address_reg = 7'd0, cmd_address_next; +reg cmd_start_reg = 1'b0, cmd_start_next; +reg cmd_write_reg = 1'b0, cmd_write_next; +reg cmd_stop_reg = 1'b0, cmd_stop_next; +reg cmd_valid_reg = 1'b0, cmd_valid_next; + +reg [7:0] data_out_reg = 8'd0, data_out_next; +reg data_out_valid_reg = 1'b0, data_out_valid_next; + +reg start_flag_reg = 1'b0, start_flag_next; + +reg busy_reg = 1'b0; + +assign cmd_address = cmd_address_reg; +assign cmd_start = cmd_start_reg; +assign cmd_read = 1'b0; +assign cmd_write = cmd_write_reg; +assign cmd_write_multiple = 1'b0; +assign cmd_stop = cmd_stop_reg; +assign cmd_valid = cmd_valid_reg; + +assign data_out = data_out_reg; +assign data_out_valid = data_out_valid_reg; +assign data_out_last = 1'b1; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + address_next = address_reg; + address_ptr_next = address_ptr_reg; + data_ptr_next = data_ptr_reg; + + cur_address_next = cur_address_reg; + + cmd_address_next = cmd_address_reg; + cmd_start_next = cmd_start_reg & ~(cmd_valid & cmd_ready); + cmd_write_next = cmd_write_reg & ~(cmd_valid & cmd_ready); + cmd_stop_next = cmd_stop_reg & ~(cmd_valid & cmd_ready); + cmd_valid_next = cmd_valid_reg & ~cmd_ready; + + data_out_next = data_out_reg; + data_out_valid_next = data_out_valid_reg & ~data_out_ready; + + start_flag_next = start_flag_reg; + + if (cmd_valid | data_out_valid) begin + // wait for output registers to clear + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // wait for start signal + if (~start_flag_reg & start) begin + address_next = {AW{1'b0}}; + start_flag_next = 1'b1; + state_next = STATE_RUN; + end else begin + state_next = STATE_IDLE; + end + end + STATE_RUN: begin + // process commands + if (init_data_reg[8] == 1'b1) begin + // write data + cmd_write_next = 1'b1; + cmd_stop_next = 1'b0; + cmd_valid_next = 1'b1; + + data_out_next = init_data_reg[7:0]; + data_out_valid_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_RUN; + end else if (init_data_reg[8:7] == 2'b01) begin + // write address + cmd_address_next = init_data_reg[6:0]; + cmd_start_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_RUN; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_RUN; + end + end + STATE_TABLE_1: begin + // find address table start + if (init_data_reg == 9'b000001000) begin + // address table start + address_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_2; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 1) begin + // exit mode + address_next = address_reg + 1; + state_next = STATE_RUN; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end + end + STATE_TABLE_2: begin + // find next address + if (init_data_reg[8:7] == 2'b01) begin + // write address command + // store address and move to data table + cur_address_next = init_data_reg[6:0]; + address_ptr_next = address_reg + 1; + address_next = data_ptr_reg; + state_next = STATE_TABLE_3; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'd1) begin + // exit mode + address_next = address_reg + 1; + state_next = STATE_RUN; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_TABLE_2; + end + end + STATE_TABLE_3: begin + // process data table with selected address + if (init_data_reg[8] == 1'b1) begin + // write data + cmd_write_next = 1'b1; + cmd_stop_next = 1'b0; + cmd_valid_next = 1'b1; + + data_out_next = init_data_reg[7:0]; + data_out_valid_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_TABLE_3; + end else if (init_data_reg[8:7] == 2'b01) begin + // write address + cmd_address_next = init_data_reg[6:0]; + cmd_start_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_TABLE_3; + end else if (init_data_reg == 9'b000000011) begin + // write current address + cmd_address_next = cur_address_reg; + cmd_start_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_TABLE_3; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'b000001000) begin + // address table start + address_next = address_ptr_reg; + state_next = STATE_TABLE_2; + end else if (init_data_reg == 9'd1) begin + // exit mode + address_next = address_reg + 1; + state_next = STATE_RUN; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_TABLE_3; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + init_data_reg <= 9'd0; + + address_reg <= {AW{1'b0}}; + address_ptr_reg <= {AW{1'b0}}; + data_ptr_reg <= {AW{1'b0}}; + + cur_address_reg <= 7'd0; + + cmd_valid_reg <= 1'b0; + + data_out_valid_reg <= 1'b0; + + start_flag_reg <= 1'b0; + + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + // read init_data ROM + init_data_reg <= init_data[address_next]; + + address_reg <= address_next; + address_ptr_reg <= address_ptr_next; + data_ptr_reg <= data_ptr_next; + + cur_address_reg <= cur_address_next; + + cmd_valid_reg <= cmd_valid_next; + + data_out_valid_reg <= data_out_valid_next; + + start_flag_reg <= start & start_flag_next; + + busy_reg <= (state_reg != STATE_IDLE); + end + + cmd_address_reg <= cmd_address_next; + cmd_start_reg <= cmd_start_next; + cmd_write_reg <= cmd_write_next; + cmd_stop_reg <= cmd_stop_next; + + data_out_reg <= data_out_next; +end + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga/rtl/i2c_master.v b/fpga/lib/eth/example/HXT100G/fpga/rtl/i2c_master.v new file mode 100644 index 000000000..95d3a5212 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/rtl/i2c_master.v @@ -0,0 +1,895 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * I2C master + */ +module i2c_master ( + input wire clk, + input wire rst, + + /* + * Host interface + */ + input wire [6:0] cmd_address, + input wire cmd_start, + input wire cmd_read, + input wire cmd_write, + input wire cmd_write_multiple, + input wire cmd_stop, + input wire cmd_valid, + output wire cmd_ready, + + input wire [7:0] data_in, + input wire data_in_valid, + output wire data_in_ready, + input wire data_in_last, + + output wire [7:0] data_out, + output wire data_out_valid, + input wire data_out_ready, + output wire data_out_last, + + /* + * I2C interface + */ + input wire scl_i, + output wire scl_o, + output wire scl_t, + input wire sda_i, + output wire sda_o, + output wire sda_t, + + /* + * Status + */ + output wire busy, + output wire bus_control, + output wire bus_active, + output wire missed_ack, + + /* + * Configuration + */ + input wire [15:0] prescale, + input wire stop_on_idle +); + +/* + +I2C + +Read + __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __ +sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_\_R___A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A____/ + ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____ +scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP + +Write + __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __ +sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_/_W_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_/_N_\__/ + ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____ +scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP + +Commands: + +read + read data byte + set start to force generation of a start condition + start is implied when bus is inactive or active with write or different address + set stop to issue a stop condition after reading current byte + if stop is set with read command, then data_out_last will be set + +write + write data byte + set start to force generation of a start condition + start is implied when bus is inactive or active with read or different address + set stop to issue a stop condition after writing current byte + +write multiple + write multiple data bytes (until data_in_last) + set start to force generation of a start condition + start is implied when bus is inactive or active with read or different address + set stop to issue a stop condition after writing block + +stop + issue stop condition if bus is active + +Status: + +busy + module is communicating over the bus + +bus_control + module has control of bus in active state + +bus_active + bus is active, not necessarily controlled by this module + +missed_ack + strobed when a slave ack is missed + +Parameters: + +prescale + set prescale to 1/4 of the minimum clock period in units + of input clk cycles (prescale = Fclk / (FI2Cclk * 4)) + +stop_on_idle + automatically issue stop when command input is not valid + +Example of interfacing with tristate pins: +(this will work for any tristate bus) + +assign scl_i = scl_pin; +assign scl_pin = scl_t ? 1'bz : scl_o; +assign sda_i = sda_pin; +assign sda_pin = sda_t ? 1'bz : sda_o; + +Equivalent code that does not use *_t connections: +(we can get away with this because I2C is open-drain) + +assign scl_i = scl_pin; +assign scl_pin = scl_o ? 1'bz : 1'b0; +assign sda_i = sda_pin; +assign sda_pin = sda_o ? 1'bz : 1'b0; + +Example of two interconnected I2C devices: + +assign scl_1_i = scl_1_o & scl_2_o; +assign scl_2_i = scl_1_o & scl_2_o; +assign sda_1_i = sda_1_o & sda_2_o; +assign sda_2_i = sda_1_o & sda_2_o; + +Example of two I2C devices sharing the same pins: + +assign scl_1_i = scl_pin; +assign scl_2_i = scl_pin; +assign scl_pin = (scl_1_o & scl_2_o) ? 1'bz : 1'b0; +assign sda_1_i = sda_pin; +assign sda_2_i = sda_pin; +assign sda_pin = (sda_1_o & sda_2_o) ? 1'bz : 1'b0; + +Notes: + +scl_o should not be connected directly to scl_i, only via AND logic or a tristate +I/O pin. This would prevent devices from stretching the clock period. + +*/ + +localparam [4:0] + STATE_IDLE = 4'd0, + STATE_ACTIVE_WRITE = 4'd1, + STATE_ACTIVE_READ = 4'd2, + STATE_START_WAIT = 4'd3, + STATE_START = 4'd4, + STATE_ADDRESS_1 = 4'd5, + STATE_ADDRESS_2 = 4'd6, + STATE_WRITE_1 = 4'd7, + STATE_WRITE_2 = 4'd8, + STATE_WRITE_3 = 4'd9, + STATE_READ = 4'd10, + STATE_STOP = 4'd11; + +reg [4:0] state_reg = STATE_IDLE, state_next; + +localparam [4:0] + PHY_STATE_IDLE = 5'd0, + PHY_STATE_ACTIVE = 5'd1, + PHY_STATE_REPEATED_START_1 = 5'd2, + PHY_STATE_REPEATED_START_2 = 5'd3, + PHY_STATE_START_1 = 5'd4, + PHY_STATE_START_2 = 5'd5, + PHY_STATE_WRITE_BIT_1 = 5'd6, + PHY_STATE_WRITE_BIT_2 = 5'd7, + PHY_STATE_WRITE_BIT_3 = 5'd8, + PHY_STATE_READ_BIT_1 = 5'd9, + PHY_STATE_READ_BIT_2 = 5'd10, + PHY_STATE_READ_BIT_3 = 5'd11, + PHY_STATE_READ_BIT_4 = 5'd12, + PHY_STATE_STOP_1 = 5'd13, + PHY_STATE_STOP_2 = 5'd14, + PHY_STATE_STOP_3 = 5'd15; + +reg [4:0] phy_state_reg = STATE_IDLE, phy_state_next; + +reg phy_start_bit; +reg phy_stop_bit; +reg phy_write_bit; +reg phy_read_bit; +reg phy_release_bus; + +reg phy_tx_data; + +reg phy_rx_data_reg = 1'b0, phy_rx_data_next; + +reg [6:0] addr_reg = 7'd0, addr_next; +reg [7:0] data_reg = 8'd0, data_next; +reg last_reg = 1'b0, last_next; + +reg mode_read_reg = 1'b0, mode_read_next; +reg mode_write_multiple_reg = 1'b0, mode_write_multiple_next; +reg mode_stop_reg = 1'b0, mode_stop_next; + +reg [16:0] delay_reg = 16'd0, delay_next; +reg delay_scl_reg = 1'b0, delay_scl_next; +reg delay_sda_reg = 1'b0, delay_sda_next; + +reg [3:0] bit_count_reg = 4'd0, bit_count_next; + +reg cmd_ready_reg = 1'b0, cmd_ready_next; + +reg data_in_ready_reg = 1'b0, data_in_ready_next; + +reg [7:0] data_out_reg = 8'd0, data_out_next; +reg data_out_valid_reg = 1'b0, data_out_valid_next; +reg data_out_last_reg = 1'b0, data_out_last_next; + +reg scl_i_reg = 1'b1; +reg sda_i_reg = 1'b1; + +reg scl_o_reg = 1'b1, scl_o_next; +reg sda_o_reg = 1'b1, sda_o_next; + +reg last_scl_i_reg = 1'b1; +reg last_sda_i_reg = 1'b1; + +reg busy_reg = 1'b0; +reg bus_active_reg = 1'b0; +reg bus_control_reg = 1'b0, bus_control_next; +reg missed_ack_reg = 1'b0, missed_ack_next; + +assign cmd_ready = cmd_ready_reg; + +assign data_in_ready = data_in_ready_reg; + +assign data_out = data_out_reg; +assign data_out_valid = data_out_valid_reg; +assign data_out_last = data_out_last_reg; + +assign scl_o = scl_o_reg; +assign scl_t = scl_o_reg; +assign sda_o = sda_o_reg; +assign sda_t = sda_o_reg; + +assign busy = busy_reg; +assign bus_active = bus_active_reg; +assign bus_control = bus_control_reg; +assign missed_ack = missed_ack_reg; + +wire scl_posedge = scl_i_reg & ~last_scl_i_reg; +wire scl_negedge = ~scl_i_reg & last_scl_i_reg; +wire sda_posedge = sda_i_reg & ~last_sda_i_reg; +wire sda_negedge = ~sda_i_reg & last_sda_i_reg; + +wire start_bit = sda_negedge & scl_i_reg; +wire stop_bit = sda_posedge & scl_i_reg; + +always @* begin + state_next = STATE_IDLE; + + phy_start_bit = 1'b0; + phy_stop_bit = 1'b0; + phy_write_bit = 1'b0; + phy_read_bit = 1'b0; + phy_tx_data = 1'b0; + phy_release_bus = 1'b0; + + addr_next = addr_reg; + data_next = data_reg; + last_next = last_reg; + + mode_read_next = mode_read_reg; + mode_write_multiple_next = mode_write_multiple_reg; + mode_stop_next = mode_stop_reg; + + bit_count_next = bit_count_reg; + + cmd_ready_next = 1'b0; + + data_in_ready_next = 1'b0; + + data_out_next = data_out_reg; + data_out_valid_next = data_out_valid_reg & ~data_out_ready; + data_out_last_next = data_out_last_reg; + + missed_ack_next = 1'b0; + + // generate delays + if (phy_state_reg != PHY_STATE_IDLE && phy_state_reg != PHY_STATE_ACTIVE) begin + // wait for phy operation + state_next = state_reg; + end else begin + // process states + case (state_reg) + STATE_IDLE: begin + // line idle + cmd_ready_next = 1'b1; + + if (cmd_ready & cmd_valid) begin + // command valid + if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin + // read or write command + addr_next = cmd_address; + mode_read_next = cmd_read; + mode_write_multiple_next = cmd_write_multiple; + mode_stop_next = cmd_stop; + + cmd_ready_next = 1'b0; + + // start bit + if (bus_active) begin + state_next = STATE_START_WAIT; + end else begin + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end + end else begin + // invalid or unspecified - ignore + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_ACTIVE_WRITE: begin + // line active with current address and read/write mode + cmd_ready_next = 1'b1; + + if (cmd_ready & cmd_valid) begin + // command valid + if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin + // read or write command + addr_next = cmd_address; + mode_read_next = cmd_read; + mode_write_multiple_next = cmd_write_multiple; + mode_stop_next = cmd_stop; + + cmd_ready_next = 1'b0; + + if (cmd_start || cmd_address != addr_reg || cmd_read) begin + // address or mode mismatch or forced start - repeated start + + // repeated start bit + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end else begin + // address and mode match + + // start write + data_in_ready_next = 1'b1; + state_next = STATE_WRITE_1; + end + end else if (cmd_stop && !(cmd_read || cmd_write || cmd_write_multiple)) begin + // stop command + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end else begin + // invalid or unspecified - ignore + state_next = STATE_ACTIVE_WRITE; + end + end else begin + if (stop_on_idle & cmd_ready & ~cmd_valid) begin + // no waiting command and stop_on_idle selected, issue stop condition + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_ACTIVE_WRITE; + end + end + end + STATE_ACTIVE_READ: begin + // line active to current address + cmd_ready_next = ~data_out_valid; + + if (cmd_ready & cmd_valid) begin + // command valid + if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin + // read or write command + addr_next = cmd_address; + mode_read_next = cmd_read; + mode_write_multiple_next = cmd_write_multiple; + mode_stop_next = cmd_stop; + + cmd_ready_next = 1'b0; + + if (cmd_start || cmd_address != addr_reg || cmd_write) begin + // address or mode mismatch or forced start - repeated start + + // write nack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + // repeated start bit + state_next = STATE_START; + end else begin + // address and mode match + + // write ack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b0; + // start next read + bit_count_next = 4'd8; + data_next = 8'd0; + state_next = STATE_READ; + end + end else if (cmd_stop && !(cmd_read || cmd_write || cmd_write_multiple)) begin + // stop command + // write nack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + // send stop bit + state_next = STATE_STOP; + end else begin + // invalid or unspecified - ignore + state_next = STATE_ACTIVE_READ; + end + end else begin + if (stop_on_idle & cmd_ready & ~cmd_valid) begin + // no waiting command and stop_on_idle selected, issue stop condition + // write ack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + // send stop bit + state_next = STATE_STOP; + end else begin + state_next = STATE_ACTIVE_READ; + end + end + end + STATE_START_WAIT: begin + // wait for bus idle + + if (bus_active) begin + state_next = STATE_START_WAIT; + end else begin + // bus is idle, take control + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end + end + STATE_START: begin + // send start bit + + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end + STATE_ADDRESS_1: begin + // send address + bit_count_next = bit_count_reg - 1; + if (bit_count_reg > 1) begin + // send address + phy_write_bit = 1'b1; + phy_tx_data = addr_reg[bit_count_reg-2]; + state_next = STATE_ADDRESS_1; + end else if (bit_count_reg > 0) begin + // send read/write bit + phy_write_bit = 1'b1; + phy_tx_data = mode_read_reg; + state_next = STATE_ADDRESS_1; + end else begin + // read ack bit + phy_read_bit = 1'b1; + state_next = STATE_ADDRESS_2; + end + end + STATE_ADDRESS_2: begin + // read ack bit + missed_ack_next = phy_rx_data_reg; + + if (mode_read_reg) begin + // start read + bit_count_next = 4'd8; + data_next = 1'b0; + state_next = STATE_READ; + end else begin + // start write + data_in_ready_next = 1'b1; + state_next = STATE_WRITE_1; + end + end + STATE_WRITE_1: begin + data_in_ready_next = 1'b1; + + if (data_in_ready & data_in_valid) begin + // got data, start write + data_next = data_in; + last_next = data_in_last; + bit_count_next = 4'd8; + data_in_ready_next = 1'b0; + state_next = STATE_WRITE_2; + end else begin + // wait for data + state_next = STATE_WRITE_1; + end + end + STATE_WRITE_2: begin + // send data + bit_count_next = bit_count_reg - 1; + if (bit_count_reg > 0) begin + // write data bit + phy_write_bit = 1'b1; + phy_tx_data = data_reg[bit_count_reg-1]; + state_next = STATE_WRITE_2; + end else begin + // read ack bit + phy_read_bit = 1'b1; + state_next = STATE_WRITE_3; + end + end + STATE_WRITE_3: begin + // read ack bit + missed_ack_next = phy_rx_data_reg; + + if (mode_write_multiple_reg && !last_reg) begin + // more to write + state_next = STATE_WRITE_1; + end else if (mode_stop_reg) begin + // last cycle and stop selected + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end else begin + // otherwise, return to bus active state + state_next = STATE_ACTIVE_WRITE; + end + end + STATE_READ: begin + // read data + + bit_count_next = bit_count_reg - 1; + data_next = {data_reg[6:0], phy_rx_data_reg}; + if (bit_count_reg > 0) begin + // read next bit + phy_read_bit = 1'b1; + state_next = STATE_READ; + end else begin + // output data word + data_out_next = data_next; + data_out_valid_next = 1'b1; + data_out_last_next = 1'b0; + if (mode_stop_reg) begin + // send nack and stop + data_out_last_next = 1'b1; + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + state_next = STATE_STOP; + end else begin + // return to bus active state + state_next = STATE_ACTIVE_READ; + end + end + end + STATE_STOP: begin + // send stop bit + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end + endcase + end +end + +always @* begin + phy_state_next = PHY_STATE_IDLE; + + phy_rx_data_next = phy_rx_data_reg; + + delay_next = delay_reg; + delay_scl_next = delay_scl_reg; + delay_sda_next = delay_sda_reg; + + scl_o_next = scl_o_reg; + sda_o_next = sda_o_reg; + + bus_control_next = bus_control_reg; + + if (phy_release_bus) begin + // release bus and return to idle state + sda_o_next = 1'b1; + scl_o_next = 1'b1; + delay_scl_next = 1'b0; + delay_sda_next = 1'b0; + delay_next = 1'b0; + phy_state_next = PHY_STATE_IDLE; + end else if (delay_scl_reg) begin + // wait for SCL to match command + delay_scl_next = scl_o_reg & ~scl_i_reg; + phy_state_next = phy_state_reg; + end else if (delay_sda_reg) begin + // wait for SDA to match command + delay_sda_next = sda_o_reg & ~sda_i_reg; + phy_state_next = phy_state_reg; + end else if (delay_reg > 0) begin + // time delay + delay_next = delay_reg - 1; + phy_state_next = phy_state_reg; + end else begin + case (phy_state_reg) + PHY_STATE_IDLE: begin + // bus idle - wait for start command + sda_o_next = 1'b1; + scl_o_next = 1'b1; + if (phy_start_bit) begin + sda_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_START_1; + end else begin + phy_state_next = PHY_STATE_IDLE; + end + end + PHY_STATE_ACTIVE: begin + // bus active + if (phy_start_bit) begin + sda_o_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_REPEATED_START_1; + end else if (phy_write_bit) begin + sda_o_next = phy_tx_data; + delay_next = prescale; + phy_state_next = PHY_STATE_WRITE_BIT_1; + end else if (phy_read_bit) begin + sda_o_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_1; + end else if (phy_stop_bit) begin + sda_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_STOP_1; + end else begin + phy_state_next = PHY_STATE_ACTIVE; + end + end + PHY_STATE_REPEATED_START_1: begin + // generate repeated start bit + // ______ + // sda XXX/ \_______ + // _______ + // scl ______/ \___ + // + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_REPEATED_START_2; + end + PHY_STATE_REPEATED_START_2: begin + // generate repeated start bit + // ______ + // sda XXX/ \_______ + // _______ + // scl ______/ \___ + // + + sda_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_START_1; + end + PHY_STATE_START_1: begin + // generate start bit + // ___ + // sda \_______ + // _______ + // scl \___ + // + + scl_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_START_2; + end + PHY_STATE_START_2: begin + // generate start bit + // ___ + // sda \_______ + // _______ + // scl \___ + // + + bus_control_next = 1'b1; + phy_state_next = PHY_STATE_ACTIVE; + end + PHY_STATE_WRITE_BIT_1: begin + // write bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale << 1; + phy_state_next = PHY_STATE_WRITE_BIT_2; + end + PHY_STATE_WRITE_BIT_2: begin + // write bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_WRITE_BIT_3; + end + PHY_STATE_WRITE_BIT_3: begin + // write bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + phy_state_next = PHY_STATE_ACTIVE; + end + PHY_STATE_READ_BIT_1: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_2; + end + PHY_STATE_READ_BIT_2: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + phy_rx_data_next = sda_i_reg; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_3; + end + PHY_STATE_READ_BIT_3: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_4; + end + PHY_STATE_READ_BIT_4: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + phy_state_next = PHY_STATE_ACTIVE; + end + PHY_STATE_STOP_1: begin + // stop bit + // ___ + // sda XXX\_______/ + // _______ + // scl _______/ + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_STOP_2; + end + PHY_STATE_STOP_2: begin + // stop bit + // ___ + // sda XXX\_______/ + // _______ + // scl _______/ + + sda_o_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_STOP_3; + end + PHY_STATE_STOP_3: begin + // stop bit + // ___ + // sda XXX\_______/ + // _______ + // scl _______/ + + bus_control_next = 1'b0; + phy_state_next = PHY_STATE_IDLE; + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + phy_state_reg <= PHY_STATE_IDLE; + delay_reg <= 16'd0; + delay_scl_reg <= 1'b0; + delay_sda_reg <= 1'b0; + cmd_ready_reg <= 1'b0; + data_in_ready_reg <= 1'b0; + data_out_valid_reg <= 1'b0; + scl_o_reg <= 1'b1; + sda_o_reg <= 1'b1; + busy_reg <= 1'b0; + bus_active_reg <= 1'b0; + bus_control_reg <= 1'b0; + missed_ack_reg <= 1'b0; + end else begin + state_reg <= state_next; + phy_state_reg <= phy_state_next; + + delay_reg <= delay_next; + delay_scl_reg <= delay_scl_next; + delay_sda_reg <= delay_sda_next; + + cmd_ready_reg <= cmd_ready_next; + data_in_ready_reg <= data_in_ready_next; + data_out_valid_reg <= data_out_valid_next; + + scl_o_reg <= scl_o_next; + sda_o_reg <= sda_o_next; + + busy_reg <= !(state_reg == STATE_IDLE || state_reg == STATE_ACTIVE_WRITE || state_reg == STATE_ACTIVE_READ); + + if (start_bit) begin + bus_active_reg <= 1'b1; + end else if (stop_bit) begin + bus_active_reg <= 1'b0; + end else begin + bus_active_reg <= bus_active_reg; + end + + bus_control_reg <= bus_control_next; + missed_ack_reg <= missed_ack_next; + end + + phy_rx_data_reg <= phy_rx_data_next; + + addr_reg <= addr_next; + data_reg <= data_next; + last_reg <= last_next; + + mode_read_reg <= mode_read_next; + mode_write_multiple_reg <= mode_write_multiple_next; + mode_stop_reg <= mode_stop_next; + + bit_count_reg <= bit_count_next; + + data_out_reg <= data_out_next; + data_out_last_reg <= data_out_last_next; + + scl_i_reg <= scl_i; + sda_i_reg <= sda_i; + last_scl_i_reg <= scl_i_reg; + last_sda_i_reg <= sda_i_reg; +end + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga/rtl/sync_reset.v b/fpga/lib/eth/example/HXT100G/fpga/rtl/sync_reset.v new file mode 100644 index 000000000..fe097029f --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga/rtl/sync_signal.v b/fpga/lib/eth/example/HXT100G/fpga/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/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/fpga/lib/eth/example/HXT100G/fpga/tb/arp_ep.py b/fpga/lib/eth/example/HXT100G/fpga/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga/tb/axis_ep.py b/fpga/lib/eth/example/HXT100G/fpga/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga/tb/eth_ep.py b/fpga/lib/eth/example/HXT100G/fpga/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga/tb/ip_ep.py b/fpga/lib/eth/example/HXT100G/fpga/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga/tb/test_fpga_core.py b/fpga/lib/eth/example/HXT100G/fpga/tb/test_fpga_core.py new file mode 100755 index 000000000..dac66e783 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/tb/test_fpga_core.py @@ -0,0 +1,610 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import xgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_10g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_10g.v") +srcs.append("../lib/eth/rtl/axis_xgmii_rx_64.v") +srcs.append("../lib/eth/rtl/axis_xgmii_tx_64.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx_64.v") +srcs.append("../lib/eth/rtl/eth_axis_tx_64.v") +srcs.append("../lib/eth/rtl/udp_complete_64.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen_64.v") +srcs.append("../lib/eth/rtl/udp_64.v") +srcs.append("../lib/eth/rtl/udp_ip_rx_64.v") +srcs.append("../lib/eth/rtl/udp_ip_tx_64.v") +srcs.append("../lib/eth/rtl/ip_complete_64.v") +srcs.append("../lib/eth/rtl/ip_64.v") +srcs.append("../lib/eth/rtl/ip_eth_rx_64.v") +srcs.append("../lib/eth/rtl/ip_eth_tx_64.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp_64.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx_64.v") +srcs.append("../lib/eth/rtl/arp_eth_tx_64.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + sw = Signal(intbv(0)[2:]) + jp = Signal(intbv(0)[4:]) + uart_suspend = Signal(bool(0)) + uart_dtr = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + amh_right_mdio_i = Signal(bool(0)) + amh_left_mdio_i = Signal(bool(0)) + eth_r0_rxd = Signal(intbv(0)[64:]) + eth_r0_rxc = Signal(intbv(0)[8:]) + eth_r1_rxd = Signal(intbv(0)[64:]) + eth_r1_rxc = Signal(intbv(0)[8:]) + eth_r2_rxd = Signal(intbv(0)[64:]) + eth_r2_rxc = Signal(intbv(0)[8:]) + eth_r3_rxd = Signal(intbv(0)[64:]) + eth_r3_rxc = Signal(intbv(0)[8:]) + eth_r4_rxd = Signal(intbv(0)[64:]) + eth_r4_rxc = Signal(intbv(0)[8:]) + eth_r5_rxd = Signal(intbv(0)[64:]) + eth_r5_rxc = Signal(intbv(0)[8:]) + eth_r6_rxd = Signal(intbv(0)[64:]) + eth_r6_rxc = Signal(intbv(0)[8:]) + eth_r7_rxd = Signal(intbv(0)[64:]) + eth_r7_rxc = Signal(intbv(0)[8:]) + eth_r8_rxd = Signal(intbv(0)[64:]) + eth_r8_rxc = Signal(intbv(0)[8:]) + eth_r9_rxd = Signal(intbv(0)[64:]) + eth_r9_rxc = Signal(intbv(0)[8:]) + eth_r10_rxd = Signal(intbv(0)[64:]) + eth_r10_rxc = Signal(intbv(0)[8:]) + eth_r11_rxd = Signal(intbv(0)[64:]) + eth_r11_rxc = Signal(intbv(0)[8:]) + eth_l0_rxd = Signal(intbv(0)[64:]) + eth_l0_rxc = Signal(intbv(0)[8:]) + eth_l1_rxd = Signal(intbv(0)[64:]) + eth_l1_rxc = Signal(intbv(0)[8:]) + eth_l2_rxd = Signal(intbv(0)[64:]) + eth_l2_rxc = Signal(intbv(0)[8:]) + eth_l3_rxd = Signal(intbv(0)[64:]) + eth_l3_rxc = Signal(intbv(0)[8:]) + eth_l4_rxd = Signal(intbv(0)[64:]) + eth_l4_rxc = Signal(intbv(0)[8:]) + eth_l5_rxd = Signal(intbv(0)[64:]) + eth_l5_rxc = Signal(intbv(0)[8:]) + eth_l6_rxd = Signal(intbv(0)[64:]) + eth_l6_rxc = Signal(intbv(0)[8:]) + eth_l7_rxd = Signal(intbv(0)[64:]) + eth_l7_rxc = Signal(intbv(0)[8:]) + eth_l8_rxd = Signal(intbv(0)[64:]) + eth_l8_rxc = Signal(intbv(0)[8:]) + eth_l9_rxd = Signal(intbv(0)[64:]) + eth_l9_rxc = Signal(intbv(0)[8:]) + eth_l10_rxd = Signal(intbv(0)[64:]) + eth_l10_rxc = Signal(intbv(0)[8:]) + eth_l11_rxd = Signal(intbv(0)[64:]) + eth_l11_rxc = Signal(intbv(0)[8:]) + + # Outputs + led = Signal(intbv(0)[4:]) + uart_rst = Signal(bool(0)) + uart_ri = Signal(bool(0)) + uart_dcd = Signal(bool(0)) + uart_dsr = Signal(bool(0)) + uart_rxd = Signal(bool(1)) + uart_cts = Signal(bool(0)) + amh_right_mdc = Signal(bool(1)) + amh_right_mdio_o = Signal(bool(1)) + amh_right_mdio_t = Signal(bool(1)) + amh_left_mdc = Signal(bool(1)) + amh_left_mdio_o = Signal(bool(1)) + amh_left_mdio_t = Signal(bool(1)) + eth_r0_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r0_txc = Signal(intbv(0xff)[8:]) + eth_r1_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r1_txc = Signal(intbv(0xff)[8:]) + eth_r2_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r2_txc = Signal(intbv(0xff)[8:]) + eth_r3_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r3_txc = Signal(intbv(0xff)[8:]) + eth_r4_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r4_txc = Signal(intbv(0xff)[8:]) + eth_r5_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r5_txc = Signal(intbv(0xff)[8:]) + eth_r6_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r6_txc = Signal(intbv(0xff)[8:]) + eth_r7_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r7_txc = Signal(intbv(0xff)[8:]) + eth_r8_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r8_txc = Signal(intbv(0xff)[8:]) + eth_r9_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r9_txc = Signal(intbv(0xff)[8:]) + eth_r10_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r10_txc = Signal(intbv(0xff)[8:]) + eth_r11_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r11_txc = Signal(intbv(0xff)[8:]) + eth_l0_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l0_txc = Signal(intbv(0xff)[8:]) + eth_l1_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l1_txc = Signal(intbv(0xff)[8:]) + eth_l2_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l2_txc = Signal(intbv(0xff)[8:]) + eth_l3_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l3_txc = Signal(intbv(0xff)[8:]) + eth_l4_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l4_txc = Signal(intbv(0xff)[8:]) + eth_l5_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l5_txc = Signal(intbv(0xff)[8:]) + eth_l6_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l6_txc = Signal(intbv(0xff)[8:]) + eth_l7_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l7_txc = Signal(intbv(0xff)[8:]) + eth_l8_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l8_txc = Signal(intbv(0xff)[8:]) + eth_l9_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l9_txc = Signal(intbv(0xff)[8:]) + eth_l10_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l10_txc = Signal(intbv(0xff)[8:]) + eth_l11_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l11_txc = Signal(intbv(0xff)[8:]) + + # sources and sinks + eth_r0_source = xgmii_ep.XGMIISource() + eth_r0_source_logic = eth_r0_source.create_logic(clk, rst, txd=eth_r0_rxd, txc=eth_r0_rxc, name='eth_r0_source') + + eth_r0_sink = xgmii_ep.XGMIISink() + eth_r0_sink_logic = eth_r0_sink.create_logic(clk, rst, rxd=eth_r0_txd, rxc=eth_r0_txc, name='eth_r0_sink') + + eth_r1_source = xgmii_ep.XGMIISource() + eth_r1_source_logic = eth_r1_source.create_logic(clk, rst, txd=eth_r1_rxd, txc=eth_r1_rxc, name='eth_r1_source') + + eth_r1_sink = xgmii_ep.XGMIISink() + eth_r1_sink_logic = eth_r1_sink.create_logic(clk, rst, rxd=eth_r1_txd, rxc=eth_r1_txc, name='eth_r1_sink') + + eth_r2_source = xgmii_ep.XGMIISource() + eth_r2_source_logic = eth_r2_source.create_logic(clk, rst, txd=eth_r2_rxd, txc=eth_r2_rxc, name='eth_r2_source') + + eth_r2_sink = xgmii_ep.XGMIISink() + eth_r2_sink_logic = eth_r2_sink.create_logic(clk, rst, rxd=eth_r2_txd, rxc=eth_r2_txc, name='eth_r2_sink') + + eth_r3_source = xgmii_ep.XGMIISource() + eth_r3_source_logic = eth_r3_source.create_logic(clk, rst, txd=eth_r3_rxd, txc=eth_r3_rxc, name='eth_r3_source') + + eth_r3_sink = xgmii_ep.XGMIISink() + eth_r3_sink_logic = eth_r3_sink.create_logic(clk, rst, rxd=eth_r3_txd, rxc=eth_r3_txc, name='eth_r3_sink') + + eth_r4_source = xgmii_ep.XGMIISource() + eth_r4_source_logic = eth_r4_source.create_logic(clk, rst, txd=eth_r4_rxd, txc=eth_r4_rxc, name='eth_r4_source') + + eth_r4_sink = xgmii_ep.XGMIISink() + eth_r4_sink_logic = eth_r4_sink.create_logic(clk, rst, rxd=eth_r4_txd, rxc=eth_r4_txc, name='eth_r4_sink') + + eth_r5_source = xgmii_ep.XGMIISource() + eth_r5_source_logic = eth_r5_source.create_logic(clk, rst, txd=eth_r5_rxd, txc=eth_r5_rxc, name='eth_r5_source') + + eth_r5_sink = xgmii_ep.XGMIISink() + eth_r5_sink_logic = eth_r5_sink.create_logic(clk, rst, rxd=eth_r5_txd, rxc=eth_r5_txc, name='eth_r5_sink') + + eth_r6_source = xgmii_ep.XGMIISource() + eth_r6_source_logic = eth_r6_source.create_logic(clk, rst, txd=eth_r6_rxd, txc=eth_r6_rxc, name='eth_r6_source') + + eth_r6_sink = xgmii_ep.XGMIISink() + eth_r6_sink_logic = eth_r6_sink.create_logic(clk, rst, rxd=eth_r6_txd, rxc=eth_r6_txc, name='eth_r6_sink') + + eth_r7_source = xgmii_ep.XGMIISource() + eth_r7_source_logic = eth_r7_source.create_logic(clk, rst, txd=eth_r7_rxd, txc=eth_r7_rxc, name='eth_r7_source') + + eth_r7_sink = xgmii_ep.XGMIISink() + eth_r7_sink_logic = eth_r7_sink.create_logic(clk, rst, rxd=eth_r7_txd, rxc=eth_r7_txc, name='eth_r7_sink') + + eth_r8_source = xgmii_ep.XGMIISource() + eth_r8_source_logic = eth_r8_source.create_logic(clk, rst, txd=eth_r8_rxd, txc=eth_r8_rxc, name='eth_r8_source') + + eth_r8_sink = xgmii_ep.XGMIISink() + eth_r8_sink_logic = eth_r8_sink.create_logic(clk, rst, rxd=eth_r8_txd, rxc=eth_r8_txc, name='eth_r8_sink') + + eth_r9_source = xgmii_ep.XGMIISource() + eth_r9_source_logic = eth_r9_source.create_logic(clk, rst, txd=eth_r9_rxd, txc=eth_r9_rxc, name='eth_r9_source') + + eth_r9_sink = xgmii_ep.XGMIISink() + eth_r9_sink_logic = eth_r9_sink.create_logic(clk, rst, rxd=eth_r9_txd, rxc=eth_r9_txc, name='eth_r9_sink') + + eth_r10_source = xgmii_ep.XGMIISource() + eth_r10_source_logic = eth_r10_source.create_logic(clk, rst, txd=eth_r10_rxd, txc=eth_r10_rxc, name='eth_r10_source') + + eth_r10_sink = xgmii_ep.XGMIISink() + eth_r10_sink_logic = eth_r10_sink.create_logic(clk, rst, rxd=eth_r10_txd, rxc=eth_r10_txc, name='eth_r10_sink') + + eth_r11_source = xgmii_ep.XGMIISource() + eth_r11_source_logic = eth_r11_source.create_logic(clk, rst, txd=eth_r11_rxd, txc=eth_r11_rxc, name='eth_r11_source') + + eth_r11_sink = xgmii_ep.XGMIISink() + eth_r11_sink_logic = eth_r11_sink.create_logic(clk, rst, rxd=eth_r11_txd, rxc=eth_r11_txc, name='eth_r11_sink') + + eth_l0_source = xgmii_ep.XGMIISource() + eth_l0_source_logic = eth_l0_source.create_logic(clk, rst, txd=eth_l0_rxd, txc=eth_l0_rxc, name='eth_l0_source') + + eth_l0_sink = xgmii_ep.XGMIISink() + eth_l0_sink_logic = eth_l0_sink.create_logic(clk, rst, rxd=eth_l0_txd, rxc=eth_l0_txc, name='eth_l0_sink') + + eth_l1_source = xgmii_ep.XGMIISource() + eth_l1_source_logic = eth_l1_source.create_logic(clk, rst, txd=eth_l1_rxd, txc=eth_l1_rxc, name='eth_l1_source') + + eth_l1_sink = xgmii_ep.XGMIISink() + eth_l1_sink_logic = eth_l1_sink.create_logic(clk, rst, rxd=eth_l1_txd, rxc=eth_l1_txc, name='eth_l1_sink') + + eth_l2_source = xgmii_ep.XGMIISource() + eth_l2_source_logic = eth_l2_source.create_logic(clk, rst, txd=eth_l2_rxd, txc=eth_l2_rxc, name='eth_l2_source') + + eth_l2_sink = xgmii_ep.XGMIISink() + eth_l2_sink_logic = eth_l2_sink.create_logic(clk, rst, rxd=eth_l2_txd, rxc=eth_l2_txc, name='eth_l2_sink') + + eth_l3_source = xgmii_ep.XGMIISource() + eth_l3_source_logic = eth_l3_source.create_logic(clk, rst, txd=eth_l3_rxd, txc=eth_l3_rxc, name='eth_l3_source') + + eth_l3_sink = xgmii_ep.XGMIISink() + eth_l3_sink_logic = eth_l3_sink.create_logic(clk, rst, rxd=eth_l3_txd, rxc=eth_l3_txc, name='eth_l3_sink') + + eth_l4_source = xgmii_ep.XGMIISource() + eth_l4_source_logic = eth_l4_source.create_logic(clk, rst, txd=eth_l4_rxd, txc=eth_l4_rxc, name='eth_l4_source') + + eth_l4_sink = xgmii_ep.XGMIISink() + eth_l4_sink_logic = eth_l4_sink.create_logic(clk, rst, rxd=eth_l4_txd, rxc=eth_l4_txc, name='eth_l4_sink') + + eth_l5_source = xgmii_ep.XGMIISource() + eth_l5_source_logic = eth_l5_source.create_logic(clk, rst, txd=eth_l5_rxd, txc=eth_l5_rxc, name='eth_l5_source') + + eth_l5_sink = xgmii_ep.XGMIISink() + eth_l5_sink_logic = eth_l5_sink.create_logic(clk, rst, rxd=eth_l5_txd, rxc=eth_l5_txc, name='eth_l5_sink') + + eth_l6_source = xgmii_ep.XGMIISource() + eth_l6_source_logic = eth_l6_source.create_logic(clk, rst, txd=eth_l6_rxd, txc=eth_l6_rxc, name='eth_l6_source') + + eth_l6_sink = xgmii_ep.XGMIISink() + eth_l6_sink_logic = eth_l6_sink.create_logic(clk, rst, rxd=eth_l6_txd, rxc=eth_l6_txc, name='eth_l6_sink') + + eth_l7_source = xgmii_ep.XGMIISource() + eth_l7_source_logic = eth_l7_source.create_logic(clk, rst, txd=eth_l7_rxd, txc=eth_l7_rxc, name='eth_l7_source') + + eth_l7_sink = xgmii_ep.XGMIISink() + eth_l7_sink_logic = eth_l7_sink.create_logic(clk, rst, rxd=eth_l7_txd, rxc=eth_l7_txc, name='eth_l7_sink') + + eth_l8_source = xgmii_ep.XGMIISource() + eth_l8_source_logic = eth_l8_source.create_logic(clk, rst, txd=eth_l8_rxd, txc=eth_l8_rxc, name='eth_l8_source') + + eth_l8_sink = xgmii_ep.XGMIISink() + eth_l8_sink_logic = eth_l8_sink.create_logic(clk, rst, rxd=eth_l8_txd, rxc=eth_l8_txc, name='eth_l8_sink') + + eth_l9_source = xgmii_ep.XGMIISource() + eth_l9_source_logic = eth_l9_source.create_logic(clk, rst, txd=eth_l9_rxd, txc=eth_l9_rxc, name='eth_l9_source') + + eth_l9_sink = xgmii_ep.XGMIISink() + eth_l9_sink_logic = eth_l9_sink.create_logic(clk, rst, rxd=eth_l9_txd, rxc=eth_l9_txc, name='eth_l9_sink') + + eth_l10_source = xgmii_ep.XGMIISource() + eth_l10_source_logic = eth_l10_source.create_logic(clk, rst, txd=eth_l10_rxd, txc=eth_l10_rxc, name='eth_l10_source') + + eth_l10_sink = xgmii_ep.XGMIISink() + eth_l10_sink_logic = eth_l10_sink.create_logic(clk, rst, rxd=eth_l10_txd, rxc=eth_l10_txc, name='eth_l10_sink') + + eth_l11_source = xgmii_ep.XGMIISource() + eth_l11_source_logic = eth_l11_source.create_logic(clk, rst, txd=eth_l11_rxd, txc=eth_l11_rxc, name='eth_l11_source') + + eth_l11_sink = xgmii_ep.XGMIISink() + eth_l11_sink_logic = eth_l11_sink.create_logic(clk, rst, rxd=eth_l11_txd, rxc=eth_l11_txc, name='eth_l11_sink') + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + sw=sw, + jp=jp, + led=led, + + uart_rst=uart_rst, + uart_suspend=uart_suspend, + uart_ri=uart_ri, + uart_dcd=uart_dcd, + uart_dtr=uart_dtr, + uart_dsr=uart_dsr, + uart_txd=uart_txd, + uart_rxd=uart_rxd, + uart_rts=uart_rts, + uart_cts=uart_cts, + + amh_right_mdc=amh_right_mdc, + amh_right_mdio_i=amh_right_mdio_i, + amh_right_mdio_o=amh_right_mdio_o, + amh_right_mdio_t=amh_right_mdio_t, + amh_left_mdc=amh_left_mdc, + amh_left_mdio_i=amh_left_mdio_i, + amh_left_mdio_o=amh_left_mdio_o, + amh_left_mdio_t=amh_left_mdio_t, + + eth_r0_txd=eth_r0_txd, + eth_r0_txc=eth_r0_txc, + eth_r0_rxd=eth_r0_rxd, + eth_r0_rxc=eth_r0_rxc, + eth_r1_txd=eth_r1_txd, + eth_r1_txc=eth_r1_txc, + eth_r1_rxd=eth_r1_rxd, + eth_r1_rxc=eth_r1_rxc, + eth_r2_txd=eth_r2_txd, + eth_r2_txc=eth_r2_txc, + eth_r2_rxd=eth_r2_rxd, + eth_r2_rxc=eth_r2_rxc, + eth_r3_txd=eth_r3_txd, + eth_r3_txc=eth_r3_txc, + eth_r3_rxd=eth_r3_rxd, + eth_r3_rxc=eth_r3_rxc, + eth_r4_txd=eth_r4_txd, + eth_r4_txc=eth_r4_txc, + eth_r4_rxd=eth_r4_rxd, + eth_r4_rxc=eth_r4_rxc, + eth_r5_txd=eth_r5_txd, + eth_r5_txc=eth_r5_txc, + eth_r5_rxd=eth_r5_rxd, + eth_r5_rxc=eth_r5_rxc, + eth_r6_txd=eth_r6_txd, + eth_r6_txc=eth_r6_txc, + eth_r6_rxd=eth_r6_rxd, + eth_r6_rxc=eth_r6_rxc, + eth_r7_txd=eth_r7_txd, + eth_r7_txc=eth_r7_txc, + eth_r7_rxd=eth_r7_rxd, + eth_r7_rxc=eth_r7_rxc, + eth_r8_txd=eth_r8_txd, + eth_r8_txc=eth_r8_txc, + eth_r8_rxd=eth_r8_rxd, + eth_r8_rxc=eth_r8_rxc, + eth_r9_txd=eth_r9_txd, + eth_r9_txc=eth_r9_txc, + eth_r9_rxd=eth_r9_rxd, + eth_r9_rxc=eth_r9_rxc, + eth_r10_txd=eth_r10_txd, + eth_r10_txc=eth_r10_txc, + eth_r10_rxd=eth_r10_rxd, + eth_r10_rxc=eth_r10_rxc, + eth_r11_txd=eth_r11_txd, + eth_r11_txc=eth_r11_txc, + eth_r11_rxd=eth_r11_rxd, + eth_r11_rxc=eth_r11_rxc, + eth_l0_txd=eth_l0_txd, + eth_l0_txc=eth_l0_txc, + eth_l0_rxd=eth_l0_rxd, + eth_l0_rxc=eth_l0_rxc, + eth_l1_txd=eth_l1_txd, + eth_l1_txc=eth_l1_txc, + eth_l1_rxd=eth_l1_rxd, + eth_l1_rxc=eth_l1_rxc, + eth_l2_txd=eth_l2_txd, + eth_l2_txc=eth_l2_txc, + eth_l2_rxd=eth_l2_rxd, + eth_l2_rxc=eth_l2_rxc, + eth_l3_txd=eth_l3_txd, + eth_l3_txc=eth_l3_txc, + eth_l3_rxd=eth_l3_rxd, + eth_l3_rxc=eth_l3_rxc, + eth_l4_txd=eth_l4_txd, + eth_l4_txc=eth_l4_txc, + eth_l4_rxd=eth_l4_rxd, + eth_l4_rxc=eth_l4_rxc, + eth_l5_txd=eth_l5_txd, + eth_l5_txc=eth_l5_txc, + eth_l5_rxd=eth_l5_rxd, + eth_l5_rxc=eth_l5_rxc, + eth_l6_txd=eth_l6_txd, + eth_l6_txc=eth_l6_txc, + eth_l6_rxd=eth_l6_rxd, + eth_l6_rxc=eth_l6_rxc, + eth_l7_txd=eth_l7_txd, + eth_l7_txc=eth_l7_txc, + eth_l7_rxd=eth_l7_rxd, + eth_l7_rxc=eth_l7_rxc, + eth_l8_txd=eth_l8_txd, + eth_l8_txc=eth_l8_txc, + eth_l8_rxd=eth_l8_rxd, + eth_l8_rxc=eth_l8_rxc, + eth_l9_txd=eth_l9_txd, + eth_l9_txc=eth_l9_txc, + eth_l9_rxd=eth_l9_rxd, + eth_l9_rxc=eth_l9_rxc, + eth_l10_txd=eth_l10_txd, + eth_l10_txc=eth_l10_txc, + eth_l10_rxd=eth_l10_rxd, + eth_l10_rxc=eth_l10_rxc, + eth_l11_txd=eth_l11_txd, + eth_l11_txc=eth_l11_txc, + eth_l11_rxd=eth_l11_rxd, + eth_l11_rxc=eth_l11_rxc + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + eth_l0_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while eth_l0_sink.empty(): + yield clk.posedge + + rx_frame = eth_l0_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + eth_l0_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while eth_l0_sink.empty(): + yield clk.posedge + + rx_frame = eth_l0_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert eth_l0_source.empty() + assert eth_l0_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/HXT100G/fpga/tb/test_fpga_core.v b/fpga/lib/eth/example/HXT100G/fpga/tb/test_fpga_core.v new file mode 100644 index 000000000..370c25e29 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/tb/test_fpga_core.v @@ -0,0 +1,416 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [1:0] sw = 0; +reg [3:0] jp = 0; +reg uart_suspend = 0; +reg uart_dtr = 0; +reg uart_txd = 0; +reg uart_rts = 0; +reg amh_right_mdio_i = 0; +reg amh_left_mdio_i = 0; +reg [63:0] eth_r0_rxd = 0; +reg [7:0] eth_r0_rxc = 0; +reg [63:0] eth_r1_rxd = 0; +reg [7:0] eth_r1_rxc = 0; +reg [63:0] eth_r2_rxd = 0; +reg [7:0] eth_r2_rxc = 0; +reg [63:0] eth_r3_rxd = 0; +reg [7:0] eth_r3_rxc = 0; +reg [63:0] eth_r4_rxd = 0; +reg [7:0] eth_r4_rxc = 0; +reg [63:0] eth_r5_rxd = 0; +reg [7:0] eth_r5_rxc = 0; +reg [63:0] eth_r6_rxd = 0; +reg [7:0] eth_r6_rxc = 0; +reg [63:0] eth_r7_rxd = 0; +reg [7:0] eth_r7_rxc = 0; +reg [63:0] eth_r8_rxd = 0; +reg [7:0] eth_r8_rxc = 0; +reg [63:0] eth_r9_rxd = 0; +reg [7:0] eth_r9_rxc = 0; +reg [63:0] eth_r10_rxd = 0; +reg [7:0] eth_r10_rxc = 0; +reg [63:0] eth_r11_rxd = 0; +reg [7:0] eth_r11_rxc = 0; +reg [63:0] eth_l0_rxd = 0; +reg [7:0] eth_l0_rxc = 0; +reg [63:0] eth_l1_rxd = 0; +reg [7:0] eth_l1_rxc = 0; +reg [63:0] eth_l2_rxd = 0; +reg [7:0] eth_l2_rxc = 0; +reg [63:0] eth_l3_rxd = 0; +reg [7:0] eth_l3_rxc = 0; +reg [63:0] eth_l4_rxd = 0; +reg [7:0] eth_l4_rxc = 0; +reg [63:0] eth_l5_rxd = 0; +reg [7:0] eth_l5_rxc = 0; +reg [63:0] eth_l6_rxd = 0; +reg [7:0] eth_l6_rxc = 0; +reg [63:0] eth_l7_rxd = 0; +reg [7:0] eth_l7_rxc = 0; +reg [63:0] eth_l8_rxd = 0; +reg [7:0] eth_l8_rxc = 0; +reg [63:0] eth_l9_rxd = 0; +reg [7:0] eth_l9_rxc = 0; +reg [63:0] eth_l10_rxd = 0; +reg [7:0] eth_l10_rxc = 0; +reg [63:0] eth_l11_rxd = 0; +reg [7:0] eth_l11_rxc = 0; + +// Outputs +wire [3:0] led; +wire uart_rst; +wire uart_ri; +wire uart_dcd; +wire uart_dsr; +wire uart_rxd; +wire uart_cts; +wire amh_right_mdc; +wire amh_right_mdio_o; +wire amh_right_mdio_t; +wire amh_left_mdc; +wire amh_left_mdio_o; +wire amh_left_mdio_t; +wire [63:0] eth_r0_txd; +wire [7:0] eth_r0_txc; +wire [63:0] eth_r1_txd; +wire [7:0] eth_r1_txc; +wire [63:0] eth_r2_txd; +wire [7:0] eth_r2_txc; +wire [63:0] eth_r3_txd; +wire [7:0] eth_r3_txc; +wire [63:0] eth_r4_txd; +wire [7:0] eth_r4_txc; +wire [63:0] eth_r5_txd; +wire [7:0] eth_r5_txc; +wire [63:0] eth_r6_txd; +wire [7:0] eth_r6_txc; +wire [63:0] eth_r7_txd; +wire [7:0] eth_r7_txc; +wire [63:0] eth_r8_txd; +wire [7:0] eth_r8_txc; +wire [63:0] eth_r9_txd; +wire [7:0] eth_r9_txc; +wire [63:0] eth_r10_txd; +wire [7:0] eth_r10_txc; +wire [63:0] eth_r11_txd; +wire [7:0] eth_r11_txc; +wire [63:0] eth_l0_txd; +wire [7:0] eth_l0_txc; +wire [63:0] eth_l1_txd; +wire [7:0] eth_l1_txc; +wire [63:0] eth_l2_txd; +wire [7:0] eth_l2_txc; +wire [63:0] eth_l3_txd; +wire [7:0] eth_l3_txc; +wire [63:0] eth_l4_txd; +wire [7:0] eth_l4_txc; +wire [63:0] eth_l5_txd; +wire [7:0] eth_l5_txc; +wire [63:0] eth_l6_txd; +wire [7:0] eth_l6_txc; +wire [63:0] eth_l7_txd; +wire [7:0] eth_l7_txc; +wire [63:0] eth_l8_txd; +wire [7:0] eth_l8_txc; +wire [63:0] eth_l9_txd; +wire [7:0] eth_l9_txc; +wire [63:0] eth_l10_txd; +wire [7:0] eth_l10_txc; +wire [63:0] eth_l11_txd; +wire [7:0] eth_l11_txc; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + sw, + jp, + uart_suspend, + uart_dtr, + uart_txd, + uart_rts, + amh_right_mdio_i, + amh_left_mdio_i, + eth_r0_rxd, + eth_r0_rxc, + eth_r1_rxd, + eth_r1_rxc, + eth_r2_rxd, + eth_r2_rxc, + eth_r3_rxd, + eth_r3_rxc, + eth_r4_rxd, + eth_r4_rxc, + eth_r5_rxd, + eth_r5_rxc, + eth_r6_rxd, + eth_r6_rxc, + eth_r7_rxd, + eth_r7_rxc, + eth_r8_rxd, + eth_r8_rxc, + eth_r9_rxd, + eth_r9_rxc, + eth_r10_rxd, + eth_r10_rxc, + eth_r11_rxd, + eth_r11_rxc, + eth_l0_rxd, + eth_l0_rxc, + eth_l1_rxd, + eth_l1_rxc, + eth_l2_rxd, + eth_l2_rxc, + eth_l3_rxd, + eth_l3_rxc, + eth_l4_rxd, + eth_l4_rxc, + eth_l5_rxd, + eth_l5_rxc, + eth_l6_rxd, + eth_l6_rxc, + eth_l7_rxd, + eth_l7_rxc, + eth_l8_rxd, + eth_l8_rxc, + eth_l9_rxd, + eth_l9_rxc, + eth_l10_rxd, + eth_l10_rxc, + eth_l11_rxd, + eth_l11_rxc + ); + $to_myhdl( + led, + uart_rst, + uart_ri, + uart_dcd, + uart_dsr, + uart_rxd, + uart_cts, + amh_right_mdc, + amh_right_mdio_o, + amh_right_mdio_t, + amh_left_mdc, + amh_left_mdio_o, + amh_left_mdio_t, + eth_r0_txd, + eth_r0_txc, + eth_r1_txd, + eth_r1_txc, + eth_r2_txd, + eth_r2_txc, + eth_r3_txd, + eth_r3_txc, + eth_r4_txd, + eth_r4_txc, + eth_r5_txd, + eth_r5_txc, + eth_r6_txd, + eth_r6_txc, + eth_r7_txd, + eth_r7_txc, + eth_r8_txd, + eth_r8_txc, + eth_r9_txd, + eth_r9_txc, + eth_r10_txd, + eth_r10_txc, + eth_r11_txd, + eth_r11_txc, + eth_l0_txd, + eth_l0_txc, + eth_l1_txd, + eth_l1_txc, + eth_l2_txd, + eth_l2_txc, + eth_l3_txd, + eth_l3_txc, + eth_l4_txd, + eth_l4_txc, + eth_l5_txd, + eth_l5_txc, + eth_l6_txd, + eth_l6_txc, + eth_l7_txd, + eth_l7_txc, + eth_l8_txd, + eth_l8_txc, + eth_l9_txd, + eth_l9_txc, + eth_l10_txd, + eth_l10_txc, + eth_l11_txd, + eth_l11_txc + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .sw(sw), + .jp(jp), + .led(led), + .uart_rst(uart_rst), + .uart_suspend(uart_suspend), + .uart_ri(uart_ri), + .uart_dcd(uart_dcd), + .uart_dtr(uart_dtr), + .uart_dsr(uart_dsr), + .uart_txd(uart_txd), + .uart_rxd(uart_rxd), + .uart_rts(uart_rts), + .uart_cts(uart_cts), + .amh_right_mdc(amh_right_mdc), + .amh_right_mdio_i(amh_right_mdio_i), + .amh_right_mdio_o(amh_right_mdio_o), + .amh_right_mdio_t(amh_right_mdio_t), + .amh_left_mdc(amh_left_mdc), + .amh_left_mdio_i(amh_left_mdio_i), + .amh_left_mdio_o(amh_left_mdio_o), + .amh_left_mdio_t(amh_left_mdio_t), + .eth_r0_txd(eth_r0_txd), + .eth_r0_txc(eth_r0_txc), + .eth_r0_rxd(eth_r0_rxd), + .eth_r0_rxc(eth_r0_rxc), + .eth_r1_txd(eth_r1_txd), + .eth_r1_txc(eth_r1_txc), + .eth_r1_rxd(eth_r1_rxd), + .eth_r1_rxc(eth_r1_rxc), + .eth_r2_txd(eth_r2_txd), + .eth_r2_txc(eth_r2_txc), + .eth_r2_rxd(eth_r2_rxd), + .eth_r2_rxc(eth_r2_rxc), + .eth_r3_txd(eth_r3_txd), + .eth_r3_txc(eth_r3_txc), + .eth_r3_rxd(eth_r3_rxd), + .eth_r3_rxc(eth_r3_rxc), + .eth_r4_txd(eth_r4_txd), + .eth_r4_txc(eth_r4_txc), + .eth_r4_rxd(eth_r4_rxd), + .eth_r4_rxc(eth_r4_rxc), + .eth_r5_txd(eth_r5_txd), + .eth_r5_txc(eth_r5_txc), + .eth_r5_rxd(eth_r5_rxd), + .eth_r5_rxc(eth_r5_rxc), + .eth_r6_txd(eth_r6_txd), + .eth_r6_txc(eth_r6_txc), + .eth_r6_rxd(eth_r6_rxd), + .eth_r6_rxc(eth_r6_rxc), + .eth_r7_txd(eth_r7_txd), + .eth_r7_txc(eth_r7_txc), + .eth_r7_rxd(eth_r7_rxd), + .eth_r7_rxc(eth_r7_rxc), + .eth_r8_txd(eth_r8_txd), + .eth_r8_txc(eth_r8_txc), + .eth_r8_rxd(eth_r8_rxd), + .eth_r8_rxc(eth_r8_rxc), + .eth_r9_txd(eth_r9_txd), + .eth_r9_txc(eth_r9_txc), + .eth_r9_rxd(eth_r9_rxd), + .eth_r9_rxc(eth_r9_rxc), + .eth_r10_txd(eth_r10_txd), + .eth_r10_txc(eth_r10_txc), + .eth_r10_rxd(eth_r10_rxd), + .eth_r10_rxc(eth_r10_rxc), + .eth_r11_txd(eth_r11_txd), + .eth_r11_txc(eth_r11_txc), + .eth_r11_rxd(eth_r11_rxd), + .eth_r11_rxc(eth_r11_rxc), + .eth_l0_txd(eth_l0_txd), + .eth_l0_txc(eth_l0_txc), + .eth_l0_rxd(eth_l0_rxd), + .eth_l0_rxc(eth_l0_rxc), + .eth_l1_txd(eth_l1_txd), + .eth_l1_txc(eth_l1_txc), + .eth_l1_rxd(eth_l1_rxd), + .eth_l1_rxc(eth_l1_rxc), + .eth_l2_txd(eth_l2_txd), + .eth_l2_txc(eth_l2_txc), + .eth_l2_rxd(eth_l2_rxd), + .eth_l2_rxc(eth_l2_rxc), + .eth_l3_txd(eth_l3_txd), + .eth_l3_txc(eth_l3_txc), + .eth_l3_rxd(eth_l3_rxd), + .eth_l3_rxc(eth_l3_rxc), + .eth_l4_txd(eth_l4_txd), + .eth_l4_txc(eth_l4_txc), + .eth_l4_rxd(eth_l4_rxd), + .eth_l4_rxc(eth_l4_rxc), + .eth_l5_txd(eth_l5_txd), + .eth_l5_txc(eth_l5_txc), + .eth_l5_rxd(eth_l5_rxd), + .eth_l5_rxc(eth_l5_rxc), + .eth_l6_txd(eth_l6_txd), + .eth_l6_txc(eth_l6_txc), + .eth_l6_rxd(eth_l6_rxd), + .eth_l6_rxc(eth_l6_rxc), + .eth_l7_txd(eth_l7_txd), + .eth_l7_txc(eth_l7_txc), + .eth_l7_rxd(eth_l7_rxd), + .eth_l7_rxc(eth_l7_rxc), + .eth_l8_txd(eth_l8_txd), + .eth_l8_txc(eth_l8_txc), + .eth_l8_rxd(eth_l8_rxd), + .eth_l8_rxc(eth_l8_rxc), + .eth_l9_txd(eth_l9_txd), + .eth_l9_txc(eth_l9_txc), + .eth_l9_rxd(eth_l9_rxd), + .eth_l9_rxc(eth_l9_rxc), + .eth_l10_txd(eth_l10_txd), + .eth_l10_txc(eth_l10_txc), + .eth_l10_rxd(eth_l10_rxd), + .eth_l10_rxc(eth_l10_rxc), + .eth_l11_txd(eth_l11_txd), + .eth_l11_txc(eth_l11_txc), + .eth_l11_rxd(eth_l11_rxd), + .eth_l11_rxc(eth_l11_rxc) +); + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga/tb/udp_ep.py b/fpga/lib/eth/example/HXT100G/fpga/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga/tb/xgmii_ep.py b/fpga/lib/eth/example/HXT100G/fpga/tb/xgmii_ep.py new file mode 120000 index 000000000..63b6d3567 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga/tb/xgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/xgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/Makefile b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/Makefile new file mode 100644 index 000000000..9f8bd4048 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/Makefile @@ -0,0 +1,25 @@ +# Targets +TARGETS:= + +# Subdirectories +SUBDIRS = coregen 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/fpga/lib/eth/example/HXT100G/fpga_cxpt16/README.md b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/README.md new file mode 100644 index 000000000..1128f97c5 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/README.md @@ -0,0 +1,27 @@ +# Verilog Ethernet HXT100G Crosspoint Switch Design + +## Introduction + +This design targets the HiTech Global HXT100G FPGA board. + +The design forms a 16x16 crosspoint switch for 10G Ethernet. It is capable of +connecting any output port to any input port based on configuration frames +received over a dedicated configuration interface. + +FPGA: XC6VHX565T-2FFG1923 +PHY: 10G BASE-R PHY IP core and internal GTH transceiver + +## How to build + +Run make to build. Ensure that the Xilinx ISE toolchain components are +in PATH. + +## How to use + +SFP left ports 0-7 are connected to crosspoint input/output ports 0-7, SFP +right ports 0-7 are connected to crosspoint input/output ports 8-15. SFP port +left 11 is the control port. Send an Ethernet frame with ethtype 0x8099 to +this port to reconfigure the switch, the first 16 payload bytes corresponding +to the 16 switch output ports, each byte selecting which input port will be +connected. It is possible to connect multiple output ports to the same input +port. diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/common/xilinx.mk b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/common/xilinx.mk new file mode 100644 index 000000000..f10a45f8e --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/common/xilinx.mk @@ -0,0 +1,191 @@ +############################################################################# +# Author: Lane Brooks/Keith Fife +# Date: 04/28/2006 +# License: GPL +# Desc: This is a Makefile intended to take a verilog rtl design +# through the Xilinx ISE tools to generate configuration files for +# Xilinx FPGAs. This file is generic and just a template. As such +# all design specific options such as synthesis files, fpga part type, +# prom part type, etc should be set in the top Makefile prior to +# including this file. Alternatively, all parameters can be passed +# in from the command line as well. +# +############################################################################## +# +# Parameter: +# SYN_FILES - Space seperated list of files to be synthesized +# PART - FPGA part (see Xilinx documentation) +# PROM - PROM part +# NGC_PATHS - Space seperated list of any dirs with pre-compiled ngc files. +# UCF_FILES - Space seperated list of user constraint files. Defaults to xilinx/$(FPGA_TOP).ucf +# +# +# Example Calling Makefile: +# +# SYN_FILES = fpga.v fifo.v clks.v +# PART = xc3s1000 +# FPGA_TOP = fpga +# PROM = xc18v04 +# NGC_PATH = ipLib1 ipLib2 +# FPGA_ARCH = spartan6 +# SPI_PROM_SIZE = (in bytes) +# include xilinx.mk +############################################################################# +# +# Command Line Example: +# make -f xilinx.mk PART=xc3s1000-4fg320 SYN_FILES="fpga.v test.v" FPGA_TOP=fpga +# +############################################################################## +# +# Required Setup: +# +# %.ucf - user constraint file. Needed by ngdbuild +# +# Optional Files: +# %.xcf - user constraint file. Needed by xst. +# %.ut - File for pin states needed by bitgen + + +.PHONY: clean bit prom fpga spi + + +# Mark the intermediate files as PRECIOUS to prevent make from +# deleting them (see make manual section 10.4). +.PRECIOUS: %.ngc %.ngd %_map.ncd %.ncd %.twr %.bit %_timesim.v + +# include the local Makefile for project for any project specific targets +CONFIG ?= config.mk +-include ../$(CONFIG) + +SYN_FILES_REL = $(patsubst %, ../%, $(SYN_FILES)) +INC_FILES_REL = $(patsubst %, ../%, $(INC_FILES)) +INC_PATHS_REL = $(patsubst %, ../%, $(INC_PATHS)) +NGC_PATHS_REL = $(patsubst %, ../%, $(NGC_PATHS)) + +ifdef UCF_FILES + UCF_FILES_REL = $(patsubst %, ../%, $(UCF_FILES)) +else + UCF_FILES_REL = $(FPGA_TOP).ucf +endif + + + +fpga: $(FPGA_TOP).bit + +mcs: $(FPGA_TOP).mcs + +prom: $(FPGA_TOP).spi + +spi: $(FPGA_TOP).spi + +fpgasim: $(FPGA_TOP)_sim.v + + +########################### XST TEMPLATES ############################ +# There are 2 files that XST uses for synthesis that we auto generate. +# The first is a project file which is just a list of all the verilog +# files. The second is the src file which passes XST all the options. +# See XST user manual for XST options. +%.ngc: $(SYN_FILES_REL) $(INC_FILES_REL) + rm -rf xst $*.prj $*.xst defines.v + touch defines.v + mkdir -p xst/tmp + for x in $(DEFS); do echo '`define' $$x >> defines.v; done + echo verilog work defines.v > $*.prj + for x in $(SYN_FILES_REL); do echo verilog work $$x >> $*.prj; done + @echo "set -tmpdir ./xst/tmp" >> $*.xst + @echo "set -xsthdpdir ./xst" >> $*.xst + @echo "run" >> $*.xst + @echo "-ifn $*.prj" >> $*.xst + @echo "-ifmt mixed" >> $*.xst + @echo "-top $*" >> $*.xst + @echo "-ofn $*" >> $*.xst + @echo "-ofmt NGC" >> $*.xst + @echo "-opt_mode Speed" >> $*.xst + @echo "-opt_level 1" >> $*.xst + # @echo "-verilog2001 YES" >> $*.xst + @echo "-keep_hierarchy NO" >> $*.xst + @echo "-p $(FPGA_PART)" >> $*.xst + xst -ifn $*.xst -ofn $*.log + + +########################### ISE TRANSLATE ############################ +# ngdbuild will automatically use a ucf called %.ucf if one is found. +# We setup the dependancy such that %.ucf file is required. If any +# pre-compiled ncd files are needed, set the NGC_PATH variable as a space +# seperated list of directories that include the pre-compiled ngc files. +%.ngd: %.ngc $(UCF_FILES_REL) + ngdbuild -dd ngdbuild $(patsubst %,-sd %, $(NGC_PATHS_REL)) $(patsubst %,-uc %, $(UCF_FILES_REL)) -p $(FPGA_PART) $< $@ + + +########################### ISE MAP ################################### +ifeq ($(FPGA_ARCH),spartan6) + MAP_OPTS= -register_duplication on -timing -xe n +else + MAP_OPTS= -cm speed -register_duplication on -timing -xe n -pr b +endif + +%_map.ncd: %.ngd + map -p $(FPGA_PART) $(MAP_OPTS) -w -o $@ $< $*.pcf + +# map -p $(FPGA_PART) -cm area -pr b -k 4 -c 100 -o $@ $< $*.pcf + + +########################### ISE PnR ################################### +%.ncd: %_map.ncd + par -w -ol high $< $@ $*.pcf + +# par -w -ol std -t 1 $< $@ $*.pcf + + +##################### ISE Static Timing Analysis ##################### +%.twr: %.ncd + -trce -e 3 -l 3 -u -xml $* $< -o $@ $*.pcf + +%_sim.v: %.ncd + netgen -s 4 -pcf $*.pcf -sdf_anno true -ism -sdf_path netgen -w -dir . -ofmt verilog -sim $< $@ + +# netgen -ise "/home/lane/Second/xilinx/Second/Second" -intstyle ise -s 4 -pcf Second.pcf -sdf_anno true -sdf_path netgen/par -w -dir netgen/par -ofmt verilog -sim Second.ncd Second_timesim.v + + +########################### ISE Bitgen ############################# +%.bit: %.twr + bitgen $(BITGEN_OPTIONS) -w $*.ncd $*.bit + mkdir -p rev + EXT=bit; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +########################### ISE Promgen ############################# +%.mcs: %.bit + promgen -spi -w -p mcs -s $(SPI_PROM_SIZE) -o $@ -u 0 $< + # promgen -w -p mcs -c FF -o $@ -u 0 $< -x $(PROM) + mkdir -p rev + EXT=mcs; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +%.spi: %.mcs + objcopy -I ihex -O binary $< $@ + EXT=spi; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + + +tmpclean: + -rm -rf xst ngdbuild *_map.* *.ncd *.ngc *.log *.xst *.prj *.lso *~ *.pcf *.bld *.ngd *.xpi *_pad.* *.unroutes *.twx *.par *.twr *.pad *.drc *.bgn *.prm *.sig netgen *.v *.nlf *.xml + +clean: tmpclean + -rm -rf *.bit *.mcs + +# clean everything +distclean: clean + -rm -rf rev + diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/Makefile b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/Makefile new file mode 100644 index 000000000..09ec2cdf4 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/Makefile @@ -0,0 +1,33 @@ +# Tools +COREGEN:=coregen +XAW2VERILOG:=xaw2verilog + +# Source +XCO:=ten_gig_eth_pcs_pma_v2_6.xco ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper.xco +XAW:= + +# Targets +TARGETS += $(XCO:.xco=) +TARGETS += $(XAW:.xaw=) + +# Rules +.PHONY: all +all: $(TARGETS) + +.PHONY: clean +clean: + -rm -rf $(TARGETS) + +%: %.xco + $(eval $@_TMP := $(shell mktemp -d)) + cp -a coregen.cgp $($@_TMP) + cp -a $< $($@_TMP) + cd $($@_TMP) && $(COREGEN) -p coregen.cgp -b $(notdir $<) + mv $($@_TMP) $@ + +%: %.xaw + $(eval $@_TMP := $(shell mktemp -d)) + cp -a coregen.cgp $($@_TMP) + cp -a $< $($@_TMP) + cd $($@_TMP) && $(XAW2VERILOG) -st $(notdir $<) $(notdir $*) + mv $($@_TMP) $@ diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/coregen.cgp b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/coregen.cgp new file mode 100644 index 000000000..dbe49f4c0 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/coregen.cgp @@ -0,0 +1,22 @@ +# Date: Wed Apr 1 17:38:18 2015 + +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6vhx565t +SET devicefamily = virtex6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = ff1923 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -2 +SET verilogsim = true +SET vhdlsim = false +SET workingdirectory = ./tmp/ + +# CRC: 3d2f7d04 diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/ten_gig_eth_pcs_pma_v2_6.xco b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/ten_gig_eth_pcs_pma_v2_6.xco new file mode 100644 index 000000000..2d141c5b0 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/ten_gig_eth_pcs_pma_v2_6.xco @@ -0,0 +1,53 @@ +############################################################## +# +# Xilinx Core Generator version 14.7 +# Date: Wed Apr 1 17:39:05 2015 +# +############################################################## +# +# This file contains the customisation parameters for a +# Xilinx CORE Generator IP GUI. It is strongly recommended +# that you do not manually alter this file as it may cause +# unexpected and unsupported behavior. +# +############################################################## +# +# Generated from component: xilinx.com:ip:ten_gig_eth_pcs_pma:2.6 +# +############################################################## +# +# BEGIN Project Options +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6vhx565t +SET devicefamily = virtex6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = ff1923 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -2 +SET verilogsim = true +SET vhdlsim = false +# END Project Options +# BEGIN Select +SELECT Ten_Gigabit_Ethernet_PCS/PMA_(10GBASE-R/KR) xilinx.com:ip:ten_gig_eth_pcs_pma:2.6 +# END Select +# BEGIN Parameters +CSET autonegotiation=false +CSET base_kr=BASE-R +CSET component_name=ten_gig_eth_pcs_pma_v2_6 +CSET fec=false +CSET ieee_1588=None +CSET mdio_management=false +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2012-10-08T14:58:05Z +# END Extra information +GENERATE +# CRC: d3dbdcf5 diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper.xco b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper.xco new file mode 100644 index 000000000..3c380f101 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper.xco @@ -0,0 +1,185 @@ +############################################################## +# +# Xilinx Core Generator version 14.7 +# Date: Wed Apr 1 18:19:03 2015 +# +############################################################## +# +# This file contains the customisation parameters for a +# Xilinx CORE Generator IP GUI. It is strongly recommended +# that you do not manually alter this file as it may cause +# unexpected and unsupported behavior. +# +############################################################## +# +# Generated from component: xilinx.com:ip:v6_gthwizard:1.11 +# +############################################################## +# +# BEGIN Project Options +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6vhx565t +SET devicefamily = virtex6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = ff1923 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -2 +SET verilogsim = true +SET vhdlsim = false +# END Project Options +# BEGIN Select +SELECT Virtex-6_FPGA_GTH_Transceiver_Wizard xilinx.com:ip:v6_gthwizard:1.11 +# END Select +# BEGIN Parameters +CSET component_name=ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper +CSET config_identical=true +CSET drp_clock=50.00 +CSET gth0_datapath_width=64 +CSET gth0_encoding=10GbE_64B/66B +CSET gth0_line_rate=10.3125 +CSET gth0_postcursor_emphasis=0 +CSET gth0_precursor_emphasis=0 +CSET gth0_protocol_file=10GBASE-R +CSET gth0_rxeqmix=1000 +CSET gth0_tx_swing=800 +CSET gth0_use_dfetrainctrl=false +CSET gth0_use_lat_msr=false +CSET gth0_use_port_powerdown=true +CSET gth0_use_port_rxbufreset=true +CSET gth0_use_port_rxcodeerr=true +CSET gth0_use_port_rxctrl=true +CSET gth0_use_port_rxdisperr=false +CSET gth0_use_port_rxencommadet=false +CSET gth0_use_port_rxpolarity=false +CSET gth0_use_port_rxpowerdown=true +CSET gth0_use_port_rxslip=false +CSET gth0_use_port_rxvalid=false +CSET gth0_use_port_txbufreset=true +CSET gth0_use_port_txctrl=true +CSET gth0_use_port_txpowerdown=true +CSET gth1_datapath_width=64 +CSET gth1_encoding=10GbE_64B/66B +CSET gth1_line_rate=10.3125 +CSET gth1_postcursor_emphasis=0 +CSET gth1_precursor_emphasis=0 +CSET gth1_protocol_file=10GBASE-R +CSET gth1_rxeqmix=1000 +CSET gth1_tx_swing=800 +CSET gth1_use_dfetrainctrl=false +CSET gth1_use_lat_msr=false +CSET gth1_use_port_powerdown=true +CSET gth1_use_port_rxbufreset=true +CSET gth1_use_port_rxcodeerr=true +CSET gth1_use_port_rxctrl=true +CSET gth1_use_port_rxdisperr=false +CSET gth1_use_port_rxencommadet=false +CSET gth1_use_port_rxpolarity=false +CSET gth1_use_port_rxpowerdown=true +CSET gth1_use_port_rxslip=false +CSET gth1_use_port_rxvalid=false +CSET gth1_use_port_txbufreset=true +CSET gth1_use_port_txctrl=true +CSET gth1_use_port_txpowerdown=true +CSET gth2_datapath_width=64 +CSET gth2_encoding=10GbE_64B/66B +CSET gth2_line_rate=10.3125 +CSET gth2_postcursor_emphasis=0 +CSET gth2_precursor_emphasis=0 +CSET gth2_protocol_file=10GBASE-R +CSET gth2_rxeqmix=1000 +CSET gth2_tx_swing=800 +CSET gth2_use_dfetrainctrl=false +CSET gth2_use_lat_msr=false +CSET gth2_use_port_powerdown=true +CSET gth2_use_port_rxbufreset=true +CSET gth2_use_port_rxcodeerr=true +CSET gth2_use_port_rxctrl=true +CSET gth2_use_port_rxdisperr=false +CSET gth2_use_port_rxencommadet=false +CSET gth2_use_port_rxpolarity=false +CSET gth2_use_port_rxpowerdown=true +CSET gth2_use_port_rxslip=false +CSET gth2_use_port_rxvalid=false +CSET gth2_use_port_txbufreset=true +CSET gth2_use_port_txctrl=true +CSET gth2_use_port_txpowerdown=true +CSET gth3_datapath_width=64 +CSET gth3_encoding=10GbE_64B/66B +CSET gth3_line_rate=10.3125 +CSET gth3_postcursor_emphasis=0 +CSET gth3_precursor_emphasis=0 +CSET gth3_protocol_file=10GBASE-R +CSET gth3_rxeqmix=1000 +CSET gth3_tx_swing=800 +CSET gth3_use_dfetrainctrl=false +CSET gth3_use_lat_msr=false +CSET gth3_use_port_powerdown=true +CSET gth3_use_port_rxbufreset=true +CSET gth3_use_port_rxcodeerr=true +CSET gth3_use_port_rxctrl=true +CSET gth3_use_port_rxdisperr=false +CSET gth3_use_port_rxencommadet=false +CSET gth3_use_port_rxpolarity=false +CSET gth3_use_port_rxpowerdown=true +CSET gth3_use_port_rxslip=false +CSET gth3_use_port_rxvalid=false +CSET gth3_use_port_txbufreset=true +CSET gth3_use_port_txctrl=true +CSET gth3_use_port_txpowerdown=true +CSET gth_column=Right_Column_(X1) +CSET gthx4lane=false +CSET protocol_template=10GBASE-R +CSET refclk_x0y0=CLK_Y0 +CSET refclk_x0y1=CLK_Y1 +CSET refclk_x0y2=CLK_Y2 +CSET refclk_x1y0=CLK_Y0 +CSET refclk_x1y1=CLK_Y1 +CSET refclk_x1y2=CLK_Y2 +CSET reference_clock=156.25 +CSET target_line_rate=10.3125 +CSET use_gth0_x0y0=true +CSET use_gth0_x0y1=true +CSET use_gth0_x0y2=true +CSET use_gth0_x1y0=true +CSET use_gth0_x1y1=true +CSET use_gth0_x1y2=true +CSET use_gth1_x0y0=true +CSET use_gth1_x0y1=true +CSET use_gth1_x0y2=true +CSET use_gth1_x1y0=true +CSET use_gth1_x1y1=true +CSET use_gth1_x1y2=true +CSET use_gth2_x0y0=true +CSET use_gth2_x0y1=true +CSET use_gth2_x0y2=true +CSET use_gth2_x1y0=true +CSET use_gth2_x1y1=true +CSET use_gth2_x1y2=true +CSET use_gth3_x0y0=true +CSET use_gth3_x0y1=true +CSET use_gth3_x0y2=true +CSET use_gth3_x1y0=true +CSET use_gth3_x1y1=true +CSET use_gth3_x1y2=true +CSET use_gth_quad_x0y0=false +CSET use_gth_quad_x0y1=false +CSET use_gth_quad_x0y2=false +CSET use_gth_quad_x1y0=true +CSET use_gth_quad_x1y1=false +CSET use_gth_quad_x1y2=false +CSET use_no_rx=false +CSET use_no_tx=false +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2011-04-05T17:48:34Z +# END Extra information +GENERATE +# CRC: 75800c49 diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/fpga.ucf b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/fpga.ucf new file mode 100644 index 000000000..784dcb369 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/fpga.ucf @@ -0,0 +1,269 @@ +# User Constraints File for the HTG-V6HXT-100GIG FPGA board + +CONFIG PART = xc6vhx565t-2ff1923; + +# 50 MHz clock +NET "sys_clk" LOC = "AP33" | IOSTANDARD=LVCMOS25; # SYS_CLK, 50MHz +NET "sys_clk" TNM_NET = "sys_clk"; +TIMESPEC "TS_sys_clk" = PERIOD "sys_clk" 20.000 ns HIGH 50% INPUT_JITTER 200.0ps; + +# 156.25 MHz transceiver clock +NET "*txclk156" TNM_NET = "txclk156"; +TIMESPEC "TS_txclk156" = PERIOD "txclk156" 6400 ps; + +NET "*rx_clk_0" TNM_NET = "rx_clk"; +NET "*rx_clk_1" TNM_NET = "rx_clk"; +NET "*rx_clk_2" TNM_NET = "rx_clk"; +NET "*rx_clk_3" TNM_NET = "rx_clk"; +TIMESPEC "TS_rx_clk" = PERIOD "rx_clk" 6400 ps; + +# clock crossing constraints +TIMESPEC "TS_txclk156_to_sys_clk" = FROM "txclk156" TO "sys_clk" 10 ns; +TIMESPEC "TS_sys_clk_to_txclk156" = FROM "sys_clk" TO "txclk156" 10 ns; +TIMESPEC "TS_sys_clk_to_rx_clk" = FROM "sys_clk" TO "rx_clk" 10 ns; +TIMESPEC "TS_rx_clk_to_sys_clk" = FROM "rx_clk" TO "sys_clk" 10 ns; + +# PHY elastic buffer constraints +NET "*elastic_buffer_i*rd_truegray" MAXDELAY = 6.0 ns; +NET "*elastic_buffer_i?can_insert_wra" TIG; +NET "*wr_gray*" MAXDELAY = 6.0 ns; +NET "*rd_lastgray*" MAXDELAY = 6.0 ns; + +TIMESPEC "TS_txclk156_to_rx_clk" = FROM "txclk156" TO "rx_clk" 10 ns; +TIMESPEC "TS_rx_clk_to_txclk156" = FROM "rx_clk" TO "txclk156" 10 ns; + +# 200 MHz DDR3 clock +#NET "clk_ddr3_p" LOC = "AL13" | IOSTANDARD=LVDS_25; +#NET "clk_ddr3_n" LOC = "AL12" | IOSTANDARD=LVDS_25; +#NET "clk_ddr3_p" TNM_NET = "clk_ddr3"; +#TIMESPEC "TS_clk_ddr3" = PERIOD "clk_ddr3" 5.000 ns HIGH 50% INPUT_JITTER 20.0ps; + +# User clock +#NET "clk_usr_p" LOC = "J33" | IOSTANDARD=LVDS_25; +#NET "clk_usr_n" LOC = "H33" | IOSTANDARD=LVDS_25; +#NET "clk_usr_p" TNM_NET = "clk_usr"; +#TIMESPEC "TS_clk_usr" = PERIOD "clk_usr" 5.000 ns HIGH 50% INPUT_JITTER 20.0ps; + +# Button +NET "reset_n" LOC = "AY12" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (S7) + +# DIP switches +NET "sw<0>" LOC = "BB32" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (USER_SW0) +NET "sw<1>" LOC = "AT30" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (USER_SW1) + +# Jumpers +NET "jp<0>" LOC = "AW34" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (IO0, JP18) +NET "jp<1>" LOC = "AY35" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (IO1, JP19) +NET "jp<2>" LOC = "AW35" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (IO2, JP20) +NET "jp<3>" LOC = "AV33" | IOSTANDARD=LVCMOS25; # IO_L1N_M0_CMPMISO_2 (IO3, JP21) + +# LEDs +NET "led<0>" LOC = "AY33" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L52N_M1DQ15 (D20) +NET "led<1>" LOC = "AW33" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L52N_M1DQ15 (D23) +NET "led<2>" LOC = "AK35" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L52N_M1DQ15 (D26) +NET "led<3>" LOC = "AL35" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 1, IO_L52N_M1DQ15 (D31) + +# Silicon Labs CP2102 +NET "uart_rst" LOC = "D10" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_suspend" LOC = "E12" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_ri" LOC = "B12" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_dcd" LOC = "C11" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_dtr" LOC = "B11" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_dsr" LOC = "D11" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_rxd" LOC = "E11" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # IO_L66N_SCP0 +NET "uart_txd" LOC = "B9" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # IO_L66P_SCP1 +NET "uart_rts" LOC = "A9" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # +NET "uart_cts" LOC = "F12" | IOSTANDARD=LVCMOS25 | SLEW=SLOW | DRIVE=2; # + +# Clock muxes +NET "clk_gth_scl" LOC = "M11"; +NET "clk_gth_sda" LOC = "L10"; +NET "clk_gth_rst_n" LOC = "AR7"; +NET "clk_gthl_alm" LOC = "M34"; +NET "clk_gthr_alm" LOC = "R13"; +NET "clk_gthl_lol" LOC = "L34"; +NET "clk_gthr_lol" LOC = "L12"; + +# AirMax I/O +#NET "amh_right_io<0>" LOC="N33"; # AMH1R_IO0 J6.TX[12]_P mdc +#NET "amh_right_io<1>" LOC="J34"; # AMH1R_IO1 J6.TX[12]_N mdio +#NET "amh_right_io<2>" LOC="F34"; # AMH1R_IO2 J6.TX[13]_P +#NET "amh_right_io<3>" LOC="G34"; # AMH1R_IO3 J6.TX[13]_N +#NET "amh_right_io<4>" LOC="K33"; # AMH1R_IO4 J6.TX[14]_P +#NET "amh_right_io<5>" LOC="L33"; # AMH1R_IO5 J6.TX[14]_N +#NET "amh_right_io<6>" LOC="N34"; # AMH1R_IO6 J6.TX[15]_P +#NET "amh_right_io<7>" LOC="H34"; # AMH1R_IO7 J6.TX[15]_N reset_n + +#NET "amh_left_io<0>" LOC="E35"; # AMH1L_IO0 J3.TX[12]_P mdc +#NET "amh_left_io<1>" LOC="B37"; # AMH1L_IO1 J3.TX[12]_N mdio +#NET "amh_left_io<2>" LOC="A35"; # AMH1L_IO2 J3.TX[13]_P +#NET "amh_left_io<3>" LOC="B35"; # AMH1L_IO3 J3.TX[13]_N +#NET "amh_left_io<4>" LOC="G35"; # AMH1L_IO4 J3.TX[14]_P +#NET "amh_left_io<5>" LOC="F35"; # AMH1L_IO5 J3.TX[14]_N +#NET "amh_left_io<6>" LOC="A37"; # AMH1L_IO6 J3.TX[15]_P +#NET "amh_left_io<7>" LOC="B36"; # AMH1L_IO7 J3.TX[15]_N reset_n + +NET "amh_right_mdc" LOC="N33"; # AMH1R_IO0 J6.TX[12]_P mdc +NET "amh_right_mdio" LOC="J34"; # AMH1R_IO1 J6.TX[12]_N mdio +NET "amh_right_phy_rst_n" LOC="H34" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; + +NET "amh_left_mdc" LOC="E35"; # AMH1L_IO0 J3.TX[12]_P mdc +NET "amh_left_mdio" LOC="B37"; # AMH1L_IO1 J3.TX[12]_N mdio +NET "amh_left_phy_rst_n" LOC="B36" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; + +# 10G Ethernet interfaces +# Quad A X0Y0 +# Ports 11, 9, 10, 8 (right) +# SFP 0, 2, 4, 6 +NET "gth_quad_A_refclk_p" LOC = "R41"; +NET "gth_quad_A_refclk_n" LOC = "R42"; + +NET "gth_quad_A_txp_0" LOC = "T43"; +NET "gth_quad_A_txn_0" LOC = "T44"; +NET "gth_quad_A_rxp_0" LOC = "U41"; +NET "gth_quad_A_rxn_0" LOC = "U42"; + +NET "gth_quad_A_txp_1" LOC = "P43"; +NET "gth_quad_A_txn_1" LOC = "P44"; +NET "gth_quad_A_rxp_1" LOC = "T39"; +NET "gth_quad_A_rxn_1" LOC = "T40"; + +NET "gth_quad_A_txp_2" LOC = "M43"; +NET "gth_quad_A_txn_2" LOC = "M44"; +NET "gth_quad_A_rxp_2" LOC = "N37"; +NET "gth_quad_A_rxn_2" LOC = "N38"; + +NET "gth_quad_A_txp_3" LOC = "N41"; +NET "gth_quad_A_txn_3" LOC = "N42"; +NET "gth_quad_A_rxp_3" LOC = "M39"; +NET "gth_quad_A_rxn_3" LOC = "M40"; + +# Quad B X0Y1 +# Ports 4, 6, 5, 7 (right) +# SFP 3, 5, 1, 7 +NET "gth_quad_B_refclk_p" LOC = "J41"; +NET "gth_quad_B_refclk_n" LOC = "J42"; + +NET "gth_quad_B_txp_0" LOC = "L41"; +NET "gth_quad_B_txn_0" LOC = "L42"; +NET "gth_quad_B_rxp_0" LOC = "K39"; +NET "gth_quad_B_rxn_0" LOC = "K40"; + +NET "gth_quad_B_txp_1" LOC = "K43"; +NET "gth_quad_B_txn_1" LOC = "K44"; +NET "gth_quad_B_rxp_1" LOC = "L37"; +NET "gth_quad_B_rxn_1" LOC = "L38"; + +NET "gth_quad_B_txp_2" LOC = "G41"; +NET "gth_quad_B_txn_2" LOC = "G42"; +NET "gth_quad_B_rxp_2" LOC = "H39"; +NET "gth_quad_B_rxn_2" LOC = "H40"; + +NET "gth_quad_B_txp_3" LOC = "H43"; +NET "gth_quad_B_txn_3" LOC = "H44"; +NET "gth_quad_B_rxp_3" LOC = "J37"; +NET "gth_quad_B_rxn_3" LOC = "J38"; + +# Quad C X0Y2 +# Ports 1, 0, 3, 2 (right) +# SFP 8, 9, 11, 10 +NET "gth_quad_C_refclk_p" LOC = "E41"; +NET "gth_quad_C_refclk_n" LOC = "E42"; + +NET "gth_quad_C_txp_0" LOC = "F43"; +NET "gth_quad_C_txn_0" LOC = "F44"; +NET "gth_quad_C_rxp_0" LOC = "G37"; +NET "gth_quad_C_rxn_0" LOC = "G38"; + +NET "gth_quad_C_txp_1" LOC = "D43"; +NET "gth_quad_C_txn_1" LOC = "D44"; +NET "gth_quad_C_rxp_1" LOC = "F39"; +NET "gth_quad_C_rxn_1" LOC = "F40"; + +NET "gth_quad_C_txp_2" LOC = "A41"; +NET "gth_quad_C_txn_2" LOC = "A42"; +NET "gth_quad_C_rxp_2" LOC = "B39"; +NET "gth_quad_C_rxn_2" LOC = "B40"; + +NET "gth_quad_C_txp_3" LOC = "C41"; +NET "gth_quad_C_txn_3" LOC = "C42"; +NET "gth_quad_C_rxp_3" LOC = "D39"; +NET "gth_quad_C_rxn_3" LOC = "D40"; + +# Quad D X1Y0 +# Ports 11, 9, 10, 8 (left) +# SFP 0, 2, 4, 6 +NET "gth_quad_D_refclk_p" LOC = "R4"; +NET "gth_quad_D_refclk_n" LOC = "R3"; + +NET "gth_quad_D_txp_0" LOC = "T2"; +NET "gth_quad_D_txn_0" LOC = "T1"; +NET "gth_quad_D_rxp_0" LOC = "U4"; +NET "gth_quad_D_rxn_0" LOC = "U3"; + +NET "gth_quad_D_txp_1" LOC = "P2"; +NET "gth_quad_D_txn_1" LOC = "P1"; +NET "gth_quad_D_rxp_1" LOC = "T6"; +NET "gth_quad_D_rxn_1" LOC = "T5"; + +NET "gth_quad_D_txp_2" LOC = "M2"; +NET "gth_quad_D_txn_2" LOC = "M1"; +NET "gth_quad_D_rxp_2" LOC = "N8"; +NET "gth_quad_D_rxn_2" LOC = "N7"; + +NET "gth_quad_D_txp_3" LOC = "N4"; +NET "gth_quad_D_txn_3" LOC = "N3"; +NET "gth_quad_D_rxp_3" LOC = "M6"; +NET "gth_quad_D_rxn_3" LOC = "M5"; + +# Quad E X1Y1 +# Ports 4, 6, 5, 7 (left) +# SFP 3, 5, 1, 7 +NET "gth_quad_E_refclk_p" LOC = "J4"; +NET "gth_quad_E_refclk_n" LOC = "J3"; + +NET "gth_quad_E_txp_0" LOC = "L4"; +NET "gth_quad_E_txn_0" LOC = "L3"; +NET "gth_quad_E_rxp_0" LOC = "K6"; +NET "gth_quad_E_rxn_0" LOC = "K5"; + +NET "gth_quad_E_txp_1" LOC = "K2"; +NET "gth_quad_E_txn_1" LOC = "K1"; +NET "gth_quad_E_rxp_1" LOC = "L8"; +NET "gth_quad_E_rxn_1" LOC = "L7"; + +NET "gth_quad_E_txp_2" LOC = "G4"; +NET "gth_quad_E_txn_2" LOC = "G3"; +NET "gth_quad_E_rxp_2" LOC = "H6"; +NET "gth_quad_E_rxn_2" LOC = "H5"; + +NET "gth_quad_E_txp_3" LOC = "H2"; +NET "gth_quad_E_txn_3" LOC = "H1"; +NET "gth_quad_E_rxp_3" LOC = "J8"; +NET "gth_quad_E_rxn_3" LOC = "J7"; + +# Quad F X1Y2 +# Ports 1, 0, 3, 2 (left) +# SFP 8, 9, 11, 10 +NET "gth_quad_F_refclk_p" LOC = "E4"; +NET "gth_quad_F_refclk_n" LOC = "E3"; + +NET "gth_quad_F_txp_0" LOC = "F2"; +NET "gth_quad_F_txn_0" LOC = "F1"; +NET "gth_quad_F_rxp_0" LOC = "G8"; +NET "gth_quad_F_rxn_0" LOC = "G7"; + +NET "gth_quad_F_txp_1" LOC = "D2"; +NET "gth_quad_F_txn_1" LOC = "D1"; +NET "gth_quad_F_rxp_1" LOC = "F6"; +NET "gth_quad_F_rxn_1" LOC = "F5"; + +NET "gth_quad_F_txp_2" LOC = "A4"; +NET "gth_quad_F_txn_2" LOC = "A3"; +NET "gth_quad_F_rxp_2" LOC = "B6"; +NET "gth_quad_F_rxn_2" LOC = "B5"; + +NET "gth_quad_F_txp_3" LOC = "C4"; +NET "gth_quad_F_txn_3" LOC = "C3"; +NET "gth_quad_F_rxp_3" LOC = "D6"; +NET "gth_quad_F_rxn_3" LOC = "D5"; diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/fpga/Makefile b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/fpga/Makefile new file mode 100644 index 000000000..d2771adf9 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/fpga/Makefile @@ -0,0 +1,66 @@ + +# FPGA settings +FPGA_PART = xc6vhx565t-2ff1923 +FPGA_TOP = fpga +FPGA_ARCH = spartan6 + +# PROM settings +#PROM = xc18v04 +#SPI_PROM_SIZE = (in bytes) + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += rtl/i2c_master.v +SYN_FILES += rtl/gth_i2c_init.v +SYN_FILES += rtl/eth_gth_phy_quad.v +SYN_FILES += lib/eth/rtl/eth_mac_10g_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_10g.v +SYN_FILES += lib/eth/rtl/axis_xgmii_rx_64.v +SYN_FILES += lib/eth/rtl/axis_xgmii_tx_64.v +SYN_FILES += lib/eth/rtl/lfsr.v +SYN_FILES += lib/eth/rtl/eth_axis_rx_64.v +SYN_FILES += lib/eth/rtl/eth_axis_tx_64.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_crosspoint.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_async_fifo.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6/ten_gig_eth_pcs_pma_v2_6.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6/ten_gig_eth_pcs_pma_v2_6/example_design/ten_gig_eth_pcs_pma_v2_6_management_arbiter.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_quad.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_gth_reset.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_gth_init.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_gth_tx_pcs_reset.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_gth_rx_pcs_cdr_reset.v +SYN_FILES += coregen/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper/ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_pulse_synchronizer.v + +# UCF files +UCF_FILES = fpga.ucf + +# NGC paths for ngdbuild +NGC_PATHS = coregen/ten_gig_eth_pcs_pma_v2_6 + +# Bitgen options +BITGEN_OPTIONS = -g StartupClk:Cclk -g ConfigRate:26 + +include ../common/xilinx.mk + +program: $(FPGA_TOP).bit + echo "setmode -bscan" > program.cmd + echo "setcable -p auto" >> program.cmd + echo "identify" >> program.cmd + echo "assignfile -p 1 -file $(FPGA_TOP).bit" >> program.cmd + echo "program -p 1" >> program.cmd + echo "quit" >> program.cmd + impact -batch program.cmd + +program_flash: $(FPGA_TOP).mcs + echo "setmode -bscan" > program.cmd + echo "setcable -p auto" >> program.cmd + echo "identify" >> program.cmd + echo "assignfile -p 1 -file $(FPGA_TOP).mcs" >> program.cmd + echo "program -p 1 -e" >> program.cmd + echo "quit" >> program.cmd + impact -batch program.cmd + diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/lib/eth b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/debounce_switch.v b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/debounce_switch.v new file mode 100644 index 000000000..69f30d706 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/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 + +`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) 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/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/eth_gth_phy_quad.v b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/eth_gth_phy_quad.v new file mode 100644 index 000000000..60ab0f137 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/eth_gth_phy_quad.v @@ -0,0 +1,569 @@ +/* + +Copyright (c) 2015-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. + +*/ + +module eth_gth_phy_quad ( + /* + * Clock and reset + */ + input wire clk156, + input wire rst156, + input wire dclk, + input wire dclk_reset, + + output wire txclk156, + + input wire gth_reset, + output wire gth_reset_done, + + /* + * Transciever pins + */ + input wire refclk_p, + input wire refclk_n, + output wire txp_0, + output wire txn_0, + input wire rxp_0, + input wire rxn_0, + output wire txp_1, + output wire txn_1, + input wire rxp_1, + input wire rxn_1, + output wire txp_2, + output wire txn_2, + input wire rxp_2, + input wire rxn_2, + output wire txp_3, + output wire txn_3, + input wire rxp_3, + input wire rxn_3, + + /* + * XGMII interfaces + */ + input wire [63:0] xgmii_txd_0, + input wire [7:0] xgmii_txc_0, + output wire [63:0] xgmii_rxd_0, + output wire [7:0] xgmii_rxc_0, + input wire [63:0] xgmii_txd_1, + input wire [7:0] xgmii_txc_1, + output wire [63:0] xgmii_rxd_1, + output wire [7:0] xgmii_rxc_1, + input wire [63:0] xgmii_txd_2, + input wire [7:0] xgmii_txc_2, + output wire [63:0] xgmii_rxd_2, + output wire [7:0] xgmii_rxc_2, + input wire [63:0] xgmii_txd_3, + input wire [7:0] xgmii_txc_3, + output wire [63:0] xgmii_rxd_3, + output wire [7:0] xgmii_rxc_3, + + /* + * Control + */ + input wire tx_powerdown_0, + input wire rx_powerdown_0, + input wire tx_powerdown_1, + input wire rx_powerdown_1, + input wire tx_powerdown_2, + input wire rx_powerdown_2, + input wire tx_powerdown_3, + input wire rx_powerdown_3 +); + +wire [63:0] gth_txd_0; +wire [7:0] gth_txc_0; +wire [63:0] gth_rxd_0; +wire [7:0] gth_rxc_0; +wire [63:0] gth_txd_1; +wire [7:0] gth_txc_1; +wire [63:0] gth_rxd_1; +wire [7:0] gth_rxc_1; +wire [63:0] gth_txd_2; +wire [7:0] gth_txc_2; +wire [63:0] gth_rxd_2; +wire [7:0] gth_rxc_2; +wire [63:0] gth_txd_3; +wire [7:0] gth_txc_3; +wire [63:0] gth_rxd_3; +wire [7:0] gth_rxc_3; + +wire mgmt_rd; +wire mgmt_wr; +wire mgmt_rdack; +wire [20:0] mgmt_addr; +wire [15:0] mgmt_rddata; +wire [15:0] mgmt_wrdata; + +wire mgmt_rd0; +wire mgmt_wr0; +wire [20:0] mgmt_addr0; +wire [15:0] mgmt_wrdata0; + +wire mgmt_req0; +wire mgmt_gnt0; + +wire mgmt_rd1; +wire mgmt_wr1; +wire [20:0] mgmt_addr1; +wire [15:0] mgmt_wrdata1; + +wire mgmt_req1; +wire mgmt_gnt1; + +wire mgmt_rd2; +wire mgmt_wr2; +wire [20:0] mgmt_addr2; +wire [15:0] mgmt_wrdata2; + +wire mgmt_req2; +wire mgmt_gnt2; + +wire mgmt_rd3; +wire mgmt_wr3; +wire [20:0] mgmt_addr3; +wire [15:0] mgmt_wrdata3; + +wire mgmt_req3; +wire mgmt_gnt3; + +// clocking +wire refclk; + +IBUFDS_GTHE1 refclk_ibufds_inst +( + .I(refclk_p), + .IB(refclk_n), + .O(refclk) +); + +wire rx_clk_0; +wire rx_clk_0_buf; +wire rx_clk_1; +wire rx_clk_1_buf; +wire rx_clk_2; +wire rx_clk_2_buf; +wire rx_clk_3; +wire rx_clk_3_buf; + +BUFR #( + .SIM_DEVICE("VIRTEX6") +) +rx_clk_0_buf_inst +( + .CE(1'b1), + .CLR(1'b0), + .I(rx_clk_0), + .O(rx_clk_0_buf) +); + +BUFR #( + .SIM_DEVICE("VIRTEX6") +) +rx_clk_1_buf_inst +( + .CE(1'b1), + .CLR(1'b0), + .I(rx_clk_1), + .O(rx_clk_1_buf) +); + +BUFG rx_clk_2_buf_inst +( + .I(rx_clk_2), + .O(rx_clk_2_buf) +); + +BUFG rx_clk_3_buf_inst +( + .I(rx_clk_3), + .O(rx_clk_3_buf) +); + +wire tx_resetdone_0; +wire rx_resetdone_0; +wire tx_resetdone_1; +wire rx_resetdone_1; +wire tx_resetdone_2; +wire rx_resetdone_2; +wire tx_resetdone_3; +wire rx_resetdone_3; + +wire resetdone_0 = tx_resetdone_0 & rx_resetdone_0; +wire resetdone_1 = tx_resetdone_1 & rx_resetdone_1; +wire resetdone_2 = tx_resetdone_2 & rx_resetdone_2; +wire resetdone_3 = tx_resetdone_3 & rx_resetdone_3; + +reg gth_reset_done_reg = 0; + +assign gth_reset_done = gth_reset_done_reg; + +// register overall reset done output +always @(posedge clk156) begin + gth_reset_done_reg <= resetdone_0 & resetdone_1 & resetdone_2 & resetdone_3; +end + +wire disable_drp_mgmt; + +wire disable_drp = gth_reset_done & disable_drp_mgmt; + +wire lane_sel = {mgmt_gnt3, mgmt_gnt2, mgmt_gnt1, mgmt_gnt0}; + +// GTH quad wrapper +ten_gig_eth_pcs_pma_v2_6_v6gth_wrapper_QUAD # +( + // Simulation attributes + .QUAD_SIM_GTHRESET_SPEEDUP(0) +) +gth_inst +( + //------------------------------- Resetdone -------------------------------- + .TX_PCS_RESETDONE0_OUT (tx_resetdone_0), + .RX_PCS_CDR_RESETDONE0_OUT (rx_resetdone_0), + .TX_PCS_RESETDONE1_OUT (tx_resetdone_1), + .RX_PCS_CDR_RESETDONE1_OUT (rx_resetdone_1), + .TX_PCS_RESETDONE2_OUT (tx_resetdone_2), + .RX_PCS_CDR_RESETDONE2_OUT (rx_resetdone_2), + .TX_PCS_RESETDONE3_OUT (tx_resetdone_3), + .RX_PCS_CDR_RESETDONE3_OUT (rx_resetdone_3), + //------------------------------- Initdone --------------------------------- + .GTHINITDONE_OUT (), + //------------------------------- PCS Resets ------------------------------- + .TX_PCS_RESET0_IN (1'b0), + .RX_PCS_CDR_RESET0_IN (1'b0), + .TX_PCS_RESET1_IN (1'b0), + .RX_PCS_CDR_RESET1_IN (1'b0), + .TX_PCS_RESET2_IN (1'b0), + .RX_PCS_CDR_RESET2_IN (1'b0), + .TX_PCS_RESET3_IN (1'b0), + .RX_PCS_CDR_RESET3_IN (1'b0), + //------------------------------- Powerdown -------------------------------- + .POWERDOWN0_IN (1'b0), + .POWERDOWN1_IN (1'b0), + .POWERDOWN2_IN (1'b0), + .POWERDOWN3_IN (1'b0), + .RXPOWERDOWN0_IN ({rx_powerdown_0 | (~gth_reset_done), 1'b0}), + .RXPOWERDOWN1_IN ({rx_powerdown_1 | (~gth_reset_done), 1'b0}), + .RXPOWERDOWN2_IN ({rx_powerdown_2 | (~gth_reset_done), 1'b0}), + .RXPOWERDOWN3_IN ({rx_powerdown_3 | (~gth_reset_done), 1'b0}), + .TXPOWERDOWN0_IN ({tx_powerdown_0 | (~gth_reset_done), 1'b0}), + .TXPOWERDOWN1_IN ({tx_powerdown_1 | (~gth_reset_done), 1'b0}), + .TXPOWERDOWN2_IN ({tx_powerdown_2 | (~gth_reset_done), 1'b0}), + .TXPOWERDOWN3_IN ({tx_powerdown_3 | (~gth_reset_done), 1'b0}), + //----------------------------- Receive Ports ------------------------------ + .RXBUFRESET0_IN (1'b0), + .RXBUFRESET1_IN (1'b0), + .RXBUFRESET2_IN (1'b0), + .RXBUFRESET3_IN (1'b0), + .RXCODEERR0_OUT (), + .RXCODEERR1_OUT (), + .RXCODEERR2_OUT (), + .RXCODEERR3_OUT (), + .RXCTRL0_OUT (gth_rxc_0), + .RXCTRL1_OUT (gth_rxc_1), + .RXCTRL2_OUT (gth_rxc_2), + .RXCTRL3_OUT (gth_rxc_3), + .RXCTRLACK0_OUT (), + .RXCTRLACK1_OUT (), + .RXCTRLACK2_OUT (), + .RXCTRLACK3_OUT (), + .RXDATA0_OUT (gth_rxd_0), + .RXDATA1_OUT (gth_rxd_1), + .RXDATA2_OUT (gth_rxd_2), + .RXDATA3_OUT (gth_rxd_3), + .RXN0_IN (rxn_0), + .RXN1_IN (rxn_1), + .RXN2_IN (rxn_2), + .RXN3_IN (rxn_3), + .RXP0_IN (rxp_0), + .RXP1_IN (rxp_1), + .RXP2_IN (rxp_2), + .RXP3_IN (rxp_3), + .RXUSERCLKIN0_IN (rx_clk_0_buf), + .RXUSERCLKIN1_IN (rx_clk_1_buf), + .RXUSERCLKIN2_IN (rx_clk_2_buf), + .RXUSERCLKIN3_IN (rx_clk_3_buf), + .RXUSERCLKOUT0_OUT (rx_clk_0), + .RXUSERCLKOUT1_OUT (rx_clk_1), + .RXUSERCLKOUT2_OUT (rx_clk_2), + .RXUSERCLKOUT3_OUT (rx_clk_3), + //----------- Shared Ports - Dynamic Reconfiguration Port () ------------ + .DADDR_IN (16'h0000), + .DCLK_IN (dclk), + .DEN_IN (1'b0), + .DI_IN (16'h0000), + .DISABLEDRP_IN (disable_drp), + .DRDY_OUT (), + .DRPDO_OUT (), + .DWE_IN (1'b0), + //-------------------------- Shared Ports - Other -------------------------- + .TSTREFCLKFAB_OUT (), + .TSTREFCLKOUT_OUT (), + .GTHINIT_IN (1'b0), + .GTHRESET_IN (gth_reset), + .MGMTPCSLANESEL_IN (lane_sel), + .MGMTPCSMMDADDR_IN (mgmt_addr[20:16]), + .MGMTPCSRDACK_OUT (mgmt_rdack), + .MGMTPCSRDDATA_OUT (mgmt_rddata), + .MGMTPCSREGADDR_IN (mgmt_addr[15:0]), + .MGMTPCSREGRD_IN (mgmt_rd), + .MGMTPCSREGWR_IN (mgmt_wr), + .MGMTPCSWRDATA_IN (mgmt_wrdata), + .REFCLK_IN (refclk), + //----------------------------- Transmit Ports ----------------------------- + .TXBUFRESET0_IN (1'b0), + .TXBUFRESET1_IN (1'b0), + .TXBUFRESET2_IN (1'b0), + .TXBUFRESET3_IN (1'b0), + .TXCTRL0_IN (gth_txc_0), + .TXCTRL1_IN (gth_txc_1), + .TXCTRL2_IN (gth_txc_2), + .TXCTRL3_IN (gth_txc_3), + .TXCTRLACK0_OUT (), + .TXCTRLACK1_OUT (), + .TXCTRLACK2_OUT (), + .TXCTRLACK3_OUT (), + .TXDATA0_IN (gth_txd_0), + .TXDATA1_IN (gth_txd_1), + .TXDATA2_IN (gth_txd_2), + .TXDATA3_IN (gth_txd_3), + .TXN0_OUT (txn_0), + .TXN1_OUT (txn_1), + .TXN2_OUT (txn_2), + .TXN3_OUT (txn_3), + .TXP0_OUT (txp_0), + .TXP1_OUT (txp_1), + .TXP2_OUT (txp_2), + .TXP3_OUT (txp_3), + .TXUSERCLKIN0_IN (clk156), + .TXUSERCLKIN1_IN (clk156), + .TXUSERCLKIN2_IN (clk156), + .TXUSERCLKIN3_IN (clk156), + .TXUSERCLKOUT0_OUT (txclk156), + .TXUSERCLKOUT1_OUT (), + .TXUSERCLKOUT2_OUT (), + .TXUSERCLKOUT3_OUT () +); + +wire [535:0] configuration_vector; + +assign configuration_vector[14:1] = 0; +assign configuration_vector[79:17] = 0; +assign configuration_vector[109:84] = 0; +assign configuration_vector[175:170] = 0; +assign configuration_vector[239:234] = 0; +assign configuration_vector[269:246] = 0; +assign configuration_vector[511:272] = 0; +assign configuration_vector[515:513] = 0; +assign configuration_vector[517:517] = 0; +assign configuration_vector[0] = 0; // pma_loopback; +assign configuration_vector[15] = 0; // pma_reset; +assign configuration_vector[16] = 0; // global_tx_disable; +assign configuration_vector[83:80] = 0; // pma_vs_loopback; +assign configuration_vector[110] = 0; // pcs_loopback; +assign configuration_vector[111] = 0; // pcs_reset; +assign configuration_vector[169:112] = 0; // test_patt_a; +assign configuration_vector[233:176] = 0; // test_patt_b; +assign configuration_vector[240] = 0; // data_patt_sel; +assign configuration_vector[241] = 0; // test_patt_sel; +assign configuration_vector[242] = 0; // rx_test_patt_en; +assign configuration_vector[243] = 0; // tx_test_patt_en; +assign configuration_vector[244] = 0; // prbs31_tx_en; +assign configuration_vector[245] = 0; // prbs31_rx_en; +assign configuration_vector[271:270] = 0; // pcs_vs_loopback; +assign configuration_vector[512] = 0; // set_pma_link_status; +assign configuration_vector[516] = 0; // set_pcs_link_status; +assign configuration_vector[518] = 0; // clear_pcs_status2; +assign configuration_vector[519] = 0; // clear_test_patt_err_count; +assign configuration_vector[535:520] = 0; + +ten_gig_eth_pcs_pma_v2_6 +ten_gig_eth_pcs_pma_core_inst_0 +( + .reset(rst156), + .clk156(clk156), + .rxclk156(rx_clk_0_buf), + .xgmii_txd(xgmii_txd_0), + .xgmii_txc(xgmii_txc_0), + .xgmii_rxd(xgmii_rxd_0), + .xgmii_rxc(xgmii_rxc_0), + .configuration_vector(configuration_vector), + .status_vector(), + .dclk(dclk), + .mgmt_req(mgmt_req0), + .mgmt_gnt(mgmt_gnt0), + .mgmt_rd_out(mgmt_rd0), + .mgmt_wr_out(mgmt_wr0), + .mgmt_addr_out(mgmt_addr0), + .mgmt_rdack_in(mgmt_rdack), + .mgmt_rddata_in(mgmt_rddata), + .mgmt_wrdata_out(mgmt_wrdata0), + .gt_txd(gth_txd_0), + .gt_txc(gth_txc_0), + .gt_rxd(gth_rxd_0), + .gt_rxc(gth_rxc_0), + .signal_detect(1'b1), + .tx_fault(1'b0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_v2_6 +ten_gig_eth_pcs_pma_core_inst_1 +( + .reset(rst156), + .clk156(clk156), + .rxclk156(rx_clk_1_buf), + .xgmii_txd(xgmii_txd_1), + .xgmii_txc(xgmii_txc_1), + .xgmii_rxd(xgmii_rxd_1), + .xgmii_rxc(xgmii_rxc_1), + .configuration_vector(configuration_vector), + .status_vector(), + .dclk(dclk), + .mgmt_req(mgmt_req1), + .mgmt_gnt(mgmt_gnt1), + .mgmt_rd_out(mgmt_rd1), + .mgmt_wr_out(mgmt_wr1), + .mgmt_addr_out(mgmt_addr1), + .mgmt_rdack_in(mgmt_rdack), + .mgmt_rddata_in(mgmt_rddata), + .mgmt_wrdata_out(mgmt_wrdata1), + .gt_txd(gth_txd_1), + .gt_txc(gth_txc_1), + .gt_rxd(gth_rxd_1), + .gt_rxc(gth_rxc_1), + .signal_detect(1'b1), + .tx_fault(1'b0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_v2_6 +ten_gig_eth_pcs_pma_core_inst_2 +( + .reset(rst156), + .clk156(clk156), + .rxclk156(rx_clk_2_buf), + .xgmii_txd(xgmii_txd_2), + .xgmii_txc(xgmii_txc_2), + .xgmii_rxd(xgmii_rxd_2), + .xgmii_rxc(xgmii_rxc_2), + .configuration_vector(configuration_vector), + .status_vector(), + .dclk(dclk), + .mgmt_req(mgmt_req2), + .mgmt_gnt(mgmt_gnt2), + .mgmt_rd_out(mgmt_rd2), + .mgmt_wr_out(mgmt_wr2), + .mgmt_addr_out(mgmt_addr2), + .mgmt_rdack_in(mgmt_rdack), + .mgmt_rddata_in(mgmt_rddata), + .mgmt_wrdata_out(mgmt_wrdata2), + .gt_txd(gth_txd_2), + .gt_txc(gth_txc_2), + .gt_rxd(gth_rxd_2), + .gt_rxc(gth_rxc_2), + .signal_detect(1'b1), + .tx_fault(1'b0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_v2_6 +ten_gig_eth_pcs_pma_core_inst_3 +( + .reset(rst156), + .clk156(clk156), + .rxclk156(rx_clk_3_buf), + .xgmii_txd(xgmii_txd_3), + .xgmii_txc(xgmii_txc_3), + .xgmii_rxd(xgmii_rxd_3), + .xgmii_rxc(xgmii_rxc_3), + .configuration_vector(configuration_vector), + .status_vector(), + .dclk(dclk), + .mgmt_req(mgmt_req3), + .mgmt_gnt(mgmt_gnt3), + .mgmt_rd_out(mgmt_rd3), + .mgmt_wr_out(mgmt_wr3), + .mgmt_addr_out(mgmt_addr3), + .mgmt_rdack_in(mgmt_rdack), + .mgmt_rddata_in(mgmt_rddata), + .mgmt_wrdata_out(mgmt_wrdata3), + .gt_txd(gth_txd_3), + .gt_txc(gth_txc_3), + .gt_rxd(gth_rxd_3), + .gt_rxc(gth_rxc_3), + .signal_detect(1'b1), + .tx_fault(1'b0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_v2_6_management_arbiter +mgmt_arb_inst +( + .dclk(dclk), + .reset(dclk_reset), + + .mgmt_rd0(mgmt_rd0), + .mgmt_wr0(mgmt_wr0), + .mgmt_addr0(mgmt_addr0), + .mgmt_wrdata0(mgmt_wrdata0), + + .mgmt_req0(mgmt_req0), + .mgmt_gnt0(mgmt_gnt0), + + .mgmt_rd1(mgmt_rd1), + .mgmt_wr1(mgmt_wr1), + .mgmt_addr1(mgmt_addr1), + .mgmt_wrdata1(mgmt_wrdata1), + + .mgmt_req1(mgmt_req1), + .mgmt_gnt1(mgmt_gnt1), + + .mgmt_rd2(mgmt_rd2), + .mgmt_wr2(mgmt_wr2), + .mgmt_addr2(mgmt_addr2), + .mgmt_wrdata2(mgmt_wrdata2), + + .mgmt_req2(mgmt_req2), + .mgmt_gnt2(mgmt_gnt2), + + .mgmt_rd3(mgmt_rd3), + .mgmt_wr3(mgmt_wr3), + .mgmt_addr3(mgmt_addr3), + .mgmt_wrdata3(mgmt_wrdata3), + + .mgmt_req3(mgmt_req3), + .mgmt_gnt3(mgmt_gnt3), + + .mgmt_rd(mgmt_rd), + .mgmt_wr(mgmt_wr), + .mgmt_addr(mgmt_addr), + .mgmt_wrdata(mgmt_wrdata), + + .drp_req(1'b0), + .drp_gnt(), + + .disable_drp(disable_drp_mgmt) +); + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/fpga.v b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/fpga.v new file mode 100644 index 000000000..66f1df8d7 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/fpga.v @@ -0,0 +1,1131 @@ +/* + +Copyright (c) 2016-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. + +*/ + +module fpga ( + /* + * Clock: 50MHz + */ + input wire sys_clk, + /* + * Clock: 200MHz + */ + //input wire clk_ddr3_p, + //input wire clk_ddr3_n, + /* + * Clock: User + */ + //input wire clk_usr_p, + //input wire clk_usr_pr_n, + /* + * Reset: Push button, active low + */ + input wire reset_n, + /* + * GPIO + */ + input wire [1:0] sw, + input wire [3:0] jp, + output wire [3:0] led, + /* + * Silicon Labs CP2102 USB UART + */ + output wire uart_rst, + input wire uart_suspend, + output wire uart_ri, + output wire uart_dcd, + input wire uart_dtr, + output wire uart_dsr, + input wire uart_txd, + output wire uart_rxd, + input wire uart_rts, + output wire uart_cts, + /* + * Clock muxes + */ + inout wire clk_gth_scl, + inout wire clk_gth_sda, + output wire clk_gth_rst_n, + input wire clk_gthl_alm, + input wire clk_gthl_lol, + input wire clk_gthr_alm, + input wire clk_gthr_lol, + /* + * AirMax I/O + */ + output wire amh_right_mdc, + inout wire amh_right_mdio, + output wire amh_right_phy_rst_n, + output wire amh_left_mdc, + inout wire amh_left_mdio, + output wire amh_left_phy_rst_n, + /* + * 10G Ethernet + */ + input wire gth_quad_A_refclk_p, + input wire gth_quad_A_refclk_n, + output wire gth_quad_A_txp_0, + output wire gth_quad_A_txn_0, + input wire gth_quad_A_rxp_0, + input wire gth_quad_A_rxn_0, + output wire gth_quad_A_txp_1, + output wire gth_quad_A_txn_1, + input wire gth_quad_A_rxp_1, + input wire gth_quad_A_rxn_1, + output wire gth_quad_A_txp_2, + output wire gth_quad_A_txn_2, + input wire gth_quad_A_rxp_2, + input wire gth_quad_A_rxn_2, + output wire gth_quad_A_txp_3, + output wire gth_quad_A_txn_3, + input wire gth_quad_A_rxp_3, + input wire gth_quad_A_rxn_3, + input wire gth_quad_B_refclk_p, + input wire gth_quad_B_refclk_n, + output wire gth_quad_B_txp_0, + output wire gth_quad_B_txn_0, + input wire gth_quad_B_rxp_0, + input wire gth_quad_B_rxn_0, + output wire gth_quad_B_txp_1, + output wire gth_quad_B_txn_1, + input wire gth_quad_B_rxp_1, + input wire gth_quad_B_rxn_1, + output wire gth_quad_B_txp_2, + output wire gth_quad_B_txn_2, + input wire gth_quad_B_rxp_2, + input wire gth_quad_B_rxn_2, + output wire gth_quad_B_txp_3, + output wire gth_quad_B_txn_3, + input wire gth_quad_B_rxp_3, + input wire gth_quad_B_rxn_3, + input wire gth_quad_C_refclk_p, + input wire gth_quad_C_refclk_n, + output wire gth_quad_C_txp_0, + output wire gth_quad_C_txn_0, + input wire gth_quad_C_rxp_0, + input wire gth_quad_C_rxn_0, + output wire gth_quad_C_txp_1, + output wire gth_quad_C_txn_1, + input wire gth_quad_C_rxp_1, + input wire gth_quad_C_rxn_1, + output wire gth_quad_C_txp_2, + output wire gth_quad_C_txn_2, + input wire gth_quad_C_rxp_2, + input wire gth_quad_C_rxn_2, + output wire gth_quad_C_txp_3, + output wire gth_quad_C_txn_3, + input wire gth_quad_C_rxp_3, + input wire gth_quad_C_rxn_3, + input wire gth_quad_D_refclk_p, + input wire gth_quad_D_refclk_n, + output wire gth_quad_D_txp_0, + output wire gth_quad_D_txn_0, + input wire gth_quad_D_rxp_0, + input wire gth_quad_D_rxn_0, + output wire gth_quad_D_txp_1, + output wire gth_quad_D_txn_1, + input wire gth_quad_D_rxp_1, + input wire gth_quad_D_rxn_1, + output wire gth_quad_D_txp_2, + output wire gth_quad_D_txn_2, + input wire gth_quad_D_rxp_2, + input wire gth_quad_D_rxn_2, + output wire gth_quad_D_txp_3, + output wire gth_quad_D_txn_3, + input wire gth_quad_D_rxp_3, + input wire gth_quad_D_rxn_3, + input wire gth_quad_E_refclk_p, + input wire gth_quad_E_refclk_n, + output wire gth_quad_E_txp_0, + output wire gth_quad_E_txn_0, + input wire gth_quad_E_rxp_0, + input wire gth_quad_E_rxn_0, + output wire gth_quad_E_txp_1, + output wire gth_quad_E_txn_1, + input wire gth_quad_E_rxp_1, + input wire gth_quad_E_rxn_1, + output wire gth_quad_E_txp_2, + output wire gth_quad_E_txn_2, + input wire gth_quad_E_rxp_2, + input wire gth_quad_E_rxn_2, + output wire gth_quad_E_txp_3, + output wire gth_quad_E_txn_3, + input wire gth_quad_E_rxp_3, + input wire gth_quad_E_rxn_3, + input wire gth_quad_F_refclk_p, + input wire gth_quad_F_refclk_n, + output wire gth_quad_F_txp_0, + output wire gth_quad_F_txn_0, + input wire gth_quad_F_rxp_0, + input wire gth_quad_F_rxn_0, + output wire gth_quad_F_txp_1, + output wire gth_quad_F_txn_1, + input wire gth_quad_F_rxp_1, + input wire gth_quad_F_rxn_1, + output wire gth_quad_F_txp_2, + output wire gth_quad_F_txn_2, + input wire gth_quad_F_rxp_2, + input wire gth_quad_F_rxn_2, + output wire gth_quad_F_txp_3, + output wire gth_quad_F_txn_3, + input wire gth_quad_F_rxp_3, + input wire gth_quad_F_rxn_3 +); + +/* + * Clock: 50MHz + */ +wire sys_clk_ibufg; +wire sys_clk_int; + +/* + * Clock: 156.25 MHz + */ +wire clk_156mhz; + +/* + * Synchronous reset + */ +wire sys_rst; +wire rst_156mhz; + +/* + * GPIO + */ +wire [1:0] sw_int; +wire [3:0] jp_int; +wire [3:0] led_int; + +/* + * Silicon Labs CP2102 USB UART + */ +wire uart_sys_rst; +wire uart_suspend_int; +wire uart_ri_int; +wire uart_dcd_int; +wire uart_dtr_int; +wire uart_dsr_int; +wire uart_txd_int; +wire uart_rxd_int; +wire uart_rts_int; +wire uart_cts_int; + +/* + * Clock muxes + */ +wire clk_gth_scl_i; +wire clk_gth_scl_o; +wire clk_gth_scl_t; +wire clk_gth_sda_i; +wire clk_gth_sda_o; +wire clk_gth_sda_t; +wire clk_gthl_alm_int; +wire clk_gthl_lol_int; +wire clk_gthr_alm_int; +wire clk_gthr_lol_int; + +/* + * AirMax I/O + */ +wire amh_right_mdc_int; +wire amh_right_mdio_i_int; +wire amh_right_mdio_o_int; +wire amh_right_mdio_t_int; +wire amh_left_mdc_int; +wire amh_left_mdio_i_int; +wire amh_left_mdio_o_int; +wire amh_left_mdio_t_int; + +/* + * 10G Ethernet + */ +wire [63:0] eth_r0_txd; +wire [7:0] eth_r0_txc; +wire [63:0] eth_r0_rxd; +wire [7:0] eth_r0_rxc; +wire [63:0] eth_r1_txd; +wire [7:0] eth_r1_txc; +wire [63:0] eth_r1_rxd; +wire [7:0] eth_r1_rxc; +wire [63:0] eth_r2_txd; +wire [7:0] eth_r2_txc; +wire [63:0] eth_r2_rxd; +wire [7:0] eth_r2_rxc; +wire [63:0] eth_r3_txd; +wire [7:0] eth_r3_txc; +wire [63:0] eth_r3_rxd; +wire [7:0] eth_r3_rxc; +wire [63:0] eth_r4_txd; +wire [7:0] eth_r4_txc; +wire [63:0] eth_r4_rxd; +wire [7:0] eth_r4_rxc; +wire [63:0] eth_r5_txd; +wire [7:0] eth_r5_txc; +wire [63:0] eth_r5_rxd; +wire [7:0] eth_r5_rxc; +wire [63:0] eth_r6_txd; +wire [7:0] eth_r6_txc; +wire [63:0] eth_r6_rxd; +wire [7:0] eth_r6_rxc; +wire [63:0] eth_r7_txd; +wire [7:0] eth_r7_txc; +wire [63:0] eth_r7_rxd; +wire [7:0] eth_r7_rxc; +wire [63:0] eth_r8_txd; +wire [7:0] eth_r8_txc; +wire [63:0] eth_r8_rxd; +wire [7:0] eth_r8_rxc; +wire [63:0] eth_r9_txd; +wire [7:0] eth_r9_txc; +wire [63:0] eth_r9_rxd; +wire [7:0] eth_r9_rxc; +wire [63:0] eth_r10_txd; +wire [7:0] eth_r10_txc; +wire [63:0] eth_r10_rxd; +wire [7:0] eth_r10_rxc; +wire [63:0] eth_r11_txd; +wire [7:0] eth_r11_txc; +wire [63:0] eth_r11_rxd; +wire [7:0] eth_r11_rxc; +wire [63:0] eth_l0_txd; +wire [7:0] eth_l0_txc; +wire [63:0] eth_l0_rxd; +wire [7:0] eth_l0_rxc; +wire [63:0] eth_l1_txd; +wire [7:0] eth_l1_txc; +wire [63:0] eth_l1_rxd; +wire [7:0] eth_l1_rxc; +wire [63:0] eth_l2_txd; +wire [7:0] eth_l2_txc; +wire [63:0] eth_l2_rxd; +wire [7:0] eth_l2_rxc; +wire [63:0] eth_l3_txd; +wire [7:0] eth_l3_txc; +wire [63:0] eth_l3_rxd; +wire [7:0] eth_l3_rxc; +wire [63:0] eth_l4_txd; +wire [7:0] eth_l4_txc; +wire [63:0] eth_l4_rxd; +wire [7:0] eth_l4_rxc; +wire [63:0] eth_l5_txd; +wire [7:0] eth_l5_txc; +wire [63:0] eth_l5_rxd; +wire [7:0] eth_l5_rxc; +wire [63:0] eth_l6_txd; +wire [7:0] eth_l6_txc; +wire [63:0] eth_l6_rxd; +wire [7:0] eth_l6_rxc; +wire [63:0] eth_l7_txd; +wire [7:0] eth_l7_txc; +wire [63:0] eth_l7_rxd; +wire [7:0] eth_l7_rxc; +wire [63:0] eth_l8_txd; +wire [7:0] eth_l8_txc; +wire [63:0] eth_l8_rxd; +wire [7:0] eth_l8_rxc; +wire [63:0] eth_l9_txd; +wire [7:0] eth_l9_txc; +wire [63:0] eth_l9_rxd; +wire [7:0] eth_l9_rxc; +wire [63:0] eth_l10_txd; +wire [7:0] eth_l10_txc; +wire [63:0] eth_l10_rxd; +wire [7:0] eth_l10_rxc; +wire [63:0] eth_l11_txd; +wire [7:0] eth_l11_txc; +wire [63:0] eth_l11_rxd; +wire [7:0] eth_l11_rxc; + +// Clock buffering for 50 MHz sys_clk +IBUFG +sys_clk_ibufg_inst ( + .I(sys_clk), + .O(sys_clk_ibufg) +); + +BUFG +sys_clk_bufg_inst ( + .I(sys_clk_ibufg), + .O(sys_clk_int) +); + +// 156.25 MHz clock from GTH +wire txclk156; + +BUFG +clk156_bufg_inst ( + .I(txclk156), + .O(clk_156mhz) +); + +// Synchronize reset signal +sync_reset #( + .N(6) +) +sync_reset_inst ( + .clk(sys_clk_int), + .rst(~reset_n), + .sync_reset_out(sys_rst) +); + +sync_signal #( + .WIDTH(4), + .N(2) +) +sync_signal_50mhz_inst ( + .clk(sys_clk_int), + .in({clk_gthl_alm, + clk_gthl_lol, + clk_gthr_alm, + clk_gthr_lol}), + .out({clk_gthl_alm_int, + clk_gthl_lol_int, + clk_gthr_alm_int, + clk_gthr_lol_int}) +); + +sync_signal #( + .WIDTH(4), + .N(2) +) +sync_signal_156mhz_inst ( + .clk(clk_156mhz), + .in({uart_suspend, + uart_dtr, + uart_txd, + uart_rts}), + .out({uart_suspend_int, + uart_dtr_int, + uart_txd_int, + uart_rts_int}) +); + +// Debounce switch inputs +debounce_switch #( + .WIDTH(6), + .N(4), + .RATE(50000) +) +debounce_switch_inst ( + .clk(sys_clk_int), + .rst(sys_rst), + .in({sw, jp}), + .out({sw_int, jp_int}) +); + +// pass through outputs +assign led = led_int; + +assign uart_rst = uart_rst_int; +assign uart_ri = uart_ri_int; +assign uart_dcd = uart_dcd_int; +assign uart_dsr = uart_dsr_int; +assign uart_rxd = uart_rxd_int; +assign uart_cts = uart_cts_int; + +// clock mux I2C +assign clk_gth_scl_i = clk_gth_scl; +assign clk_gth_scl = clk_gth_scl_t ? 1'bz : clk_gth_scl_o; +assign clk_gth_sda_i = clk_gth_sda; +assign clk_gth_sda = clk_gth_sda_t ? 1'bz : clk_gth_sda_o; + +assign clk_gth_rst_n = ~sys_rst; + +wire [6:0] clk_gth_i2c_cmd_address; +wire clk_gth_i2c_cmd_start; +wire clk_gth_i2c_cmd_read; +wire clk_gth_i2c_cmd_write; +wire clk_gth_i2c_cmd_write_multiple; +wire clk_gth_i2c_cmd_stop; +wire clk_gth_i2c_cmd_valid; +wire clk_gth_i2c_cmd_ready; + +wire [7:0] clk_gth_i2c_data; +wire clk_gth_i2c_data_valid; +wire clk_gth_i2c_data_ready; +wire clk_gth_i2c_data_last; + +gth_i2c_init +clk_gth_i2c_init ( + .clk(sys_clk_int), + .rst(sys_rst), + .cmd_address(clk_gth_i2c_cmd_address), + .cmd_start(clk_gth_i2c_cmd_start), + .cmd_read(clk_gth_i2c_cmd_read), + .cmd_write(clk_gth_i2c_cmd_write), + .cmd_write_multiple(clk_gth_i2c_cmd_write_multiple), + .cmd_stop(clk_gth_i2c_cmd_stop), + .cmd_valid(clk_gth_i2c_cmd_valid), + .cmd_ready(clk_gth_i2c_cmd_ready), + .data_out(clk_gth_i2c_data), + .data_out_valid(clk_gth_i2c_data_valid), + .data_out_ready(clk_gth_i2c_data_ready), + .data_out_last(clk_gth_i2c_data_last), + .busy(), + .start(1) +); + +i2c_master +clk_gth_i2c_master ( + .clk(sys_clk_int), + .rst(sys_rst), + .cmd_address(clk_gth_i2c_cmd_address), + .cmd_start(clk_gth_i2c_cmd_start), + .cmd_read(clk_gth_i2c_cmd_read), + .cmd_write(clk_gth_i2c_cmd_write), + .cmd_write_multiple(clk_gth_i2c_cmd_write_multiple), + .cmd_stop(clk_gth_i2c_cmd_stop), + .cmd_valid(clk_gth_i2c_cmd_valid), + .cmd_ready(clk_gth_i2c_cmd_ready), + .data_in(clk_gth_i2c_data), + .data_in_valid(clk_gth_i2c_data_valid), + .data_in_ready(clk_gth_i2c_data_ready), + .data_in_last(clk_gth_i2c_data_last), + .data_out(), + .data_out_valid(), + .data_out_ready(1), + .data_out_last(), + .scl_i(clk_gth_scl_i), + .scl_o(clk_gth_scl_o), + .scl_t(clk_gth_scl_t), + .sda_i(clk_gth_sda_i), + .sda_o(clk_gth_sda_o), + .sda_t(clk_gth_sda_t), + .busy(), + .bus_control(), + .bus_active(), + .missed_ack(), + .prescale(312), + .stop_on_idle(1) +); + +// reset logic +wire gth_reset; + +wire gth_reset_done_A; +wire gth_reset_done_B; +wire gth_reset_done_C; +wire gth_reset_done_D; +wire gth_reset_done_E; +wire gth_reset_done_F; + +wire gth_reset_done = gth_reset_done_A & gth_reset_done_B & gth_reset_done_C & gth_reset_done_D & gth_reset_done_E & gth_reset_done_F; + +wire clk_gth_ready = ~clk_gthl_lol_int & ~clk_gthr_lol_int; + +sync_reset #( + .N(6) +) +sync_reset_gth_inst ( + .clk(sys_clk_int), + .rst(sys_rst | ~clk_gth_ready), + .sync_reset_out(gth_reset) +); + +sync_reset #( + .N(6) +) +sync_reset_156mhz_inst ( + .clk(clk_156mhz), + .rst(gth_reset | ~gth_reset_done), + .sync_reset_out(rst_156mhz) +); + +assign amh_right_phy_rst_n = ~rst_156mhz; +assign amh_left_phy_rst_n = ~rst_156mhz; + +// AirMax I/O + +assign amh_right_mdc = amh_right_mdc_int; + +assign amh_right_mdio_i_int = amh_right_mdio; +assign amh_right_mdio = amh_right_mdio_t_int ? 1'bz : amh_right_mdio_o_int; + +assign amh_left_mdc = amh_left_mdc_int; + +assign amh_left_mdio_i_int = amh_left_mdio; +assign amh_left_mdio = amh_left_mdio_t_int ? 1'bz : amh_left_mdio_o_int; + +// 10G Ethernet PCS/PMA + +// Quad A X0Y0 +eth_gth_phy_quad +eth_gth_phy_quad_A_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(txclk156), // pickoff one transmit clock for 156.25 MHz core clock + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_A), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_A_refclk_p), + .refclk_n(gth_quad_A_refclk_n), + .txn_0(gth_quad_A_txn_0), + .txp_0(gth_quad_A_txp_0), + .rxn_0(gth_quad_A_rxn_0), + .rxp_0(gth_quad_A_rxp_0), + .txn_1(gth_quad_A_txn_1), + .txp_1(gth_quad_A_txp_1), + .rxn_1(gth_quad_A_rxn_1), + .rxp_1(gth_quad_A_rxp_1), + .txn_2(gth_quad_A_txn_2), + .txp_2(gth_quad_A_txp_2), + .rxn_2(gth_quad_A_rxn_2), + .rxp_2(gth_quad_A_rxp_2), + .txn_3(gth_quad_A_txn_3), + .txp_3(gth_quad_A_txp_3), + .rxn_3(gth_quad_A_rxn_3), + .rxp_3(gth_quad_A_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_r0_txd), + .xgmii_txc_0(eth_r0_txc), + .xgmii_rxd_0(eth_r0_rxd), + .xgmii_rxc_0(eth_r0_rxc), + .xgmii_txd_1(eth_r2_txd), + .xgmii_txc_1(eth_r2_txc), + .xgmii_rxd_1(eth_r2_rxd), + .xgmii_rxc_1(eth_r2_rxc), + .xgmii_txd_2(eth_r4_txd), + .xgmii_txc_2(eth_r4_txc), + .xgmii_rxd_2(eth_r4_rxd), + .xgmii_rxc_2(eth_r4_rxc), + .xgmii_txd_3(eth_r6_txd), + .xgmii_txc_3(eth_r6_txc), + .xgmii_rxd_3(eth_r6_rxd), + .xgmii_rxc_3(eth_r6_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + +// Quad B X0Y1 +eth_gth_phy_quad +eth_gth_phy_quad_B_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(), + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_B), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_B_refclk_p), + .refclk_n(gth_quad_B_refclk_n), + .txn_0(gth_quad_B_txn_0), + .txp_0(gth_quad_B_txp_0), + .rxn_0(gth_quad_B_rxn_0), + .rxp_0(gth_quad_B_rxp_0), + .txn_1(gth_quad_B_txn_1), + .txp_1(gth_quad_B_txp_1), + .rxn_1(gth_quad_B_rxn_1), + .rxp_1(gth_quad_B_rxp_1), + .txn_2(gth_quad_B_txn_2), + .txp_2(gth_quad_B_txp_2), + .rxn_2(gth_quad_B_rxn_2), + .rxp_2(gth_quad_B_rxp_2), + .txn_3(gth_quad_B_txn_3), + .txp_3(gth_quad_B_txp_3), + .rxn_3(gth_quad_B_rxn_3), + .rxp_3(gth_quad_B_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_r3_txd), + .xgmii_txc_0(eth_r3_txc), + .xgmii_rxd_0(eth_r3_rxd), + .xgmii_rxc_0(eth_r3_rxc), + .xgmii_txd_1(eth_r5_txd), + .xgmii_txc_1(eth_r5_txc), + .xgmii_rxd_1(eth_r5_rxd), + .xgmii_rxc_1(eth_r5_rxc), + .xgmii_txd_2(eth_r1_txd), + .xgmii_txc_2(eth_r1_txc), + .xgmii_rxd_2(eth_r1_rxd), + .xgmii_rxc_2(eth_r1_rxc), + .xgmii_txd_3(eth_r7_txd), + .xgmii_txc_3(eth_r7_txc), + .xgmii_rxd_3(eth_r7_rxd), + .xgmii_rxc_3(eth_r7_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + +// Quad C X0Y2 +eth_gth_phy_quad +eth_gth_phy_quad_C_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(), + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_C), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_C_refclk_p), + .refclk_n(gth_quad_C_refclk_n), + .txn_0(gth_quad_C_txn_0), + .txp_0(gth_quad_C_txp_0), + .rxn_0(gth_quad_C_rxn_0), + .rxp_0(gth_quad_C_rxp_0), + .txn_1(gth_quad_C_txn_1), + .txp_1(gth_quad_C_txp_1), + .rxn_1(gth_quad_C_rxn_1), + .rxp_1(gth_quad_C_rxp_1), + .txn_2(gth_quad_C_txn_2), + .txp_2(gth_quad_C_txp_2), + .rxn_2(gth_quad_C_rxn_2), + .rxp_2(gth_quad_C_rxp_2), + .txn_3(gth_quad_C_txn_3), + .txp_3(gth_quad_C_txp_3), + .rxn_3(gth_quad_C_rxn_3), + .rxp_3(gth_quad_C_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_r8_txd), + .xgmii_txc_0(eth_r8_txc), + .xgmii_rxd_0(eth_r8_rxd), + .xgmii_rxc_0(eth_r8_rxc), + .xgmii_txd_1(eth_r9_txd), + .xgmii_txc_1(eth_r9_txc), + .xgmii_rxd_1(eth_r9_rxd), + .xgmii_rxc_1(eth_r9_rxc), + .xgmii_txd_2(eth_r11_txd), + .xgmii_txc_2(eth_r11_txc), + .xgmii_rxd_2(eth_r11_rxd), + .xgmii_rxc_2(eth_r11_rxc), + .xgmii_txd_3(eth_r10_txd), + .xgmii_txc_3(eth_r10_txc), + .xgmii_rxd_3(eth_r10_rxd), + .xgmii_rxc_3(eth_r10_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + +// Quad D X1Y0 +eth_gth_phy_quad +eth_gth_phy_quad_D_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(), + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_D), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_D_refclk_p), + .refclk_n(gth_quad_D_refclk_n), + .txn_0(gth_quad_D_txn_0), + .txp_0(gth_quad_D_txp_0), + .rxn_0(gth_quad_D_rxn_0), + .rxp_0(gth_quad_D_rxp_0), + .txn_1(gth_quad_D_txn_1), + .txp_1(gth_quad_D_txp_1), + .rxn_1(gth_quad_D_rxn_1), + .rxp_1(gth_quad_D_rxp_1), + .txn_2(gth_quad_D_txn_2), + .txp_2(gth_quad_D_txp_2), + .rxn_2(gth_quad_D_rxn_2), + .rxp_2(gth_quad_D_rxp_2), + .txn_3(gth_quad_D_txn_3), + .txp_3(gth_quad_D_txp_3), + .rxn_3(gth_quad_D_rxn_3), + .rxp_3(gth_quad_D_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_l0_txd), + .xgmii_txc_0(eth_l0_txc), + .xgmii_rxd_0(eth_l0_rxd), + .xgmii_rxc_0(eth_l0_rxc), + .xgmii_txd_1(eth_l2_txd), + .xgmii_txc_1(eth_l2_txc), + .xgmii_rxd_1(eth_l2_rxd), + .xgmii_rxc_1(eth_l2_rxc), + .xgmii_txd_2(eth_l4_txd), + .xgmii_txc_2(eth_l4_txc), + .xgmii_rxd_2(eth_l4_rxd), + .xgmii_rxc_2(eth_l4_rxc), + .xgmii_txd_3(eth_l6_txd), + .xgmii_txc_3(eth_l6_txc), + .xgmii_rxd_3(eth_l6_rxd), + .xgmii_rxc_3(eth_l6_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + +// Quad E X1Y1 +eth_gth_phy_quad +eth_gth_phy_quad_E_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(), + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_E), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_E_refclk_p), + .refclk_n(gth_quad_E_refclk_n), + .txn_0(gth_quad_E_txn_0), + .txp_0(gth_quad_E_txp_0), + .rxn_0(gth_quad_E_rxn_0), + .rxp_0(gth_quad_E_rxp_0), + .txn_1(gth_quad_E_txn_1), + .txp_1(gth_quad_E_txp_1), + .rxn_1(gth_quad_E_rxn_1), + .rxp_1(gth_quad_E_rxp_1), + .txn_2(gth_quad_E_txn_2), + .txp_2(gth_quad_E_txp_2), + .rxn_2(gth_quad_E_rxn_2), + .rxp_2(gth_quad_E_rxp_2), + .txn_3(gth_quad_E_txn_3), + .txp_3(gth_quad_E_txp_3), + .rxn_3(gth_quad_E_rxn_3), + .rxp_3(gth_quad_E_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_l3_txd), + .xgmii_txc_0(eth_l3_txc), + .xgmii_rxd_0(eth_l3_rxd), + .xgmii_rxc_0(eth_l3_rxc), + .xgmii_txd_1(eth_l5_txd), + .xgmii_txc_1(eth_l5_txc), + .xgmii_rxd_1(eth_l5_rxd), + .xgmii_rxc_1(eth_l5_rxc), + .xgmii_txd_2(eth_l1_txd), + .xgmii_txc_2(eth_l1_txc), + .xgmii_rxd_2(eth_l1_rxd), + .xgmii_rxc_2(eth_l1_rxc), + .xgmii_txd_3(eth_l7_txd), + .xgmii_txc_3(eth_l7_txc), + .xgmii_rxd_3(eth_l7_rxd), + .xgmii_rxc_3(eth_l7_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + +// Quad F X1Y2 +eth_gth_phy_quad +eth_gth_phy_quad_F_inst ( + /* + * Clock and reset + */ + .clk156(clk_156mhz), + .rst156(rst_156mhz), + .dclk(sys_clk_int), + .dclk_reset(sys_rst), + .txclk156(), + + .gth_reset(gth_reset), + .gth_reset_done(gth_reset_done_F), + + /* + * Transciever pins + */ + .refclk_p(gth_quad_F_refclk_p), + .refclk_n(gth_quad_F_refclk_n), + .txn_0(gth_quad_F_txn_0), + .txp_0(gth_quad_F_txp_0), + .rxn_0(gth_quad_F_rxn_0), + .rxp_0(gth_quad_F_rxp_0), + .txn_1(gth_quad_F_txn_1), + .txp_1(gth_quad_F_txp_1), + .rxn_1(gth_quad_F_rxn_1), + .rxp_1(gth_quad_F_rxp_1), + .txn_2(gth_quad_F_txn_2), + .txp_2(gth_quad_F_txp_2), + .rxn_2(gth_quad_F_rxn_2), + .rxp_2(gth_quad_F_rxp_2), + .txn_3(gth_quad_F_txn_3), + .txp_3(gth_quad_F_txp_3), + .rxn_3(gth_quad_F_rxn_3), + .rxp_3(gth_quad_F_rxp_3), + + /* + * XGMII interfaces + */ + .xgmii_txd_0(eth_l8_txd), + .xgmii_txc_0(eth_l8_txc), + .xgmii_rxd_0(eth_l8_rxd), + .xgmii_rxc_0(eth_l8_rxc), + .xgmii_txd_1(eth_l9_txd), + .xgmii_txc_1(eth_l9_txc), + .xgmii_rxd_1(eth_l9_rxd), + .xgmii_rxc_1(eth_l9_rxc), + .xgmii_txd_2(eth_l11_txd), + .xgmii_txc_2(eth_l11_txc), + .xgmii_rxd_2(eth_l11_rxd), + .xgmii_rxc_2(eth_l11_rxc), + .xgmii_txd_3(eth_l10_txd), + .xgmii_txc_3(eth_l10_txc), + .xgmii_rxd_3(eth_l10_rxd), + .xgmii_rxc_3(eth_l10_rxc), + + /* + * Control + */ + .tx_powerdown_0(1'b0), + .rx_powerdown_0(1'b0), + .tx_powerdown_1(1'b0), + .rx_powerdown_1(1'b0), + .tx_powerdown_2(1'b0), + .rx_powerdown_2(1'b0), + .tx_powerdown_3(1'b0), + .rx_powerdown_3(1'b0) +); + + +fpga_core +core_inst ( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + .clk(clk_156mhz), + .rst(rst_156mhz), + /* + * GPIO + */ + .sw(sw_int), + .jp(jp_int), + .led(led_int), + /* + * Silicon Labs CP2102 USB UART + */ + .uart_rst(uart_rst_int), + .uart_suspend(uart_suspend_int), + .uart_ri(uart_ri_int), + .uart_dcd(uart_dcd_int), + .uart_dtr(uart_dtr_int), + .uart_dsr(uart_dsr_int), + .uart_txd(uart_txd_int), + .uart_rxd(uart_rxd_int), + .uart_rts(uart_rts_int), + .uart_cts(uart_cts_int), + /* + * AirMax I/O + */ + .amh_right_mdc(amh_right_mdc_int), + .amh_right_mdio_i(amh_right_mdio_i_int), + .amh_right_mdio_o(amh_right_mdio_o_int), + .amh_right_mdio_t(amh_right_mdio_t_int), + .amh_left_mdc(amh_left_mdc_int), + .amh_left_mdio_i(amh_left_mdio_i_int), + .amh_left_mdio_o(amh_left_mdio_o_int), + .amh_left_mdio_t(amh_left_mdio_t_int), + /* + * 10G Ethernet XGMII + */ + .eth_r0_txd(eth_r0_txd), + .eth_r0_txc(eth_r0_txc), + .eth_r0_rxd(eth_r0_rxd), + .eth_r0_rxc(eth_r0_rxc), + .eth_r1_txd(eth_r1_txd), + .eth_r1_txc(eth_r1_txc), + .eth_r1_rxd(eth_r1_rxd), + .eth_r1_rxc(eth_r1_rxc), + .eth_r2_txd(eth_r2_txd), + .eth_r2_txc(eth_r2_txc), + .eth_r2_rxd(eth_r2_rxd), + .eth_r2_rxc(eth_r2_rxc), + .eth_r3_txd(eth_r3_txd), + .eth_r3_txc(eth_r3_txc), + .eth_r3_rxd(eth_r3_rxd), + .eth_r3_rxc(eth_r3_rxc), + .eth_r4_txd(eth_r4_txd), + .eth_r4_txc(eth_r4_txc), + .eth_r4_rxd(eth_r4_rxd), + .eth_r4_rxc(eth_r4_rxc), + .eth_r5_txd(eth_r5_txd), + .eth_r5_txc(eth_r5_txc), + .eth_r5_rxd(eth_r5_rxd), + .eth_r5_rxc(eth_r5_rxc), + .eth_r6_txd(eth_r6_txd), + .eth_r6_txc(eth_r6_txc), + .eth_r6_rxd(eth_r6_rxd), + .eth_r6_rxc(eth_r6_rxc), + .eth_r7_txd(eth_r7_txd), + .eth_r7_txc(eth_r7_txc), + .eth_r7_rxd(eth_r7_rxd), + .eth_r7_rxc(eth_r7_rxc), + .eth_r8_txd(eth_r8_txd), + .eth_r8_txc(eth_r8_txc), + .eth_r8_rxd(eth_r8_rxd), + .eth_r8_rxc(eth_r8_rxc), + .eth_r9_txd(eth_r9_txd), + .eth_r9_txc(eth_r9_txc), + .eth_r9_rxd(eth_r9_rxd), + .eth_r9_rxc(eth_r9_rxc), + .eth_r10_txd(eth_r10_txd), + .eth_r10_txc(eth_r10_txc), + .eth_r10_rxd(eth_r10_rxd), + .eth_r10_rxc(eth_r10_rxc), + .eth_r11_txd(eth_r11_txd), + .eth_r11_txc(eth_r11_txc), + .eth_r11_rxd(eth_r11_rxd), + .eth_r11_rxc(eth_r11_rxc), + .eth_l0_txd(eth_l0_txd), + .eth_l0_txc(eth_l0_txc), + .eth_l0_rxd(eth_l0_rxd), + .eth_l0_rxc(eth_l0_rxc), + .eth_l1_txd(eth_l1_txd), + .eth_l1_txc(eth_l1_txc), + .eth_l1_rxd(eth_l1_rxd), + .eth_l1_rxc(eth_l1_rxc), + .eth_l2_txd(eth_l2_txd), + .eth_l2_txc(eth_l2_txc), + .eth_l2_rxd(eth_l2_rxd), + .eth_l2_rxc(eth_l2_rxc), + .eth_l3_txd(eth_l3_txd), + .eth_l3_txc(eth_l3_txc), + .eth_l3_rxd(eth_l3_rxd), + .eth_l3_rxc(eth_l3_rxc), + .eth_l4_txd(eth_l4_txd), + .eth_l4_txc(eth_l4_txc), + .eth_l4_rxd(eth_l4_rxd), + .eth_l4_rxc(eth_l4_rxc), + .eth_l5_txd(eth_l5_txd), + .eth_l5_txc(eth_l5_txc), + .eth_l5_rxd(eth_l5_rxd), + .eth_l5_rxc(eth_l5_rxc), + .eth_l6_txd(eth_l6_txd), + .eth_l6_txc(eth_l6_txc), + .eth_l6_rxd(eth_l6_rxd), + .eth_l6_rxc(eth_l6_rxc), + .eth_l7_txd(eth_l7_txd), + .eth_l7_txc(eth_l7_txc), + .eth_l7_rxd(eth_l7_rxd), + .eth_l7_rxc(eth_l7_rxc), + .eth_l8_txd(eth_l8_txd), + .eth_l8_txc(eth_l8_txc), + .eth_l8_rxd(eth_l8_rxd), + .eth_l8_rxc(eth_l8_rxc), + .eth_l9_txd(eth_l9_txd), + .eth_l9_txc(eth_l9_txc), + .eth_l9_rxd(eth_l9_rxd), + .eth_l9_rxc(eth_l9_rxc), + .eth_l10_txd(eth_l10_txd), + .eth_l10_txc(eth_l10_txc), + .eth_l10_rxd(eth_l10_rxd), + .eth_l10_rxc(eth_l10_rxc), + .eth_l11_txd(eth_l11_txd), + .eth_l11_txc(eth_l11_txc), + .eth_l11_rxd(eth_l11_rxd), + .eth_l11_rxc(eth_l11_rxc) +); + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/fpga_core.v b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/fpga_core.v new file mode 100644 index 000000000..ccf2b23b7 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/fpga_core.v @@ -0,0 +1,458 @@ +/* + +Copyright (c) 2016-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 + +module fpga_core +( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + /* + * GPIO + */ + input wire [1:0] sw, + input wire [3:0] jp, + output wire [3:0] led, + /* + * Silicon Labs CP2102 USB UART + */ + output wire uart_rst, + input wire uart_suspend, + output wire uart_ri, + output wire uart_dcd, + input wire uart_dtr, + output wire uart_dsr, + input wire uart_txd, + output wire uart_rxd, + input wire uart_rts, + output wire uart_cts, + /* + * AirMax I/O + */ + output wire amh_right_mdc, + input wire amh_right_mdio_i, + output wire amh_right_mdio_o, + output wire amh_right_mdio_t, + output wire amh_left_mdc, + input wire amh_left_mdio_i, + output wire amh_left_mdio_o, + output wire amh_left_mdio_t, + /* + * 10G Ethernet + */ + output wire [63:0] eth_r0_txd, + output wire [7:0] eth_r0_txc, + input wire [63:0] eth_r0_rxd, + input wire [7:0] eth_r0_rxc, + output wire [63:0] eth_r1_txd, + output wire [7:0] eth_r1_txc, + input wire [63:0] eth_r1_rxd, + input wire [7:0] eth_r1_rxc, + output wire [63:0] eth_r2_txd, + output wire [7:0] eth_r2_txc, + input wire [63:0] eth_r2_rxd, + input wire [7:0] eth_r2_rxc, + output wire [63:0] eth_r3_txd, + output wire [7:0] eth_r3_txc, + input wire [63:0] eth_r3_rxd, + input wire [7:0] eth_r3_rxc, + output wire [63:0] eth_r4_txd, + output wire [7:0] eth_r4_txc, + input wire [63:0] eth_r4_rxd, + input wire [7:0] eth_r4_rxc, + output wire [63:0] eth_r5_txd, + output wire [7:0] eth_r5_txc, + input wire [63:0] eth_r5_rxd, + input wire [7:0] eth_r5_rxc, + output wire [63:0] eth_r6_txd, + output wire [7:0] eth_r6_txc, + input wire [63:0] eth_r6_rxd, + input wire [7:0] eth_r6_rxc, + output wire [63:0] eth_r7_txd, + output wire [7:0] eth_r7_txc, + input wire [63:0] eth_r7_rxd, + input wire [7:0] eth_r7_rxc, + output wire [63:0] eth_r8_txd, + output wire [7:0] eth_r8_txc, + input wire [63:0] eth_r8_rxd, + input wire [7:0] eth_r8_rxc, + output wire [63:0] eth_r9_txd, + output wire [7:0] eth_r9_txc, + input wire [63:0] eth_r9_rxd, + input wire [7:0] eth_r9_rxc, + output wire [63:0] eth_r10_txd, + output wire [7:0] eth_r10_txc, + input wire [63:0] eth_r10_rxd, + input wire [7:0] eth_r10_rxc, + output wire [63:0] eth_r11_txd, + output wire [7:0] eth_r11_txc, + input wire [63:0] eth_r11_rxd, + input wire [7:0] eth_r11_rxc, + output wire [63:0] eth_l0_txd, + output wire [7:0] eth_l0_txc, + input wire [63:0] eth_l0_rxd, + input wire [7:0] eth_l0_rxc, + output wire [63:0] eth_l1_txd, + output wire [7:0] eth_l1_txc, + input wire [63:0] eth_l1_rxd, + input wire [7:0] eth_l1_rxc, + output wire [63:0] eth_l2_txd, + output wire [7:0] eth_l2_txc, + input wire [63:0] eth_l2_rxd, + input wire [7:0] eth_l2_rxc, + output wire [63:0] eth_l3_txd, + output wire [7:0] eth_l3_txc, + input wire [63:0] eth_l3_rxd, + input wire [7:0] eth_l3_rxc, + output wire [63:0] eth_l4_txd, + output wire [7:0] eth_l4_txc, + input wire [63:0] eth_l4_rxd, + input wire [7:0] eth_l4_rxc, + output wire [63:0] eth_l5_txd, + output wire [7:0] eth_l5_txc, + input wire [63:0] eth_l5_rxd, + input wire [7:0] eth_l5_rxc, + output wire [63:0] eth_l6_txd, + output wire [7:0] eth_l6_txc, + input wire [63:0] eth_l6_rxd, + input wire [7:0] eth_l6_rxc, + output wire [63:0] eth_l7_txd, + output wire [7:0] eth_l7_txc, + input wire [63:0] eth_l7_rxd, + input wire [7:0] eth_l7_rxc, + output wire [63:0] eth_l8_txd, + output wire [7:0] eth_l8_txc, + input wire [63:0] eth_l8_rxd, + input wire [7:0] eth_l8_rxc, + output wire [63:0] eth_l9_txd, + output wire [7:0] eth_l9_txc, + input wire [63:0] eth_l9_rxd, + input wire [7:0] eth_l9_rxc, + output wire [63:0] eth_l10_txd, + output wire [7:0] eth_l10_txc, + input wire [63:0] eth_l10_rxd, + input wire [7:0] eth_l10_rxc, + output wire [63:0] eth_l11_txd, + output wire [7:0] eth_l11_txc, + input wire [63:0] eth_l11_rxd, + input wire [7:0] eth_l11_rxc +); + +// UART +assign uart_rst = 1'b1; +assign uart_txd = 1'b1; + +// AirMax I/O +assign amh_right_mdc = 1'b1; +assign amh_right_mdio_o = 1'b1; +assign amh_right_mdio_t = 1'b1; +assign amh_left_mdc = 1'b1; +assign amh_left_mdio_o = 1'b1; +assign amh_left_mdio_t = 1'b1; + +assign eth_l8_txd = 64'h0707070707070707; +assign eth_l8_txc = 8'hff; +assign eth_l9_txd = 64'h0707070707070707; +assign eth_l9_txc = 8'hff; +assign eth_l10_txd = 64'h0707070707070707; +assign eth_l10_txc = 8'hff; +//assign eth_l11_txd = 64'h0707070707070707; +//assign eth_l11_txc = 8'hff; + +assign eth_r8_txd = 64'h0707070707070707; +assign eth_r8_txc = 8'hff; +assign eth_r9_txd = 64'h0707070707070707; +assign eth_r9_txc = 8'hff; +assign eth_r10_txd = 64'h0707070707070707; +assign eth_r10_txc = 8'hff; +assign eth_r11_txd = 64'h0707070707070707; +assign eth_r11_txc = 8'hff; + +reg [7:0] select_reg_0 = 0; +reg [7:0] select_reg_1 = 1; +reg [7:0] select_reg_2 = 2; +reg [7:0] select_reg_3 = 3; +reg [7:0] select_reg_4 = 4; +reg [7:0] select_reg_5 = 5; +reg [7:0] select_reg_6 = 6; +reg [7:0] select_reg_7 = 7; +reg [7:0] select_reg_8 = 8; +reg [7:0] select_reg_9 = 9; +reg [7:0] select_reg_10 = 10; +reg [7:0] select_reg_11 = 11; +reg [7:0] select_reg_12 = 12; +reg [7:0] select_reg_13 = 13; +reg [7:0] select_reg_14 = 14; +reg [7:0] select_reg_15 = 15; + + +axis_crosspoint #( + .S_COUNT(16), + .M_COUNT(16), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0) +) +axis_crosspoint_inst ( + .clk(clk), + .rst(rst), + + .s_axis_tdata({eth_r7_rxd, eth_r6_rxd, eth_r5_rxd, eth_r4_rxd, eth_r3_rxd, eth_r2_rxd, eth_r1_rxd, eth_r0_rxd, eth_l7_rxd, eth_l6_rxd, eth_l5_rxd, eth_l4_rxd, eth_l3_rxd, eth_l2_rxd, eth_l1_rxd, eth_l0_rxd}), + .s_axis_tkeep({eth_r7_rxc, eth_r6_rxc, eth_r5_rxc, eth_r4_rxc, eth_r3_rxc, eth_r2_rxc, eth_r1_rxc, eth_r0_rxc, eth_l7_rxc, eth_l6_rxc, eth_l5_rxc, eth_l4_rxc, eth_l3_rxc, eth_l2_rxc, eth_l1_rxc, eth_l0_rxc}), + .s_axis_tvalid(16'hffff), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + .m_axis_tdata({eth_r7_txd, eth_r6_txd, eth_r5_txd, eth_r4_txd, eth_r3_txd, eth_r2_txd, eth_r1_txd, eth_r0_txd, eth_l7_txd, eth_l6_txd, eth_l5_txd, eth_l4_txd, eth_l3_txd, eth_l2_txd, eth_l1_txd, eth_l0_txd}), + .m_axis_tkeep({eth_r7_txc, eth_r6_txc, eth_r5_txc, eth_r4_txc, eth_r3_txc, eth_r2_txc, eth_r1_txc, eth_r0_txc, eth_l7_txc, eth_l6_txc, eth_l5_txc, eth_l4_txc, eth_l3_txc, eth_l2_txc, eth_l1_txc, eth_l0_txc}), + .m_axis_tvalid(), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + .select({select_reg_15[3:0], select_reg_14[3:0], select_reg_13[3:0], select_reg_12[3:0], select_reg_11[3:0], select_reg_10[3:0], select_reg_9[3:0], select_reg_8[3:0], select_reg_7[3:0], select_reg_6[3:0], select_reg_5[3:0], select_reg_4[3:0], select_reg_3[3:0], select_reg_2[3:0], select_reg_1[3:0], select_reg_0[3:0]}) +); + +wire [63:0] eth_rx_axis_tdata; +wire [7:0] eth_rx_axis_tkeep; +wire eth_rx_axis_tvalid; +wire eth_rx_axis_tready; +wire eth_rx_axis_tlast; +wire eth_rx_axis_tuser; + +wire eth_rx_hdr_valid; +wire eth_rx_hdr_ready; +wire [47:0] eth_rx_dest_mac; +wire [47:0] eth_rx_src_mac; +wire [15:0] eth_rx_type; +wire [63:0] eth_rx_payload_axis_tdata; +wire [7:0] eth_rx_payload_axis_tkeep; +wire eth_rx_payload_axis_tvalid; +wire eth_rx_payload_axis_tready; +wire eth_rx_payload_axis_tlast; +wire eth_rx_payload_axis_tuser; + +eth_mac_10g_fifo #( + .ENABLE_PADDING(1), + .ENABLE_DIC(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(9), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(9), + .RX_FRAME_FIFO(1) +) +eth_mac_fifo_inst ( + .rx_clk(clk), + .rx_rst(rst), + .tx_clk(clk), + .tx_rst(rst), + .logic_clk(clk), + .logic_rst(rst), + .tx_axis_tdata(0), + .tx_axis_tkeep(0), + .tx_axis_tvalid(0), + .tx_axis_tready(), + .tx_axis_tlast(0), + .tx_axis_tuser(0), + .rx_axis_tdata(eth_rx_axis_tdata), + .rx_axis_tkeep(eth_rx_axis_tkeep), + .rx_axis_tvalid(eth_rx_axis_tvalid), + .rx_axis_tready(eth_rx_axis_tready), + .rx_axis_tlast(eth_rx_axis_tlast), + .rx_axis_tuser(eth_rx_axis_tuser), + .xgmii_rxd(eth_l11_rxd), + .xgmii_rxc(eth_l11_rxc), + .xgmii_txd(eth_l11_txd), + .xgmii_txc(eth_l11_txc), + .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(), + .ifg_delay(12) +); + +eth_axis_rx_64 +eth_axis_rx_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(eth_rx_axis_tdata), + .s_axis_tkeep(eth_rx_axis_tkeep), + .s_axis_tvalid(eth_rx_axis_tvalid), + .s_axis_tready(eth_rx_axis_tready), + .s_axis_tlast(eth_rx_axis_tlast), + .s_axis_tuser(eth_rx_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(eth_rx_hdr_valid), + .m_eth_hdr_ready(eth_rx_hdr_ready), + .m_eth_dest_mac(eth_rx_dest_mac), + .m_eth_src_mac(eth_rx_src_mac), + .m_eth_type(eth_rx_type), + .m_eth_payload_axis_tdata(eth_rx_payload_axis_tdata), + .m_eth_payload_axis_tkeep(eth_rx_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(eth_rx_payload_axis_tvalid), + .m_eth_payload_axis_tready(eth_rx_payload_axis_tready), + .m_eth_payload_axis_tlast(eth_rx_payload_axis_tlast), + .m_eth_payload_axis_tuser(eth_rx_payload_axis_tuser), + // Status signals + .busy(), + .error_header_early_termination() +); + +// interpret config packet +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_WORD_0 = 3'd1, + STATE_WORD_1 = 3'd2, + STATE_WAIT = 3'd3; + +reg [2:0] state_reg = STATE_IDLE; + +reg eth_rx_hdr_ready_reg = 0; +reg eth_rx_payload_axis_tready_reg = 0; + +assign eth_rx_hdr_ready = eth_rx_hdr_ready_reg; +assign eth_rx_payload_axis_tready = eth_rx_payload_axis_tready_reg; + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + eth_rx_hdr_ready_reg <= 0; + eth_rx_payload_axis_tready_reg <= 0; + select_reg_0 <= 0; + select_reg_1 <= 1; + select_reg_2 <= 2; + select_reg_3 <= 3; + select_reg_4 <= 4; + select_reg_5 <= 5; + select_reg_6 <= 6; + select_reg_7 <= 7; + select_reg_8 <= 8; + select_reg_9 <= 9; + select_reg_10 <= 10; + select_reg_11 <= 11; + select_reg_12 <= 12; + select_reg_13 <= 13; + select_reg_14 <= 14; + select_reg_15 <= 15; + end else begin + case (state_reg) + STATE_IDLE: begin + eth_rx_hdr_ready_reg <= 1; + eth_rx_payload_axis_tready_reg <= 0; + if (eth_rx_hdr_ready && eth_rx_hdr_valid) begin + if (eth_rx_type == 16'h8099) begin + state_reg <= STATE_WORD_0; + eth_rx_hdr_ready_reg <= 0; + eth_rx_payload_axis_tready_reg <= 1; + end else begin + state_reg <= STATE_WAIT; + eth_rx_hdr_ready_reg <= 0; + eth_rx_payload_axis_tready_reg <= 1; + end + end + end + STATE_WORD_0: begin + eth_rx_hdr_ready_reg <= 0; + eth_rx_payload_axis_tready_reg <= 1; + if (eth_rx_payload_axis_tready && eth_rx_payload_axis_tvalid) begin + if (eth_rx_payload_axis_tlast) begin + state_reg <= STATE_IDLE; + eth_rx_hdr_ready_reg <= 1; + eth_rx_payload_axis_tready_reg <= 0; + end else begin + select_reg_0 <= eth_rx_payload_axis_tdata[7:0]; + select_reg_1 <= eth_rx_payload_axis_tdata[15:8]; + select_reg_2 <= eth_rx_payload_axis_tdata[23:16]; + select_reg_3 <= eth_rx_payload_axis_tdata[31:24]; + select_reg_4 <= eth_rx_payload_axis_tdata[39:32]; + select_reg_5 <= eth_rx_payload_axis_tdata[47:40]; + select_reg_6 <= eth_rx_payload_axis_tdata[55:48]; + select_reg_7 <= eth_rx_payload_axis_tdata[63:56]; + state_reg <= STATE_WORD_1; + eth_rx_hdr_ready_reg <= 0; + eth_rx_payload_axis_tready_reg <= 1; + end + end + end + STATE_WORD_1: begin + eth_rx_hdr_ready_reg <= 0; + eth_rx_payload_axis_tready_reg <= 1; + if (eth_rx_payload_axis_tready && eth_rx_payload_axis_tvalid) begin + if (eth_rx_payload_axis_tlast) begin + state_reg <= STATE_IDLE; + eth_rx_hdr_ready_reg <= 1; + eth_rx_payload_axis_tready_reg <= 0; + end else begin + select_reg_8 <= eth_rx_payload_axis_tdata[7:0]; + select_reg_9 <= eth_rx_payload_axis_tdata[15:8]; + select_reg_10 <= eth_rx_payload_axis_tdata[23:16]; + select_reg_11 <= eth_rx_payload_axis_tdata[31:24]; + select_reg_12 <= eth_rx_payload_axis_tdata[39:32]; + select_reg_13 <= eth_rx_payload_axis_tdata[47:40]; + select_reg_14 <= eth_rx_payload_axis_tdata[55:48]; + select_reg_15 <= eth_rx_payload_axis_tdata[63:56]; + state_reg <= STATE_WAIT; + eth_rx_hdr_ready_reg <= 0; + eth_rx_payload_axis_tready_reg <= 1; + end + end + end + STATE_WAIT: begin + eth_rx_hdr_ready_reg <= 0; + eth_rx_payload_axis_tready_reg <= 1; + if (eth_rx_payload_axis_tready && eth_rx_payload_axis_tvalid) begin + if (eth_rx_payload_axis_tlast) begin + state_reg <= STATE_IDLE; + eth_rx_hdr_ready_reg <= 1; + eth_rx_payload_axis_tready_reg <= 0; + end else begin + state_reg <= STATE_WAIT; + eth_rx_hdr_ready_reg <= 0; + eth_rx_payload_axis_tready_reg <= 1; + end + end + end + endcase + end +end + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/gth_i2c_init.v b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/gth_i2c_init.v new file mode 100644 index 000000000..a8eff170a --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/gth_i2c_init.v @@ -0,0 +1,508 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * gth_i2c_init + */ +module gth_i2c_init ( + input wire clk, + input wire rst, + + /* + * I2C master interface + */ + output wire [6:0] cmd_address, + output wire cmd_start, + output wire cmd_read, + output wire cmd_write, + output wire cmd_write_multiple, + output wire cmd_stop, + output wire cmd_valid, + input wire cmd_ready, + + output wire [7:0] data_out, + output wire data_out_valid, + input wire data_out_ready, + output wire data_out_last, + + /* + * Status + */ + output wire busy, + + /* + * Configuration + */ + input wire start +); + +/* + +Generic module for I2C bus initialization. Good for use when multiple devices +on an I2C bus must be initialized on system start without intervention of a +general-purpose processor. + +Copy this file and change init_data and INIT_DATA_LEN as needed. + +This module can be used in two modes: simple device initalization, or multiple +device initialization. In multiple device mode, the same initialization sequence +can be performed on multiple different device addresses. + +To use single device mode, only use the start write to address and write data commands. +The module will generate the I2C commands in sequential order. Terminate the list +with a 0 entry. + +To use the multiple device mode, use the start data and start address block commands +to set up lists of initialization data and device addresses. The module enters +multiple device mode upon seeing a start data block command. The module stores the +offset of the start of the data block and then skips ahead until it reaches a start +address block command. The module will store the offset to the address block and +read the first address in the block. Then it will jump back to the data block +and execute it, substituting the stored address for each current address write +command. Upon reaching the start address block command, the module will read out the +next address and start again at the top of the data block. If the module encounters +a start data block command while looking for an address, then it will store a new data +offset and then look for a start address block command. Terminate the list with a 0 +entry. Normal address commands will operate normally inside a data block. + +Commands: + +00 0000000 : stop +00 0000001 : exit multiple device mode +00 0000011 : start write to current address +00 0001000 : start address block +00 0001001 : start data block +01 aaaaaaa : start write to address +1 dddddddd : write 8-bit data + +Examples + +write 0x11223344 to register 0x0004 on device at 0x50 + +01 1010000 start write to 0x50 +1 00000000 write address 0x0004 +1 00000100 +1 00010001 write data 0x11223344 +1 00100010 +1 00110011 +1 01000100 +0 00000000 stop + +write 0x11223344 to register 0x0004 on devices at 0x50, 0x51, 0x52, and 0x53 + +00 0001001 start data block +00 0000011 start write to current address +1 00000100 +1 00010001 write data 0x11223344 +1 00100010 +1 00110011 +1 01000100 +00 0001000 start address block +01 1010000 address 0x50 +01 1010000 address 0x51 +01 1010000 address 0x52 +01 1010000 address 0x53 +00 0000000 stop + +*/ + +// init_data ROM +localparam INIT_DATA_LEN = 68; + +reg [8:0] init_data [INIT_DATA_LEN-1:0]; + +initial begin + // init clock mux registers + init_data[0] = {2'b00, 7'b0001001}; // start data block + init_data[1] = {2'b00, 7'b0000011}; // start write to current address + init_data[2] = {1'b1, 8'd2}; // select PLL bandwidth + //init_data[3] = {1'b1, 8'hA2}; + init_data[3] = {1'b1, 8'hA0}; + init_data[4] = {2'b00, 7'b0000011}; // start write to current address + init_data[5] = {1'b1, 8'd3}; // disable outputs during ICAL + //init_data[6] = {1'b1, 8'h15}; + init_data[6] = {1'b1, 8'h10}; + init_data[7] = {2'b00, 7'b0000011}; // start write to current address + init_data[8] = {1'b1, 8'd5}; // set CLKOUT1 and CLKOUT2 to LVPECL + init_data[9] = {1'b1, 8'hED}; + init_data[10] = {2'b00, 7'b0000011}; // start write to current address + init_data[11] = {1'b1, 8'd6}; // set CLKOUT3 to LVPECL and CLKOUT4 to LVDS + //init_data[12] = {1'b1, 8'h2D}; + init_data[12] = {1'b1, 8'h3D}; + init_data[13] = {2'b00, 7'b0000011}; // start write to current address + init_data[14] = {1'b1, 8'd7}; // set CLKOUT5 to to LVDS + //init_data[15] = {1'b1, 8'h0A}; + init_data[15] = {1'b1, 8'h3A}; + init_data[16] = {2'b00, 7'b0000011}; // start write to current address + init_data[17] = {1'b1, 8'd20}; // enable LOL output + init_data[18] = {1'b1, 8'h3E}; + init_data[19] = {2'b00, 7'b0000011}; // start write to current address + init_data[20] = {1'b1, 8'd25}; // N1_HS + init_data[21] = {1'b1, 8'h40}; + init_data[22] = {2'b00, 7'b0000011}; // start write to current address + init_data[23] = {1'b1, 8'd27}; // NC1_LS + init_data[24] = {1'b1, 8'h05}; + init_data[25] = {2'b00, 7'b0000011}; // start write to current address + init_data[26] = {1'b1, 8'd30}; // NC2_LS + init_data[27] = {1'b1, 8'h05}; + init_data[28] = {2'b00, 7'b0000011}; // start write to current address + init_data[29] = {1'b1, 8'd33}; // NC3_LS + init_data[30] = {1'b1, 8'h05}; + init_data[31] = {2'b00, 7'b0000011}; // start write to current address + init_data[32] = {1'b1, 8'd36}; // NC4_LS + init_data[33] = {1'b1, 8'h05}; + init_data[34] = {2'b00, 7'b0000011}; // start write to current address + init_data[35] = {1'b1, 8'd39}; // NC5_LS + init_data[36] = {1'b1, 8'h05}; + init_data[37] = {2'b00, 7'b0000011}; // start write to current address + init_data[38] = {1'b1, 8'd40}; // N2_HS + init_data[39] = {1'b1, 8'hA0}; + init_data[40] = {2'b00, 7'b0000011}; // start write to current address + init_data[41] = {1'b1, 8'd41}; // N2_LS + init_data[42] = {1'b1, 8'h01}; + init_data[43] = {2'b00, 7'b0000011}; // start write to current address + init_data[44] = {1'b1, 8'd42}; // N2_LS + init_data[45] = {1'b1, 8'h3B}; + init_data[46] = {2'b00, 7'b0000011}; // start write to current address + init_data[47] = {1'b1, 8'd45}; // N31 + init_data[48] = {1'b1, 8'h4E}; + init_data[49] = {2'b00, 7'b0000011}; // start write to current address + init_data[50] = {1'b1, 8'd48}; // N32 + init_data[51] = {1'b1, 8'h4E}; + init_data[52] = {2'b00, 7'b0000011}; // start write to current address + init_data[53] = {1'b1, 8'd51}; // N33 + init_data[54] = {1'b1, 8'h4E}; + init_data[55] = {2'b00, 7'b0000011}; // start write to current address + init_data[56] = {1'b1, 8'd54}; // N34 + init_data[57] = {1'b1, 8'h4E}; + init_data[58] = {2'b00, 7'b0000011}; // start write to current address + init_data[59] = {1'b1, 8'd141}; // Zero independent skew + init_data[60] = {1'b1, 8'h00}; + init_data[61] = {2'b00, 7'b0000011}; // start write to current address + init_data[62] = {1'b1, 8'd136}; // Soft reset + init_data[63] = {1'b1, 8'h40}; + init_data[64] = {2'b00, 7'b0001000}; // start address block + init_data[65] = {2'b01, 7'b1101001}; // first clock mux + init_data[66] = {2'b01, 7'b1101000}; // second clock mux + init_data[67] = 9'd0; // stop +end + +localparam [3:0] + STATE_IDLE = 3'd0, + STATE_RUN = 3'd1, + STATE_TABLE_1 = 3'd2, + STATE_TABLE_2 = 3'd3, + STATE_TABLE_3 = 3'd4; + +reg [4:0] state_reg = STATE_IDLE, state_next; + +parameter AW = $clog2(INIT_DATA_LEN); + +reg [8:0] init_data_reg = 9'd0; + +reg [AW-1:0] address_reg = {AW{1'b0}}, address_next; +reg [AW-1:0] address_ptr_reg = {AW{1'b0}}, address_ptr_next; +reg [AW-1:0] data_ptr_reg = {AW{1'b0}}, data_ptr_next; + +reg [6:0] cur_address_reg = 7'd0, cur_address_next; + +reg [6:0] cmd_address_reg = 7'd0, cmd_address_next; +reg cmd_start_reg = 1'b0, cmd_start_next; +reg cmd_write_reg = 1'b0, cmd_write_next; +reg cmd_stop_reg = 1'b0, cmd_stop_next; +reg cmd_valid_reg = 1'b0, cmd_valid_next; + +reg [7:0] data_out_reg = 8'd0, data_out_next; +reg data_out_valid_reg = 1'b0, data_out_valid_next; + +reg start_flag_reg = 1'b0, start_flag_next; + +reg busy_reg = 1'b0; + +assign cmd_address = cmd_address_reg; +assign cmd_start = cmd_start_reg; +assign cmd_read = 1'b0; +assign cmd_write = cmd_write_reg; +assign cmd_write_multiple = 1'b0; +assign cmd_stop = cmd_stop_reg; +assign cmd_valid = cmd_valid_reg; + +assign data_out = data_out_reg; +assign data_out_valid = data_out_valid_reg; +assign data_out_last = 1'b1; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + address_next = address_reg; + address_ptr_next = address_ptr_reg; + data_ptr_next = data_ptr_reg; + + cur_address_next = cur_address_reg; + + cmd_address_next = cmd_address_reg; + cmd_start_next = cmd_start_reg & ~(cmd_valid & cmd_ready); + cmd_write_next = cmd_write_reg & ~(cmd_valid & cmd_ready); + cmd_stop_next = cmd_stop_reg & ~(cmd_valid & cmd_ready); + cmd_valid_next = cmd_valid_reg & ~cmd_ready; + + data_out_next = data_out_reg; + data_out_valid_next = data_out_valid_reg & ~data_out_ready; + + start_flag_next = start_flag_reg; + + if (cmd_valid | data_out_valid) begin + // wait for output registers to clear + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // wait for start signal + if (~start_flag_reg & start) begin + address_next = {AW{1'b0}}; + start_flag_next = 1'b1; + state_next = STATE_RUN; + end else begin + state_next = STATE_IDLE; + end + end + STATE_RUN: begin + // process commands + if (init_data_reg[8] == 1'b1) begin + // write data + cmd_write_next = 1'b1; + cmd_stop_next = 1'b0; + cmd_valid_next = 1'b1; + + data_out_next = init_data_reg[7:0]; + data_out_valid_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_RUN; + end else if (init_data_reg[8:7] == 2'b01) begin + // write address + cmd_address_next = init_data_reg[6:0]; + cmd_start_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_RUN; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_RUN; + end + end + STATE_TABLE_1: begin + // find address table start + if (init_data_reg == 9'b000001000) begin + // address table start + address_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_2; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 1) begin + // exit mode + address_next = address_reg + 1; + state_next = STATE_RUN; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end + end + STATE_TABLE_2: begin + // find next address + if (init_data_reg[8:7] == 2'b01) begin + // write address command + // store address and move to data table + cur_address_next = init_data_reg[6:0]; + address_ptr_next = address_reg + 1; + address_next = data_ptr_reg; + state_next = STATE_TABLE_3; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'd1) begin + // exit mode + address_next = address_reg + 1; + state_next = STATE_RUN; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_TABLE_2; + end + end + STATE_TABLE_3: begin + // process data table with selected address + if (init_data_reg[8] == 1'b1) begin + // write data + cmd_write_next = 1'b1; + cmd_stop_next = 1'b0; + cmd_valid_next = 1'b1; + + data_out_next = init_data_reg[7:0]; + data_out_valid_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_TABLE_3; + end else if (init_data_reg[8:7] == 2'b01) begin + // write address + cmd_address_next = init_data_reg[6:0]; + cmd_start_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_TABLE_3; + end else if (init_data_reg == 9'b000000011) begin + // write current address + cmd_address_next = cur_address_reg; + cmd_start_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_TABLE_3; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'b000001000) begin + // address table start + address_next = address_ptr_reg; + state_next = STATE_TABLE_2; + end else if (init_data_reg == 9'd1) begin + // exit mode + address_next = address_reg + 1; + state_next = STATE_RUN; + end else if (init_data_reg == 9'd0) begin + // stop + cmd_start_next = 1'b0; + cmd_write_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + state_next = STATE_IDLE; + end else begin + // invalid command, skip + address_next = address_reg + 1; + state_next = STATE_TABLE_3; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + init_data_reg <= 9'd0; + + address_reg <= {AW{1'b0}}; + address_ptr_reg <= {AW{1'b0}}; + data_ptr_reg <= {AW{1'b0}}; + + cur_address_reg <= 7'd0; + + cmd_valid_reg <= 1'b0; + + data_out_valid_reg <= 1'b0; + + start_flag_reg <= 1'b0; + + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + // read init_data ROM + init_data_reg <= init_data[address_next]; + + address_reg <= address_next; + address_ptr_reg <= address_ptr_next; + data_ptr_reg <= data_ptr_next; + + cur_address_reg <= cur_address_next; + + cmd_valid_reg <= cmd_valid_next; + + data_out_valid_reg <= data_out_valid_next; + + start_flag_reg <= start & start_flag_next; + + busy_reg <= (state_reg != STATE_IDLE); + end + + cmd_address_reg <= cmd_address_next; + cmd_start_reg <= cmd_start_next; + cmd_write_reg <= cmd_write_next; + cmd_stop_reg <= cmd_stop_next; + + data_out_reg <= data_out_next; +end + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/i2c_master.v b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/i2c_master.v new file mode 100644 index 000000000..95d3a5212 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/i2c_master.v @@ -0,0 +1,895 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * I2C master + */ +module i2c_master ( + input wire clk, + input wire rst, + + /* + * Host interface + */ + input wire [6:0] cmd_address, + input wire cmd_start, + input wire cmd_read, + input wire cmd_write, + input wire cmd_write_multiple, + input wire cmd_stop, + input wire cmd_valid, + output wire cmd_ready, + + input wire [7:0] data_in, + input wire data_in_valid, + output wire data_in_ready, + input wire data_in_last, + + output wire [7:0] data_out, + output wire data_out_valid, + input wire data_out_ready, + output wire data_out_last, + + /* + * I2C interface + */ + input wire scl_i, + output wire scl_o, + output wire scl_t, + input wire sda_i, + output wire sda_o, + output wire sda_t, + + /* + * Status + */ + output wire busy, + output wire bus_control, + output wire bus_active, + output wire missed_ack, + + /* + * Configuration + */ + input wire [15:0] prescale, + input wire stop_on_idle +); + +/* + +I2C + +Read + __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __ +sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_\_R___A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A____/ + ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____ +scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP + +Write + __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __ +sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_/_W_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_/_N_\__/ + ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____ +scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP + +Commands: + +read + read data byte + set start to force generation of a start condition + start is implied when bus is inactive or active with write or different address + set stop to issue a stop condition after reading current byte + if stop is set with read command, then data_out_last will be set + +write + write data byte + set start to force generation of a start condition + start is implied when bus is inactive or active with read or different address + set stop to issue a stop condition after writing current byte + +write multiple + write multiple data bytes (until data_in_last) + set start to force generation of a start condition + start is implied when bus is inactive or active with read or different address + set stop to issue a stop condition after writing block + +stop + issue stop condition if bus is active + +Status: + +busy + module is communicating over the bus + +bus_control + module has control of bus in active state + +bus_active + bus is active, not necessarily controlled by this module + +missed_ack + strobed when a slave ack is missed + +Parameters: + +prescale + set prescale to 1/4 of the minimum clock period in units + of input clk cycles (prescale = Fclk / (FI2Cclk * 4)) + +stop_on_idle + automatically issue stop when command input is not valid + +Example of interfacing with tristate pins: +(this will work for any tristate bus) + +assign scl_i = scl_pin; +assign scl_pin = scl_t ? 1'bz : scl_o; +assign sda_i = sda_pin; +assign sda_pin = sda_t ? 1'bz : sda_o; + +Equivalent code that does not use *_t connections: +(we can get away with this because I2C is open-drain) + +assign scl_i = scl_pin; +assign scl_pin = scl_o ? 1'bz : 1'b0; +assign sda_i = sda_pin; +assign sda_pin = sda_o ? 1'bz : 1'b0; + +Example of two interconnected I2C devices: + +assign scl_1_i = scl_1_o & scl_2_o; +assign scl_2_i = scl_1_o & scl_2_o; +assign sda_1_i = sda_1_o & sda_2_o; +assign sda_2_i = sda_1_o & sda_2_o; + +Example of two I2C devices sharing the same pins: + +assign scl_1_i = scl_pin; +assign scl_2_i = scl_pin; +assign scl_pin = (scl_1_o & scl_2_o) ? 1'bz : 1'b0; +assign sda_1_i = sda_pin; +assign sda_2_i = sda_pin; +assign sda_pin = (sda_1_o & sda_2_o) ? 1'bz : 1'b0; + +Notes: + +scl_o should not be connected directly to scl_i, only via AND logic or a tristate +I/O pin. This would prevent devices from stretching the clock period. + +*/ + +localparam [4:0] + STATE_IDLE = 4'd0, + STATE_ACTIVE_WRITE = 4'd1, + STATE_ACTIVE_READ = 4'd2, + STATE_START_WAIT = 4'd3, + STATE_START = 4'd4, + STATE_ADDRESS_1 = 4'd5, + STATE_ADDRESS_2 = 4'd6, + STATE_WRITE_1 = 4'd7, + STATE_WRITE_2 = 4'd8, + STATE_WRITE_3 = 4'd9, + STATE_READ = 4'd10, + STATE_STOP = 4'd11; + +reg [4:0] state_reg = STATE_IDLE, state_next; + +localparam [4:0] + PHY_STATE_IDLE = 5'd0, + PHY_STATE_ACTIVE = 5'd1, + PHY_STATE_REPEATED_START_1 = 5'd2, + PHY_STATE_REPEATED_START_2 = 5'd3, + PHY_STATE_START_1 = 5'd4, + PHY_STATE_START_2 = 5'd5, + PHY_STATE_WRITE_BIT_1 = 5'd6, + PHY_STATE_WRITE_BIT_2 = 5'd7, + PHY_STATE_WRITE_BIT_3 = 5'd8, + PHY_STATE_READ_BIT_1 = 5'd9, + PHY_STATE_READ_BIT_2 = 5'd10, + PHY_STATE_READ_BIT_3 = 5'd11, + PHY_STATE_READ_BIT_4 = 5'd12, + PHY_STATE_STOP_1 = 5'd13, + PHY_STATE_STOP_2 = 5'd14, + PHY_STATE_STOP_3 = 5'd15; + +reg [4:0] phy_state_reg = STATE_IDLE, phy_state_next; + +reg phy_start_bit; +reg phy_stop_bit; +reg phy_write_bit; +reg phy_read_bit; +reg phy_release_bus; + +reg phy_tx_data; + +reg phy_rx_data_reg = 1'b0, phy_rx_data_next; + +reg [6:0] addr_reg = 7'd0, addr_next; +reg [7:0] data_reg = 8'd0, data_next; +reg last_reg = 1'b0, last_next; + +reg mode_read_reg = 1'b0, mode_read_next; +reg mode_write_multiple_reg = 1'b0, mode_write_multiple_next; +reg mode_stop_reg = 1'b0, mode_stop_next; + +reg [16:0] delay_reg = 16'd0, delay_next; +reg delay_scl_reg = 1'b0, delay_scl_next; +reg delay_sda_reg = 1'b0, delay_sda_next; + +reg [3:0] bit_count_reg = 4'd0, bit_count_next; + +reg cmd_ready_reg = 1'b0, cmd_ready_next; + +reg data_in_ready_reg = 1'b0, data_in_ready_next; + +reg [7:0] data_out_reg = 8'd0, data_out_next; +reg data_out_valid_reg = 1'b0, data_out_valid_next; +reg data_out_last_reg = 1'b0, data_out_last_next; + +reg scl_i_reg = 1'b1; +reg sda_i_reg = 1'b1; + +reg scl_o_reg = 1'b1, scl_o_next; +reg sda_o_reg = 1'b1, sda_o_next; + +reg last_scl_i_reg = 1'b1; +reg last_sda_i_reg = 1'b1; + +reg busy_reg = 1'b0; +reg bus_active_reg = 1'b0; +reg bus_control_reg = 1'b0, bus_control_next; +reg missed_ack_reg = 1'b0, missed_ack_next; + +assign cmd_ready = cmd_ready_reg; + +assign data_in_ready = data_in_ready_reg; + +assign data_out = data_out_reg; +assign data_out_valid = data_out_valid_reg; +assign data_out_last = data_out_last_reg; + +assign scl_o = scl_o_reg; +assign scl_t = scl_o_reg; +assign sda_o = sda_o_reg; +assign sda_t = sda_o_reg; + +assign busy = busy_reg; +assign bus_active = bus_active_reg; +assign bus_control = bus_control_reg; +assign missed_ack = missed_ack_reg; + +wire scl_posedge = scl_i_reg & ~last_scl_i_reg; +wire scl_negedge = ~scl_i_reg & last_scl_i_reg; +wire sda_posedge = sda_i_reg & ~last_sda_i_reg; +wire sda_negedge = ~sda_i_reg & last_sda_i_reg; + +wire start_bit = sda_negedge & scl_i_reg; +wire stop_bit = sda_posedge & scl_i_reg; + +always @* begin + state_next = STATE_IDLE; + + phy_start_bit = 1'b0; + phy_stop_bit = 1'b0; + phy_write_bit = 1'b0; + phy_read_bit = 1'b0; + phy_tx_data = 1'b0; + phy_release_bus = 1'b0; + + addr_next = addr_reg; + data_next = data_reg; + last_next = last_reg; + + mode_read_next = mode_read_reg; + mode_write_multiple_next = mode_write_multiple_reg; + mode_stop_next = mode_stop_reg; + + bit_count_next = bit_count_reg; + + cmd_ready_next = 1'b0; + + data_in_ready_next = 1'b0; + + data_out_next = data_out_reg; + data_out_valid_next = data_out_valid_reg & ~data_out_ready; + data_out_last_next = data_out_last_reg; + + missed_ack_next = 1'b0; + + // generate delays + if (phy_state_reg != PHY_STATE_IDLE && phy_state_reg != PHY_STATE_ACTIVE) begin + // wait for phy operation + state_next = state_reg; + end else begin + // process states + case (state_reg) + STATE_IDLE: begin + // line idle + cmd_ready_next = 1'b1; + + if (cmd_ready & cmd_valid) begin + // command valid + if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin + // read or write command + addr_next = cmd_address; + mode_read_next = cmd_read; + mode_write_multiple_next = cmd_write_multiple; + mode_stop_next = cmd_stop; + + cmd_ready_next = 1'b0; + + // start bit + if (bus_active) begin + state_next = STATE_START_WAIT; + end else begin + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end + end else begin + // invalid or unspecified - ignore + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_ACTIVE_WRITE: begin + // line active with current address and read/write mode + cmd_ready_next = 1'b1; + + if (cmd_ready & cmd_valid) begin + // command valid + if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin + // read or write command + addr_next = cmd_address; + mode_read_next = cmd_read; + mode_write_multiple_next = cmd_write_multiple; + mode_stop_next = cmd_stop; + + cmd_ready_next = 1'b0; + + if (cmd_start || cmd_address != addr_reg || cmd_read) begin + // address or mode mismatch or forced start - repeated start + + // repeated start bit + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end else begin + // address and mode match + + // start write + data_in_ready_next = 1'b1; + state_next = STATE_WRITE_1; + end + end else if (cmd_stop && !(cmd_read || cmd_write || cmd_write_multiple)) begin + // stop command + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end else begin + // invalid or unspecified - ignore + state_next = STATE_ACTIVE_WRITE; + end + end else begin + if (stop_on_idle & cmd_ready & ~cmd_valid) begin + // no waiting command and stop_on_idle selected, issue stop condition + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_ACTIVE_WRITE; + end + end + end + STATE_ACTIVE_READ: begin + // line active to current address + cmd_ready_next = ~data_out_valid; + + if (cmd_ready & cmd_valid) begin + // command valid + if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin + // read or write command + addr_next = cmd_address; + mode_read_next = cmd_read; + mode_write_multiple_next = cmd_write_multiple; + mode_stop_next = cmd_stop; + + cmd_ready_next = 1'b0; + + if (cmd_start || cmd_address != addr_reg || cmd_write) begin + // address or mode mismatch or forced start - repeated start + + // write nack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + // repeated start bit + state_next = STATE_START; + end else begin + // address and mode match + + // write ack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b0; + // start next read + bit_count_next = 4'd8; + data_next = 8'd0; + state_next = STATE_READ; + end + end else if (cmd_stop && !(cmd_read || cmd_write || cmd_write_multiple)) begin + // stop command + // write nack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + // send stop bit + state_next = STATE_STOP; + end else begin + // invalid or unspecified - ignore + state_next = STATE_ACTIVE_READ; + end + end else begin + if (stop_on_idle & cmd_ready & ~cmd_valid) begin + // no waiting command and stop_on_idle selected, issue stop condition + // write ack for previous read + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + // send stop bit + state_next = STATE_STOP; + end else begin + state_next = STATE_ACTIVE_READ; + end + end + end + STATE_START_WAIT: begin + // wait for bus idle + + if (bus_active) begin + state_next = STATE_START_WAIT; + end else begin + // bus is idle, take control + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end + end + STATE_START: begin + // send start bit + + phy_start_bit = 1'b1; + bit_count_next = 4'd8; + state_next = STATE_ADDRESS_1; + end + STATE_ADDRESS_1: begin + // send address + bit_count_next = bit_count_reg - 1; + if (bit_count_reg > 1) begin + // send address + phy_write_bit = 1'b1; + phy_tx_data = addr_reg[bit_count_reg-2]; + state_next = STATE_ADDRESS_1; + end else if (bit_count_reg > 0) begin + // send read/write bit + phy_write_bit = 1'b1; + phy_tx_data = mode_read_reg; + state_next = STATE_ADDRESS_1; + end else begin + // read ack bit + phy_read_bit = 1'b1; + state_next = STATE_ADDRESS_2; + end + end + STATE_ADDRESS_2: begin + // read ack bit + missed_ack_next = phy_rx_data_reg; + + if (mode_read_reg) begin + // start read + bit_count_next = 4'd8; + data_next = 1'b0; + state_next = STATE_READ; + end else begin + // start write + data_in_ready_next = 1'b1; + state_next = STATE_WRITE_1; + end + end + STATE_WRITE_1: begin + data_in_ready_next = 1'b1; + + if (data_in_ready & data_in_valid) begin + // got data, start write + data_next = data_in; + last_next = data_in_last; + bit_count_next = 4'd8; + data_in_ready_next = 1'b0; + state_next = STATE_WRITE_2; + end else begin + // wait for data + state_next = STATE_WRITE_1; + end + end + STATE_WRITE_2: begin + // send data + bit_count_next = bit_count_reg - 1; + if (bit_count_reg > 0) begin + // write data bit + phy_write_bit = 1'b1; + phy_tx_data = data_reg[bit_count_reg-1]; + state_next = STATE_WRITE_2; + end else begin + // read ack bit + phy_read_bit = 1'b1; + state_next = STATE_WRITE_3; + end + end + STATE_WRITE_3: begin + // read ack bit + missed_ack_next = phy_rx_data_reg; + + if (mode_write_multiple_reg && !last_reg) begin + // more to write + state_next = STATE_WRITE_1; + end else if (mode_stop_reg) begin + // last cycle and stop selected + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end else begin + // otherwise, return to bus active state + state_next = STATE_ACTIVE_WRITE; + end + end + STATE_READ: begin + // read data + + bit_count_next = bit_count_reg - 1; + data_next = {data_reg[6:0], phy_rx_data_reg}; + if (bit_count_reg > 0) begin + // read next bit + phy_read_bit = 1'b1; + state_next = STATE_READ; + end else begin + // output data word + data_out_next = data_next; + data_out_valid_next = 1'b1; + data_out_last_next = 1'b0; + if (mode_stop_reg) begin + // send nack and stop + data_out_last_next = 1'b1; + phy_write_bit = 1'b1; + phy_tx_data = 1'b1; + state_next = STATE_STOP; + end else begin + // return to bus active state + state_next = STATE_ACTIVE_READ; + end + end + end + STATE_STOP: begin + // send stop bit + phy_stop_bit = 1'b1; + state_next = STATE_IDLE; + end + endcase + end +end + +always @* begin + phy_state_next = PHY_STATE_IDLE; + + phy_rx_data_next = phy_rx_data_reg; + + delay_next = delay_reg; + delay_scl_next = delay_scl_reg; + delay_sda_next = delay_sda_reg; + + scl_o_next = scl_o_reg; + sda_o_next = sda_o_reg; + + bus_control_next = bus_control_reg; + + if (phy_release_bus) begin + // release bus and return to idle state + sda_o_next = 1'b1; + scl_o_next = 1'b1; + delay_scl_next = 1'b0; + delay_sda_next = 1'b0; + delay_next = 1'b0; + phy_state_next = PHY_STATE_IDLE; + end else if (delay_scl_reg) begin + // wait for SCL to match command + delay_scl_next = scl_o_reg & ~scl_i_reg; + phy_state_next = phy_state_reg; + end else if (delay_sda_reg) begin + // wait for SDA to match command + delay_sda_next = sda_o_reg & ~sda_i_reg; + phy_state_next = phy_state_reg; + end else if (delay_reg > 0) begin + // time delay + delay_next = delay_reg - 1; + phy_state_next = phy_state_reg; + end else begin + case (phy_state_reg) + PHY_STATE_IDLE: begin + // bus idle - wait for start command + sda_o_next = 1'b1; + scl_o_next = 1'b1; + if (phy_start_bit) begin + sda_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_START_1; + end else begin + phy_state_next = PHY_STATE_IDLE; + end + end + PHY_STATE_ACTIVE: begin + // bus active + if (phy_start_bit) begin + sda_o_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_REPEATED_START_1; + end else if (phy_write_bit) begin + sda_o_next = phy_tx_data; + delay_next = prescale; + phy_state_next = PHY_STATE_WRITE_BIT_1; + end else if (phy_read_bit) begin + sda_o_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_1; + end else if (phy_stop_bit) begin + sda_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_STOP_1; + end else begin + phy_state_next = PHY_STATE_ACTIVE; + end + end + PHY_STATE_REPEATED_START_1: begin + // generate repeated start bit + // ______ + // sda XXX/ \_______ + // _______ + // scl ______/ \___ + // + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_REPEATED_START_2; + end + PHY_STATE_REPEATED_START_2: begin + // generate repeated start bit + // ______ + // sda XXX/ \_______ + // _______ + // scl ______/ \___ + // + + sda_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_START_1; + end + PHY_STATE_START_1: begin + // generate start bit + // ___ + // sda \_______ + // _______ + // scl \___ + // + + scl_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_START_2; + end + PHY_STATE_START_2: begin + // generate start bit + // ___ + // sda \_______ + // _______ + // scl \___ + // + + bus_control_next = 1'b1; + phy_state_next = PHY_STATE_ACTIVE; + end + PHY_STATE_WRITE_BIT_1: begin + // write bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale << 1; + phy_state_next = PHY_STATE_WRITE_BIT_2; + end + PHY_STATE_WRITE_BIT_2: begin + // write bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_WRITE_BIT_3; + end + PHY_STATE_WRITE_BIT_3: begin + // write bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + phy_state_next = PHY_STATE_ACTIVE; + end + PHY_STATE_READ_BIT_1: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_2; + end + PHY_STATE_READ_BIT_2: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + phy_rx_data_next = sda_i_reg; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_3; + end + PHY_STATE_READ_BIT_3: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + scl_o_next = 1'b0; + delay_next = prescale; + phy_state_next = PHY_STATE_READ_BIT_4; + end + PHY_STATE_READ_BIT_4: begin + // read bit + // ________ + // sda X________X + // ____ + // scl __/ \__ + + phy_state_next = PHY_STATE_ACTIVE; + end + PHY_STATE_STOP_1: begin + // stop bit + // ___ + // sda XXX\_______/ + // _______ + // scl _______/ + + scl_o_next = 1'b1; + delay_scl_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_STOP_2; + end + PHY_STATE_STOP_2: begin + // stop bit + // ___ + // sda XXX\_______/ + // _______ + // scl _______/ + + sda_o_next = 1'b1; + delay_next = prescale; + phy_state_next = PHY_STATE_STOP_3; + end + PHY_STATE_STOP_3: begin + // stop bit + // ___ + // sda XXX\_______/ + // _______ + // scl _______/ + + bus_control_next = 1'b0; + phy_state_next = PHY_STATE_IDLE; + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + phy_state_reg <= PHY_STATE_IDLE; + delay_reg <= 16'd0; + delay_scl_reg <= 1'b0; + delay_sda_reg <= 1'b0; + cmd_ready_reg <= 1'b0; + data_in_ready_reg <= 1'b0; + data_out_valid_reg <= 1'b0; + scl_o_reg <= 1'b1; + sda_o_reg <= 1'b1; + busy_reg <= 1'b0; + bus_active_reg <= 1'b0; + bus_control_reg <= 1'b0; + missed_ack_reg <= 1'b0; + end else begin + state_reg <= state_next; + phy_state_reg <= phy_state_next; + + delay_reg <= delay_next; + delay_scl_reg <= delay_scl_next; + delay_sda_reg <= delay_sda_next; + + cmd_ready_reg <= cmd_ready_next; + data_in_ready_reg <= data_in_ready_next; + data_out_valid_reg <= data_out_valid_next; + + scl_o_reg <= scl_o_next; + sda_o_reg <= sda_o_next; + + busy_reg <= !(state_reg == STATE_IDLE || state_reg == STATE_ACTIVE_WRITE || state_reg == STATE_ACTIVE_READ); + + if (start_bit) begin + bus_active_reg <= 1'b1; + end else if (stop_bit) begin + bus_active_reg <= 1'b0; + end else begin + bus_active_reg <= bus_active_reg; + end + + bus_control_reg <= bus_control_next; + missed_ack_reg <= missed_ack_next; + end + + phy_rx_data_reg <= phy_rx_data_next; + + addr_reg <= addr_next; + data_reg <= data_next; + last_reg <= last_next; + + mode_read_reg <= mode_read_next; + mode_write_multiple_reg <= mode_write_multiple_next; + mode_stop_reg <= mode_stop_next; + + bit_count_reg <= bit_count_next; + + data_out_reg <= data_out_next; + data_out_last_reg <= data_out_last_next; + + scl_i_reg <= scl_i; + sda_i_reg <= sda_i; + last_scl_i_reg <= scl_i_reg; + last_sda_i_reg <= sda_i_reg; +end + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/sync_reset.v b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/sync_reset.v new file mode 100644 index 000000000..fe097029f --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/sync_signal.v b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/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/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/axis_ep.py b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/eth_ep.py b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/test_fpga_core.py b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/test_fpga_core.py new file mode 100755 index 000000000..5957dc951 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/test_fpga_core.py @@ -0,0 +1,544 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import xgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_10g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_10g.v") +srcs.append("../lib/eth/rtl/axis_xgmii_rx_64.v") +srcs.append("../lib/eth/rtl/axis_xgmii_tx_64.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx_64.v") +srcs.append("../lib/eth/rtl/eth_axis_tx_64.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_crosspoint.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + sw = Signal(intbv(0)[2:]) + jp = Signal(intbv(0)[4:]) + uart_suspend = Signal(bool(0)) + uart_dtr = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + amh_right_mdio_i = Signal(bool(0)) + amh_left_mdio_i = Signal(bool(0)) + eth_r0_rxd = Signal(intbv(0)[64:]) + eth_r0_rxc = Signal(intbv(0)[8:]) + eth_r1_rxd = Signal(intbv(0)[64:]) + eth_r1_rxc = Signal(intbv(0)[8:]) + eth_r2_rxd = Signal(intbv(0)[64:]) + eth_r2_rxc = Signal(intbv(0)[8:]) + eth_r3_rxd = Signal(intbv(0)[64:]) + eth_r3_rxc = Signal(intbv(0)[8:]) + eth_r4_rxd = Signal(intbv(0)[64:]) + eth_r4_rxc = Signal(intbv(0)[8:]) + eth_r5_rxd = Signal(intbv(0)[64:]) + eth_r5_rxc = Signal(intbv(0)[8:]) + eth_r6_rxd = Signal(intbv(0)[64:]) + eth_r6_rxc = Signal(intbv(0)[8:]) + eth_r7_rxd = Signal(intbv(0)[64:]) + eth_r7_rxc = Signal(intbv(0)[8:]) + eth_r8_rxd = Signal(intbv(0)[64:]) + eth_r8_rxc = Signal(intbv(0)[8:]) + eth_r9_rxd = Signal(intbv(0)[64:]) + eth_r9_rxc = Signal(intbv(0)[8:]) + eth_r10_rxd = Signal(intbv(0)[64:]) + eth_r10_rxc = Signal(intbv(0)[8:]) + eth_r11_rxd = Signal(intbv(0)[64:]) + eth_r11_rxc = Signal(intbv(0)[8:]) + eth_l0_rxd = Signal(intbv(0)[64:]) + eth_l0_rxc = Signal(intbv(0)[8:]) + eth_l1_rxd = Signal(intbv(0)[64:]) + eth_l1_rxc = Signal(intbv(0)[8:]) + eth_l2_rxd = Signal(intbv(0)[64:]) + eth_l2_rxc = Signal(intbv(0)[8:]) + eth_l3_rxd = Signal(intbv(0)[64:]) + eth_l3_rxc = Signal(intbv(0)[8:]) + eth_l4_rxd = Signal(intbv(0)[64:]) + eth_l4_rxc = Signal(intbv(0)[8:]) + eth_l5_rxd = Signal(intbv(0)[64:]) + eth_l5_rxc = Signal(intbv(0)[8:]) + eth_l6_rxd = Signal(intbv(0)[64:]) + eth_l6_rxc = Signal(intbv(0)[8:]) + eth_l7_rxd = Signal(intbv(0)[64:]) + eth_l7_rxc = Signal(intbv(0)[8:]) + eth_l8_rxd = Signal(intbv(0)[64:]) + eth_l8_rxc = Signal(intbv(0)[8:]) + eth_l9_rxd = Signal(intbv(0)[64:]) + eth_l9_rxc = Signal(intbv(0)[8:]) + eth_l10_rxd = Signal(intbv(0)[64:]) + eth_l10_rxc = Signal(intbv(0)[8:]) + eth_l11_rxd = Signal(intbv(0)[64:]) + eth_l11_rxc = Signal(intbv(0)[8:]) + + # Outputs + led = Signal(intbv(0)[4:]) + uart_rst = Signal(bool(0)) + uart_ri = Signal(bool(0)) + uart_dcd = Signal(bool(0)) + uart_dsr = Signal(bool(0)) + uart_rxd = Signal(bool(1)) + uart_cts = Signal(bool(0)) + amh_right_mdc = Signal(bool(1)) + amh_right_mdio_o = Signal(bool(1)) + amh_right_mdio_t = Signal(bool(1)) + amh_left_mdc = Signal(bool(1)) + amh_left_mdio_o = Signal(bool(1)) + amh_left_mdio_t = Signal(bool(1)) + eth_r0_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r0_txc = Signal(intbv(0xff)[8:]) + eth_r1_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r1_txc = Signal(intbv(0xff)[8:]) + eth_r2_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r2_txc = Signal(intbv(0xff)[8:]) + eth_r3_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r3_txc = Signal(intbv(0xff)[8:]) + eth_r4_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r4_txc = Signal(intbv(0xff)[8:]) + eth_r5_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r5_txc = Signal(intbv(0xff)[8:]) + eth_r6_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r6_txc = Signal(intbv(0xff)[8:]) + eth_r7_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r7_txc = Signal(intbv(0xff)[8:]) + eth_r8_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r8_txc = Signal(intbv(0xff)[8:]) + eth_r9_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r9_txc = Signal(intbv(0xff)[8:]) + eth_r10_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r10_txc = Signal(intbv(0xff)[8:]) + eth_r11_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_r11_txc = Signal(intbv(0xff)[8:]) + eth_l0_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l0_txc = Signal(intbv(0xff)[8:]) + eth_l1_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l1_txc = Signal(intbv(0xff)[8:]) + eth_l2_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l2_txc = Signal(intbv(0xff)[8:]) + eth_l3_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l3_txc = Signal(intbv(0xff)[8:]) + eth_l4_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l4_txc = Signal(intbv(0xff)[8:]) + eth_l5_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l5_txc = Signal(intbv(0xff)[8:]) + eth_l6_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l6_txc = Signal(intbv(0xff)[8:]) + eth_l7_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l7_txc = Signal(intbv(0xff)[8:]) + eth_l8_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l8_txc = Signal(intbv(0xff)[8:]) + eth_l9_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l9_txc = Signal(intbv(0xff)[8:]) + eth_l10_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l10_txc = Signal(intbv(0xff)[8:]) + eth_l11_txd = Signal(intbv(0x0707070707070707)[64:]) + eth_l11_txc = Signal(intbv(0xff)[8:]) + + # sources and sinks + eth_r0_source = xgmii_ep.XGMIISource() + eth_r0_source_logic = eth_r0_source.create_logic(clk, rst, txd=eth_r0_rxd, txc=eth_r0_rxc, name='eth_r0_source') + + eth_r0_sink = xgmii_ep.XGMIISink() + eth_r0_sink_logic = eth_r0_sink.create_logic(clk, rst, rxd=eth_r0_txd, rxc=eth_r0_txc, name='eth_r0_sink') + + eth_r1_source = xgmii_ep.XGMIISource() + eth_r1_source_logic = eth_r1_source.create_logic(clk, rst, txd=eth_r1_rxd, txc=eth_r1_rxc, name='eth_r1_source') + + eth_r1_sink = xgmii_ep.XGMIISink() + eth_r1_sink_logic = eth_r1_sink.create_logic(clk, rst, rxd=eth_r1_txd, rxc=eth_r1_txc, name='eth_r1_sink') + + eth_r2_source = xgmii_ep.XGMIISource() + eth_r2_source_logic = eth_r2_source.create_logic(clk, rst, txd=eth_r2_rxd, txc=eth_r2_rxc, name='eth_r2_source') + + eth_r2_sink = xgmii_ep.XGMIISink() + eth_r2_sink_logic = eth_r2_sink.create_logic(clk, rst, rxd=eth_r2_txd, rxc=eth_r2_txc, name='eth_r2_sink') + + eth_r3_source = xgmii_ep.XGMIISource() + eth_r3_source_logic = eth_r3_source.create_logic(clk, rst, txd=eth_r3_rxd, txc=eth_r3_rxc, name='eth_r3_source') + + eth_r3_sink = xgmii_ep.XGMIISink() + eth_r3_sink_logic = eth_r3_sink.create_logic(clk, rst, rxd=eth_r3_txd, rxc=eth_r3_txc, name='eth_r3_sink') + + eth_r4_source = xgmii_ep.XGMIISource() + eth_r4_source_logic = eth_r4_source.create_logic(clk, rst, txd=eth_r4_rxd, txc=eth_r4_rxc, name='eth_r4_source') + + eth_r4_sink = xgmii_ep.XGMIISink() + eth_r4_sink_logic = eth_r4_sink.create_logic(clk, rst, rxd=eth_r4_txd, rxc=eth_r4_txc, name='eth_r4_sink') + + eth_r5_source = xgmii_ep.XGMIISource() + eth_r5_source_logic = eth_r5_source.create_logic(clk, rst, txd=eth_r5_rxd, txc=eth_r5_rxc, name='eth_r5_source') + + eth_r5_sink = xgmii_ep.XGMIISink() + eth_r5_sink_logic = eth_r5_sink.create_logic(clk, rst, rxd=eth_r5_txd, rxc=eth_r5_txc, name='eth_r5_sink') + + eth_r6_source = xgmii_ep.XGMIISource() + eth_r6_source_logic = eth_r6_source.create_logic(clk, rst, txd=eth_r6_rxd, txc=eth_r6_rxc, name='eth_r6_source') + + eth_r6_sink = xgmii_ep.XGMIISink() + eth_r6_sink_logic = eth_r6_sink.create_logic(clk, rst, rxd=eth_r6_txd, rxc=eth_r6_txc, name='eth_r6_sink') + + eth_r7_source = xgmii_ep.XGMIISource() + eth_r7_source_logic = eth_r7_source.create_logic(clk, rst, txd=eth_r7_rxd, txc=eth_r7_rxc, name='eth_r7_source') + + eth_r7_sink = xgmii_ep.XGMIISink() + eth_r7_sink_logic = eth_r7_sink.create_logic(clk, rst, rxd=eth_r7_txd, rxc=eth_r7_txc, name='eth_r7_sink') + + eth_r8_source = xgmii_ep.XGMIISource() + eth_r8_source_logic = eth_r8_source.create_logic(clk, rst, txd=eth_r8_rxd, txc=eth_r8_rxc, name='eth_r8_source') + + eth_r8_sink = xgmii_ep.XGMIISink() + eth_r8_sink_logic = eth_r8_sink.create_logic(clk, rst, rxd=eth_r8_txd, rxc=eth_r8_txc, name='eth_r8_sink') + + eth_r9_source = xgmii_ep.XGMIISource() + eth_r9_source_logic = eth_r9_source.create_logic(clk, rst, txd=eth_r9_rxd, txc=eth_r9_rxc, name='eth_r9_source') + + eth_r9_sink = xgmii_ep.XGMIISink() + eth_r9_sink_logic = eth_r9_sink.create_logic(clk, rst, rxd=eth_r9_txd, rxc=eth_r9_txc, name='eth_r9_sink') + + eth_r10_source = xgmii_ep.XGMIISource() + eth_r10_source_logic = eth_r10_source.create_logic(clk, rst, txd=eth_r10_rxd, txc=eth_r10_rxc, name='eth_r10_source') + + eth_r10_sink = xgmii_ep.XGMIISink() + eth_r10_sink_logic = eth_r10_sink.create_logic(clk, rst, rxd=eth_r10_txd, rxc=eth_r10_txc, name='eth_r10_sink') + + eth_r11_source = xgmii_ep.XGMIISource() + eth_r11_source_logic = eth_r11_source.create_logic(clk, rst, txd=eth_r11_rxd, txc=eth_r11_rxc, name='eth_r11_source') + + eth_r11_sink = xgmii_ep.XGMIISink() + eth_r11_sink_logic = eth_r11_sink.create_logic(clk, rst, rxd=eth_r11_txd, rxc=eth_r11_txc, name='eth_r11_sink') + + eth_l0_source = xgmii_ep.XGMIISource() + eth_l0_source_logic = eth_l0_source.create_logic(clk, rst, txd=eth_l0_rxd, txc=eth_l0_rxc, name='eth_l0_source') + + eth_l0_sink = xgmii_ep.XGMIISink() + eth_l0_sink_logic = eth_l0_sink.create_logic(clk, rst, rxd=eth_l0_txd, rxc=eth_l0_txc, name='eth_l0_sink') + + eth_l1_source = xgmii_ep.XGMIISource() + eth_l1_source_logic = eth_l1_source.create_logic(clk, rst, txd=eth_l1_rxd, txc=eth_l1_rxc, name='eth_l1_source') + + eth_l1_sink = xgmii_ep.XGMIISink() + eth_l1_sink_logic = eth_l1_sink.create_logic(clk, rst, rxd=eth_l1_txd, rxc=eth_l1_txc, name='eth_l1_sink') + + eth_l2_source = xgmii_ep.XGMIISource() + eth_l2_source_logic = eth_l2_source.create_logic(clk, rst, txd=eth_l2_rxd, txc=eth_l2_rxc, name='eth_l2_source') + + eth_l2_sink = xgmii_ep.XGMIISink() + eth_l2_sink_logic = eth_l2_sink.create_logic(clk, rst, rxd=eth_l2_txd, rxc=eth_l2_txc, name='eth_l2_sink') + + eth_l3_source = xgmii_ep.XGMIISource() + eth_l3_source_logic = eth_l3_source.create_logic(clk, rst, txd=eth_l3_rxd, txc=eth_l3_rxc, name='eth_l3_source') + + eth_l3_sink = xgmii_ep.XGMIISink() + eth_l3_sink_logic = eth_l3_sink.create_logic(clk, rst, rxd=eth_l3_txd, rxc=eth_l3_txc, name='eth_l3_sink') + + eth_l4_source = xgmii_ep.XGMIISource() + eth_l4_source_logic = eth_l4_source.create_logic(clk, rst, txd=eth_l4_rxd, txc=eth_l4_rxc, name='eth_l4_source') + + eth_l4_sink = xgmii_ep.XGMIISink() + eth_l4_sink_logic = eth_l4_sink.create_logic(clk, rst, rxd=eth_l4_txd, rxc=eth_l4_txc, name='eth_l4_sink') + + eth_l5_source = xgmii_ep.XGMIISource() + eth_l5_source_logic = eth_l5_source.create_logic(clk, rst, txd=eth_l5_rxd, txc=eth_l5_rxc, name='eth_l5_source') + + eth_l5_sink = xgmii_ep.XGMIISink() + eth_l5_sink_logic = eth_l5_sink.create_logic(clk, rst, rxd=eth_l5_txd, rxc=eth_l5_txc, name='eth_l5_sink') + + eth_l6_source = xgmii_ep.XGMIISource() + eth_l6_source_logic = eth_l6_source.create_logic(clk, rst, txd=eth_l6_rxd, txc=eth_l6_rxc, name='eth_l6_source') + + eth_l6_sink = xgmii_ep.XGMIISink() + eth_l6_sink_logic = eth_l6_sink.create_logic(clk, rst, rxd=eth_l6_txd, rxc=eth_l6_txc, name='eth_l6_sink') + + eth_l7_source = xgmii_ep.XGMIISource() + eth_l7_source_logic = eth_l7_source.create_logic(clk, rst, txd=eth_l7_rxd, txc=eth_l7_rxc, name='eth_l7_source') + + eth_l7_sink = xgmii_ep.XGMIISink() + eth_l7_sink_logic = eth_l7_sink.create_logic(clk, rst, rxd=eth_l7_txd, rxc=eth_l7_txc, name='eth_l7_sink') + + eth_l8_source = xgmii_ep.XGMIISource() + eth_l8_source_logic = eth_l8_source.create_logic(clk, rst, txd=eth_l8_rxd, txc=eth_l8_rxc, name='eth_l8_source') + + eth_l8_sink = xgmii_ep.XGMIISink() + eth_l8_sink_logic = eth_l8_sink.create_logic(clk, rst, rxd=eth_l8_txd, rxc=eth_l8_txc, name='eth_l8_sink') + + eth_l9_source = xgmii_ep.XGMIISource() + eth_l9_source_logic = eth_l9_source.create_logic(clk, rst, txd=eth_l9_rxd, txc=eth_l9_rxc, name='eth_l9_source') + + eth_l9_sink = xgmii_ep.XGMIISink() + eth_l9_sink_logic = eth_l9_sink.create_logic(clk, rst, rxd=eth_l9_txd, rxc=eth_l9_txc, name='eth_l9_sink') + + eth_l10_source = xgmii_ep.XGMIISource() + eth_l10_source_logic = eth_l10_source.create_logic(clk, rst, txd=eth_l10_rxd, txc=eth_l10_rxc, name='eth_l10_source') + + eth_l10_sink = xgmii_ep.XGMIISink() + eth_l10_sink_logic = eth_l10_sink.create_logic(clk, rst, rxd=eth_l10_txd, rxc=eth_l10_txc, name='eth_l10_sink') + + eth_l11_source = xgmii_ep.XGMIISource() + eth_l11_source_logic = eth_l11_source.create_logic(clk, rst, txd=eth_l11_rxd, txc=eth_l11_rxc, name='eth_l11_source') + + eth_l11_sink = xgmii_ep.XGMIISink() + eth_l11_sink_logic = eth_l11_sink.create_logic(clk, rst, rxd=eth_l11_txd, rxc=eth_l11_txc, name='eth_l11_sink') + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + sw=sw, + jp=jp, + led=led, + + uart_rst=uart_rst, + uart_suspend=uart_suspend, + uart_ri=uart_ri, + uart_dcd=uart_dcd, + uart_dtr=uart_dtr, + uart_dsr=uart_dsr, + uart_txd=uart_txd, + uart_rxd=uart_rxd, + uart_rts=uart_rts, + uart_cts=uart_cts, + + amh_right_mdc=amh_right_mdc, + amh_right_mdio_i=amh_right_mdio_i, + amh_right_mdio_o=amh_right_mdio_o, + amh_right_mdio_t=amh_right_mdio_t, + amh_left_mdc=amh_left_mdc, + amh_left_mdio_i=amh_left_mdio_i, + amh_left_mdio_o=amh_left_mdio_o, + amh_left_mdio_t=amh_left_mdio_t, + + eth_r0_txd=eth_r0_txd, + eth_r0_txc=eth_r0_txc, + eth_r0_rxd=eth_r0_rxd, + eth_r0_rxc=eth_r0_rxc, + eth_r1_txd=eth_r1_txd, + eth_r1_txc=eth_r1_txc, + eth_r1_rxd=eth_r1_rxd, + eth_r1_rxc=eth_r1_rxc, + eth_r2_txd=eth_r2_txd, + eth_r2_txc=eth_r2_txc, + eth_r2_rxd=eth_r2_rxd, + eth_r2_rxc=eth_r2_rxc, + eth_r3_txd=eth_r3_txd, + eth_r3_txc=eth_r3_txc, + eth_r3_rxd=eth_r3_rxd, + eth_r3_rxc=eth_r3_rxc, + eth_r4_txd=eth_r4_txd, + eth_r4_txc=eth_r4_txc, + eth_r4_rxd=eth_r4_rxd, + eth_r4_rxc=eth_r4_rxc, + eth_r5_txd=eth_r5_txd, + eth_r5_txc=eth_r5_txc, + eth_r5_rxd=eth_r5_rxd, + eth_r5_rxc=eth_r5_rxc, + eth_r6_txd=eth_r6_txd, + eth_r6_txc=eth_r6_txc, + eth_r6_rxd=eth_r6_rxd, + eth_r6_rxc=eth_r6_rxc, + eth_r7_txd=eth_r7_txd, + eth_r7_txc=eth_r7_txc, + eth_r7_rxd=eth_r7_rxd, + eth_r7_rxc=eth_r7_rxc, + eth_r8_txd=eth_r8_txd, + eth_r8_txc=eth_r8_txc, + eth_r8_rxd=eth_r8_rxd, + eth_r8_rxc=eth_r8_rxc, + eth_r9_txd=eth_r9_txd, + eth_r9_txc=eth_r9_txc, + eth_r9_rxd=eth_r9_rxd, + eth_r9_rxc=eth_r9_rxc, + eth_r10_txd=eth_r10_txd, + eth_r10_txc=eth_r10_txc, + eth_r10_rxd=eth_r10_rxd, + eth_r10_rxc=eth_r10_rxc, + eth_r11_txd=eth_r11_txd, + eth_r11_txc=eth_r11_txc, + eth_r11_rxd=eth_r11_rxd, + eth_r11_rxc=eth_r11_rxc, + eth_l0_txd=eth_l0_txd, + eth_l0_txc=eth_l0_txc, + eth_l0_rxd=eth_l0_rxd, + eth_l0_rxc=eth_l0_rxc, + eth_l1_txd=eth_l1_txd, + eth_l1_txc=eth_l1_txc, + eth_l1_rxd=eth_l1_rxd, + eth_l1_rxc=eth_l1_rxc, + eth_l2_txd=eth_l2_txd, + eth_l2_txc=eth_l2_txc, + eth_l2_rxd=eth_l2_rxd, + eth_l2_rxc=eth_l2_rxc, + eth_l3_txd=eth_l3_txd, + eth_l3_txc=eth_l3_txc, + eth_l3_rxd=eth_l3_rxd, + eth_l3_rxc=eth_l3_rxc, + eth_l4_txd=eth_l4_txd, + eth_l4_txc=eth_l4_txc, + eth_l4_rxd=eth_l4_rxd, + eth_l4_rxc=eth_l4_rxc, + eth_l5_txd=eth_l5_txd, + eth_l5_txc=eth_l5_txc, + eth_l5_rxd=eth_l5_rxd, + eth_l5_rxc=eth_l5_rxc, + eth_l6_txd=eth_l6_txd, + eth_l6_txc=eth_l6_txc, + eth_l6_rxd=eth_l6_rxd, + eth_l6_rxc=eth_l6_rxc, + eth_l7_txd=eth_l7_txd, + eth_l7_txc=eth_l7_txc, + eth_l7_rxd=eth_l7_rxd, + eth_l7_rxc=eth_l7_rxc, + eth_l8_txd=eth_l8_txd, + eth_l8_txc=eth_l8_txc, + eth_l8_rxd=eth_l8_rxd, + eth_l8_rxc=eth_l8_rxc, + eth_l9_txd=eth_l9_txd, + eth_l9_txc=eth_l9_txc, + eth_l9_rxd=eth_l9_rxd, + eth_l9_rxc=eth_l9_rxc, + eth_l10_txd=eth_l10_txd, + eth_l10_txc=eth_l10_txc, + eth_l10_rxd=eth_l10_rxd, + eth_l10_rxc=eth_l10_rxc, + eth_l11_txd=eth_l11_txd, + eth_l11_txc=eth_l11_txc, + eth_l11_rxd=eth_l11_rxd, + eth_l11_rxc=eth_l11_rxc + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(46)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + eth_l0_source.send(xgmii_frame) + + while eth_l0_sink.empty(): + yield clk.posedge + + check_frame = eth_l0_sink.recv() + + assert check_frame == xgmii_frame + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8099 + test_frame.payload = bytearray(range(15,-1,-1)) + bytearray([0]*(46-16)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + eth_l11_source.send(xgmii_frame) + + yield delay(400) + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(46)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + eth_l0_source.send(xgmii_frame) + + while eth_r7_sink.empty(): + yield clk.posedge + + check_frame = eth_r7_sink.recv() + + assert check_frame == xgmii_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/test_fpga_core.v b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/test_fpga_core.v new file mode 100644 index 000000000..370c25e29 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/test_fpga_core.v @@ -0,0 +1,416 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [1:0] sw = 0; +reg [3:0] jp = 0; +reg uart_suspend = 0; +reg uart_dtr = 0; +reg uart_txd = 0; +reg uart_rts = 0; +reg amh_right_mdio_i = 0; +reg amh_left_mdio_i = 0; +reg [63:0] eth_r0_rxd = 0; +reg [7:0] eth_r0_rxc = 0; +reg [63:0] eth_r1_rxd = 0; +reg [7:0] eth_r1_rxc = 0; +reg [63:0] eth_r2_rxd = 0; +reg [7:0] eth_r2_rxc = 0; +reg [63:0] eth_r3_rxd = 0; +reg [7:0] eth_r3_rxc = 0; +reg [63:0] eth_r4_rxd = 0; +reg [7:0] eth_r4_rxc = 0; +reg [63:0] eth_r5_rxd = 0; +reg [7:0] eth_r5_rxc = 0; +reg [63:0] eth_r6_rxd = 0; +reg [7:0] eth_r6_rxc = 0; +reg [63:0] eth_r7_rxd = 0; +reg [7:0] eth_r7_rxc = 0; +reg [63:0] eth_r8_rxd = 0; +reg [7:0] eth_r8_rxc = 0; +reg [63:0] eth_r9_rxd = 0; +reg [7:0] eth_r9_rxc = 0; +reg [63:0] eth_r10_rxd = 0; +reg [7:0] eth_r10_rxc = 0; +reg [63:0] eth_r11_rxd = 0; +reg [7:0] eth_r11_rxc = 0; +reg [63:0] eth_l0_rxd = 0; +reg [7:0] eth_l0_rxc = 0; +reg [63:0] eth_l1_rxd = 0; +reg [7:0] eth_l1_rxc = 0; +reg [63:0] eth_l2_rxd = 0; +reg [7:0] eth_l2_rxc = 0; +reg [63:0] eth_l3_rxd = 0; +reg [7:0] eth_l3_rxc = 0; +reg [63:0] eth_l4_rxd = 0; +reg [7:0] eth_l4_rxc = 0; +reg [63:0] eth_l5_rxd = 0; +reg [7:0] eth_l5_rxc = 0; +reg [63:0] eth_l6_rxd = 0; +reg [7:0] eth_l6_rxc = 0; +reg [63:0] eth_l7_rxd = 0; +reg [7:0] eth_l7_rxc = 0; +reg [63:0] eth_l8_rxd = 0; +reg [7:0] eth_l8_rxc = 0; +reg [63:0] eth_l9_rxd = 0; +reg [7:0] eth_l9_rxc = 0; +reg [63:0] eth_l10_rxd = 0; +reg [7:0] eth_l10_rxc = 0; +reg [63:0] eth_l11_rxd = 0; +reg [7:0] eth_l11_rxc = 0; + +// Outputs +wire [3:0] led; +wire uart_rst; +wire uart_ri; +wire uart_dcd; +wire uart_dsr; +wire uart_rxd; +wire uart_cts; +wire amh_right_mdc; +wire amh_right_mdio_o; +wire amh_right_mdio_t; +wire amh_left_mdc; +wire amh_left_mdio_o; +wire amh_left_mdio_t; +wire [63:0] eth_r0_txd; +wire [7:0] eth_r0_txc; +wire [63:0] eth_r1_txd; +wire [7:0] eth_r1_txc; +wire [63:0] eth_r2_txd; +wire [7:0] eth_r2_txc; +wire [63:0] eth_r3_txd; +wire [7:0] eth_r3_txc; +wire [63:0] eth_r4_txd; +wire [7:0] eth_r4_txc; +wire [63:0] eth_r5_txd; +wire [7:0] eth_r5_txc; +wire [63:0] eth_r6_txd; +wire [7:0] eth_r6_txc; +wire [63:0] eth_r7_txd; +wire [7:0] eth_r7_txc; +wire [63:0] eth_r8_txd; +wire [7:0] eth_r8_txc; +wire [63:0] eth_r9_txd; +wire [7:0] eth_r9_txc; +wire [63:0] eth_r10_txd; +wire [7:0] eth_r10_txc; +wire [63:0] eth_r11_txd; +wire [7:0] eth_r11_txc; +wire [63:0] eth_l0_txd; +wire [7:0] eth_l0_txc; +wire [63:0] eth_l1_txd; +wire [7:0] eth_l1_txc; +wire [63:0] eth_l2_txd; +wire [7:0] eth_l2_txc; +wire [63:0] eth_l3_txd; +wire [7:0] eth_l3_txc; +wire [63:0] eth_l4_txd; +wire [7:0] eth_l4_txc; +wire [63:0] eth_l5_txd; +wire [7:0] eth_l5_txc; +wire [63:0] eth_l6_txd; +wire [7:0] eth_l6_txc; +wire [63:0] eth_l7_txd; +wire [7:0] eth_l7_txc; +wire [63:0] eth_l8_txd; +wire [7:0] eth_l8_txc; +wire [63:0] eth_l9_txd; +wire [7:0] eth_l9_txc; +wire [63:0] eth_l10_txd; +wire [7:0] eth_l10_txc; +wire [63:0] eth_l11_txd; +wire [7:0] eth_l11_txc; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + sw, + jp, + uart_suspend, + uart_dtr, + uart_txd, + uart_rts, + amh_right_mdio_i, + amh_left_mdio_i, + eth_r0_rxd, + eth_r0_rxc, + eth_r1_rxd, + eth_r1_rxc, + eth_r2_rxd, + eth_r2_rxc, + eth_r3_rxd, + eth_r3_rxc, + eth_r4_rxd, + eth_r4_rxc, + eth_r5_rxd, + eth_r5_rxc, + eth_r6_rxd, + eth_r6_rxc, + eth_r7_rxd, + eth_r7_rxc, + eth_r8_rxd, + eth_r8_rxc, + eth_r9_rxd, + eth_r9_rxc, + eth_r10_rxd, + eth_r10_rxc, + eth_r11_rxd, + eth_r11_rxc, + eth_l0_rxd, + eth_l0_rxc, + eth_l1_rxd, + eth_l1_rxc, + eth_l2_rxd, + eth_l2_rxc, + eth_l3_rxd, + eth_l3_rxc, + eth_l4_rxd, + eth_l4_rxc, + eth_l5_rxd, + eth_l5_rxc, + eth_l6_rxd, + eth_l6_rxc, + eth_l7_rxd, + eth_l7_rxc, + eth_l8_rxd, + eth_l8_rxc, + eth_l9_rxd, + eth_l9_rxc, + eth_l10_rxd, + eth_l10_rxc, + eth_l11_rxd, + eth_l11_rxc + ); + $to_myhdl( + led, + uart_rst, + uart_ri, + uart_dcd, + uart_dsr, + uart_rxd, + uart_cts, + amh_right_mdc, + amh_right_mdio_o, + amh_right_mdio_t, + amh_left_mdc, + amh_left_mdio_o, + amh_left_mdio_t, + eth_r0_txd, + eth_r0_txc, + eth_r1_txd, + eth_r1_txc, + eth_r2_txd, + eth_r2_txc, + eth_r3_txd, + eth_r3_txc, + eth_r4_txd, + eth_r4_txc, + eth_r5_txd, + eth_r5_txc, + eth_r6_txd, + eth_r6_txc, + eth_r7_txd, + eth_r7_txc, + eth_r8_txd, + eth_r8_txc, + eth_r9_txd, + eth_r9_txc, + eth_r10_txd, + eth_r10_txc, + eth_r11_txd, + eth_r11_txc, + eth_l0_txd, + eth_l0_txc, + eth_l1_txd, + eth_l1_txc, + eth_l2_txd, + eth_l2_txc, + eth_l3_txd, + eth_l3_txc, + eth_l4_txd, + eth_l4_txc, + eth_l5_txd, + eth_l5_txc, + eth_l6_txd, + eth_l6_txc, + eth_l7_txd, + eth_l7_txc, + eth_l8_txd, + eth_l8_txc, + eth_l9_txd, + eth_l9_txc, + eth_l10_txd, + eth_l10_txc, + eth_l11_txd, + eth_l11_txc + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .sw(sw), + .jp(jp), + .led(led), + .uart_rst(uart_rst), + .uart_suspend(uart_suspend), + .uart_ri(uart_ri), + .uart_dcd(uart_dcd), + .uart_dtr(uart_dtr), + .uart_dsr(uart_dsr), + .uart_txd(uart_txd), + .uart_rxd(uart_rxd), + .uart_rts(uart_rts), + .uart_cts(uart_cts), + .amh_right_mdc(amh_right_mdc), + .amh_right_mdio_i(amh_right_mdio_i), + .amh_right_mdio_o(amh_right_mdio_o), + .amh_right_mdio_t(amh_right_mdio_t), + .amh_left_mdc(amh_left_mdc), + .amh_left_mdio_i(amh_left_mdio_i), + .amh_left_mdio_o(amh_left_mdio_o), + .amh_left_mdio_t(amh_left_mdio_t), + .eth_r0_txd(eth_r0_txd), + .eth_r0_txc(eth_r0_txc), + .eth_r0_rxd(eth_r0_rxd), + .eth_r0_rxc(eth_r0_rxc), + .eth_r1_txd(eth_r1_txd), + .eth_r1_txc(eth_r1_txc), + .eth_r1_rxd(eth_r1_rxd), + .eth_r1_rxc(eth_r1_rxc), + .eth_r2_txd(eth_r2_txd), + .eth_r2_txc(eth_r2_txc), + .eth_r2_rxd(eth_r2_rxd), + .eth_r2_rxc(eth_r2_rxc), + .eth_r3_txd(eth_r3_txd), + .eth_r3_txc(eth_r3_txc), + .eth_r3_rxd(eth_r3_rxd), + .eth_r3_rxc(eth_r3_rxc), + .eth_r4_txd(eth_r4_txd), + .eth_r4_txc(eth_r4_txc), + .eth_r4_rxd(eth_r4_rxd), + .eth_r4_rxc(eth_r4_rxc), + .eth_r5_txd(eth_r5_txd), + .eth_r5_txc(eth_r5_txc), + .eth_r5_rxd(eth_r5_rxd), + .eth_r5_rxc(eth_r5_rxc), + .eth_r6_txd(eth_r6_txd), + .eth_r6_txc(eth_r6_txc), + .eth_r6_rxd(eth_r6_rxd), + .eth_r6_rxc(eth_r6_rxc), + .eth_r7_txd(eth_r7_txd), + .eth_r7_txc(eth_r7_txc), + .eth_r7_rxd(eth_r7_rxd), + .eth_r7_rxc(eth_r7_rxc), + .eth_r8_txd(eth_r8_txd), + .eth_r8_txc(eth_r8_txc), + .eth_r8_rxd(eth_r8_rxd), + .eth_r8_rxc(eth_r8_rxc), + .eth_r9_txd(eth_r9_txd), + .eth_r9_txc(eth_r9_txc), + .eth_r9_rxd(eth_r9_rxd), + .eth_r9_rxc(eth_r9_rxc), + .eth_r10_txd(eth_r10_txd), + .eth_r10_txc(eth_r10_txc), + .eth_r10_rxd(eth_r10_rxd), + .eth_r10_rxc(eth_r10_rxc), + .eth_r11_txd(eth_r11_txd), + .eth_r11_txc(eth_r11_txc), + .eth_r11_rxd(eth_r11_rxd), + .eth_r11_rxc(eth_r11_rxc), + .eth_l0_txd(eth_l0_txd), + .eth_l0_txc(eth_l0_txc), + .eth_l0_rxd(eth_l0_rxd), + .eth_l0_rxc(eth_l0_rxc), + .eth_l1_txd(eth_l1_txd), + .eth_l1_txc(eth_l1_txc), + .eth_l1_rxd(eth_l1_rxd), + .eth_l1_rxc(eth_l1_rxc), + .eth_l2_txd(eth_l2_txd), + .eth_l2_txc(eth_l2_txc), + .eth_l2_rxd(eth_l2_rxd), + .eth_l2_rxc(eth_l2_rxc), + .eth_l3_txd(eth_l3_txd), + .eth_l3_txc(eth_l3_txc), + .eth_l3_rxd(eth_l3_rxd), + .eth_l3_rxc(eth_l3_rxc), + .eth_l4_txd(eth_l4_txd), + .eth_l4_txc(eth_l4_txc), + .eth_l4_rxd(eth_l4_rxd), + .eth_l4_rxc(eth_l4_rxc), + .eth_l5_txd(eth_l5_txd), + .eth_l5_txc(eth_l5_txc), + .eth_l5_rxd(eth_l5_rxd), + .eth_l5_rxc(eth_l5_rxc), + .eth_l6_txd(eth_l6_txd), + .eth_l6_txc(eth_l6_txc), + .eth_l6_rxd(eth_l6_rxd), + .eth_l6_rxc(eth_l6_rxc), + .eth_l7_txd(eth_l7_txd), + .eth_l7_txc(eth_l7_txc), + .eth_l7_rxd(eth_l7_rxd), + .eth_l7_rxc(eth_l7_rxc), + .eth_l8_txd(eth_l8_txd), + .eth_l8_txc(eth_l8_txc), + .eth_l8_rxd(eth_l8_rxd), + .eth_l8_rxc(eth_l8_rxc), + .eth_l9_txd(eth_l9_txd), + .eth_l9_txc(eth_l9_txc), + .eth_l9_rxd(eth_l9_rxd), + .eth_l9_rxc(eth_l9_rxc), + .eth_l10_txd(eth_l10_txd), + .eth_l10_txc(eth_l10_txc), + .eth_l10_rxd(eth_l10_rxd), + .eth_l10_rxc(eth_l10_rxc), + .eth_l11_txd(eth_l11_txd), + .eth_l11_txc(eth_l11_txc), + .eth_l11_rxd(eth_l11_rxd), + .eth_l11_rxc(eth_l11_rxc) +); + +endmodule diff --git a/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/xgmii_ep.py b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/xgmii_ep.py new file mode 120000 index 000000000..63b6d3567 --- /dev/null +++ b/fpga/lib/eth/example/HXT100G/fpga_cxpt16/tb/xgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/xgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/Makefile b/fpga/lib/eth/example/KC705/fpga_gmii/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/KC705/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/fpga/lib/eth/example/KC705/fpga_gmii/README.md b/fpga/lib/eth/example/KC705/fpga_gmii/README.md new file mode 100644 index 000000000..e524f6276 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/README.md @@ -0,0 +1,28 @@ +# Verilog Ethernet KC705 Example Design + +## Introduction + +This example design targets the Xilinx KC705 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. + +Configure the PHY for GMII by placing J29 and J30 across pins 1 and 2 and +opening J64. + +FPGA: XC7K325T-2FFG900C +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. + + diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/clock.xdc b/fpga/lib/eth/example/KC705/fpga_gmii/clock.xdc new file mode 100644 index 000000000..93d13048b --- /dev/null +++ b/fpga/lib/eth/example/KC705/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/fpga/lib/eth/example/KC705/fpga_gmii/common/vivado.mk b/fpga/lib/eth/example/KC705/fpga_gmii/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/KC705/fpga_gmii/fpga.xdc b/fpga/lib/eth/example/KC705/fpga_gmii/fpga.xdc new file mode 100644 index 000000000..e52f2e3b7 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/fpga.xdc @@ -0,0 +1,78 @@ +# XDC constraints for the Xilinx KC705 board +# part: xc7k325tffg900-2 + +# General configuration +set_property CFGBVS VCCO [current_design] +set_property CONFIG_VOLTAGE 2.5 [current_design] +set_property BITSTREAM.GENERAL.COMPRESS true [current_design] + +# System clocks +# 200 MHz +set_property -dict {LOC AD12 IOSTANDARD LVDS} [get_ports clk_200mhz_p] +set_property -dict {LOC AD11 IOSTANDARD LVDS} [get_ports clk_200mhz_n] +create_clock -period 5.000 -name clk_200mhz [get_ports clk_200mhz_p] + +# LEDs +set_property -dict {LOC AB8 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {led[0]}] +set_property -dict {LOC AA8 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {led[1]}] +set_property -dict {LOC AC9 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {led[2]}] +set_property -dict {LOC AB9 IOSTANDARD LVCMOS15 SLEW SLOW DRIVE 12} [get_ports {led[3]}] +set_property -dict {LOC AE26 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[4]}] +set_property -dict {LOC G19 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[5]}] +set_property -dict {LOC E18 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[6]}] +set_property -dict {LOC F16 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[7]}] + +# Reset button +set_property -dict {LOC AB7 IOSTANDARD LVCMOS15} [get_ports reset] + +# Push buttons +set_property -dict {LOC AA12 IOSTANDARD LVCMOS15} [get_ports btnu] +set_property -dict {LOC AC6 IOSTANDARD LVCMOS15} [get_ports btnl] +set_property -dict {LOC AB12 IOSTANDARD LVCMOS15} [get_ports btnd] +set_property -dict {LOC AG5 IOSTANDARD LVCMOS15} [get_ports btnr] +set_property -dict {LOC G12 IOSTANDARD LVCMOS25} [get_ports btnc] + +# Toggle switches +set_property -dict {LOC Y29 IOSTANDARD LVCMOS25} [get_ports {sw[0]}] +set_property -dict {LOC W29 IOSTANDARD LVCMOS25} [get_ports {sw[1]}] +set_property -dict {LOC AA28 IOSTANDARD LVCMOS25} [get_ports {sw[2]}] +set_property -dict {LOC Y28 IOSTANDARD LVCMOS25} [get_ports {sw[3]}] + +# UART +set_property -dict {LOC K24 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports uart_txd] +set_property -dict {LOC M19 IOSTANDARD LVCMOS25} [get_ports uart_rxd] +set_property -dict {LOC L27 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports uart_rts] +set_property -dict {LOC K23 IOSTANDARD LVCMOS25} [get_ports uart_cts] + +# Gigabit Ethernet GMII PHY +set_property -dict {LOC U27 IOSTANDARD LVCMOS25} [get_ports phy_rx_clk] +set_property -dict {LOC U30 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[0]}] +set_property -dict {LOC U25 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[1]}] +set_property -dict {LOC T25 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[2]}] +set_property -dict {LOC U28 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[3]}] +set_property -dict {LOC R19 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[4]}] +set_property -dict {LOC T27 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[5]}] +set_property -dict {LOC T26 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[6]}] +set_property -dict {LOC T28 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[7]}] +set_property -dict {LOC R28 IOSTANDARD LVCMOS25} [get_ports phy_rx_dv] +set_property -dict {LOC V26 IOSTANDARD LVCMOS25} [get_ports phy_rx_er] +set_property -dict {LOC K30 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_gtx_clk] +set_property -dict {LOC M28 IOSTANDARD LVCMOS25} [get_ports phy_tx_clk] +set_property -dict {LOC N27 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[0]}] +set_property -dict {LOC N25 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[1]}] +set_property -dict {LOC M29 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[2]}] +set_property -dict {LOC L28 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[3]}] +set_property -dict {LOC J26 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[4]}] +set_property -dict {LOC K26 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[5]}] +set_property -dict {LOC L30 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[6]}] +set_property -dict {LOC J28 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[7]}] +set_property -dict {LOC M27 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_tx_en] +set_property -dict {LOC N29 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_tx_er] +set_property -dict {LOC L20 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports phy_reset_n] +set_property -dict {LOC N30 IOSTANDARD LVCMOS25} [get_ports phy_int_n] +#set_property -dict {LOC J21 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports phy_mdio] +#set_property -dict {LOC R23 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports phy_mdc] + +create_clock -period 40.000 -name phy_tx_clk [get_ports phy_tx_clk] +create_clock -period 8.000 -name phy_rx_clk [get_ports phy_rx_clk] + diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/fpga/Makefile b/fpga/lib/eth/example/KC705/fpga_gmii/fpga/Makefile new file mode 100644 index 000000000..7231fad40 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/fpga/Makefile @@ -0,0 +1,65 @@ + +# 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_reset.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 + +# 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 + +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/fpga/lib/eth/example/KC705/fpga_gmii/lib/eth b/fpga/lib/eth/example/KC705/fpga_gmii/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/rtl/debounce_switch.v b/fpga/lib/eth/example/KC705/fpga_gmii/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/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 + +`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/fpga/lib/eth/example/KC705/fpga_gmii/rtl/fpga.v b/fpga/lib/eth/example/KC705/fpga_gmii/rtl/fpga.v new file mode 100644 index 000000000..8db1ad5c5 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/rtl/fpga.v @@ -0,0 +1,254 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 200MHz + * Reset: Push button, active high + */ + input wire clk_200mhz_p, + input wire clk_200mhz_n, + input wire reset, + + /* + * 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 GMII + */ + input wire phy_rx_clk, + input wire [7:0] phy_rxd, + input wire phy_rx_dv, + input wire phy_rx_er, + output wire phy_gtx_clk, + input wire phy_tx_clk, + output wire [7:0] phy_txd, + output wire phy_tx_en, + output wire phy_tx_er, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 500000 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// Clock and reset + +wire clk_200mhz_ibufg; +wire clk_200mhz_bufg; +wire clk_200mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_int; +wire rst_int; + +wire mmcm_rst = reset; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS +clk_200mhz_ibufgds_inst( + .I(clk_200mhz_p), + .IB(clk_200mhz_n), + .O(clk_200mhz_ibufg) +); + +// MMCM instance +// 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 +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(0), + .CLKOUT2_DIVIDE(1), + .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(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_bufg_inst ( + .I(clk_mmcm_out), + .O(clk_int) +); + +sync_reset #( + .N(4) +) +sync_reset_inst ( + .clk(clk_int), + .rst(~mmcm_locked), + .sync_reset_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; +wire uart_cts_int; + +sync_signal #( + .WIDTH(2), + .N(2) +) +sync_signal_inst ( + .clk(clk_int), + .in({uart_rxd, uart_cts}), + .out({uart_rxd_int, uart_cts_int}) +); + +fpga_core +core_inst ( + /* + * Clock: 125MHz + * Synchronous reset + */ + .clk(clk_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 GMII + */ + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_dv(phy_rx_dv), + .phy_rx_er(phy_rx_er), + .phy_gtx_clk(phy_gtx_clk), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_en(phy_tx_en), + .phy_tx_er(phy_tx_er), + .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), + .uart_rts(uart_rts), + .uart_cts(uart_cts_int) +); + +endmodule diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/rtl/fpga_core.v b/fpga/lib/eth/example/KC705/fpga_gmii/rtl/fpga_core.v new file mode 100644 index 000000000..cd2002c81 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/rtl/fpga_core.v @@ -0,0 +1,587 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * Clock: 125MHz + * Synchronous reset + */ + input wire clk, + 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 GMII + */ + input wire phy_rx_clk, + input wire [7:0] phy_rxd, + input wire phy_rx_dv, + input wire phy_rx_er, + output wire phy_gtx_clk, + input wire phy_tx_clk, + output wire [7:0] phy_txd, + output wire phy_tx_en, + output wire phy_tx_er, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 115200 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// 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; +assign uart_rts = 0; + +eth_mac_1g_gmii_fifo #( + .TARGET(TARGET), + .IODDR_STYLE("IODDR"), + .CLOCK_INPUT_STYLE("BUFR"), + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_inst ( + .gtx_clk(clk), + .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), + + .gmii_rx_clk(phy_rx_clk), + .gmii_rxd(phy_rxd), + .gmii_rx_dv(phy_rx_dv), + .gmii_rx_er(phy_rx_er), + .gmii_tx_clk(phy_gtx_clk), + .mii_tx_clk(phy_tx_clk), + .gmii_txd(phy_txd), + .gmii_tx_en(phy_tx_en), + .gmii_tx_er(phy_tx_er), + + .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 #( + .ADDR_WIDTH(12), + .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/fpga/lib/eth/example/KC705/fpga_gmii/rtl/sync_reset.v b/fpga/lib/eth/example/KC705/fpga_gmii/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/rtl/sync_signal.v b/fpga/lib/eth/example/KC705/fpga_gmii/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/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/fpga/lib/eth/example/KC705/fpga_gmii/tb/arp_ep.py b/fpga/lib/eth/example/KC705/fpga_gmii/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/tb/axis_ep.py b/fpga/lib/eth/example/KC705/fpga_gmii/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/tb/eth_ep.py b/fpga/lib/eth/example/KC705/fpga_gmii/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/tb/gmii_ep.py b/fpga/lib/eth/example/KC705/fpga_gmii/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/tb/ip_ep.py b/fpga/lib/eth/example/KC705/fpga_gmii/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/tb/test_fpga_core.py b/fpga/lib/eth/example/KC705/fpga_gmii/tb/test_fpga_core.py new file mode 100755 index 000000000..18864b43c --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/tb/test_fpga_core.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import gmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/oddr.v") +srcs.append("../lib/eth/rtl/ssio_sdr_in.v") +srcs.append("../lib/eth/rtl/ssio_sdr_out.v") +srcs.append("../lib/eth/rtl/gmii_phy_if.v") +srcs.append("../lib/eth/rtl/eth_mac_1g_gmii_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g_gmii.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/udp_complete.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen.v") +srcs.append("../lib/eth/rtl/udp.v") +srcs.append("../lib/eth/rtl/udp_ip_rx.v") +srcs.append("../lib/eth/rtl/udp_ip_tx.v") +srcs.append("../lib/eth/rtl/ip_complete.v") +srcs.append("../lib/eth/rtl/ip.v") +srcs.append("../lib/eth/rtl/ip_eth_rx.v") +srcs.append("../lib/eth/rtl/ip_eth_tx.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx.v") +srcs.append("../lib/eth/rtl/arp_eth_tx.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[4:]) + phy_rx_clk = Signal(bool(0)) + phy_rxd = Signal(intbv(0)[8:]) + phy_rx_dv = Signal(bool(0)) + phy_rx_er = Signal(bool(0)) + phy_tx_clk = Signal(bool(0)) + phy_int_n = Signal(bool(1)) + uart_rxd = Signal(bool(0)) + uart_cts = Signal(bool(0)) + + # Outputs + led = Signal(intbv(0)[8:]) + phy_gtx_clk = Signal(bool(0)) + phy_txd = Signal(intbv(0)[8:]) + phy_tx_en = Signal(bool(0)) + phy_tx_er = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + + # sources and sinks + mii_select = Signal(bool(0)) + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + phy_rx_clk, + rst, + txd=phy_rxd, + tx_en=phy_rx_dv, + tx_er=phy_rx_er, + mii_select=mii_select, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + phy_tx_clk, + rst, + rxd=phy_txd, + rx_dv=phy_tx_en, + rx_er=phy_tx_er, + mii_select=mii_select, + name='gmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + led=led, + + phy_rx_clk=phy_rx_clk, + phy_rxd=phy_rxd, + phy_rx_dv=phy_rx_dv, + phy_rx_er=phy_rx_er, + phy_gtx_clk=phy_gtx_clk, + phy_tx_clk=phy_tx_clk, + phy_txd=phy_txd, + phy_tx_en=phy_tx_en, + phy_tx_er=phy_tx_er, + phy_reset_n=phy_reset_n, + phy_int_n=phy_int_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd, + uart_rts=uart_rts, + uart_cts=uart_cts + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + phy_rx_clk.next = not phy_rx_clk + phy_tx_clk.next = not phy_tx_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/tb/test_fpga_core.v b/fpga/lib/eth/example/KC705/fpga_gmii/tb/test_fpga_core.v new file mode 100644 index 000000000..bbe377cba --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/tb/test_fpga_core.v @@ -0,0 +1,134 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters +parameter TARGET = "SIM"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [3:0] sw = 0; +reg phy_rx_clk = 0; +reg [7:0] phy_rxd = 0; +reg phy_rx_dv = 0; +reg phy_rx_er = 0; +reg phy_tx_clk = 0; +reg phy_int_n = 1; +reg uart_rxd = 0; +reg uart_cts = 0; + +// Outputs +wire [7:0] led; +wire phy_gtx_clk; +wire [7:0] phy_txd; +wire phy_tx_en; +wire phy_tx_er; +wire phy_reset_n; +wire uart_txd; +wire uart_rts; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + phy_rx_clk, + phy_rxd, + phy_rx_dv, + phy_rx_er, + phy_tx_clk, + phy_int_n, + uart_rxd, + uart_cts + ); + $to_myhdl( + led, + phy_gtx_clk, + phy_txd, + phy_tx_en, + phy_tx_er, + phy_reset_n, + uart_txd, + uart_rts + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core #( + .TARGET(TARGET) +) +UUT ( + .clk(clk), + .rst(rst), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .led(led), + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_dv(phy_rx_dv), + .phy_rx_er(phy_rx_er), + .phy_gtx_clk(phy_gtx_clk), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_en(phy_tx_en), + .phy_tx_er(phy_tx_er), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts) +); + +endmodule diff --git a/fpga/lib/eth/example/KC705/fpga_gmii/tb/udp_ep.py b/fpga/lib/eth/example/KC705/fpga_gmii/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/KC705/fpga_gmii/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/Makefile b/fpga/lib/eth/example/ML605/fpga_gmii/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/ML605/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/fpga/lib/eth/example/ML605/fpga_gmii/README.md b/fpga/lib/eth/example/ML605/fpga_gmii/README.md new file mode 100644 index 000000000..5b6159716 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/README.md @@ -0,0 +1,28 @@ +# Verilog Ethernet ML605 Example Design + +## Introduction + +This example design targets the Xilinx ML605 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. + +Configure the PHY for GMII by placing J66 and J67 across pins 1 and 2 and +opening J68. + +FPGA: XC6SlX45-2CSG324 +PHY: Marvell M88E1111 + +## How to build + +Run make to build. Ensure that the Xilinx ISE toolchain components are +in PATH. + +## How to test + +Run make program to program the ML605 board with the Xilinx Impact software. +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. + + diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/clock.ucf b/fpga/lib/eth/example/ML605/fpga_gmii/clock.ucf new file mode 100644 index 000000000..e048a42f1 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/clock.ucf @@ -0,0 +1,6 @@ +# UCF file for clock module domain crossing constraints + +NET "clk_125mhz_int" TNM = "ffs_clk_125mhz_int"; +NET "core_inst/eth_mac_inst/rx_clk" TNM = "ffs_gmii_rx_clk"; +TIMESPEC "TS_clk_125mhz_int_to_gmii_rx_clk" = FROM "ffs_clk_125mhz_int" TO "ffs_gmii_rx_clk" 10 ns; +TIMESPEC "TS_gmii_rx_clk_to_clk_125mhz_int" = FROM "ffs_gmii_rx_clk" TO "ffs_clk_125mhz_int" 10 ns; diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/common/xilinx.mk b/fpga/lib/eth/example/ML605/fpga_gmii/common/xilinx.mk new file mode 100644 index 000000000..f10a45f8e --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/common/xilinx.mk @@ -0,0 +1,191 @@ +############################################################################# +# Author: Lane Brooks/Keith Fife +# Date: 04/28/2006 +# License: GPL +# Desc: This is a Makefile intended to take a verilog rtl design +# through the Xilinx ISE tools to generate configuration files for +# Xilinx FPGAs. This file is generic and just a template. As such +# all design specific options such as synthesis files, fpga part type, +# prom part type, etc should be set in the top Makefile prior to +# including this file. Alternatively, all parameters can be passed +# in from the command line as well. +# +############################################################################## +# +# Parameter: +# SYN_FILES - Space seperated list of files to be synthesized +# PART - FPGA part (see Xilinx documentation) +# PROM - PROM part +# NGC_PATHS - Space seperated list of any dirs with pre-compiled ngc files. +# UCF_FILES - Space seperated list of user constraint files. Defaults to xilinx/$(FPGA_TOP).ucf +# +# +# Example Calling Makefile: +# +# SYN_FILES = fpga.v fifo.v clks.v +# PART = xc3s1000 +# FPGA_TOP = fpga +# PROM = xc18v04 +# NGC_PATH = ipLib1 ipLib2 +# FPGA_ARCH = spartan6 +# SPI_PROM_SIZE = (in bytes) +# include xilinx.mk +############################################################################# +# +# Command Line Example: +# make -f xilinx.mk PART=xc3s1000-4fg320 SYN_FILES="fpga.v test.v" FPGA_TOP=fpga +# +############################################################################## +# +# Required Setup: +# +# %.ucf - user constraint file. Needed by ngdbuild +# +# Optional Files: +# %.xcf - user constraint file. Needed by xst. +# %.ut - File for pin states needed by bitgen + + +.PHONY: clean bit prom fpga spi + + +# Mark the intermediate files as PRECIOUS to prevent make from +# deleting them (see make manual section 10.4). +.PRECIOUS: %.ngc %.ngd %_map.ncd %.ncd %.twr %.bit %_timesim.v + +# include the local Makefile for project for any project specific targets +CONFIG ?= config.mk +-include ../$(CONFIG) + +SYN_FILES_REL = $(patsubst %, ../%, $(SYN_FILES)) +INC_FILES_REL = $(patsubst %, ../%, $(INC_FILES)) +INC_PATHS_REL = $(patsubst %, ../%, $(INC_PATHS)) +NGC_PATHS_REL = $(patsubst %, ../%, $(NGC_PATHS)) + +ifdef UCF_FILES + UCF_FILES_REL = $(patsubst %, ../%, $(UCF_FILES)) +else + UCF_FILES_REL = $(FPGA_TOP).ucf +endif + + + +fpga: $(FPGA_TOP).bit + +mcs: $(FPGA_TOP).mcs + +prom: $(FPGA_TOP).spi + +spi: $(FPGA_TOP).spi + +fpgasim: $(FPGA_TOP)_sim.v + + +########################### XST TEMPLATES ############################ +# There are 2 files that XST uses for synthesis that we auto generate. +# The first is a project file which is just a list of all the verilog +# files. The second is the src file which passes XST all the options. +# See XST user manual for XST options. +%.ngc: $(SYN_FILES_REL) $(INC_FILES_REL) + rm -rf xst $*.prj $*.xst defines.v + touch defines.v + mkdir -p xst/tmp + for x in $(DEFS); do echo '`define' $$x >> defines.v; done + echo verilog work defines.v > $*.prj + for x in $(SYN_FILES_REL); do echo verilog work $$x >> $*.prj; done + @echo "set -tmpdir ./xst/tmp" >> $*.xst + @echo "set -xsthdpdir ./xst" >> $*.xst + @echo "run" >> $*.xst + @echo "-ifn $*.prj" >> $*.xst + @echo "-ifmt mixed" >> $*.xst + @echo "-top $*" >> $*.xst + @echo "-ofn $*" >> $*.xst + @echo "-ofmt NGC" >> $*.xst + @echo "-opt_mode Speed" >> $*.xst + @echo "-opt_level 1" >> $*.xst + # @echo "-verilog2001 YES" >> $*.xst + @echo "-keep_hierarchy NO" >> $*.xst + @echo "-p $(FPGA_PART)" >> $*.xst + xst -ifn $*.xst -ofn $*.log + + +########################### ISE TRANSLATE ############################ +# ngdbuild will automatically use a ucf called %.ucf if one is found. +# We setup the dependancy such that %.ucf file is required. If any +# pre-compiled ncd files are needed, set the NGC_PATH variable as a space +# seperated list of directories that include the pre-compiled ngc files. +%.ngd: %.ngc $(UCF_FILES_REL) + ngdbuild -dd ngdbuild $(patsubst %,-sd %, $(NGC_PATHS_REL)) $(patsubst %,-uc %, $(UCF_FILES_REL)) -p $(FPGA_PART) $< $@ + + +########################### ISE MAP ################################### +ifeq ($(FPGA_ARCH),spartan6) + MAP_OPTS= -register_duplication on -timing -xe n +else + MAP_OPTS= -cm speed -register_duplication on -timing -xe n -pr b +endif + +%_map.ncd: %.ngd + map -p $(FPGA_PART) $(MAP_OPTS) -w -o $@ $< $*.pcf + +# map -p $(FPGA_PART) -cm area -pr b -k 4 -c 100 -o $@ $< $*.pcf + + +########################### ISE PnR ################################### +%.ncd: %_map.ncd + par -w -ol high $< $@ $*.pcf + +# par -w -ol std -t 1 $< $@ $*.pcf + + +##################### ISE Static Timing Analysis ##################### +%.twr: %.ncd + -trce -e 3 -l 3 -u -xml $* $< -o $@ $*.pcf + +%_sim.v: %.ncd + netgen -s 4 -pcf $*.pcf -sdf_anno true -ism -sdf_path netgen -w -dir . -ofmt verilog -sim $< $@ + +# netgen -ise "/home/lane/Second/xilinx/Second/Second" -intstyle ise -s 4 -pcf Second.pcf -sdf_anno true -sdf_path netgen/par -w -dir netgen/par -ofmt verilog -sim Second.ncd Second_timesim.v + + +########################### ISE Bitgen ############################# +%.bit: %.twr + bitgen $(BITGEN_OPTIONS) -w $*.ncd $*.bit + mkdir -p rev + EXT=bit; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +########################### ISE Promgen ############################# +%.mcs: %.bit + promgen -spi -w -p mcs -s $(SPI_PROM_SIZE) -o $@ -u 0 $< + # promgen -w -p mcs -c FF -o $@ -u 0 $< -x $(PROM) + mkdir -p rev + EXT=mcs; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +%.spi: %.mcs + objcopy -I ihex -O binary $< $@ + EXT=spi; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + + +tmpclean: + -rm -rf xst ngdbuild *_map.* *.ncd *.ngc *.log *.xst *.prj *.lso *~ *.pcf *.bld *.ngd *.xpi *_pad.* *.unroutes *.twx *.par *.twr *.pad *.drc *.bgn *.prm *.sig netgen *.v *.nlf *.xml + +clean: tmpclean + -rm -rf *.bit *.mcs + +# clean everything +distclean: clean + -rm -rf rev + diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/fpga.ucf b/fpga/lib/eth/example/ML605/fpga_gmii/fpga.ucf new file mode 100644 index 000000000..1da1978d6 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/fpga.ucf @@ -0,0 +1,87 @@ +# User Constraints File for the Xilinx ML605 board, rev C + +#CONFIG PART = xc6vlx130t-1ff1156; +#CONFIG PART = xc6vlx240t-1ff1156; + +# 200MHz clock +NET "sys_clk_p" LOC = "J9" | IOSTANDARD=LVDS_25; # Bank = 34, IO_L0P_GC_34 (GCLK) +NET "sys_clk_n" LOC = "H9" | IOSTANDARD=LVDS_25; # Bank = 34, IO_L0N_GC_34 (GCLK) +NET "sys_clk_p" TNM_NET = "sys_clk_pin"; +TIMESPEC "TS_sys_clk_pin" = PERIOD "sys_clk_pin" 200000 kHz; + +# Light Emitting Diodes +NET "ledu" LOC = "AH27" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 23, IO_L0P_23 (DS20) +NET "ledl" LOC = "AD21" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 22, IO_L0N_22 (DS17) +NET "ledd" LOC = "AH28" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 23, IO_L0N_23 (DS18) +NET "ledr" LOC = "AE21" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 22, IO_L0P_22 (DS19) +NET "ledc" LOC = "AP24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 23, IO_L19N_23 (DS16) + +NET "led<0>" LOC = "AC22" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L19P_24 (DS12) +NET "led<1>" LOC = "AC24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L18N_24 (DS11) +NET "led<2>" LOC = "AE22" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L17N_VRP_24 (DS9) +NET "led<3>" LOC = "AE23" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L17P_VRN_24 (DS10) +NET "led<4>" LOC = "AB23" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L16N_CSO_B_24 (DS15) +NET "led<5>" LOC = "AG23" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L15N_RS1_24 (DS14) +NET "led<6>" LOC = "AE24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L11N_SRCC_24 (DS22) +NET "led<7>" LOC = "AD24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L11P_SRCC_24 (DS21) + +# Reset Button: I/O Bank 2 +NET "reset" LOC = "H10" | IOSTANDARD=LVCMOS15; # Bank = 35, IO_L6P_SM3P_35 (SW10) + +# Push Buttons: I/O Bank 3 +NET "btnu" LOC = "A19" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L15N_26 (SW5) +NET "btnl" LOC = "H17" | IOSTANDARD=LVCMOS15; # Bank = 36, IO_L3P_36 (SW8) +NET "btnd" LOC = "A18" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L15P_26 (SW6) +NET "btnr" LOC = "G17" | IOSTANDARD=LVCMOS15; # Bank = 36, IO_L3N_36 (SW7) +NET "btnc" LOC = "G26" | IOSTANDARD=LVCMOS15; # Bank = 25, IO_L6P_25 (SW9) + +# Toggle Switches +NET "sw<0>" LOC = "D22" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L19N_26 (SW1.1) +NET "sw<1>" LOC = "C22" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L19P_26 (SW1.2) +NET "sw<2>" LOC = "L21" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L18N_26 (SW1.3) +NET "sw<3>" LOC = "L20" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L18P_26 (SW1.4) +NET "sw<4>" LOC = "C18" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L17N_26 (SW1.5) +NET "sw<5>" LOC = "B18" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L17P_26 (SW1.6) +NET "sw<6>" LOC = "K22" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L16N_26 (SW1.7) +NET "sw<7>" LOC = "K21" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L16P_26 (SW1.8) + +# Marvell M88E1111 Tri-Mode Ethernet PHY (1000BASE-T) +# Interrupt, Reset, MDIO +#NET "phy_int_n" LOC = "AH14" | IOSTANDARD=LVCMOS25; # (E-INT) +NET "phy_reset_n" LOC = "AH13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L18P_33 (E-RESET) +#NET "phy_mdc" LOC = "AP14" | IOSTANDARD=LVCMOS25; # (E-MDC) +#NET "phy_mdio" LOC = "AN14" | IOSTANDARD=LVCMOS25; # (E-MDIO) +# GMII Transmit +NET "phy_gtx_clk" LOC = "AH12" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L16N_33 (E-GTXCLK) +NET "phy_tx_clk" LOC = "AD12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L10P_MRCC_33 (E-TXCLK) +NET "phy_txd<0>" LOC = "AM11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L7N_33 (E-TXD0) +NET "phy_txd<1>" LOC = "AL11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L7P_33 (E-TXD1) +NET "phy_txd<2>" LOC = "AG10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L6N_33 (E-TXD2) +NET "phy_txd<3>" LOC = "AG11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L6P_33 (E-TXD3) +NET "phy_txd<4>" LOC = "AL10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L5N_33 (E-TXD4) +NET "phy_txd<5>" LOC = "AM10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L5P_33 (E-TXD5) +NET "phy_txd<6>" LOC = "AE11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L4N_VREF_33 (E-TXD6) +NET "phy_txd<7>" LOC = "AF11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L4P_33 (E-TXD7) +NET "phy_tx_en" LOC = "AJ10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L8P_SRCC_33 (E-TXEN) +NET "phy_tx_er" LOC = "AH10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L8N_SRCC_33 (E-TXER) +# GMII Receive +NET "phy_rx_clk" LOC = "AP11" | IOSTANDARD=LVCMOS25 | TNM_NET = "clk_rx_local"; # (E-RXCLK) +NET "phy_rxd<0>" LOC = "AN13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L15P_33 (E-RXD0) +NET "phy_rxd<1>" LOC = "AF14" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L14N_VREF_33 (E-RXD1) +NET "phy_rxd<2>" LOC = "AE14" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L14P_33 (E-RXD2) +NET "phy_rxd<3>" LOC = "AN12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L13N_33 (E-RXD3) +NET "phy_rxd<4>" LOC = "AM12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L13P_33 (E-RXD4) +NET "phy_rxd<5>" LOC = "AD11" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L10N_MRCC_33 (E-RXD5) +NET "phy_rxd<6>" LOC = "AC12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L9N_MRCC_33 (E-RXD6) +NET "phy_rxd<7>" LOC = "AC13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L9P_MRCC_33 (E-RXD7) +NET "phy_rx_dv" LOC = "AM13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L15N_33 (E-RXDV) +NET "phy_rx_er" LOC = "AG12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L16P_33 (E-RXER) + +# Timing constraints for Ethernet PHY +TIMESPEC "TS_rx_clk_root" = PERIOD "clk_rx_local" 8000 ps HIGH 50 %; + +# Silicon Labs CP2103 +NET "uart_rxd" LOC = "J25" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L9P_MRCC_24 (U24.24) +NET "uart_txd" LOC = "J24" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L9N_MRCC_24 (U24.25) +NET "uart_rts" LOC = "T23" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L8N_SRCC_24 (U24.23) +NET "uart_cts" LOC = "T24" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L8P_SRCC_24 (U24.22) diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/fpga_130t/Makefile b/fpga/lib/eth/example/ML605/fpga_gmii/fpga_130t/Makefile new file mode 100644 index 000000000..cdecade0c --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/fpga_130t/Makefile @@ -0,0 +1,71 @@ + +# FPGA settings +FPGA_PART = xc6vlx130t-1ff1156 +FPGA_TOP = fpga +FPGA_ARCH = spartan6 + +# PROM settings +#PROM = xc18v04 +#SPI_PROM_SIZE = (in bytes) + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.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_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 += coregen/dcm_i100_o125/dcm_i100_o125.v + +# UCF files +UCF_FILES = fpga.ucf +UCF_FILES += clock.ucf + +# NGC paths for ngdbuild +#NGC_PATHS = coregen/dcm_i100_o125 + +# Bitgen options +BITGEN_OPTIONS = -g StartupClk:Cclk -g ConfigRate:26 + +include ../common/xilinx.mk + +program: $(FPGA_TOP).bit + echo "setmode -bscan" > program.cmd + echo "setcable -p auto" >> program.cmd + echo "identify" >> program.cmd + echo "assignfile -p 2 -file $(FPGA_TOP).bit" >> program.cmd + echo "program -p 2" >> program.cmd + echo "quit" >> program.cmd + impact -batch program.cmd + diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/fpga_240t/Makefile b/fpga/lib/eth/example/ML605/fpga_gmii/fpga_240t/Makefile new file mode 100644 index 000000000..54a553c06 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/fpga_240t/Makefile @@ -0,0 +1,71 @@ + +# FPGA settings +FPGA_PART = xc6vlx240t-1ff1156 +FPGA_TOP = fpga +FPGA_ARCH = spartan6 + +# PROM settings +#PROM = xc18v04 +#SPI_PROM_SIZE = (in bytes) + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.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_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 += coregen/dcm_i100_o125/dcm_i100_o125.v + +# UCF files +UCF_FILES = fpga.ucf +UCF_FILES += clock.ucf + +# NGC paths for ngdbuild +#NGC_PATHS = coregen/dcm_i100_o125 + +# Bitgen options +BITGEN_OPTIONS = -g StartupClk:Cclk -g ConfigRate:26 + +include ../common/xilinx.mk + +program: $(FPGA_TOP).bit + echo "setmode -bscan" > program.cmd + echo "setcable -p auto" >> program.cmd + echo "identify" >> program.cmd + echo "assignfile -p 2 -file $(FPGA_TOP).bit" >> program.cmd + echo "program -p 2" >> program.cmd + echo "quit" >> program.cmd + impact -batch program.cmd + diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/lib/eth b/fpga/lib/eth/example/ML605/fpga_gmii/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/rtl/debounce_switch.v b/fpga/lib/eth/example/ML605/fpga_gmii/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/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 + +`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/fpga/lib/eth/example/ML605/fpga_gmii/rtl/fpga.v b/fpga/lib/eth/example/ML605/fpga_gmii/rtl/fpga.v new file mode 100644 index 000000000..566fd7547 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/rtl/fpga.v @@ -0,0 +1,280 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 200MHz + * Reset: Push button, active high + */ + input wire sys_clk_p, + input wire sys_clk_n, + input wire reset, + /* + * GPIO + */ + input wire btnu, + input wire btnl, + input wire btnd, + input wire btnr, + input wire btnc, + input wire [7:0] sw, + output wire ledu, + output wire ledl, + output wire ledd, + output wire ledr, + output wire ledc, + output wire [7:0] led, + /* + * Ethernet: 1000BASE-T GMII + */ + input wire phy_rx_clk, + input wire [7:0] phy_rxd, + input wire phy_rx_dv, + input wire phy_rx_er, + output wire phy_gtx_clk, + input wire phy_tx_clk, + output wire [7:0] phy_txd, + output wire phy_tx_en, + output wire phy_tx_er, + output wire phy_reset_n, + /* + * Silicon Labs CP2103 USB UART + */ + output wire uart_rxd, + input wire uart_txd, + input wire uart_rts, + output wire uart_cts +); + +// Clock and reset + +wire sys_clk_ibufg; +wire sys_clk_bufg; +wire clk_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire rst_125mhz_int; + +wire mmcm_rst = reset; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS +clk_ibufgds_inst( + .I(sys_clk_p), + .IB(sys_clk_n), + .O(sys_clk_ibufg) +); + +// MMCM instance +// 200 MHz in, 125 MHz out +// PFD range: 10 MHz to 450 MHz +// VCO range: 600 MHz to 1200 MHz +// M = 5, D = 1 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +MMCM_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(8), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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.100), + .CLKIN1_PERIOD(5.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(sys_clk_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_125mhz_bufg_inst ( + .I(clk_125mhz_mmcm_out), + .O(clk_125mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_int) +); + +// GPIO +wire btnu_int; +wire btnl_int; +wire btnd_int; +wire btnr_int; +wire btnc_int; +wire [7:0] sw_int; + +wire ledu_int; +wire ledl_int; +wire ledd_int; +wire ledr_int; +wire ledc_int; +wire [7:0] led_int; + +wire uart_rxd_int; +wire uart_txd_int; +wire uart_rts_int; +wire uart_cts_int; + +debounce_switch #( + .WIDTH(13), + .N(4), + .RATE(125000) +) +debounce_switch_inst ( + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + .in({btnu, + btnl, + btnd, + btnr, + btnc, + sw}), + .out({btnu_int, + btnl_int, + btnd_int, + btnr_int, + btnc_int, + sw_int}) +); + +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}) +); + +assign ledu = ledu_int; +assign ledl = ledl_int; +assign ledd = ledd_int; +assign ledr = ledr_int; +assign ledc = ledc_int; +assign led = led_int; + +assign uart_rxd = uart_rxd_int; +assign uart_cts = uart_cts_int; + +fpga_core +core_inst ( + /* + * Clock: 125MHz + * Synchronous reset + */ + .clk_125mhz(clk_125mhz_int), + .rst_125mhz(rst_125mhz_int), + /* + * GPIO + */ + .btnu(btnu_int), + .btnl(btnl_int), + .btnd(btnd_int), + .btnr(btnr_int), + .btnc(btnc_int), + .sw(sw_int), + .ledu(ledu_int), + .ledl(ledl_int), + .ledd(ledd_int), + .ledr(ledr_int), + .ledc(ledc_int), + .led(led_int), + /* + * Ethernet: 1000BASE-T GMII + */ + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_dv(phy_rx_dv), + .phy_rx_er(phy_rx_er), + .phy_gtx_clk(phy_gtx_clk), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_en(phy_tx_en), + .phy_tx_er(phy_tx_er), + .phy_reset_n(phy_reset_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd_int), + .uart_rts(uart_rts_int), + .uart_cts(uart_cts_int) +); + +endmodule diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/rtl/fpga_core.v b/fpga/lib/eth/example/ML605/fpga_gmii/rtl/fpga_core.v new file mode 100644 index 000000000..9db205054 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/rtl/fpga_core.v @@ -0,0 +1,596 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * Clock: 125MHz + * Synchronous reset + */ + input wire clk_125mhz, + input wire rst_125mhz, + + /* + * GPIO + */ + input wire btnu, + input wire btnl, + input wire btnd, + input wire btnr, + input wire btnc, + input wire [7:0] sw, + output wire ledu, + output wire ledl, + output wire ledd, + output wire ledr, + output wire ledc, + output wire [7:0] led, + + /* + * Ethernet: 1000BASE-T GMII + */ + input wire phy_rx_clk, + input wire [7:0] phy_rxd, + input wire phy_rx_dv, + input wire phy_rx_er, + output wire phy_gtx_clk, + input wire phy_tx_clk, + output wire [7:0] phy_txd, + output wire phy_tx_en, + output wire phy_tx_er, + output wire phy_reset_n, + + /* + * Silicon Labs CP2103 USB UART + */ + output wire uart_rxd, + input wire uart_txd, + input wire uart_rts, + output wire uart_cts +); + +// 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_125mhz) begin + if (rst_125mhz) 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_125mhz) begin + if (rst_125mhz) 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 ledu = 0; +assign ledl = 0; +assign ledd = 0; +assign ledr = 0; +assign ledc = 0; +assign led = led_reg; +assign phy_reset_n = !rst_125mhz; + +assign uart_rxd = 0; +assign uart_cts = 0; + +eth_mac_1g_gmii_fifo #( + .TARGET(TARGET), + .IODDR_STYLE("IODDR"), + .CLOCK_INPUT_STYLE("BUFR"), + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_inst ( + .gtx_clk(clk_125mhz), + .gtx_rst(rst_125mhz), + .logic_clk(clk_125mhz), + .logic_rst(rst_125mhz), + + .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), + + .gmii_rx_clk(phy_rx_clk), + .gmii_rxd(phy_rxd), + .gmii_rx_dv(phy_rx_dv), + .gmii_rx_er(phy_rx_er), + .gmii_tx_clk(phy_gtx_clk), + .mii_tx_clk(phy_tx_clk), + .gmii_txd(phy_txd), + .gmii_tx_en(phy_tx_en), + .gmii_tx_er(phy_tx_er), + + .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_125mhz), + .rst(rst_125mhz), + // 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_125mhz), + .rst(rst_125mhz), + // 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_125mhz), + .rst(rst_125mhz), + // 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 #( + .ADDR_WIDTH(12), + .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_125mhz), + .rst(rst_125mhz), + + // 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/fpga/lib/eth/example/ML605/fpga_gmii/rtl/sync_reset.v b/fpga/lib/eth/example/ML605/fpga_gmii/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/rtl/sync_signal.v b/fpga/lib/eth/example/ML605/fpga_gmii/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/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/fpga/lib/eth/example/ML605/fpga_gmii/tb/arp_ep.py b/fpga/lib/eth/example/ML605/fpga_gmii/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/tb/axis_ep.py b/fpga/lib/eth/example/ML605/fpga_gmii/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/tb/eth_ep.py b/fpga/lib/eth/example/ML605/fpga_gmii/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/tb/gmii_ep.py b/fpga/lib/eth/example/ML605/fpga_gmii/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/tb/ip_ep.py b/fpga/lib/eth/example/ML605/fpga_gmii/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/tb/test_fpga_core.py b/fpga/lib/eth/example/ML605/fpga_gmii/tb/test_fpga_core.py new file mode 100755 index 000000000..f52a00a56 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/tb/test_fpga_core.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import gmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/iddr.v") +srcs.append("../lib/eth/rtl/oddr.v") +srcs.append("../lib/eth/rtl/ssio_sdr_in.v") +srcs.append("../lib/eth/rtl/ssio_sdr_out.v") +srcs.append("../lib/eth/rtl/gmii_phy_if.v") +srcs.append("../lib/eth/rtl/eth_mac_1g_gmii_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g_gmii.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/udp_complete.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen.v") +srcs.append("../lib/eth/rtl/udp.v") +srcs.append("../lib/eth/rtl/udp_ip_rx.v") +srcs.append("../lib/eth/rtl/udp_ip_tx.v") +srcs.append("../lib/eth/rtl/ip_complete.v") +srcs.append("../lib/eth/rtl/ip.v") +srcs.append("../lib/eth/rtl/ip_eth_rx.v") +srcs.append("../lib/eth/rtl/ip_eth_tx.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx.v") +srcs.append("../lib/eth/rtl/arp_eth_tx.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + clk_125mhz = Signal(bool(0)) + rst_125mhz = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[8:]) + phy_rx_clk = Signal(bool(0)) + phy_rxd = Signal(intbv(0)[8:]) + phy_rx_dv = Signal(bool(0)) + phy_rx_er = Signal(bool(0)) + phy_tx_clk = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + + # Outputs + ledu = Signal(bool(0)) + ledl = Signal(bool(0)) + ledd = Signal(bool(0)) + ledr = Signal(bool(0)) + ledc = Signal(bool(0)) + led = Signal(intbv(0)[8:]) + phy_gtx_clk = Signal(bool(0)) + phy_txd = Signal(intbv(0)[8:]) + phy_tx_en = Signal(bool(0)) + phy_tx_er = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_rxd = Signal(bool(0)) + uart_cts = Signal(bool(0)) + + # sources and sinks + mii_select = Signal(bool(0)) + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + phy_rx_clk, + rst, + txd=phy_rxd, + tx_en=phy_rx_dv, + tx_er=phy_rx_er, + mii_select=mii_select, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + phy_tx_clk, + rst, + rxd=phy_txd, + rx_dv=phy_tx_en, + rx_er=phy_tx_er, + mii_select=mii_select, + name='gmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk_125mhz=clk_125mhz, + rst_125mhz=rst_125mhz, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + ledu=ledu, + ledl=ledl, + ledd=ledd, + ledr=ledr, + ledc=ledc, + led=led, + + phy_rx_clk=phy_rx_clk, + phy_rxd=phy_rxd, + phy_rx_dv=phy_rx_dv, + phy_rx_er=phy_rx_er, + phy_gtx_clk=phy_gtx_clk, + phy_tx_clk=phy_tx_clk, + phy_txd=phy_txd, + phy_tx_en=phy_tx_en, + phy_tx_er=phy_tx_er, + phy_reset_n=phy_reset_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd, + uart_rts=uart_rts, + uart_cts=uart_cts + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + clk_125mhz.next = not clk_125mhz + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + phy_rx_clk.next = not phy_rx_clk + phy_tx_clk.next = not phy_tx_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + rst_125mhz.next = 1 + yield clk.posedge + rst.next = 0 + rst_125mhz.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/tb/test_fpga_core.v b/fpga/lib/eth/example/ML605/fpga_gmii/tb/test_fpga_core.v new file mode 100644 index 000000000..b237ed924 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/tb/test_fpga_core.v @@ -0,0 +1,146 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters +parameter TARGET = "SIM"; + +// Inputs +reg clk_125mhz = 0; +reg rst_125mhz = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [7:0] sw = 0; +reg phy_rx_clk = 0; +reg [7:0] phy_rxd = 0; +reg phy_rx_dv = 0; +reg phy_rx_er = 0; +reg phy_tx_clk = 0; +reg uart_txd = 0; +reg uart_rts = 0; + +// Outputs +wire ledu; +wire ledl; +wire ledd; +wire ledr; +wire ledc; +wire [7:0] led; +wire phy_gtx_clk; +wire [7:0] phy_txd; +wire phy_tx_en; +wire phy_tx_er; +wire phy_reset_n; +wire uart_rxd; +wire uart_cts; + +initial begin + // myhdl integration + $from_myhdl( + clk_125mhz, + rst_125mhz, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + phy_rx_clk, + phy_rxd, + phy_rx_dv, + phy_rx_er, + phy_tx_clk, + uart_txd, + uart_rts + ); + $to_myhdl( + ledu, + ledl, + ledd, + ledr, + ledc, + led, + phy_gtx_clk, + phy_txd, + phy_tx_en, + phy_tx_er, + phy_reset_n, + uart_rxd, + uart_cts + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core #( + .TARGET(TARGET) +) +UUT ( + .clk_125mhz(clk_125mhz), + .rst_125mhz(rst_125mhz), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .ledu(ledu), + .ledl(ledl), + .ledd(ledd), + .ledr(ledr), + .ledc(ledc), + .led(led), + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_dv(phy_rx_dv), + .phy_rx_er(phy_rx_er), + .phy_gtx_clk(phy_gtx_clk), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_en(phy_tx_en), + .phy_tx_er(phy_tx_er), + .phy_reset_n(phy_reset_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts) +); + +endmodule diff --git a/fpga/lib/eth/example/ML605/fpga_gmii/tb/udp_ep.py b/fpga/lib/eth/example/ML605/fpga_gmii/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_gmii/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/Makefile b/fpga/lib/eth/example/ML605/fpga_rgmii/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/ML605/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 Atlys --index 0 --file fpga/fpga.bit diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/README.md b/fpga/lib/eth/example/ML605/fpga_rgmii/README.md new file mode 100644 index 000000000..43005aad5 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/README.md @@ -0,0 +1,28 @@ +# Verilog Ethernet ML605 Example Design + +## Introduction + +This example design targets the Xilinx ML605 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. + +Configure the PHY for RGMII by placing J66 across pins 1 and 2, opening J67, +and shorting J68. + +FPGA: XC6SlX45-2CSG324 +PHY: Marvell M88E1111 + +## How to build + +Run make to build. Ensure that the Xilinx ISE toolchain components are +in PATH. + +## How to test + +Run make program to program the ML605 board with the Xilinx Impact software. +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. + + diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/clock.ucf b/fpga/lib/eth/example/ML605/fpga_rgmii/clock.ucf new file mode 100644 index 000000000..e048a42f1 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/clock.ucf @@ -0,0 +1,6 @@ +# UCF file for clock module domain crossing constraints + +NET "clk_125mhz_int" TNM = "ffs_clk_125mhz_int"; +NET "core_inst/eth_mac_inst/rx_clk" TNM = "ffs_gmii_rx_clk"; +TIMESPEC "TS_clk_125mhz_int_to_gmii_rx_clk" = FROM "ffs_clk_125mhz_int" TO "ffs_gmii_rx_clk" 10 ns; +TIMESPEC "TS_gmii_rx_clk_to_clk_125mhz_int" = FROM "ffs_gmii_rx_clk" TO "ffs_clk_125mhz_int" 10 ns; diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/common/xilinx.mk b/fpga/lib/eth/example/ML605/fpga_rgmii/common/xilinx.mk new file mode 100644 index 000000000..f10a45f8e --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/common/xilinx.mk @@ -0,0 +1,191 @@ +############################################################################# +# Author: Lane Brooks/Keith Fife +# Date: 04/28/2006 +# License: GPL +# Desc: This is a Makefile intended to take a verilog rtl design +# through the Xilinx ISE tools to generate configuration files for +# Xilinx FPGAs. This file is generic and just a template. As such +# all design specific options such as synthesis files, fpga part type, +# prom part type, etc should be set in the top Makefile prior to +# including this file. Alternatively, all parameters can be passed +# in from the command line as well. +# +############################################################################## +# +# Parameter: +# SYN_FILES - Space seperated list of files to be synthesized +# PART - FPGA part (see Xilinx documentation) +# PROM - PROM part +# NGC_PATHS - Space seperated list of any dirs with pre-compiled ngc files. +# UCF_FILES - Space seperated list of user constraint files. Defaults to xilinx/$(FPGA_TOP).ucf +# +# +# Example Calling Makefile: +# +# SYN_FILES = fpga.v fifo.v clks.v +# PART = xc3s1000 +# FPGA_TOP = fpga +# PROM = xc18v04 +# NGC_PATH = ipLib1 ipLib2 +# FPGA_ARCH = spartan6 +# SPI_PROM_SIZE = (in bytes) +# include xilinx.mk +############################################################################# +# +# Command Line Example: +# make -f xilinx.mk PART=xc3s1000-4fg320 SYN_FILES="fpga.v test.v" FPGA_TOP=fpga +# +############################################################################## +# +# Required Setup: +# +# %.ucf - user constraint file. Needed by ngdbuild +# +# Optional Files: +# %.xcf - user constraint file. Needed by xst. +# %.ut - File for pin states needed by bitgen + + +.PHONY: clean bit prom fpga spi + + +# Mark the intermediate files as PRECIOUS to prevent make from +# deleting them (see make manual section 10.4). +.PRECIOUS: %.ngc %.ngd %_map.ncd %.ncd %.twr %.bit %_timesim.v + +# include the local Makefile for project for any project specific targets +CONFIG ?= config.mk +-include ../$(CONFIG) + +SYN_FILES_REL = $(patsubst %, ../%, $(SYN_FILES)) +INC_FILES_REL = $(patsubst %, ../%, $(INC_FILES)) +INC_PATHS_REL = $(patsubst %, ../%, $(INC_PATHS)) +NGC_PATHS_REL = $(patsubst %, ../%, $(NGC_PATHS)) + +ifdef UCF_FILES + UCF_FILES_REL = $(patsubst %, ../%, $(UCF_FILES)) +else + UCF_FILES_REL = $(FPGA_TOP).ucf +endif + + + +fpga: $(FPGA_TOP).bit + +mcs: $(FPGA_TOP).mcs + +prom: $(FPGA_TOP).spi + +spi: $(FPGA_TOP).spi + +fpgasim: $(FPGA_TOP)_sim.v + + +########################### XST TEMPLATES ############################ +# There are 2 files that XST uses for synthesis that we auto generate. +# The first is a project file which is just a list of all the verilog +# files. The second is the src file which passes XST all the options. +# See XST user manual for XST options. +%.ngc: $(SYN_FILES_REL) $(INC_FILES_REL) + rm -rf xst $*.prj $*.xst defines.v + touch defines.v + mkdir -p xst/tmp + for x in $(DEFS); do echo '`define' $$x >> defines.v; done + echo verilog work defines.v > $*.prj + for x in $(SYN_FILES_REL); do echo verilog work $$x >> $*.prj; done + @echo "set -tmpdir ./xst/tmp" >> $*.xst + @echo "set -xsthdpdir ./xst" >> $*.xst + @echo "run" >> $*.xst + @echo "-ifn $*.prj" >> $*.xst + @echo "-ifmt mixed" >> $*.xst + @echo "-top $*" >> $*.xst + @echo "-ofn $*" >> $*.xst + @echo "-ofmt NGC" >> $*.xst + @echo "-opt_mode Speed" >> $*.xst + @echo "-opt_level 1" >> $*.xst + # @echo "-verilog2001 YES" >> $*.xst + @echo "-keep_hierarchy NO" >> $*.xst + @echo "-p $(FPGA_PART)" >> $*.xst + xst -ifn $*.xst -ofn $*.log + + +########################### ISE TRANSLATE ############################ +# ngdbuild will automatically use a ucf called %.ucf if one is found. +# We setup the dependancy such that %.ucf file is required. If any +# pre-compiled ncd files are needed, set the NGC_PATH variable as a space +# seperated list of directories that include the pre-compiled ngc files. +%.ngd: %.ngc $(UCF_FILES_REL) + ngdbuild -dd ngdbuild $(patsubst %,-sd %, $(NGC_PATHS_REL)) $(patsubst %,-uc %, $(UCF_FILES_REL)) -p $(FPGA_PART) $< $@ + + +########################### ISE MAP ################################### +ifeq ($(FPGA_ARCH),spartan6) + MAP_OPTS= -register_duplication on -timing -xe n +else + MAP_OPTS= -cm speed -register_duplication on -timing -xe n -pr b +endif + +%_map.ncd: %.ngd + map -p $(FPGA_PART) $(MAP_OPTS) -w -o $@ $< $*.pcf + +# map -p $(FPGA_PART) -cm area -pr b -k 4 -c 100 -o $@ $< $*.pcf + + +########################### ISE PnR ################################### +%.ncd: %_map.ncd + par -w -ol high $< $@ $*.pcf + +# par -w -ol std -t 1 $< $@ $*.pcf + + +##################### ISE Static Timing Analysis ##################### +%.twr: %.ncd + -trce -e 3 -l 3 -u -xml $* $< -o $@ $*.pcf + +%_sim.v: %.ncd + netgen -s 4 -pcf $*.pcf -sdf_anno true -ism -sdf_path netgen -w -dir . -ofmt verilog -sim $< $@ + +# netgen -ise "/home/lane/Second/xilinx/Second/Second" -intstyle ise -s 4 -pcf Second.pcf -sdf_anno true -sdf_path netgen/par -w -dir netgen/par -ofmt verilog -sim Second.ncd Second_timesim.v + + +########################### ISE Bitgen ############################# +%.bit: %.twr + bitgen $(BITGEN_OPTIONS) -w $*.ncd $*.bit + mkdir -p rev + EXT=bit; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +########################### ISE Promgen ############################# +%.mcs: %.bit + promgen -spi -w -p mcs -s $(SPI_PROM_SIZE) -o $@ -u 0 $< + # promgen -w -p mcs -c FF -o $@ -u 0 $< -x $(PROM) + mkdir -p rev + EXT=mcs; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +%.spi: %.mcs + objcopy -I ihex -O binary $< $@ + EXT=spi; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + + +tmpclean: + -rm -rf xst ngdbuild *_map.* *.ncd *.ngc *.log *.xst *.prj *.lso *~ *.pcf *.bld *.ngd *.xpi *_pad.* *.unroutes *.twx *.par *.twr *.pad *.drc *.bgn *.prm *.sig netgen *.v *.nlf *.xml + +clean: tmpclean + -rm -rf *.bit *.mcs + +# clean everything +distclean: clean + -rm -rf rev + diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/fpga.ucf b/fpga/lib/eth/example/ML605/fpga_rgmii/fpga.ucf new file mode 100644 index 000000000..e87d4ed39 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/fpga.ucf @@ -0,0 +1,87 @@ +# User Constraints File for the Xilinx ML605 board, rev C + +#CONFIG PART = xc6vlx130t-1ff1156; +#CONFIG PART = xc6vlx240t-1ff1156; + +# 200MHz clock +NET "sys_clk_p" LOC = "J9" | IOSTANDARD=LVDS_25; # Bank = 34, IO_L0P_GC_34 (GCLK) +NET "sys_clk_n" LOC = "H9" | IOSTANDARD=LVDS_25; # Bank = 34, IO_L0N_GC_34 (GCLK) +NET "sys_clk_p" TNM_NET = "sys_clk_pin"; +TIMESPEC "TS_sys_clk_pin" = PERIOD "sys_clk_pin" 200000 kHz; + +# Light Emitting Diodes +NET "ledu" LOC = "AH27" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 23, IO_L0P_23 (DS20) +NET "ledl" LOC = "AD21" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 22, IO_L0N_22 (DS17) +NET "ledd" LOC = "AH28" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 23, IO_L0N_23 (DS18) +NET "ledr" LOC = "AE21" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 22, IO_L0P_22 (DS19) +NET "ledc" LOC = "AP24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 23, IO_L19N_23 (DS16) + +NET "led<0>" LOC = "AC22" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L19P_24 (DS12) +NET "led<1>" LOC = "AC24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L18N_24 (DS11) +NET "led<2>" LOC = "AE22" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L17N_VRP_24 (DS9) +NET "led<3>" LOC = "AE23" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L17P_VRN_24 (DS10) +NET "led<4>" LOC = "AB23" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L16N_CSO_B_24 (DS15) +NET "led<5>" LOC = "AG23" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L15N_RS1_24 (DS14) +NET "led<6>" LOC = "AE24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L11N_SRCC_24 (DS22) +NET "led<7>" LOC = "AD24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L11P_SRCC_24 (DS21) + +# Reset Button: I/O Bank 2 +NET "reset" LOC = "H10" | IOSTANDARD=LVCMOS15; # Bank = 35, IO_L6P_SM3P_35 (SW10) + +# Push Buttons: I/O Bank 3 +NET "btnu" LOC = "A19" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L15N_26 (SW5) +NET "btnl" LOC = "H17" | IOSTANDARD=LVCMOS15; # Bank = 36, IO_L3P_36 (SW8) +NET "btnd" LOC = "A18" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L15P_26 (SW6) +NET "btnr" LOC = "G17" | IOSTANDARD=LVCMOS15; # Bank = 36, IO_L3N_36 (SW7) +NET "btnc" LOC = "G26" | IOSTANDARD=LVCMOS15; # Bank = 25, IO_L6P_25 (SW9) + +# Toggle Switches +NET "sw<0>" LOC = "D22" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L19N_26 (SW1.1) +NET "sw<1>" LOC = "C22" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L19P_26 (SW1.2) +NET "sw<2>" LOC = "L21" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L18N_26 (SW1.3) +NET "sw<3>" LOC = "L20" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L18P_26 (SW1.4) +NET "sw<4>" LOC = "C18" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L17N_26 (SW1.5) +NET "sw<5>" LOC = "B18" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L17P_26 (SW1.6) +NET "sw<6>" LOC = "K22" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L16N_26 (SW1.7) +NET "sw<7>" LOC = "K21" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L16P_26 (SW1.8) + +# Marvell M88E1111 Tri-Mode Ethernet PHY (1000BASE-T) +# Interrupt, Reset, MDIO +#NET "phy_int_n" LOC = "AH14" | IOSTANDARD=LVCMOS25; # (E-INT) +NET "phy_reset_n" LOC = "AH13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L18P_33 (E-RESET) +#NET "phy_mdc" LOC = "AP14" | IOSTANDARD=LVCMOS25; # (E-MDC) +#NET "phy_mdio" LOC = "AN14" | IOSTANDARD=LVCMOS25; # (E-MDIO) +# RGMII Transmit +NET "phy_tx_clk" LOC = "AH12" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L16N_33 (E-GTXCLK) +#NET "phy_tx_clk" LOC = "AD12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L10P_MRCC_33 (E-TXCLK) +NET "phy_txd<0>" LOC = "AM11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L7N_33 (E-TXD0) +NET "phy_txd<1>" LOC = "AL11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L7P_33 (E-TXD1) +NET "phy_txd<2>" LOC = "AG10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L6N_33 (E-TXD2) +NET "phy_txd<3>" LOC = "AG11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L6P_33 (E-TXD3) +#NET "phy_txd<4>" LOC = "AL10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L5N_33 (E-TXD4) +#NET "phy_txd<5>" LOC = "AM10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L5P_33 (E-TXD5) +#NET "phy_txd<6>" LOC = "AE11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L4N_VREF_33 (E-TXD6) +#NET "phy_txd<7>" LOC = "AF11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L4P_33 (E-TXD7) +NET "phy_tx_ctl" LOC = "AJ10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L8P_SRCC_33 (E-TXEN) +#NET "phy_tx_er" LOC = "AH10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L8N_SRCC_33 (E-TXER) +# RGMII Receive +NET "phy_rx_clk" LOC = "AP11" | IOSTANDARD=LVCMOS25 | TNM_NET = "clk_rx_local"; # (E-RXCLK) +NET "phy_rxd<0>" LOC = "AN13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L15P_33 (E-RXD0) +NET "phy_rxd<1>" LOC = "AF14" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L14N_VREF_33 (E-RXD1) +NET "phy_rxd<2>" LOC = "AE14" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L14P_33 (E-RXD2) +NET "phy_rxd<3>" LOC = "AN12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L13N_33 (E-RXD3) +#NET "phy_rxd<4>" LOC = "AM12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L13P_33 (E-RXD4) +#NET "phy_rxd<5>" LOC = "AD11" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L10N_MRCC_33 (E-RXD5) +#NET "phy_rxd<6>" LOC = "AC12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L9N_MRCC_33 (E-RXD6) +#NET "phy_rxd<7>" LOC = "AC13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L9P_MRCC_33 (E-RXD7) +NET "phy_rx_ctl" LOC = "AM13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L15N_33 (E-RXDV) +#NET "phy_rx_er" LOC = "AG12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L16P_33 (E-RXER) + +# Timing constraints for Ethernet PHY +TIMESPEC "TS_rx_clk_root" = PERIOD "clk_rx_local" 8000 ps HIGH 50 %; + +# Silicon Labs CP2103 +NET "uart_rxd" LOC = "J25" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L9P_MRCC_24 (U24.24) +NET "uart_txd" LOC = "J24" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L9N_MRCC_24 (U24.25) +NET "uart_rts" LOC = "T23" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L8N_SRCC_24 (U24.23) +NET "uart_cts" LOC = "T24" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L8P_SRCC_24 (U24.22) diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/fpga_130t/Makefile b/fpga/lib/eth/example/ML605/fpga_rgmii/fpga_130t/Makefile new file mode 100644 index 000000000..b315b1c06 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/fpga_130t/Makefile @@ -0,0 +1,71 @@ + +# FPGA settings +FPGA_PART = xc6vlx130t-1ff1156 +FPGA_TOP = fpga +FPGA_ARCH = spartan6 + +# PROM settings +#PROM = xc18v04 +#SPI_PROM_SIZE = (in bytes) + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.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/ssio_ddr_out.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 += coregen/dcm_i100_o125/dcm_i100_o125.v + +# UCF files +UCF_FILES = fpga.ucf +UCF_FILES += clock.ucf + +# NGC paths for ngdbuild +#NGC_PATHS = coregen/dcm_i100_o125 + +# Bitgen options +BITGEN_OPTIONS = -g StartupClk:Cclk -g ConfigRate:26 + +include ../common/xilinx.mk + +program: $(FPGA_TOP).bit + echo "setmode -bscan" > program.cmd + echo "setcable -p auto" >> program.cmd + echo "identify" >> program.cmd + echo "assignfile -p 2 -file $(FPGA_TOP).bit" >> program.cmd + echo "program -p 2" >> program.cmd + echo "quit" >> program.cmd + impact -batch program.cmd + diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/fpga_240t/Makefile b/fpga/lib/eth/example/ML605/fpga_rgmii/fpga_240t/Makefile new file mode 100644 index 000000000..4d46cec26 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/fpga_240t/Makefile @@ -0,0 +1,71 @@ + +# FPGA settings +FPGA_PART = xc6vlx240t-1ff1156 +FPGA_TOP = fpga +FPGA_ARCH = spartan6 + +# PROM settings +#PROM = xc18v04 +#SPI_PROM_SIZE = (in bytes) + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.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/ssio_ddr_out.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 += coregen/dcm_i100_o125/dcm_i100_o125.v + +# UCF files +UCF_FILES = fpga.ucf +UCF_FILES += clock.ucf + +# NGC paths for ngdbuild +#NGC_PATHS = coregen/dcm_i100_o125 + +# Bitgen options +BITGEN_OPTIONS = -g StartupClk:Cclk -g ConfigRate:26 + +include ../common/xilinx.mk + +program: $(FPGA_TOP).bit + echo "setmode -bscan" > program.cmd + echo "setcable -p auto" >> program.cmd + echo "identify" >> program.cmd + echo "assignfile -p 2 -file $(FPGA_TOP).bit" >> program.cmd + echo "program -p 2" >> program.cmd + echo "quit" >> program.cmd + impact -batch program.cmd + diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/lib/eth b/fpga/lib/eth/example/ML605/fpga_rgmii/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/debounce_switch.v b/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/ML605/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 + +`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/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/fpga.v b/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/fpga.v new file mode 100644 index 000000000..ba445b48c --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/fpga.v @@ -0,0 +1,283 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 200MHz + * Reset: Push button, active high + */ + input wire sys_clk_p, + input wire sys_clk_n, + input wire reset, + /* + * GPIO + */ + input wire btnu, + input wire btnl, + input wire btnd, + input wire btnr, + input wire btnc, + input wire [7:0] sw, + output wire ledu, + output wire ledl, + output wire ledd, + output wire ledr, + output wire ledc, + 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, + /* + * Silicon Labs CP2103 USB UART + */ + output wire uart_rxd, + input wire uart_txd, + input wire uart_rts, + output wire uart_cts +); + +// Clock and reset + +wire sys_clk_ibufg; +wire sys_clk_bufg; +wire clk_125mhz_mmcm_out; +wire clk90_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire clk90_125mhz_int; +wire rst_125mhz_int; + +wire mmcm_rst = reset; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS +clk_ibufgds_inst( + .I(sys_clk_p), + .IB(sys_clk_n), + .O(sys_clk_ibufg) +); + +// MMCM instance +// 200 MHz in, 125 MHz out +// PFD range: 10 MHz to 450 MHz +// VCO range: 600 MHz to 1200 MHz +// M = 5, D = 1 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +MMCM_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(1), + .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.100), + .CLKIN1_PERIOD(5.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(sys_clk_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(clk90_125mhz_mmcm_out), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .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) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_int) +); + +// GPIO +wire btnu_int; +wire btnl_int; +wire btnd_int; +wire btnr_int; +wire btnc_int; +wire [7:0] sw_int; + +wire ledu_int; +wire ledl_int; +wire ledd_int; +wire ledr_int; +wire ledc_int; +wire [7:0] led_int; + +wire uart_rxd_int; +wire uart_txd_int; +wire uart_rts_int; +wire uart_cts_int; + +debounce_switch #( + .WIDTH(13), + .N(4), + .RATE(125000) +) +debounce_switch_inst ( + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + .in({btnu, + btnl, + btnd, + btnr, + btnc, + sw}), + .out({btnu_int, + btnl_int, + btnd_int, + btnr_int, + btnc_int, + sw_int}) +); + +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}) +); + +assign ledu = ledu_int; +assign ledl = ledl_int; +assign ledd = ledd_int; +assign ledr = ledr_int; +assign ledc = ledc_int; +assign led = led_int; + +assign uart_rxd = uart_rxd_int; +assign uart_cts = uart_cts_int; + +fpga_core +core_inst ( + /* + * Clock: 125MHz + * Synchronous reset + */ + .clk_125mhz(clk_125mhz_int), + .clk90_125mhz(clk90_125mhz_int), + .rst_125mhz(rst_125mhz_int), + /* + * GPIO + */ + .btnu(btnu_int), + .btnl(btnl_int), + .btnd(btnd_int), + .btnr(btnr_int), + .btnc(btnc_int), + .sw(sw_int), + .ledu(ledu_int), + .ledl(ledl_int), + .ledd(ledd_int), + .ledr(ledr_int), + .ledc(ledc_int), + .led(led_int), + /* + * Ethernet: 1000BASE-T RGMII + */ + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_ctl(phy_rx_ctl), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_ctl(phy_tx_ctl), + .phy_reset_n(phy_reset_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd_int), + .uart_rts(uart_rts_int), + .uart_cts(uart_cts_int) +); + +endmodule diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/fpga_core.v b/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/fpga_core.v new file mode 100644 index 000000000..d01a4034c --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/fpga_core.v @@ -0,0 +1,593 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * Clock: 125MHz + * Synchronous reset + */ + input wire clk_125mhz, + input wire clk90_125mhz, + input wire rst_125mhz, + + /* + * GPIO + */ + input wire btnu, + input wire btnl, + input wire btnd, + input wire btnr, + input wire btnc, + input wire [7:0] sw, + output wire ledu, + output wire ledl, + output wire ledd, + output wire ledr, + output wire ledc, + 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, + + /* + * Silicon Labs CP2103 USB UART + */ + output wire uart_rxd, + input wire uart_txd, + input wire uart_rts, + output wire uart_cts +); + +// 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_125mhz) begin + if (rst_125mhz) 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_125mhz) begin + if (rst_125mhz) 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 ledu = 0; +assign ledl = 0; +assign ledd = 0; +assign ledr = 0; +assign ledc = 0; +assign led = led_reg; +assign phy_reset_n = !rst_125mhz; + +assign uart_rxd = 0; +assign uart_cts = 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_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_inst ( + .gtx_clk(clk_125mhz), + .gtx_clk90(clk90_125mhz), + .gtx_rst(rst_125mhz), + .logic_clk(clk_125mhz), + .logic_rst(rst_125mhz), + + .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_125mhz), + .rst(rst_125mhz), + // 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_125mhz), + .rst(rst_125mhz), + // 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_125mhz), + .rst(rst_125mhz), + // 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 #( + .ADDR_WIDTH(12), + .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_125mhz), + .rst(rst_125mhz), + + // 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/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/sync_reset.v b/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/sync_signal.v b/fpga/lib/eth/example/ML605/fpga_rgmii/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/ML605/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/fpga/lib/eth/example/ML605/fpga_rgmii/tb/arp_ep.py b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/tb/axis_ep.py b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/tb/eth_ep.py b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/tb/gmii_ep.py b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/tb/ip_ep.py b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/tb/rgmii_ep.py b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/rgmii_ep.py new file mode 120000 index 000000000..986c56280 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/rgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/rgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/tb/test_fpga_core.py b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/test_fpga_core.py new file mode 100755 index 000000000..5b03aa9ae --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/test_fpga_core.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import rgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/iddr.v") +srcs.append("../lib/eth/rtl/oddr.v") +srcs.append("../lib/eth/rtl/ssio_ddr_in.v") +srcs.append("../lib/eth/rtl/ssio_ddr_out.v") +srcs.append("../lib/eth/rtl/rgmii_phy_if.v") +srcs.append("../lib/eth/rtl/eth_mac_1g_rgmii_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g_rgmii.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/udp_complete.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen.v") +srcs.append("../lib/eth/rtl/udp.v") +srcs.append("../lib/eth/rtl/udp_ip_rx.v") +srcs.append("../lib/eth/rtl/udp_ip_tx.v") +srcs.append("../lib/eth/rtl/ip_complete.v") +srcs.append("../lib/eth/rtl/ip.v") +srcs.append("../lib/eth/rtl/ip_eth_rx.v") +srcs.append("../lib/eth/rtl/ip_eth_tx.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx.v") +srcs.append("../lib/eth/rtl/arp_eth_tx.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + clk_125mhz = Signal(bool(0)) + clk90_125mhz = Signal(bool(0)) + rst_125mhz = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[8:]) + phy_rx_clk = Signal(bool(0)) + phy_rxd = Signal(intbv(0)[4:]) + phy_rx_ctl = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + + # Outputs + ledu = Signal(bool(0)) + ledl = Signal(bool(0)) + ledd = Signal(bool(0)) + ledr = Signal(bool(0)) + ledc = Signal(bool(0)) + led = Signal(intbv(0)[8:]) + phy_tx_clk = Signal(bool(0)) + phy_txd = Signal(intbv(0)[4:]) + phy_tx_ctl = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_rxd = Signal(bool(0)) + uart_cts = Signal(bool(0)) + + # sources and sinks + mii_select = Signal(bool(0)) + + rgmii_source = rgmii_ep.RGMIISource() + + rgmii_source_logic = rgmii_source.create_logic( + phy_rx_clk, + rst, + txd=phy_rxd, + tx_ctl=phy_rx_ctl, + mii_select=mii_select, + name='rgmii_source' + ) + + rgmii_sink = rgmii_ep.RGMIISink() + + rgmii_sink_logic = rgmii_sink.create_logic( + phy_tx_clk, + rst, + rxd=phy_txd, + rx_ctl=phy_tx_ctl, + mii_select=mii_select, + name='rgmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk_125mhz=clk_125mhz, + clk90_125mhz=clk90_125mhz, + rst_125mhz=rst_125mhz, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + ledu=ledu, + ledl=ledl, + ledd=ledd, + ledr=ledr, + ledc=ledc, + led=led, + + phy_rx_clk=phy_rx_clk, + phy_rxd=phy_rxd, + phy_rx_ctl=phy_rx_ctl, + phy_tx_clk=phy_tx_clk, + phy_txd=phy_txd, + phy_tx_ctl=phy_tx_ctl, + phy_reset_n=phy_reset_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd, + uart_rts=uart_rts, + uart_cts=uart_cts + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + clk_125mhz.next = not clk_125mhz + + @instance + def clkgen2(): + yield delay(4+2) + while True: + clk90_125mhz.next = not clk90_125mhz + yield delay(4) + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + phy_rx_clk.next = not phy_rx_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + rst_125mhz.next = 1 + yield clk.posedge + rst.next = 0 + rst_125mhz.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + rgmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while rgmii_sink.empty(): + yield clk.posedge + + rx_frame = rgmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + rgmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while rgmii_sink.empty(): + yield clk.posedge + + rx_frame = rgmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert rgmii_source.empty() + assert rgmii_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/tb/test_fpga_core.v b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/test_fpga_core.v new file mode 100644 index 000000000..a2c412486 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/test_fpga_core.v @@ -0,0 +1,140 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters +parameter TARGET = "SIM"; + +// Inputs +reg clk_125mhz = 0; +reg clk90_125mhz = 0; +reg rst_125mhz = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [7:0] sw = 0; +reg phy_rx_clk = 0; +reg [3:0] phy_rxd = 0; +reg phy_rx_ctl = 0; +reg uart_txd = 0; +reg uart_rts = 0; + +// Outputs +wire ledu; +wire ledl; +wire ledd; +wire ledr; +wire ledc; +wire [7:0] led; +wire phy_tx_clk; +wire [3:0] phy_txd; +wire phy_tx_ctl; +wire phy_reset_n; +wire uart_rxd; +wire uart_cts; + +initial begin + // myhdl integration + $from_myhdl( + clk_125mhz, + clk90_125mhz, + rst_125mhz, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + phy_rx_clk, + phy_rxd, + phy_rx_ctl, + uart_txd, + uart_rts + ); + $to_myhdl( + ledu, + ledl, + ledd, + ledr, + ledc, + led, + phy_tx_clk, + phy_txd, + phy_tx_ctl, + phy_reset_n, + uart_rxd, + uart_cts + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core #( + .TARGET(TARGET) +) +UUT ( + .clk_125mhz(clk_125mhz), + .clk90_125mhz(clk90_125mhz), + .rst_125mhz(rst_125mhz), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .ledu(ledu), + .ledl(ledl), + .ledd(ledd), + .ledr(ledr), + .ledc(ledc), + .led(led), + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_ctl(phy_rx_ctl), + .phy_tx_clk(phy_tx_clk), + .phy_txd(phy_txd), + .phy_tx_ctl(phy_tx_ctl), + .phy_reset_n(phy_reset_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts) +); + +endmodule diff --git a/fpga/lib/eth/example/ML605/fpga_rgmii/tb/udp_ep.py b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_rgmii/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/Makefile b/fpga/lib/eth/example/ML605/fpga_sgmii/Makefile new file mode 100644 index 000000000..9f8bd4048 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/Makefile @@ -0,0 +1,25 @@ +# Targets +TARGETS:= + +# Subdirectories +SUBDIRS = coregen 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/fpga/lib/eth/example/ML605/fpga_sgmii/README.md b/fpga/lib/eth/example/ML605/fpga_sgmii/README.md new file mode 100644 index 000000000..b08c34422 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/README.md @@ -0,0 +1,28 @@ +# Verilog Ethernet ML605 SGMII Example Design + +## Introduction + +This example design targets the Xilinx ML605 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. + +Configure the PHY for SGMII by placing J66 and J67 across pins 2 and 3 and +opening J68. + +FPGA: XC6SlX45-2CSG324 +PHY: Marvell M88E1111 + +## How to build + +Run make to build. Ensure that the Xilinx ISE toolchain components are +in PATH. + +## How to test + +Run make program to program the ML605 board with the Xilinx Impact software. +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. + + diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/clock.ucf b/fpga/lib/eth/example/ML605/fpga_sgmii/clock.ucf new file mode 100644 index 000000000..7033a3576 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/clock.ucf @@ -0,0 +1,24 @@ +# UCF file for clock module domain crossing constraints + +NET "phy_sgmii_txoutclk" TNM_NET = "txoutclk"; +TIMESPEC "TS_txoutclk" = PERIOD "txoutclk" 8 ns HIGH 50 %; + +NET "eth_pcspma/transceiver_inst/RXRECCLK" TNM_NET = "rxrecclk"; +TIMESPEC "ts_rxrecclk" = PERIOD "rxrecclk" 8 ns; + +# Identify clock domain crossing registers +INST "eth_pcspma/transceiver_inst/rx_elastic_buffer_inst/wr_addr_gray*" TNM = "wr_graycode"; +INST "eth_pcspma/transceiver_inst/rx_elastic_buffer_inst/rd_addr_gray*" TNM = "rd_graycode"; + +# Control Gray Code delay and skew across clock boundary +TIMESPEC "ts_rx_skew_control1" = FROM "wr_graycode" TO "FFS" 14 ns DATAPATHONLY; +TIMESPEC "ts_rx_skew_control2" = FROM "rd_graycode" TO "FFS" 14 ns DATAPATHONLY; + +# Constrain between Distributed Memory (output data) and the 1st set of flip-flops +INST "eth_pcspma/transceiver_inst/rx_elastic_buffer_inst/*rd_data*" TNM = "fifo_read"; +TIMESPEC "ts_ram_read_false_path" = FROM "RAMS" TO "fifo_read" 6 ns DATAPATHONLY; + +NET "clk_125mhz_int" TNM = "ffs_clk_125mhz_int"; +NET "phy_sgmii_txoutclk" TNM = "ffs_sgmii_clk"; +TIMESPEC "TS_clk_125mhz_int_to_sgmii_clk" = FROM "ffs_clk_125mhz_int" TO "ffs_sgmii_clk" 10 ns; +TIMESPEC "TS_sgmii_clk_to_clk_125mhz_int" = FROM "ffs_sgmii_clk" TO "ffs_clk_125mhz_int" 10 ns; diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/common/xilinx.mk b/fpga/lib/eth/example/ML605/fpga_sgmii/common/xilinx.mk new file mode 100644 index 000000000..f10a45f8e --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/common/xilinx.mk @@ -0,0 +1,191 @@ +############################################################################# +# Author: Lane Brooks/Keith Fife +# Date: 04/28/2006 +# License: GPL +# Desc: This is a Makefile intended to take a verilog rtl design +# through the Xilinx ISE tools to generate configuration files for +# Xilinx FPGAs. This file is generic and just a template. As such +# all design specific options such as synthesis files, fpga part type, +# prom part type, etc should be set in the top Makefile prior to +# including this file. Alternatively, all parameters can be passed +# in from the command line as well. +# +############################################################################## +# +# Parameter: +# SYN_FILES - Space seperated list of files to be synthesized +# PART - FPGA part (see Xilinx documentation) +# PROM - PROM part +# NGC_PATHS - Space seperated list of any dirs with pre-compiled ngc files. +# UCF_FILES - Space seperated list of user constraint files. Defaults to xilinx/$(FPGA_TOP).ucf +# +# +# Example Calling Makefile: +# +# SYN_FILES = fpga.v fifo.v clks.v +# PART = xc3s1000 +# FPGA_TOP = fpga +# PROM = xc18v04 +# NGC_PATH = ipLib1 ipLib2 +# FPGA_ARCH = spartan6 +# SPI_PROM_SIZE = (in bytes) +# include xilinx.mk +############################################################################# +# +# Command Line Example: +# make -f xilinx.mk PART=xc3s1000-4fg320 SYN_FILES="fpga.v test.v" FPGA_TOP=fpga +# +############################################################################## +# +# Required Setup: +# +# %.ucf - user constraint file. Needed by ngdbuild +# +# Optional Files: +# %.xcf - user constraint file. Needed by xst. +# %.ut - File for pin states needed by bitgen + + +.PHONY: clean bit prom fpga spi + + +# Mark the intermediate files as PRECIOUS to prevent make from +# deleting them (see make manual section 10.4). +.PRECIOUS: %.ngc %.ngd %_map.ncd %.ncd %.twr %.bit %_timesim.v + +# include the local Makefile for project for any project specific targets +CONFIG ?= config.mk +-include ../$(CONFIG) + +SYN_FILES_REL = $(patsubst %, ../%, $(SYN_FILES)) +INC_FILES_REL = $(patsubst %, ../%, $(INC_FILES)) +INC_PATHS_REL = $(patsubst %, ../%, $(INC_PATHS)) +NGC_PATHS_REL = $(patsubst %, ../%, $(NGC_PATHS)) + +ifdef UCF_FILES + UCF_FILES_REL = $(patsubst %, ../%, $(UCF_FILES)) +else + UCF_FILES_REL = $(FPGA_TOP).ucf +endif + + + +fpga: $(FPGA_TOP).bit + +mcs: $(FPGA_TOP).mcs + +prom: $(FPGA_TOP).spi + +spi: $(FPGA_TOP).spi + +fpgasim: $(FPGA_TOP)_sim.v + + +########################### XST TEMPLATES ############################ +# There are 2 files that XST uses for synthesis that we auto generate. +# The first is a project file which is just a list of all the verilog +# files. The second is the src file which passes XST all the options. +# See XST user manual for XST options. +%.ngc: $(SYN_FILES_REL) $(INC_FILES_REL) + rm -rf xst $*.prj $*.xst defines.v + touch defines.v + mkdir -p xst/tmp + for x in $(DEFS); do echo '`define' $$x >> defines.v; done + echo verilog work defines.v > $*.prj + for x in $(SYN_FILES_REL); do echo verilog work $$x >> $*.prj; done + @echo "set -tmpdir ./xst/tmp" >> $*.xst + @echo "set -xsthdpdir ./xst" >> $*.xst + @echo "run" >> $*.xst + @echo "-ifn $*.prj" >> $*.xst + @echo "-ifmt mixed" >> $*.xst + @echo "-top $*" >> $*.xst + @echo "-ofn $*" >> $*.xst + @echo "-ofmt NGC" >> $*.xst + @echo "-opt_mode Speed" >> $*.xst + @echo "-opt_level 1" >> $*.xst + # @echo "-verilog2001 YES" >> $*.xst + @echo "-keep_hierarchy NO" >> $*.xst + @echo "-p $(FPGA_PART)" >> $*.xst + xst -ifn $*.xst -ofn $*.log + + +########################### ISE TRANSLATE ############################ +# ngdbuild will automatically use a ucf called %.ucf if one is found. +# We setup the dependancy such that %.ucf file is required. If any +# pre-compiled ncd files are needed, set the NGC_PATH variable as a space +# seperated list of directories that include the pre-compiled ngc files. +%.ngd: %.ngc $(UCF_FILES_REL) + ngdbuild -dd ngdbuild $(patsubst %,-sd %, $(NGC_PATHS_REL)) $(patsubst %,-uc %, $(UCF_FILES_REL)) -p $(FPGA_PART) $< $@ + + +########################### ISE MAP ################################### +ifeq ($(FPGA_ARCH),spartan6) + MAP_OPTS= -register_duplication on -timing -xe n +else + MAP_OPTS= -cm speed -register_duplication on -timing -xe n -pr b +endif + +%_map.ncd: %.ngd + map -p $(FPGA_PART) $(MAP_OPTS) -w -o $@ $< $*.pcf + +# map -p $(FPGA_PART) -cm area -pr b -k 4 -c 100 -o $@ $< $*.pcf + + +########################### ISE PnR ################################### +%.ncd: %_map.ncd + par -w -ol high $< $@ $*.pcf + +# par -w -ol std -t 1 $< $@ $*.pcf + + +##################### ISE Static Timing Analysis ##################### +%.twr: %.ncd + -trce -e 3 -l 3 -u -xml $* $< -o $@ $*.pcf + +%_sim.v: %.ncd + netgen -s 4 -pcf $*.pcf -sdf_anno true -ism -sdf_path netgen -w -dir . -ofmt verilog -sim $< $@ + +# netgen -ise "/home/lane/Second/xilinx/Second/Second" -intstyle ise -s 4 -pcf Second.pcf -sdf_anno true -sdf_path netgen/par -w -dir netgen/par -ofmt verilog -sim Second.ncd Second_timesim.v + + +########################### ISE Bitgen ############################# +%.bit: %.twr + bitgen $(BITGEN_OPTIONS) -w $*.ncd $*.bit + mkdir -p rev + EXT=bit; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +########################### ISE Promgen ############################# +%.mcs: %.bit + promgen -spi -w -p mcs -s $(SPI_PROM_SIZE) -o $@ -u 0 $< + # promgen -w -p mcs -c FF -o $@ -u 0 $< -x $(PROM) + mkdir -p rev + EXT=mcs; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; + + +%.spi: %.mcs + objcopy -I ihex -O binary $< $@ + EXT=spi; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do let COUNT=COUNT+1; done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + + +tmpclean: + -rm -rf xst ngdbuild *_map.* *.ncd *.ngc *.log *.xst *.prj *.lso *~ *.pcf *.bld *.ngd *.xpi *_pad.* *.unroutes *.twx *.par *.twr *.pad *.drc *.bgn *.prm *.sig netgen *.v *.nlf *.xml + +clean: tmpclean + -rm -rf *.bit *.mcs + +# clean everything +distclean: clean + -rm -rf rev + diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/coregen/Makefile b/fpga/lib/eth/example/ML605/fpga_sgmii/coregen/Makefile new file mode 100644 index 000000000..b36831721 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/coregen/Makefile @@ -0,0 +1,33 @@ +# Tools +COREGEN:=coregen +XAW2VERILOG:=xaw2verilog + +# Source +XCO:=gig_eth_pcs_pma_v11_5.xco +XAW:= + +# Targets +TARGETS += $(XCO:.xco=) +TARGETS += $(XAW:.xaw=) + +# Rules +.PHONY: all +all: $(TARGETS) + +.PHONY: clean +clean: + -rm -rf $(TARGETS) + +%: %.xco + $(eval $@_TMP := $(shell mktemp -d)) + cp -a coregen.cgp $($@_TMP) + cp -a $< $($@_TMP) + cd $($@_TMP) && $(COREGEN) -p coregen.cgp -b $(notdir $<) + mv $($@_TMP) $@ + +%: %.xaw + $(eval $@_TMP := $(shell mktemp -d)) + cp -a coregen.cgp $($@_TMP) + cp -a $< $($@_TMP) + cd $($@_TMP) && $(XAW2VERILOG) -st $(notdir $<) $(notdir $*) + mv $($@_TMP) $@ diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/coregen/coregen.cgp b/fpga/lib/eth/example/ML605/fpga_sgmii/coregen/coregen.cgp new file mode 100644 index 000000000..e9b3f544d --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/coregen/coregen.cgp @@ -0,0 +1,22 @@ +# Date: Wed Jun 21 16:05:33 2017 + +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6vlx130t +SET devicefamily = virtex6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = ff1156 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -1 +SET verilogsim = true +SET vhdlsim = false +SET workingdirectory = ./tmp/ + +# CRC: 2db0eed3 diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/coregen/gig_eth_pcs_pma_v11_5.xco b/fpga/lib/eth/example/ML605/fpga_sgmii/coregen/gig_eth_pcs_pma_v11_5.xco new file mode 100644 index 000000000..efe30f59e --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/coregen/gig_eth_pcs_pma_v11_5.xco @@ -0,0 +1,57 @@ +############################################################## +# +# Xilinx Core Generator version 14.7 +# Date: Wed Jun 21 16:07:22 2017 +# +############################################################## +# +# This file contains the customisation parameters for a +# Xilinx CORE Generator IP GUI. It is strongly recommended +# that you do not manually alter this file as it may cause +# unexpected and unsupported behavior. +# +############################################################## +# +# Generated from component: xilinx.com:ip:gig_eth_pcs_pma:11.5 +# +############################################################## +# +# BEGIN Project Options +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6vlx130t +SET devicefamily = virtex6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = ff1156 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -1 +SET verilogsim = true +SET vhdlsim = false +# END Project Options +# BEGIN Select +SELECT Ethernet_1000BASE-X_PCS/PMA_or_SGMII xilinx.com:ip:gig_eth_pcs_pma:11.5 +# END Select +# BEGIN Parameters +CSET auto_negotiation=true +CSET component_name=gig_eth_pcs_pma_v11_5 +CSET enable_1588=false +CSET management_interface=false +CSET physical_interface=Transceiver +CSET sgmii_mode=10_100_1000 +CSET sgmii_phy_mode=false +CSET standard=SGMII +CSET timing_sim=false +CSET transceiver_tile=A +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2012-07-11T07:25:50Z +# END Extra information +GENERATE +# CRC: 7ca270ae diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/fpga.ucf b/fpga/lib/eth/example/ML605/fpga_sgmii/fpga.ucf new file mode 100644 index 000000000..fdffeba94 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/fpga.ucf @@ -0,0 +1,95 @@ +# User Constraints File for the Xilinx ML605 board, rev C + +#CONFIG PART = xc6vlx130t-1ff1156; +#CONFIG PART = xc6vlx240t-1ff1156; + +# 200MHz clock +NET "sys_clk_p" LOC = "J9" | IOSTANDARD=LVDS_25; # Bank = 34, IO_L0P_GC_34 (GCLK) +NET "sys_clk_n" LOC = "H9" | IOSTANDARD=LVDS_25; # Bank = 34, IO_L0N_GC_34 (GCLK) +NET "sys_clk_p" TNM_NET = "sys_clk_pin"; +TIMESPEC "TS_sys_clk_pin" = PERIOD "sys_clk_pin" 200000 kHz; + +# Light Emitting Diodes +NET "ledu" LOC = "AH27" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 23, IO_L0P_23 (DS20) +NET "ledl" LOC = "AD21" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 22, IO_L0N_22 (DS17) +NET "ledd" LOC = "AH28" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 23, IO_L0N_23 (DS18) +NET "ledr" LOC = "AE21" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 22, IO_L0P_22 (DS19) +NET "ledc" LOC = "AP24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 23, IO_L19N_23 (DS16) + +NET "led<0>" LOC = "AC22" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L19P_24 (DS12) +NET "led<1>" LOC = "AC24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L18N_24 (DS11) +NET "led<2>" LOC = "AE22" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L17N_VRP_24 (DS9) +NET "led<3>" LOC = "AE23" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L17P_VRN_24 (DS10) +NET "led<4>" LOC = "AB23" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L16N_CSO_B_24 (DS15) +NET "led<5>" LOC = "AG23" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L15N_RS1_24 (DS14) +NET "led<6>" LOC = "AE24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L11N_SRCC_24 (DS22) +NET "led<7>" LOC = "AD24" | IOSTANDARD=LVCMOS25 | SLEW=QUIETIO | DRIVE=2; # Bank = 24, IO_L11P_SRCC_24 (DS21) + +# Reset Button: I/O Bank 2 +NET "reset" LOC = "H10" | IOSTANDARD=LVCMOS15; # Bank = 35, IO_L6P_SM3P_35 (SW10) + +# Push Buttons: I/O Bank 3 +NET "btnu" LOC = "A19" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L15N_26 (SW5) +NET "btnl" LOC = "H17" | IOSTANDARD=LVCMOS15; # Bank = 36, IO_L3P_36 (SW8) +NET "btnd" LOC = "A18" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L15P_26 (SW6) +NET "btnr" LOC = "G17" | IOSTANDARD=LVCMOS15; # Bank = 36, IO_L3N_36 (SW7) +NET "btnc" LOC = "G26" | IOSTANDARD=LVCMOS15; # Bank = 25, IO_L6P_25 (SW9) + +# Toggle Switches +NET "sw<0>" LOC = "D22" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L19N_26 (SW1.1) +NET "sw<1>" LOC = "C22" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L19P_26 (SW1.2) +NET "sw<2>" LOC = "L21" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L18N_26 (SW1.3) +NET "sw<3>" LOC = "L20" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L18P_26 (SW1.4) +NET "sw<4>" LOC = "C18" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L17N_26 (SW1.5) +NET "sw<5>" LOC = "B18" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L17P_26 (SW1.6) +NET "sw<6>" LOC = "K22" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L16N_26 (SW1.7) +NET "sw<7>" LOC = "K21" | IOSTANDARD=LVCMOS15; # Bank = 26, IO_L16P_26 (SW1.8) + +# Marvell M88E1111 Tri-Mode Ethernet PHY (1000BASE-T) +# Interrupt, Reset, MDIO +#NET "phy_int_n" LOC = "AH14" | IOSTANDARD=LVCMOS25; # (E-INT) +NET "phy_reset_n" LOC = "AH13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L18P_33 (E-RESET) +#NET "phy_mdc" LOC = "AP14" | IOSTANDARD=LVCMOS25; # (E-MDC) +#NET "phy_mdio" LOC = "AN14" | IOSTANDARD=LVCMOS25; # (E-MDIO) +# GMII Transmit +#NET "phy_gtx_clk" LOC = "AH12" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L16N_33 (E-GTXCLK) +#NET "phy_tx_clk" LOC = "AD12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L10P_MRCC_33 (E-TXCLK) +#NET "phy_txd<0>" LOC = "AM11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L7N_33 (E-TXD0) +#NET "phy_txd<1>" LOC = "AL11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L7P_33 (E-TXD1) +#NET "phy_txd<2>" LOC = "AG10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L6N_33 (E-TXD2) +#NET "phy_txd<3>" LOC = "AG11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L6P_33 (E-TXD3) +#NET "phy_txd<4>" LOC = "AL10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L5N_33 (E-TXD4) +#NET "phy_txd<5>" LOC = "AM10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L5P_33 (E-TXD5) +#NET "phy_txd<6>" LOC = "AE11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L4N_VREF_33 (E-TXD6) +#NET "phy_txd<7>" LOC = "AF11" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L4P_33 (E-TXD7) +#NET "phy_tx_en" LOC = "AJ10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L8P_SRCC_33 (E-TXEN) +#NET "phy_tx_er" LOC = "AH10" | IOSTANDARD=LVCMOS25 | SLEW = FAST; # Bank = 33, IO_L8N_SRCC_33 (E-TXER) +# GMII Receive +#NET "phy_rx_clk" LOC = "AP11" | IOSTANDARD=LVCMOS25 | TNM_NET = "clk_rx_local"; # (E-RXCLK) +#NET "phy_rxd<0>" LOC = "AN13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L15P_33 (E-RXD0) +#NET "phy_rxd<1>" LOC = "AF14" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L14N_VREF_33 (E-RXD1) +#NET "phy_rxd<2>" LOC = "AE14" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L14P_33 (E-RXD2) +#NET "phy_rxd<3>" LOC = "AN12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L13N_33 (E-RXD3) +#NET "phy_rxd<4>" LOC = "AM12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L13P_33 (E-RXD4) +#NET "phy_rxd<5>" LOC = "AD11" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L10N_MRCC_33 (E-RXD5) +#NET "phy_rxd<6>" LOC = "AC12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L9N_MRCC_33 (E-RXD6) +#NET "phy_rxd<7>" LOC = "AC13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L9P_MRCC_33 (E-RXD7) +#NET "phy_rx_dv" LOC = "AM13" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L15N_33 (E-RXDV) +#NET "phy_rx_er" LOC = "AG12" | IOSTANDARD=LVCMOS25; # Bank = 33, IO_L16P_33 (E-RXER) +# SGMII interface +NET "phy_sgmii_rx_p" LOC = "B5"; +NET "phy_sgmii_rx_n" LOC = "B6"; +NET "phy_sgmii_tx_p" LOC = "A3"; +NET "phy_sgmii_tx_n" LOC = "A4"; +NET "phy_sgmii_clk_p" LOC = "H6" | TNM_NET = "sgmii_mgtrefclk"; +NET "phy_sgmii_clk_n" LOC = "H5"; + +# Timing constraints for Ethernet PHY +#TIMESPEC "TS_rx_clk_root" = PERIOD "clk_rx_local" 8000 ps HIGH 50 %; +TIMESPEC "TS_mgtrefclk" = PERIOD "sgmii_mgtrefclk" 8 ns HIGH 50 %; + +# Silicon Labs CP2103 +NET "uart_rxd" LOC = "J25" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L9P_MRCC_24 (U24.24) +NET "uart_txd" LOC = "J24" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L9N_MRCC_24 (U24.25) +NET "uart_rts" LOC = "T23" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L8N_SRCC_24 (U24.23) +NET "uart_cts" LOC = "T24" | IOSTANDARD=LVCMOS25; # Bank = 24, IO_L8P_SRCC_24 (U24.22) diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/fpga_130t/Makefile b/fpga/lib/eth/example/ML605/fpga_sgmii/fpga_130t/Makefile new file mode 100644 index 000000000..74718cc8a --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/fpga_130t/Makefile @@ -0,0 +1,79 @@ + +# FPGA settings +FPGA_PART = xc6vlx130t-1ff1156 +FPGA_TOP = fpga +FPGA_ARCH = spartan6 + +# PROM settings +#PROM = xc18v04 +#SPI_PROM_SIZE = (in bytes) + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_fifo.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 += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/sgmii_adapt/gig_eth_pcs_pma_v11_5_clk_gen.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/sgmii_adapt/gig_eth_pcs_pma_v11_5_johnson_cntr.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/sgmii_adapt/gig_eth_pcs_pma_v11_5_rx_rate_adapt.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/sgmii_adapt/gig_eth_pcs_pma_v11_5_sgmii_adapt.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/sgmii_adapt/gig_eth_pcs_pma_v11_5_tx_rate_adapt.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_double_reset.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_gtwizard_gtrxreset_seq.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_rx_elastic_buffer.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_v6_gtxwizard.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_v6_gtxwizard_gtx.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_v6_gtxwizard_top.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/gig_eth_pcs_pma_v11_5_block.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/gig_eth_pcs_pma_v11_5_reset_sync.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/gig_eth_pcs_pma_v11_5_sync_block.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5.v + +# UCF files +UCF_FILES = fpga.ucf +UCF_FILES += clock.ucf + +# NGC paths for ngdbuild +NGC_PATHS = coregen/gig_eth_pcs_pma_v11_5 + +# Bitgen options +BITGEN_OPTIONS = -g StartupClk:Cclk -g ConfigRate:26 + +include ../common/xilinx.mk + +program: $(FPGA_TOP).bit + echo "setmode -bscan" > program.cmd + echo "setcable -p auto" >> program.cmd + echo "identify" >> program.cmd + echo "assignfile -p 2 -file $(FPGA_TOP).bit" >> program.cmd + echo "program -p 2" >> program.cmd + echo "quit" >> program.cmd + impact -batch program.cmd + diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/fpga_240t/Makefile b/fpga/lib/eth/example/ML605/fpga_sgmii/fpga_240t/Makefile new file mode 100644 index 000000000..4fe5d60e5 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/fpga_240t/Makefile @@ -0,0 +1,79 @@ + +# FPGA settings +FPGA_PART = xc6vlx240t-1ff1156 +FPGA_TOP = fpga +FPGA_ARCH = spartan6 + +# PROM settings +#PROM = xc18v04 +#SPI_PROM_SIZE = (in bytes) + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_fifo.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 += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/sgmii_adapt/gig_eth_pcs_pma_v11_5_clk_gen.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/sgmii_adapt/gig_eth_pcs_pma_v11_5_johnson_cntr.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/sgmii_adapt/gig_eth_pcs_pma_v11_5_rx_rate_adapt.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/sgmii_adapt/gig_eth_pcs_pma_v11_5_sgmii_adapt.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/sgmii_adapt/gig_eth_pcs_pma_v11_5_tx_rate_adapt.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_double_reset.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_gtwizard_gtrxreset_seq.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_rx_elastic_buffer.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_v6_gtxwizard.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_v6_gtxwizard_gtx.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/transceiver/gig_eth_pcs_pma_v11_5_v6_gtxwizard_top.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/gig_eth_pcs_pma_v11_5_block.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/gig_eth_pcs_pma_v11_5_reset_sync.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5/example_design/gig_eth_pcs_pma_v11_5_sync_block.v +SYN_FILES += coregen/gig_eth_pcs_pma_v11_5/gig_eth_pcs_pma_v11_5.v + +# UCF files +UCF_FILES = fpga.ucf +UCF_FILES += clock.ucf + +# NGC paths for ngdbuild +NGC_PATHS = coregen/gig_eth_pcs_pma_v11_5 + +# Bitgen options +BITGEN_OPTIONS = -g StartupClk:Cclk -g ConfigRate:26 + +include ../common/xilinx.mk + +program: $(FPGA_TOP).bit + echo "setmode -bscan" > program.cmd + echo "setcable -p auto" >> program.cmd + echo "identify" >> program.cmd + echo "assignfile -p 2 -file $(FPGA_TOP).bit" >> program.cmd + echo "program -p 2" >> program.cmd + echo "quit" >> program.cmd + impact -batch program.cmd + diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/lib/eth b/fpga/lib/eth/example/ML605/fpga_sgmii/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/debounce_switch.v b/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/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 + +`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/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/fpga.v b/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/fpga.v new file mode 100644 index 000000000..74f43503e --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/fpga.v @@ -0,0 +1,398 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 200MHz + * Reset: Push button, active high + */ + input wire sys_clk_p, + input wire sys_clk_n, + input wire reset, + /* + * GPIO + */ + input wire btnu, + input wire btnl, + input wire btnd, + input wire btnr, + input wire btnc, + input wire [7:0] sw, + output wire ledu, + output wire ledl, + output wire ledd, + output wire ledr, + output wire ledc, + output wire [7:0] led, + /* + * Ethernet: 1000BASE-T SGMII + */ + input wire phy_sgmii_rx_p, + input wire phy_sgmii_rx_n, + output wire phy_sgmii_tx_p, + output wire phy_sgmii_tx_n, + input wire phy_sgmii_clk_p, + input wire phy_sgmii_clk_n, + output wire phy_reset_n, + /* + * Silicon Labs CP2103 USB UART + */ + output wire uart_rxd, + input wire uart_txd, + input wire uart_rts, + output wire uart_cts +); + +// Clock and reset + +wire sys_clk_ibufg; +wire sys_clk_bufg; +wire clk_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire rst_125mhz_int; + +wire mmcm_rst = reset; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS +clk_ibufgds_inst( + .I(sys_clk_p), + .IB(sys_clk_n), + .O(sys_clk_ibufg) +); + +// MMCM instance +// 200 MHz in, 125 MHz out +// PFD range: 10 MHz to 450 MHz +// VCO range: 600 MHz to 1200 MHz +// M = 5, D = 1 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +MMCM_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(8), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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.100), + .CLKIN1_PERIOD(5.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(sys_clk_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_125mhz_bufg_inst ( + .I(clk_125mhz_mmcm_out), + .O(clk_125mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_int) +); + +// GPIO +wire btnu_int; +wire btnl_int; +wire btnd_int; +wire btnr_int; +wire btnc_int; +wire [7:0] sw_int; + +wire ledu_int; +wire ledl_int; +wire ledd_int; +wire ledr_int; +wire ledc_int; +wire [7:0] led_int; + +wire uart_rxd_int; +wire uart_txd_int; +wire uart_rts_int; +wire uart_cts_int; + +debounce_switch #( + .WIDTH(13), + .N(4), + .RATE(125000) +) +debounce_switch_inst ( + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + .in({btnu, + btnl, + btnd, + btnr, + btnc, + sw}), + .out({btnu_int, + btnl_int, + btnd_int, + btnr_int, + btnc_int, + sw_int}) +); + +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}) +); + +assign ledu = ledu_int; +assign ledl = ledl_int; +assign ledd = ledd_int; +assign ledr = ledr_int; +assign ledc = ledc_int; +//assign led = led_int; + +assign uart_rxd = uart_rxd_int; +assign uart_cts = uart_cts_int; + +// SGMII interface to PHY +wire phy_gmii_clk_int; +wire phy_gmii_rst_int; +wire phy_gmii_clk_en_int; +wire [7:0] phy_gmii_txd_int; +wire phy_gmii_tx_en_int; +wire phy_gmii_tx_er_int; +wire [7:0] phy_gmii_rxd_int; +wire phy_gmii_rx_dv_int; +wire phy_gmii_rx_er_int; + +wire phy_sgmii_mgtrefclk; +wire phy_sgmii_txoutclk; +wire phy_sgmii_userclk2; + +IBUFDS_GTXE1 +phy_sgmii_ibufds_mgtrefclk ( + .CEB (1'b0), + .I (phy_sgmii_clk_p), + .IB (phy_sgmii_clk_n), + .O (phy_sgmii_mgtrefclk), + .ODIV2 () +); + +BUFG +phy_sgmii_bufg_userclk2 ( + .I (phy_sgmii_txoutclk), + .O (phy_sgmii_userclk2) +); + +assign phy_gmii_clk_int = phy_sgmii_userclk2; + +sync_reset #( + .N(4) +) +sync_reset_pcspma_inst ( + .clk(phy_gmii_clk_int), + .rst(rst_125mhz_int), + .sync_reset_out(phy_gmii_rst_int) +); + +wire [15:0] pcspma_status_vector; + +wire pcspma_status_link_status = pcspma_status_vector[0]; +wire pcspma_status_link_synchronization = pcspma_status_vector[1]; +wire pcspma_status_rudi_c = pcspma_status_vector[2]; +wire pcspma_status_rudi_i = pcspma_status_vector[3]; +wire pcspma_status_rudi_invalid = pcspma_status_vector[4]; +wire pcspma_status_rxdisperr = pcspma_status_vector[5]; +wire pcspma_status_rxnotintable = pcspma_status_vector[6]; +wire pcspma_status_phy_link_status = pcspma_status_vector[7]; +wire [1:0] pcspma_status_remote_fault_encdg = pcspma_status_vector[9:8]; +wire [1:0] pcspma_status_speed = pcspma_status_vector[11:10]; +wire pcspma_status_duplex = pcspma_status_vector[12]; +wire pcspma_status_remote_fault = pcspma_status_vector[13]; +wire [1:0] pcspma_status_pause = pcspma_status_vector[15:14]; + +wire [4:0] pcspma_config_vector; + +assign pcspma_config_vector[4] = 1'b1; // autonegotiation enable +assign pcspma_config_vector[3] = 1'b0; // isolate +assign pcspma_config_vector[2] = 1'b0; // power down +assign pcspma_config_vector[1] = 1'b0; // loopback enable +assign pcspma_config_vector[0] = 1'b0; // unidirectional enable + +wire [15:0] pcspma_an_config_vector; + +assign pcspma_an_config_vector[15] = 1'b1; // SGMII link status +assign pcspma_an_config_vector[14] = 1'b1; // SGMII Acknowledge +assign pcspma_an_config_vector[13:12] = 2'b01; // full duplex +assign pcspma_an_config_vector[11:10] = 2'b10; // SGMII speed +assign pcspma_an_config_vector[9] = 1'b0; // reserved +assign pcspma_an_config_vector[8:7] = 2'b00; // pause frames - SGMII reserved +assign pcspma_an_config_vector[6] = 1'b0; // reserved +assign pcspma_an_config_vector[5] = 1'b0; // full duplex - SGMII reserved +assign pcspma_an_config_vector[4:1] = 4'b0000; // reserved +assign pcspma_an_config_vector[0] = 1'b1; // SGMII + +gig_eth_pcs_pma_v11_5_block +eth_pcspma ( + // Transceiver Interface + .mgtrefclk (phy_sgmii_mgtrefclk), + .gtx_reset_clk (clk_125mhz_int), + .txp (phy_sgmii_tx_p), + .txn (phy_sgmii_tx_n), + .rxp (phy_sgmii_rx_p), + .rxn (phy_sgmii_rx_n), + .txoutclk (phy_sgmii_txoutclk), + .userclk2 (phy_sgmii_userclk2), + .pma_reset (rst_125mhz_int), + // GMII Interface + .sgmii_clk_r (), + .sgmii_clk_f (), + .sgmii_clk_en (phy_gmii_clk_en_int), + .gmii_txd (phy_gmii_txd_int), + .gmii_tx_en (phy_gmii_tx_en_int), + .gmii_tx_er (phy_gmii_tx_er_int), + .gmii_rxd (phy_gmii_rxd_int), + .gmii_rx_dv (phy_gmii_rx_dv_int), + .gmii_rx_er (phy_gmii_rx_er_int), + .gmii_isolate (), + // Management: Alternative to MDIO Interface + .configuration_vector (pcspma_config_vector), + .an_interrupt (), + .an_adv_config_vector (pcspma_an_config_vector), + .an_restart_config (1'b0), + .link_timer_value (9'd50), + // Speed Control + .speed_is_10_100 (pcspma_status_speed != 2'b10), + .speed_is_100 (pcspma_status_speed == 2'b01), + // General IO's + .status_vector (pcspma_status_vector), + .reset (rst_125mhz_int), + .signal_detect (1'b1) +); + +// SGMII interface debug: +// SW1:1 (sw[0]) off for payload byte, on for status vector +// SW1:2 (sw[1]) off for LSB of status vector, on for MSB +assign led = sw[7] ? (sw[6] ? pcspma_status_vector[15:8] : pcspma_status_vector[7:0]) : led_int; + +fpga_core +core_inst ( + /* + * Clock: 125MHz + * Synchronous reset + */ + .clk_125mhz(clk_125mhz_int), + .rst_125mhz(rst_125mhz_int), + /* + * GPIO + */ + .btnu(btnu_int), + .btnl(btnl_int), + .btnd(btnd_int), + .btnr(btnr_int), + .btnc(btnc_int), + .sw(sw_int), + .ledu(ledu_int), + .ledl(ledl_int), + .ledd(ledd_int), + .ledr(ledr_int), + .ledc(ledc_int), + .led(led_int), + /* + * Ethernet: 1000BASE-T SGMII + */ + .phy_gmii_clk(phy_gmii_clk_int), + .phy_gmii_rst(phy_gmii_rst_int), + .phy_gmii_clk_en(phy_gmii_clk_en_int), + .phy_gmii_rxd(phy_gmii_rxd_int), + .phy_gmii_rx_dv(phy_gmii_rx_dv_int), + .phy_gmii_rx_er(phy_gmii_rx_er_int), + .phy_gmii_txd(phy_gmii_txd_int), + .phy_gmii_tx_en(phy_gmii_tx_en_int), + .phy_gmii_tx_er(phy_gmii_tx_er_int), + .phy_reset_n(phy_reset_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd_int), + .uart_rts(uart_rts_int), + .uart_cts(uart_cts_int) +); + +endmodule diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/fpga_core.v b/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/fpga_core.v new file mode 100644 index 000000000..f699350d9 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/fpga_core.v @@ -0,0 +1,593 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core +( + /* + * Clock: 125MHz + * Synchronous reset + */ + input wire clk_125mhz, + input wire rst_125mhz, + + /* + * GPIO + */ + input wire btnu, + input wire btnl, + input wire btnd, + input wire btnr, + input wire btnc, + input wire [7:0] sw, + output wire ledu, + output wire ledl, + output wire ledd, + output wire ledr, + output wire ledc, + output wire [7:0] led, + + /* + * Ethernet: 1000BASE-T SGMII + */ + input wire phy_gmii_clk, + input wire phy_gmii_rst, + input wire phy_gmii_clk_en, + input wire [7:0] phy_gmii_rxd, + input wire phy_gmii_rx_dv, + input wire phy_gmii_rx_er, + output wire [7:0] phy_gmii_txd, + output wire phy_gmii_tx_en, + output wire phy_gmii_tx_er, + output wire phy_reset_n, + + /* + * Silicon Labs CP2103 USB UART + */ + output wire uart_rxd, + input wire uart_txd, + input wire uart_rts, + output wire uart_cts +); + +// 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_125mhz) begin + if (rst_125mhz) 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_125mhz) begin + if (rst_125mhz) 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 ledu = 0; +assign ledl = 0; +assign ledd = 0; +assign ledr = 0; +assign ledc = 0; +assign led = led_reg; +assign phy_reset_n = !rst_125mhz; + +assign uart_rxd = 0; +assign uart_cts = 0; + +eth_mac_1g_fifo #( + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_inst ( + .rx_clk(phy_gmii_clk), + .rx_rst(phy_gmii_rst), + .tx_clk(phy_gmii_clk), + .tx_rst(phy_gmii_rst), + .logic_clk(clk_125mhz), + .logic_rst(rst_125mhz), + + .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), + + .gmii_rxd(phy_gmii_rxd), + .gmii_rx_dv(phy_gmii_rx_dv), + .gmii_rx_er(phy_gmii_rx_er), + .gmii_txd(phy_gmii_txd), + .gmii_tx_en(phy_gmii_tx_en), + .gmii_tx_er(phy_gmii_tx_er), + + .rx_clk_enable(phy_gmii_clk_en), + .tx_clk_enable(phy_gmii_clk_en), + .rx_mii_select(1'b0), + .tx_mii_select(1'b0), + + .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(), + + .ifg_delay(12) +); + +eth_axis_rx +eth_axis_rx_inst ( + .clk(clk_125mhz), + .rst(rst_125mhz), + // 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_125mhz), + .rst(rst_125mhz), + // 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_125mhz), + .rst(rst_125mhz), + // 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 #( + .ADDR_WIDTH(12), + .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_125mhz), + .rst(rst_125mhz), + + // 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/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/sync_reset.v b/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/sync_signal.v b/fpga/lib/eth/example/ML605/fpga_sgmii/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/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/fpga/lib/eth/example/ML605/fpga_sgmii/tb/arp_ep.py b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/tb/axis_ep.py b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/tb/eth_ep.py b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/tb/gmii_ep.py b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/tb/ip_ep.py b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/tb/test_fpga_core.py b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/test_fpga_core.py new file mode 100755 index 000000000..259f76a41 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/test_fpga_core.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import gmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_1g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/udp_complete.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen.v") +srcs.append("../lib/eth/rtl/udp.v") +srcs.append("../lib/eth/rtl/udp_ip_rx.v") +srcs.append("../lib/eth/rtl/udp_ip_tx.v") +srcs.append("../lib/eth/rtl/ip_complete.v") +srcs.append("../lib/eth/rtl/ip.v") +srcs.append("../lib/eth/rtl/ip_eth_rx.v") +srcs.append("../lib/eth/rtl/ip_eth_tx.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx.v") +srcs.append("../lib/eth/rtl/arp_eth_tx.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + clk_125mhz = Signal(bool(0)) + rst_125mhz = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[8:]) + phy_gmii_clk = Signal(bool(0)) + phy_gmii_rst = Signal(bool(0)) + phy_gmii_clk_en = Signal(bool(0)) + phy_gmii_rxd = Signal(intbv(0)[8:]) + phy_gmii_rx_dv = Signal(bool(0)) + phy_gmii_rx_er = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + + # Outputs + ledu = Signal(bool(0)) + ledl = Signal(bool(0)) + ledd = Signal(bool(0)) + ledr = Signal(bool(0)) + ledc = Signal(bool(0)) + led = Signal(intbv(0)[8:]) + phy_gmii_txd = Signal(intbv(0)[8:]) + phy_gmii_tx_en = Signal(bool(0)) + phy_gmii_tx_er = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_rxd = Signal(bool(0)) + uart_cts = Signal(bool(0)) + + # sources and sinks + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + phy_gmii_clk, + phy_gmii_rst, + txd=phy_gmii_rxd, + tx_en=phy_gmii_rx_dv, + tx_er=phy_gmii_rx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + phy_gmii_clk, + phy_gmii_rst, + rxd=phy_gmii_txd, + rx_dv=phy_gmii_tx_en, + rx_er=phy_gmii_tx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk_125mhz=clk_125mhz, + rst_125mhz=rst_125mhz, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + ledu=ledu, + ledl=ledl, + ledd=ledd, + ledr=ledr, + ledc=ledc, + led=led, + + phy_gmii_clk=phy_gmii_clk, + phy_gmii_rst=phy_gmii_rst, + phy_gmii_clk_en=phy_gmii_clk_en, + phy_gmii_rxd=phy_gmii_rxd, + phy_gmii_rx_dv=phy_gmii_rx_dv, + phy_gmii_rx_er=phy_gmii_rx_er, + phy_gmii_txd=phy_gmii_txd, + phy_gmii_tx_en=phy_gmii_tx_en, + phy_gmii_tx_er=phy_gmii_tx_er, + phy_reset_n=phy_reset_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd, + uart_rts=uart_rts, + uart_cts=uart_cts + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + clk_125mhz.next = not clk_125mhz + phy_gmii_clk.next = not phy_gmii_clk + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + phy_gmii_clk_en.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + phy_gmii_clk_en.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + rst_125mhz.next = 1 + phy_gmii_rst.next = 1 + yield clk.posedge + rst.next = 0 + rst_125mhz.next = 0 + phy_gmii_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/tb/test_fpga_core.v b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/test_fpga_core.v new file mode 100644 index 000000000..5b0f8d538 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/test_fpga_core.v @@ -0,0 +1,143 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk_125mhz = 0; +reg rst_125mhz = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [7:0] sw = 0; +reg phy_gmii_clk = 0; +reg phy_gmii_rst = 0; +reg phy_gmii_clk_en = 0; +reg [7:0] phy_gmii_rxd = 0; +reg phy_gmii_rx_dv = 0; +reg phy_gmii_rx_er = 0; +reg uart_txd = 0; +reg uart_rts = 0; + +// Outputs +wire ledu; +wire ledl; +wire ledd; +wire ledr; +wire ledc; +wire [7:0] led; +wire [7:0] phy_gmii_txd; +wire phy_gmii_tx_en; +wire phy_gmii_tx_er; +wire phy_reset_n; +wire uart_rxd; +wire uart_cts; + +initial begin + // myhdl integration + $from_myhdl( + clk_125mhz, + rst_125mhz, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + phy_gmii_clk, + phy_gmii_rst, + phy_gmii_clk_en, + phy_gmii_rxd, + phy_gmii_rx_dv, + phy_gmii_rx_er, + uart_txd, + uart_rts + ); + $to_myhdl( + ledu, + ledl, + ledd, + ledr, + ledc, + led, + phy_gmii_txd, + phy_gmii_tx_en, + phy_gmii_tx_er, + phy_reset_n, + uart_rxd, + uart_cts + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk_125mhz(clk_125mhz), + .rst_125mhz(rst_125mhz), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .ledu(ledu), + .ledl(ledl), + .ledd(ledd), + .ledr(ledr), + .ledc(ledc), + .led(led), + .phy_gmii_clk(phy_gmii_clk), + .phy_gmii_rst(phy_gmii_rst), + .phy_gmii_clk_en(phy_gmii_clk_en), + .phy_gmii_rxd(phy_gmii_rxd), + .phy_gmii_rx_dv(phy_gmii_rx_dv), + .phy_gmii_rx_er(phy_gmii_rx_er), + .phy_gmii_txd(phy_gmii_txd), + .phy_gmii_tx_en(phy_gmii_tx_en), + .phy_gmii_tx_er(phy_gmii_tx_er), + .phy_reset_n(phy_reset_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts) +); + +endmodule diff --git a/fpga/lib/eth/example/ML605/fpga_sgmii/tb/udp_ep.py b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/ML605/fpga_sgmii/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/NexysVideo/fpga/Makefile b/fpga/lib/eth/example/NexysVideo/fpga/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/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/fpga/lib/eth/example/NexysVideo/fpga/README.md b/fpga/lib/eth/example/NexysVideo/fpga/README.md new file mode 100644 index 000000000..e3ebba018 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/README.md @@ -0,0 +1,26 @@ +# Verilog Ethernet Nexys Video Example Design + +## Introduction + +This example design targets the Digilent Nexys Video 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: XC7A200TSBG484-1 +PHY: Realtek RTL8211E + +## 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 Nexys Video board with the Digilent command +line tools. 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. + + diff --git a/fpga/lib/eth/example/NexysVideo/fpga/common/vivado.mk b/fpga/lib/eth/example/NexysVideo/fpga/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/NexysVideo/fpga/eth.xdc b/fpga/lib/eth/example/NexysVideo/fpga/eth.xdc new file mode 100644 index 000000000..d9780aa1c --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/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/fpga/lib/eth/example/NexysVideo/fpga/fpga.xdc b/fpga/lib/eth/example/NexysVideo/fpga/fpga.xdc new file mode 100644 index 000000000..2b5c9d3cd --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/fpga.xdc @@ -0,0 +1,67 @@ +# XDC constraints for the Digilent Nexys Video board +# part: xc7a200tsbg484-1 + +# General configuration +set_property CFGBVS VCCO [current_design] +set_property CONFIG_VOLTAGE 3.3 [current_design] +set_property BITSTREAM.GENERAL.COMPRESS true [current_design] + +# 100 MHz clock +set_property -dict {LOC R4 IOSTANDARD LVCMOS33} [get_ports clk] +create_clock -period 10.000 -name clk [get_ports clk] + +# LEDs +set_property -dict {LOC T14 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[0]}] +set_property -dict {LOC T15 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[1]}] +set_property -dict {LOC T16 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[2]}] +set_property -dict {LOC U16 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[3]}] +set_property -dict {LOC V15 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[4]}] +set_property -dict {LOC W16 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[5]}] +set_property -dict {LOC W15 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[6]}] +set_property -dict {LOC Y13 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports {led[7]}] + +# Reset button +set_property -dict {LOC G4 IOSTANDARD LVCMOS15} [get_ports reset_n] + +# Push buttons +set_property -dict {LOC F15 IOSTANDARD LVCMOS12} [get_ports btnu] +set_property -dict {LOC C22 IOSTANDARD LVCMOS12} [get_ports btnl] +set_property -dict {LOC D22 IOSTANDARD LVCMOS12} [get_ports btnd] +set_property -dict {LOC D14 IOSTANDARD LVCMOS12} [get_ports btnr] +set_property -dict {LOC B22 IOSTANDARD LVCMOS12} [get_ports btnc] + +# Toggle switches +set_property -dict {LOC E22 IOSTANDARD LVCMOS12} [get_ports {sw[0]}] +set_property -dict {LOC F21 IOSTANDARD LVCMOS12} [get_ports {sw[1]}] +set_property -dict {LOC G21 IOSTANDARD LVCMOS12} [get_ports {sw[2]}] +set_property -dict {LOC G22 IOSTANDARD LVCMOS12} [get_ports {sw[3]}] +set_property -dict {LOC H17 IOSTANDARD LVCMOS12} [get_ports {sw[4]}] +set_property -dict {LOC J16 IOSTANDARD LVCMOS12} [get_ports {sw[5]}] +set_property -dict {LOC K13 IOSTANDARD LVCMOS12} [get_ports {sw[6]}] +set_property -dict {LOC M17 IOSTANDARD LVCMOS12} [get_ports {sw[7]}] + +# UART +set_property -dict {LOC AA19 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports uart_txd] +set_property -dict {LOC V18 IOSTANDARD LVCMOS33} [get_ports uart_rxd] + +# Gigabit Ethernet RGMII PHY +set_property -dict {LOC V13 IOSTANDARD LVCMOS25} [get_ports phy_rx_clk] +set_property -dict {LOC AB16 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[0]}] +set_property -dict {LOC AA15 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[1]}] +set_property -dict {LOC AB15 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[2]}] +set_property -dict {LOC AB11 IOSTANDARD LVCMOS25} [get_ports {phy_rxd[3]}] +set_property -dict {LOC W10 IOSTANDARD LVCMOS25} [get_ports phy_rx_ctl] +set_property -dict {LOC AA14 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_tx_clk] +set_property -dict {LOC Y12 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[0]}] +set_property -dict {LOC W12 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[1]}] +set_property -dict {LOC W11 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[2]}] +set_property -dict {LOC Y11 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports {phy_txd[3]}] +set_property -dict {LOC V10 IOSTANDARD LVCMOS25 SLEW FAST DRIVE 16} [get_ports phy_tx_ctl] +set_property -dict {LOC U7 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports phy_reset_n] +set_property -dict {LOC Y14 IOSTANDARD LVCMOS25} [get_ports phy_int_n] +set_property -dict {LOC W14 IOSTANDARD LVCMOS25} [get_ports phy_pme_n] +#set_property -dict {LOC Y16 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports phy_mdio] +#set_property -dict {LOC AA16 IOSTANDARD LVCMOS25 SLEW SLOW DRIVE 12} [get_ports phy_mdc] + +create_clock -period 8.000 -name phy_rx_clk [get_ports phy_rx_clk] + diff --git a/fpga/lib/eth/example/NexysVideo/fpga/fpga/Makefile b/fpga/lib/eth/example/NexysVideo/fpga/fpga/Makefile new file mode 100644 index 000000000..b54be3306 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/fpga/Makefile @@ -0,0 +1,58 @@ + +# FPGA settings +FPGA_PART = xc7a200t-sbg484-1 +FPGA_TOP = fpga +FPGA_ARCH = artix7 + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.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/ssio_ddr_out.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 + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += eth.xdc +XDC_FILES += lib/eth/syn/rgmii_phy_if.tcl +XDC_FILES += lib/eth/syn/eth_mac_1g_rgmii.tcl +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl + +include ../common/vivado.mk + +program: $(FPGA_TOP).bit + djtgcfg prog -d NexysVideo --index 0 --file $(FPGA_TOP).bit + diff --git a/fpga/lib/eth/example/NexysVideo/fpga/fpga/generate_bit_iodelay.tcl b/fpga/lib/eth/example/NexysVideo/fpga/fpga/generate_bit_iodelay.tcl new file mode 100644 index 000000000..d97f96678 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/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/fpga/lib/eth/example/NexysVideo/fpga/lib/eth b/fpga/lib/eth/example/NexysVideo/fpga/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/NexysVideo/fpga/rtl/debounce_switch.v b/fpga/lib/eth/example/NexysVideo/fpga/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/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 + +`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/fpga/lib/eth/example/NexysVideo/fpga/rtl/fpga.v b/fpga/lib/eth/example/NexysVideo/fpga/rtl/fpga.v new file mode 100644 index 000000000..4a7925caa --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/rtl/fpga.v @@ -0,0 +1,370 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 100MHz + * Reset: Push button, active low + */ + input wire clk, + input wire reset_n, + + /* + * 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, + input wire phy_pme_n, + + /* + * UART: 500000 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd +); + +// Clock and reset + +wire clk_ibufg; +wire clk_bufg; +wire clk_mmcm_out; + +// Internal 125 MHz clock +wire clk_int; +wire rst_int; + +wire mmcm_rst = ~reset_n; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFG +clk_ibufg_inst( + .I(clk), + .O(clk_ibufg) +); + +wire clk90_mmcm_out; +wire clk90_int; + +wire clk_200_mmcm_out; +wire clk_200_int; + +// MMCM instance +// 100 MHz in, 125 MHz out +// PFD range: 10 MHz to 550 MHz +// VCO range: 600 MHz to 1200 MHz +// M = 10, 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(10), + .CLKFBOUT_PHASE(0), + .DIVCLK_DIVIDE(1), + .REF_JITTER1(0.010), + .CLKIN1_PERIOD(10.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(clk90_mmcm_out), + .CLKOUT1B(), + .CLKOUT2(clk_200_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_200_bufg_inst ( + .I(clk_200_mmcm_out), + .O(clk_200_int) +); + +sync_reset #( + .N(4) +) +sync_reset_inst ( + .clk(clk_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_int) +); + +// GPIO +wire btnu_int; +wire btnl_int; +wire btnd_int; +wire btnr_int; +wire btnc_int; +wire [7:0] sw_int; + +debounce_switch #( + .WIDTH(13), + .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}) +); + +sync_signal #( + .WIDTH(1), + .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_200_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 +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), + .phy_pme_n(phy_pme_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd) +); + +endmodule diff --git a/fpga/lib/eth/example/NexysVideo/fpga/rtl/fpga_core.v b/fpga/lib/eth/example/NexysVideo/fpga/rtl/fpga_core.v new file mode 100644 index 000000000..96e4a83a0 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/rtl/fpga_core.v @@ -0,0 +1,582 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * 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, + input wire phy_pme_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_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .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 #( + .ADDR_WIDTH(12), + .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/fpga/lib/eth/example/NexysVideo/fpga/rtl/sync_reset.v b/fpga/lib/eth/example/NexysVideo/fpga/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/NexysVideo/fpga/rtl/sync_signal.v b/fpga/lib/eth/example/NexysVideo/fpga/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/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/fpga/lib/eth/example/NexysVideo/fpga/tb/arp_ep.py b/fpga/lib/eth/example/NexysVideo/fpga/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/NexysVideo/fpga/tb/axis_ep.py b/fpga/lib/eth/example/NexysVideo/fpga/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/NexysVideo/fpga/tb/eth_ep.py b/fpga/lib/eth/example/NexysVideo/fpga/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/NexysVideo/fpga/tb/gmii_ep.py b/fpga/lib/eth/example/NexysVideo/fpga/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/NexysVideo/fpga/tb/ip_ep.py b/fpga/lib/eth/example/NexysVideo/fpga/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/NexysVideo/fpga/tb/rgmii_ep.py b/fpga/lib/eth/example/NexysVideo/fpga/tb/rgmii_ep.py new file mode 120000 index 000000000..986c56280 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/tb/rgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/rgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/NexysVideo/fpga/tb/test_fpga_core.py b/fpga/lib/eth/example/NexysVideo/fpga/tb/test_fpga_core.py new file mode 100755 index 000000000..c320ac269 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/tb/test_fpga_core.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import rgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/iddr.v") +srcs.append("../lib/eth/rtl/oddr.v") +srcs.append("../lib/eth/rtl/ssio_ddr_in.v") +srcs.append("../lib/eth/rtl/ssio_ddr_out.v") +srcs.append("../lib/eth/rtl/rgmii_phy_if.v") +srcs.append("../lib/eth/rtl/eth_mac_1g_rgmii_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g_rgmii.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/udp_complete.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen.v") +srcs.append("../lib/eth/rtl/udp.v") +srcs.append("../lib/eth/rtl/udp_ip_rx.v") +srcs.append("../lib/eth/rtl/udp_ip_tx.v") +srcs.append("../lib/eth/rtl/ip_complete.v") +srcs.append("../lib/eth/rtl/ip.v") +srcs.append("../lib/eth/rtl/ip_eth_rx.v") +srcs.append("../lib/eth/rtl/ip_eth_tx.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx.v") +srcs.append("../lib/eth/rtl/arp_eth_tx.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + + # Inputs + clk = Signal(bool(0)) + clk90 = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[8:]) + phy_rx_clk = Signal(bool(0)) + phy_rxd = Signal(intbv(0)[4:]) + phy_rx_ctl = Signal(bool(0)) + phy_int_n = Signal(bool(1)) + phy_pme_n = Signal(bool(1)) + uart_rxd = Signal(bool(0)) + + # Outputs + led = Signal(intbv(0)[8:]) + phy_tx_clk = Signal(bool(0)) + phy_txd = Signal(intbv(0)[4:]) + phy_tx_ctl = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_txd = Signal(bool(0)) + + # sources and sinks + mii_select = Signal(bool(0)) + + rgmii_source = rgmii_ep.RGMIISource() + + rgmii_source_logic = rgmii_source.create_logic( + phy_rx_clk, + rst, + txd=phy_rxd, + tx_ctl=phy_rx_ctl, + mii_select=mii_select, + name='rgmii_source' + ) + + rgmii_sink = rgmii_ep.RGMIISink() + + rgmii_sink_logic = rgmii_sink.create_logic( + phy_tx_clk, + rst, + rxd=phy_txd, + rx_ctl=phy_tx_ctl, + mii_select=mii_select, + name='rgmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + clk90=clk90, + rst=rst, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + led=led, + + phy_rx_clk=phy_rx_clk, + phy_rxd=phy_rxd, + phy_rx_ctl=phy_rx_ctl, + 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, + phy_pme_n=phy_pme_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def clkgen2(): + yield delay(4+2) + while True: + clk90.next = not clk90 + yield delay(4) + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + phy_rx_clk.next = not phy_rx_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + rgmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while rgmii_sink.empty(): + yield clk.posedge + + rx_frame = rgmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + rgmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while rgmii_sink.empty(): + yield clk.posedge + + rx_frame = rgmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert rgmii_source.empty() + assert rgmii_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/NexysVideo/fpga/tb/test_fpga_core.v b/fpga/lib/eth/example/NexysVideo/fpga/tb/test_fpga_core.v new file mode 100644 index 000000000..088a94d2c --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/tb/test_fpga_core.v @@ -0,0 +1,125 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters +parameter TARGET = "SIM"; + +// Inputs +reg clk = 0; +reg clk90 = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [7:0] sw = 0; +reg phy_rx_clk = 0; +reg [3:0] phy_rxd = 0; +reg phy_rx_ctl = 0; +reg phy_int_n = 1; +reg phy_pme_n = 1; +reg uart_rxd = 0; + +// Outputs +wire [7:0] led; +wire phy_tx_clk; +wire [3:0] phy_txd; +wire phy_tx_ctl; +wire phy_reset_n; +wire uart_txd; + +initial begin + // myhdl integration + $from_myhdl( + clk, + clk90, + rst, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + phy_rx_clk, + phy_rxd, + phy_rx_ctl, + phy_int_n, + phy_pme_n, + uart_rxd + ); + $to_myhdl( + led, + phy_tx_clk, + phy_txd, + phy_tx_ctl, + phy_reset_n, + uart_txd + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core #( + .TARGET(TARGET) +) +UUT ( + .clk(clk), + .clk90(clk90), + .rst(rst), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .led(led), + .phy_rx_clk(phy_rx_clk), + .phy_rxd(phy_rxd), + .phy_rx_ctl(phy_rx_ctl), + .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), + .phy_pme_n(phy_pme_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd) +); + +endmodule diff --git a/fpga/lib/eth/example/NexysVideo/fpga/tb/udp_ep.py b/fpga/lib/eth/example/NexysVideo/fpga/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/NexysVideo/fpga/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/Makefile b/fpga/lib/eth/example/VCU108/fpga_10g/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/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/fpga/lib/eth/example/VCU108/fpga_10g/README.md b/fpga/lib/eth/example/VCU108/fpga_10g/README.md new file mode 100644 index 000000000..fc508c331 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/README.md @@ -0,0 +1,33 @@ +# Verilog Ethernet VCU108 Example Design + +## Introduction + +This example design targets the Xilinx VCU108 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. The design also enables the gigabit Ethernet interface for +testing with a QSFP loopback adapter. + +FPGA: xcvu095-ffva2104-2-e +PHY: 10G BASE-R PHY IP core and internal GTY transceiver + +## 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 VCU108 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. + +Note that the gigabit PHY is also enabled for debugging. The gigabit port can +be inserted into the 10G data path between the 10G MAC and 10G PHY so that the +10G interface can be tested with a QSFP loopback adapter. Turn on SW12.1 to +insert the gigabit port into the 10G data path, or off to bypass the gigabit +port. Turn on SW12.2 to place the port in the TX path or off to place the +port in the RX path. + + diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/common/vivado.mk b/fpga/lib/eth/example/VCU108/fpga_10g/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/VCU108/fpga_10g/eth.xdc b/fpga/lib/eth/example/VCU108/fpga_10g/eth.xdc new file mode 100644 index 000000000..35dd05b08 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/eth.xdc @@ -0,0 +1,4 @@ +# Ethernet constraints + +set_property LOC BITSLICE_RX_TX_X1Y35 [get_cells -hier -filter {name =~ */lvds_transceiver_mw/serdes_1_to_10_ser8_i/idelay_cal}] +#set_false_path -to [get_pins -hier -filter {name =~ *idelayctrl_inst/RST} -include_replicated_objects ] diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/fpga.xdc b/fpga/lib/eth/example/VCU108/fpga_10g/fpga.xdc new file mode 100644 index 000000000..976f8bea2 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/fpga.xdc @@ -0,0 +1,114 @@ +# XDC constraints for the Xilinx VCU108 board +# part: xcvu095-ffva2104-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.BPI_SYNC_MODE Type1 [current_design] +set_property CONFIG_MODE BPI16 [current_design] + +# System clocks +# 300 MHz +#set_property -dict {LOC G31 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_1_p] +#set_property -dict {LOC F31 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_1_n] +#create_clock -period 3.333 -name clk_300mhz_1 [get_ports clk_300mhz_1_p] + +#set_property -dict {LOC G22 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_2_p] +#set_property -dict {LOC G21 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_2_n] +#create_clock -period 3.333 -name clk_300mhz_2 [get_ports clk_300mhz_2_p] + +# 125 MHz +set_property -dict {LOC BC9 IOSTANDARD LVDS} [get_ports clk_125mhz_p] +set_property -dict {LOC BC8 IOSTANDARD LVDS} [get_ports clk_125mhz_n] +create_clock -period 8.000 -name clk_125mhz [get_ports clk_125mhz_p] + +# 90 MHz +#set_property -dict {LOC AL20 IOSTANDARD LVCMOS18} [get_ports clk_90mhz] +#create_clock -period 11.111 -name clk_90mhz [get_ports clk_90mhz] + +# LEDs +set_property -dict {LOC AT32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[0]}] +set_property -dict {LOC AV34 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[1]}] +set_property -dict {LOC AY30 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[2]}] +set_property -dict {LOC BB32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[3]}] +set_property -dict {LOC BF32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[4]}] +set_property -dict {LOC AV36 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[5]}] +set_property -dict {LOC AY35 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[6]}] +set_property -dict {LOC BA37 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[7]}] + +# Reset button +set_property -dict {LOC E36 IOSTANDARD LVCMOS12} [get_ports reset] + +# Push buttons +set_property -dict {LOC E34 IOSTANDARD LVCMOS12} [get_ports btnu] +set_property -dict {LOC M22 IOSTANDARD LVCMOS12} [get_ports btnl] +set_property -dict {LOC D9 IOSTANDARD LVCMOS12} [get_ports btnd] +set_property -dict {LOC A10 IOSTANDARD LVCMOS12} [get_ports btnr] +set_property -dict {LOC AW27 IOSTANDARD LVCMOS12} [get_ports btnc] + +# DIP switches +set_property -dict {LOC BC40 IOSTANDARD LVCMOS12} [get_ports {sw[0]}] +set_property -dict {LOC L19 IOSTANDARD LVCMOS12} [get_ports {sw[1]}] +set_property -dict {LOC C37 IOSTANDARD LVCMOS12} [get_ports {sw[2]}] +set_property -dict {LOC C38 IOSTANDARD LVCMOS12} [get_ports {sw[3]}] + +# UART +set_property -dict {LOC BE24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_txd] +set_property -dict {LOC BC24 IOSTANDARD LVCMOS18} [get_ports uart_rxd] +set_property -dict {LOC BF24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_rts] +set_property -dict {LOC BD22 IOSTANDARD LVCMOS18} [get_ports uart_cts] + +# Gigabit Ethernet SGMII PHY +set_property -dict {LOC AR24 IOSTANDARD DIFF_HSTL_I_18} [get_ports phy_sgmii_rx_p] +set_property -dict {LOC AT24 IOSTANDARD DIFF_HSTL_I_18} [get_ports phy_sgmii_rx_n] +set_property -dict {LOC AR23 IOSTANDARD DIFF_HSTL_I_18} [get_ports phy_sgmii_tx_p] +set_property -dict {LOC AR22 IOSTANDARD DIFF_HSTL_I_18} [get_ports phy_sgmii_tx_n] +set_property -dict {LOC AT22 IOSTANDARD LVDS_25} [get_ports phy_sgmii_clk_p] +set_property -dict {LOC AU22 IOSTANDARD LVDS_25} [get_ports phy_sgmii_clk_n] +set_property -dict {LOC AU21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_reset_n] +set_property -dict {LOC AT21 IOSTANDARD LVCMOS18} [get_ports phy_int_n] +#set_property -dict {LOC AV24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_mdio] +#set_property -dict {LOC AV21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_mdc] + +# 625 MHz ref clock from SGMII PHY +create_clock -period 1.600 -name phy_sgmii_clk [get_ports phy_sgmii_clk_p] + +# QSFP+ Interface +set_property -dict {LOC AG45} [get_ports qsfp_rx1_p] ;# MGTYTXP0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC AG46} [get_ports qsfp_rx1_n] ;# MGTYTXN0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC AF43} [get_ports qsfp_rx2_p] ;# MGTYTXP1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC AF44} [get_ports qsfp_rx2_n] ;# MGTYTXN1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC AE45} [get_ports qsfp_rx3_p] ;# MGTYTXP2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC AE46} [get_ports qsfp_rx3_n] ;# MGTYTXN2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC AD43} [get_ports qsfp_rx4_p] ;# MGTYTXP3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC AD44} [get_ports qsfp_rx4_n] ;# MGTYTXN3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC AK42} [get_ports qsfp_tx1_p] ;# MGTYTXP0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC AK43} [get_ports qsfp_tx1_n] ;# MGTYTXN0_127 GTYE3_CHANNEL_X0Y12 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC AJ40} [get_ports qsfp_tx2_p] ;# MGTYTXP1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC AJ41} [get_ports qsfp_tx2_n] ;# MGTYTXN1_127 GTYE3_CHANNEL_X0Y13 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC AG40} [get_ports qsfp_tx3_p] ;# MGTYTXP2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC AG41} [get_ports qsfp_tx3_n] ;# MGTYTXN2_127 GTYE3_CHANNEL_X0Y14 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC AE40} [get_ports qsfp_tx4_p] ;# MGTYTXP3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +#set_property -dict {LOC AE41} [get_ports qsfp_tx4_n] ;# MGTYTXN3_127 GTYE3_CHANNEL_X0Y15 / GTYE3_COMMON_X0Y3 +set_property -dict {LOC AF38} [get_ports qsfp_mgt_refclk_0_p] ;# MGTREFCLK0P_127 from U32 SI570 via U102 SI53340 +#set_property -dict {LOC AF39} [get_ports qsfp_mgt_refclk_0_n] ;# MGTREFCLK0N_127 from U32 SI570 via U102 SI53340 +#set_property -dict {LOC AD38} [get_ports qsfp_mgt_refclk_1_p] ;# MGTREFCLK1P_127 from U57 CKOUT2 SI5328 +#set_property -dict {LOC AD39} [get_ports qsfp_mgt_refclk_1_n] ;# MGTREFCLK1N_127 from U57 CKOUT2 SI5328 +#set_property -dict {LOC AG34 IOSTANDARD LVDS} [get_ports qsfp_recclk_p] ;# to U57 CKIN1 SI5328 +#set_property -dict {LOC AH35 IOSTANDARD LVDS} [get_ports qsfp_recclk_n] ;# to U57 CKIN1 SI5328 +set_property -dict {LOC AL24 IOSTANDARD LVCMOS18} [get_ports qsfp_modsell] +set_property -dict {LOC AM24 IOSTANDARD LVCMOS18} [get_ports qsfp_resetl] +set_property -dict {LOC AL25 IOSTANDARD LVCMOS18} [get_ports qsfp_modprsl] +set_property -dict {LOC AL21 IOSTANDARD LVCMOS18} [get_ports qsfp_intl] +set_property -dict {LOC AM21 IOSTANDARD LVCMOS18} [get_ports qsfp_lpmode] + +# 156.25 MHz MGT reference clock +create_clock -period 6.400 -name qsfp_mgt_refclk_0 [get_ports qsfp_mgt_refclk_0_p] + +# I2C interface +set_property -dict {LOC AN21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_scl] +set_property -dict {LOC AP21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_sda] + + diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/fpga/Makefile b/fpga/lib/eth/example/VCU108/fpga_10g/fpga/Makefile new file mode 100644 index 000000000..de36e54d7 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/fpga/Makefile @@ -0,0 +1,116 @@ + +# FPGA settings +FPGA_PART = xcvu095-ffva2104-2-e +FPGA_TOP = fpga +FPGA_ARCH = VirtexUltrascale + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_fifo.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/eth_mac_10g_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_10g.v +SYN_FILES += lib/eth/rtl/axis_xgmii_rx_64.v +SYN_FILES += lib/eth/rtl/axis_xgmii_tx_64.v +SYN_FILES += lib/eth/rtl/eth_phy_10g.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_if.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_frame_sync.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_ber_mon.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx_if.v +SYN_FILES += lib/eth/rtl/xgmii_baser_dec_64.v +SYN_FILES += lib/eth/rtl/xgmii_baser_enc_64.v +SYN_FILES += lib/eth/rtl/lfsr.v +SYN_FILES += lib/eth/rtl/eth_axis_rx_64.v +SYN_FILES += lib/eth/rtl/eth_axis_tx_64.v +SYN_FILES += lib/eth/rtl/udp_complete_64.v +SYN_FILES += lib/eth/rtl/udp_checksum_gen_64.v +SYN_FILES += lib/eth/rtl/udp_64.v +SYN_FILES += lib/eth/rtl/udp_ip_rx_64.v +SYN_FILES += lib/eth/rtl/udp_ip_tx_64.v +SYN_FILES += lib/eth/rtl/ip_complete_64.v +SYN_FILES += lib/eth/rtl/ip_64.v +SYN_FILES += lib/eth/rtl/ip_eth_rx_64.v +SYN_FILES += lib/eth/rtl/ip_eth_tx_64.v +SYN_FILES += lib/eth/rtl/ip_arb_mux.v +SYN_FILES += lib/eth/rtl/arp_64.v +SYN_FILES += lib/eth/rtl/arp_cache.v +SYN_FILES += lib/eth/rtl/arp_eth_rx_64.v +SYN_FILES += lib/eth/rtl/arp_eth_tx_64.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_adapter.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_fifo.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_switch.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_register.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_async_fifo.v + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += eth.xdc +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl + +# IP +XCI_FILES = ip/gig_ethernet_pcs_pma_0.xci +XCI_FILES += ip/gtwizard_ultrascale_0.xci + +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 + +%.mcs %.prm: %.bit + echo "write_cfgmem -force -format mcs -size 128 -interface BPIx16 -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 .mcs .prm; \ + do cp $*$$x rev/$*_rev$$COUNT$$x; \ + echo "Output: rev/$*_rev$$COUNT$$x"; done; + +flash: $(FPGA_TOP).mcs $(FPGA_TOP).prm + echo "open_hw" > 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 {mt28gu01gaax1e-bpi-x16}] 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 \"$(FPGA_TOP).mcs\"] [current_hw_cfgmem]" >> flash.tcl + echo "set_property PROGRAM.PRM_FILES [list \"$(FPGA_TOP).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.BPI_RS_PINS {none} [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 + diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/ip/gig_ethernet_pcs_pma_0.xci b/fpga/lib/eth/example/VCU108/fpga_10g/ip/gig_ethernet_pcs_pma_0.xci new file mode 100644 index 000000000..91b324db6 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/ip/gig_ethernet_pcs_pma_0.xci @@ -0,0 +1,360 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gig_ethernet_pcs_pma_0 + + + 1 + 1 + 1 + 1 + + + + 0 + + + + 0 + + + 0 + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + false + false + false + false + 0 + + + + 0 + + + + 0 + false + 100000000 + + + + 0 + + + + 0 + + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + false + false + false + false + 0 + 0 + + + + 100000000 + 0 + 0.000 + + + + 100000000 + 0 + 0.000 + false + false + false + + + + 100000000 + 0 + 0.000 + 0 + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + ACTIVE_LOW + ACTIVE_LOW + ACTIVE_LOW + ACTIVE_LOW + 1 + 0 + 0 + 0 + + 1 + 100000000 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 0.000 + AXI4LITE + READ_WRITE + 0 + 0 + 0 + 0 + 0 + + + 100000000 + 0 + 0.000 + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + true + 0 + 0 + true + false + DIFF_PAIR_0 + DIFF_PAIR_1 + false + DIFF_PAIR_0 + DIFF_PAIR_1 + virtexu + 0 + gig_ethernet_pcs_pma_0 + 50.0 + false + . + false + false + false + false + virtexu + 17 + 9 + X0Y0 + 7 + 4 + GTH + false + true + false + false + false + false + true + 1 + clk0 + 125 + TXOUTCLK + true + false + gig_ethernet_pcs_pma_0_gt + true + GTHE3 + false + 1 + true + false + false + xcvu095 + false + 1 + false + true + Sync + gig_ethernet_pcs_pma_0 + Custom + 50.0 + TEMAC + Custom + 0 + false + false + false + false + X0Y0 + GTH + false + false + 625 + Custom + false + 1G + 1 + LVDS + 125 + clk0 + TXOUTCLK + DIFF_PAIR_0 + DIFF_PAIR_1 + false + 10_100_1000 + false + SGMII + Include_Shared_Logic_in_Core + Time_of_day + false + DIFF_PAIR_0 + DIFF_PAIR_1 + 1 + false + virtexu + + + xcvu095 + ffva2104 + VERILOG + + MIXED + -2 + + E + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/ip/gtwizard_ultrascale_0.xci b/fpga/lib/eth/example/VCU108/fpga_10g/ip/gtwizard_ultrascale_0.xci new file mode 100644 index 000000000..a38021fba --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/ip/gtwizard_ultrascale_0.xci @@ -0,0 +1,1402 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gtwizard_ultrascale_0 + + + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111000000000000" + 1 + 2578.125 + 0 + 0 + 125 + 27 + 1 + 2 + 0 + 2 + 0 + 0 + 1 + 0 + 1 + 1 + 250 + 0 + 0 + 0 + 0 + 0 + 1 + "00000000" + "00000000" + 1 + 2 + 0 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000" + 0 + "00000000" + 1 + 0 + 5000 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + 0 + "1010000011" + 0 + "0101111100" + 4 + 1 + 64 + 10.3125 + 12 + 1 + 156.2500000 + 4 + 0 + 0x000000000000000000000000000000000000000000000000 + 156.25 + 0 + 0 + 0 + 1 + 1 + 0 + 64 + 156.2500000 + 156.2500000 + 0 + 257.8125 + 1 + 4 + 1 + 0 + 0 + 0 + 156.25 + 0 + 0 + 1 + 4 + 1 + 64 + 10.3125 + 12 + 1 + 156.2500000 + 4 + 0 + 156.25 + 0 + 0 + 1 + 1 + 0 + 64 + 156.2500000 + 156.2500000 + 0 + X0Y15 X0Y14 X0Y13 X0Y12 + gtwizard_ultrascale_0 + 0 + 0 + + 125 + BOTH + 0 + GTY + 2 + 20 + 96 + 2 + gtye3 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + -1 + -1 + -1 + -1 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + 1 + 1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + -1 + -1 + 0 + -1 + -1 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + -1 + -1 + 0 + -1 + -1 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + -1 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + 1 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + -1 + -1 + -1 + 0 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + -1 + 0 + 0 + 0 + -1 + 0 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 13 + 0 + 10GBASE-R + 6 + 156.2500000 + 4 + 1 + 156.2500000 + false + CORE + NONE + CORE + CORE + EXAMPLE_DESIGN + CORE + EXAMPLE_DESIGN + EXAMPLE_DESIGN + false + NAME + false + 250 + false + false + 250 + GTY-10GBASE-R + 0 + MULTI + 1 + ENABLE + DISABLE + ENABLE + 00000000 + false + false + false + false + false + false + false + false + 00000000 + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 2 + 1 + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + false + false + false + false + false + false + false + false + 00000000 + DISABLE + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 0 + 5000 + ENABLE + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 1 + false + 0000000000 + false + 1010000011 + NONE + false + 0101111100 + true + 0 + AC + 64B66B_ASYNC + true + AUTO + 64 + 6.1862627 + -20 + 10.3125 + X0Y12 + RXPROGDIVCLK + QPLL0 + 200 + 0 + + 156.25 + + OFF + 0 + PROGRAMMABLE + 800 + 64 + 15 + false + 0 + 10.3125 + 257.8125 + 1 + false + QPLL0 + 156.25 + 1 + ENABLE + 64B66B_ASYNC + CUSTOM + true + 64 + 10.3125 + X0Y12 + TXPROGDIVCLK + QPLL0 + 0 + 156.25 + + 64 + false + 1 + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + true + true + false + true + true + true + false + true + true + true + false + false + false + false + false + true + false + false + false + false + false + false + false + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + virtexu + + + xcvu095 + ffva2104 + VERILOG + + MIXED + -2 + + E + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/lib/eth b/fpga/lib/eth/example/VCU108/fpga_10g/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/rtl/debounce_switch.v b/fpga/lib/eth/example/VCU108/fpga_10g/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/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 + +`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/fpga/lib/eth/example/VCU108/fpga_10g/rtl/fpga.v b/fpga/lib/eth/example/VCU108/fpga_10g/rtl/fpga.v new file mode 100644 index 000000000..9792b14d3 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/rtl/fpga.v @@ -0,0 +1,940 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 125MHz LVDS + * Reset: Push button, active low + */ + input wire clk_125mhz_p, + input wire clk_125mhz_n, + input wire reset, + + /* + * 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, + + /* + * I2C for board management + */ + inout wire i2c_scl, + inout wire i2c_sda, + + /* + * Ethernet: QSFP28 + */ + input wire qsfp_rx1_p, + input wire qsfp_rx1_n, + input wire qsfp_rx2_p, + input wire qsfp_rx2_n, + input wire qsfp_rx3_p, + input wire qsfp_rx3_n, + input wire qsfp_rx4_p, + input wire qsfp_rx4_n, + output wire qsfp_tx1_p, + output wire qsfp_tx1_n, + output wire qsfp_tx2_p, + output wire qsfp_tx2_n, + output wire qsfp_tx3_p, + output wire qsfp_tx3_n, + output wire qsfp_tx4_p, + output wire qsfp_tx4_n, + input wire qsfp_mgt_refclk_0_p, + input wire qsfp_mgt_refclk_0_n, + // input wire qsfp_mgt_refclk_1_p, + // input wire qsfp_mgt_refclk_1_n, + // output wire qsfp_recclk_p, + // output wire qsfp_recclk_n, + output wire qsfp_modsell, + output wire qsfp_resetl, + input wire qsfp_modprsl, + input wire qsfp_intl, + output wire qsfp_lpmode, + + /* + * Ethernet: 1000BASE-T SGMII + */ + input wire phy_sgmii_rx_p, + input wire phy_sgmii_rx_n, + output wire phy_sgmii_tx_p, + output wire phy_sgmii_tx_n, + input wire phy_sgmii_clk_p, + input wire phy_sgmii_clk_n, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 500000 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// Clock and reset + +wire clk_125mhz_ibufg; +wire clk_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire rst_125mhz_int; + +// Internal 156.25 MHz clock +wire clk_156mhz_int; +wire rst_156mhz_int; + +wire mmcm_rst = reset; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS #( + .DIFF_TERM("FALSE"), + .IBUF_LOW_PWR("FALSE") +) +clk_125mhz_ibufg_inst ( + .O (clk_125mhz_ibufg), + .I (clk_125mhz_p), + .IB (clk_125mhz_n) +); + +// MMCM instance +// 125 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 = 625 MHz (in range) +// Divide by 5 to get output frequency of 125 MHz +MMCME3_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(5), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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(8.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_125mhz_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_125mhz_bufg_inst ( + .I(clk_125mhz_mmcm_out), + .O(clk_125mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_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(156000) +) +debounce_switch_inst ( + .clk(clk_156mhz_int), + .rst(rst_156mhz_int), + .in({btnu, + btnl, + btnd, + btnr, + btnc, + sw}), + .out({btnu_int, + btnl_int, + btnd_int, + btnr_int, + btnc_int, + sw_int}) +); + +wire uart_rxd_int; +wire uart_cts_int; + +sync_signal #( + .WIDTH(2), + .N(2) +) +sync_signal_inst ( + .clk(clk_156mhz_int), + .in({uart_rxd, uart_cts}), + .out({uart_rxd_int, uart_cts_int}) +); + +// SI570 I2C +wire i2c_scl_i; +wire i2c_scl_o = 1'b1; +wire i2c_scl_t = 1'b1; +wire i2c_sda_i; +wire i2c_sda_o = 1'b1; +wire i2c_sda_t = 1'b1; + +assign i2c_scl_i = i2c_scl; +assign i2c_scl = i2c_scl_t ? 1'bz : i2c_scl_o; +assign i2c_sda_i = i2c_sda; +assign i2c_sda = i2c_sda_t ? 1'bz : i2c_sda_o; + +// XGMII 10G PHY +assign qsfp_modsell = 1'b0; +assign qsfp_resetl = 1'b1; +assign qsfp_lpmode = 1'b0; + +wire qsfp_tx_clk_1_int; +wire qsfp_tx_rst_1_int; +wire [63:0] qsfp_txd_1_int; +wire [7:0] qsfp_txc_1_int; +wire qsfp_rx_clk_1_int; +wire qsfp_rx_rst_1_int; +wire [63:0] qsfp_rxd_1_int; +wire [7:0] qsfp_rxc_1_int; +wire qsfp_tx_clk_2_int; +wire qsfp_tx_rst_2_int; +wire [63:0] qsfp_txd_2_int; +wire [7:0] qsfp_txc_2_int; +wire qsfp_rx_clk_2_int; +wire qsfp_rx_rst_2_int; +wire [63:0] qsfp_rxd_2_int; +wire [7:0] qsfp_rxc_2_int; +wire qsfp_tx_clk_3_int; +wire qsfp_tx_rst_3_int; +wire [63:0] qsfp_txd_3_int; +wire [7:0] qsfp_txc_3_int; +wire qsfp_rx_clk_3_int; +wire qsfp_rx_rst_3_int; +wire [63:0] qsfp_rxd_3_int; +wire [7:0] qsfp_rxc_3_int; +wire qsfp_tx_clk_4_int; +wire qsfp_tx_rst_4_int; +wire [63:0] qsfp_txd_4_int; +wire [7:0] qsfp_txc_4_int; +wire qsfp_rx_clk_4_int; +wire qsfp_rx_rst_4_int; +wire [63:0] qsfp_rxd_4_int; +wire [7:0] qsfp_rxc_4_int; + +wire qsfp_rx_block_lock_1; +wire qsfp_rx_block_lock_2; +wire qsfp_rx_block_lock_3; +wire qsfp_rx_block_lock_4; + +wire qsfp_mgt_refclk_0; + +wire [3:0] gt_txclkout; +wire gt_txusrclk; + +wire [3:0] gt_rxclkout; +wire [3:0] gt_rxusrclk; + +wire gt_reset_tx_done; +wire gt_reset_rx_done; + +wire [3:0] gt_txprgdivresetdone; +wire [3:0] gt_txpmaresetdone; +wire [3:0] gt_rxprgdivresetdone; +wire [3:0] gt_rxpmaresetdone; + +wire gt_tx_reset = ~((>_txprgdivresetdone) & (>_txpmaresetdone)); +wire gt_rx_reset = ~>_rxpmaresetdone; + +reg gt_userclk_tx_active = 1'b0; +reg [3:0] gt_userclk_rx_active = 1'b0; + +IBUFDS_GTE3 ibufds_gte3_qsfp_mgt_refclk_0_inst ( + .I (qsfp_mgt_refclk_0_p), + .IB (qsfp_mgt_refclk_0_n), + .CEB (1'b0), + .O (qsfp_mgt_refclk_0), + .ODIV2 () +); + + +BUFG_GT bufg_gt_tx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_tx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_txclkout[0]), + .O (gt_txusrclk) +); + +assign clk_156mhz_int = gt_txusrclk; + +always @(posedge gt_txusrclk, posedge gt_tx_reset) begin + if (gt_tx_reset) begin + gt_userclk_tx_active <= 1'b0; + end else begin + gt_userclk_tx_active <= 1'b1; + end +end + +genvar n; + +generate + +for (n = 0; n < 4; n = n + 1) begin + + BUFG_GT bufg_gt_rx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_rx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_rxclkout[n]), + .O (gt_rxusrclk[n]) + ); + + always @(posedge gt_rxusrclk[n], posedge gt_rx_reset) begin + if (gt_rx_reset) begin + gt_userclk_rx_active[n] <= 1'b0; + end else begin + gt_userclk_rx_active[n] <= 1'b1; + end + end + +end + +endgenerate + +sync_reset #( + .N(4) +) +sync_reset_156mhz_inst ( + .clk(clk_156mhz_int), + .rst(~gt_reset_tx_done), + .sync_reset_out(rst_156mhz_int) +); + +wire [5:0] qsfp_gt_txheader_1; +wire [127:0] qsfp_gt_txdata_1; +wire qsfp_gt_rxgearboxslip_1; +wire [5:0] qsfp_gt_rxheader_1; +wire [1:0] qsfp_gt_rxheadervalid_1; +wire [127:0] qsfp_gt_rxdata_1; +wire [1:0] qsfp_gt_rxdatavalid_1; + +wire [5:0] qsfp_gt_txheader_2; +wire [127:0] qsfp_gt_txdata_2; +wire qsfp_gt_rxgearboxslip_2; +wire [5:0] qsfp_gt_rxheader_2; +wire [1:0] qsfp_gt_rxheadervalid_2; +wire [127:0] qsfp_gt_rxdata_2; +wire [1:0] qsfp_gt_rxdatavalid_2; + +wire [5:0] qsfp_gt_txheader_3; +wire [127:0] qsfp_gt_txdata_3; +wire qsfp_gt_rxgearboxslip_3; +wire [5:0] qsfp_gt_rxheader_3; +wire [1:0] qsfp_gt_rxheadervalid_3; +wire [127:0] qsfp_gt_rxdata_3; +wire [1:0] qsfp_gt_rxdatavalid_3; + +wire [5:0] qsfp_gt_txheader_4; +wire [127:0] qsfp_gt_txdata_4; +wire qsfp_gt_rxgearboxslip_4; +wire [5:0] qsfp_gt_rxheader_4; +wire [1:0] qsfp_gt_rxheadervalid_4; +wire [127:0] qsfp_gt_rxdata_4; +wire [1:0] qsfp_gt_rxdatavalid_4; + +gtwizard_ultrascale_0 +qsfp_gty_inst ( + .gtwiz_userclk_tx_active_in(>_userclk_tx_active), + .gtwiz_userclk_rx_active_in(>_userclk_rx_active), + + .gtwiz_reset_clk_freerun_in(clk_125mhz_int), + .gtwiz_reset_all_in(rst_125mhz_int), + + .gtwiz_reset_tx_pll_and_datapath_in(1'b0), + .gtwiz_reset_tx_datapath_in(1'b0), + + .gtwiz_reset_rx_pll_and_datapath_in(1'b0), + .gtwiz_reset_rx_datapath_in(1'b0), + + .gtwiz_reset_rx_cdr_stable_out(), + + .gtwiz_reset_tx_done_out(gt_reset_tx_done), + .gtwiz_reset_rx_done_out(gt_reset_rx_done), + + .gtrefclk00_in({1{qsfp_mgt_refclk_0}}), + + .qpll0outclk_out(), + .qpll0outrefclk_out(), + + .gtyrxn_in({qsfp_rx4_n, qsfp_rx3_n, qsfp_rx2_n, qsfp_rx1_n}), + .gtyrxp_in({qsfp_rx4_p, qsfp_rx3_p, qsfp_rx2_p, qsfp_rx1_p}), + + .rxusrclk_in(gt_rxusrclk), + .rxusrclk2_in(gt_rxusrclk), + + .txdata_in({qsfp_gt_txdata_4, qsfp_gt_txdata_3, qsfp_gt_txdata_2, qsfp_gt_txdata_1}), + .txheader_in({qsfp_gt_txheader_4, qsfp_gt_txheader_3, qsfp_gt_txheader_2, qsfp_gt_txheader_1}), + .txsequence_in({4{1'b0}}), + + .txusrclk_in({4{gt_txusrclk}}), + .txusrclk2_in({4{gt_txusrclk}}), + + .gtpowergood_out(), + + .gtytxn_out({qsfp_tx4_n, qsfp_tx3_n, qsfp_tx2_n, qsfp_tx1_n}), + .gtytxp_out({qsfp_tx4_p, qsfp_tx3_p, qsfp_tx2_p, qsfp_tx1_p}), + + .rxgearboxslip_in({qsfp_gt_rxgearboxslip_4, qsfp_gt_rxgearboxslip_3, qsfp_gt_rxgearboxslip_2, qsfp_gt_rxgearboxslip_1}), + .rxdata_out({qsfp_gt_rxdata_4, qsfp_gt_rxdata_3, qsfp_gt_rxdata_2, qsfp_gt_rxdata_1}), + .rxdatavalid_out({qsfp_gt_rxdatavalid_4, qsfp_gt_rxdatavalid_3, qsfp_gt_rxdatavalid_2, qsfp_gt_rxdatavalid_1}), + .rxheader_out({qsfp_gt_rxheader_4, qsfp_gt_rxheader_3, qsfp_gt_rxheader_2, qsfp_gt_rxheader_1}), + .rxheadervalid_out({qsfp_gt_rxheadervalid_4, qsfp_gt_rxheadervalid_3, qsfp_gt_rxheadervalid_2, qsfp_gt_rxheadervalid_1}), + .rxoutclk_out(gt_rxclkout), + .rxpmaresetdone_out(gt_rxpmaresetdone), + .rxprgdivresetdone_out(gt_rxprgdivresetdone), + .rxstartofseq_out(), + + .txoutclk_out(gt_txclkout), + .txpmaresetdone_out(gt_txpmaresetdone), + .txprgdivresetdone_out(gt_txprgdivresetdone) +); + +assign qsfp_tx_clk_1_int = clk_156mhz_int; +assign qsfp_tx_rst_1_int = rst_156mhz_int; + +assign qsfp_rx_clk_1_int = gt_rxusrclk[0]; + +sync_reset #( + .N(4) +) +qsfp_rx_rst_1_reset_sync_inst ( + .clk(qsfp_rx_clk_1_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_rx_rst_1_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_phy_1_inst ( + .tx_clk(qsfp_tx_clk_1_int), + .tx_rst(qsfp_tx_rst_1_int), + .rx_clk(qsfp_rx_clk_1_int), + .rx_rst(qsfp_rx_rst_1_int), + .xgmii_txd(qsfp_txd_1_int), + .xgmii_txc(qsfp_txc_1_int), + .xgmii_rxd(qsfp_rxd_1_int), + .xgmii_rxc(qsfp_rxc_1_int), + .serdes_tx_data(qsfp_gt_txdata_1), + .serdes_tx_hdr(qsfp_gt_txheader_1), + .serdes_rx_data(qsfp_gt_rxdata_1), + .serdes_rx_hdr(qsfp_gt_rxheader_1), + .serdes_rx_bitslip(qsfp_gt_rxgearboxslip_1), + .rx_block_lock(qsfp_rx_block_lock_1), + .rx_high_ber() +); + +assign qsfp_tx_clk_2_int = clk_156mhz_int; +assign qsfp_tx_rst_2_int = rst_156mhz_int; + +assign qsfp_rx_clk_2_int = gt_rxusrclk[1]; + +sync_reset #( + .N(4) +) +qsfp_rx_rst_2_reset_sync_inst ( + .clk(qsfp_rx_clk_2_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_rx_rst_2_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_phy_2_inst ( + .tx_clk(qsfp_tx_clk_2_int), + .tx_rst(qsfp_tx_rst_2_int), + .rx_clk(qsfp_rx_clk_2_int), + .rx_rst(qsfp_rx_rst_2_int), + .xgmii_txd(qsfp_txd_2_int), + .xgmii_txc(qsfp_txc_2_int), + .xgmii_rxd(qsfp_rxd_2_int), + .xgmii_rxc(qsfp_rxc_2_int), + .serdes_tx_data(qsfp_gt_txdata_2), + .serdes_tx_hdr(qsfp_gt_txheader_2), + .serdes_rx_data(qsfp_gt_rxdata_2), + .serdes_rx_hdr(qsfp_gt_rxheader_2), + .serdes_rx_bitslip(qsfp_gt_rxgearboxslip_2), + .rx_block_lock(qsfp_rx_block_lock_2), + .rx_high_ber() +); + +assign qsfp_tx_clk_3_int = clk_156mhz_int; +assign qsfp_tx_rst_3_int = rst_156mhz_int; + +assign qsfp_rx_clk_3_int = gt_rxusrclk[2]; + +sync_reset #( + .N(4) +) +qsfp_rx_rst_3_reset_sync_inst ( + .clk(qsfp_rx_clk_3_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_rx_rst_3_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_phy_3_inst ( + .tx_clk(qsfp_tx_clk_3_int), + .tx_rst(qsfp_tx_rst_3_int), + .rx_clk(qsfp_rx_clk_3_int), + .rx_rst(qsfp_rx_rst_3_int), + .xgmii_txd(qsfp_txd_3_int), + .xgmii_txc(qsfp_txc_3_int), + .xgmii_rxd(qsfp_rxd_3_int), + .xgmii_rxc(qsfp_rxc_3_int), + .serdes_tx_data(qsfp_gt_txdata_3), + .serdes_tx_hdr(qsfp_gt_txheader_3), + .serdes_rx_data(qsfp_gt_rxdata_3), + .serdes_rx_hdr(qsfp_gt_rxheader_3), + .serdes_rx_bitslip(qsfp_gt_rxgearboxslip_3), + .rx_block_lock(qsfp_rx_block_lock_3), + .rx_high_ber() +); + +assign qsfp_tx_clk_4_int = clk_156mhz_int; +assign qsfp_tx_rst_4_int = rst_156mhz_int; + +assign qsfp_rx_clk_4_int = gt_rxusrclk[3]; + +sync_reset #( + .N(4) +) +qsfp_rx_rst_4_reset_sync_inst ( + .clk(qsfp_rx_clk_4_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp_rx_rst_4_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp_phy_4_inst ( + .tx_clk(qsfp_tx_clk_4_int), + .tx_rst(qsfp_tx_rst_4_int), + .rx_clk(qsfp_rx_clk_4_int), + .rx_rst(qsfp_rx_rst_4_int), + .xgmii_txd(qsfp_txd_4_int), + .xgmii_txc(qsfp_txc_4_int), + .xgmii_rxd(qsfp_rxd_4_int), + .xgmii_rxc(qsfp_rxc_4_int), + .serdes_tx_data(qsfp_gt_txdata_4), + .serdes_tx_hdr(qsfp_gt_txheader_4), + .serdes_rx_data(qsfp_gt_rxdata_4), + .serdes_rx_hdr(qsfp_gt_rxheader_4), + .serdes_rx_bitslip(qsfp_gt_rxgearboxslip_4), + .rx_block_lock(qsfp_rx_block_lock_4), + .rx_high_ber() +); + +// // XGMII 10G PHY +// assign qsfp_modsell = 1'b0; +// assign qsfp_resetl = 1'b1; +// assign qsfp_lpmode = 1'b0; + +// wire [63:0] qsfp_txd_1_int; +// wire [7:0] qsfp_txc_1_int; +// wire [63:0] qsfp_rxd_1_int; +// wire [7:0] qsfp_rxc_1_int; +// wire [63:0] qsfp_txd_2_int; +// wire [7:0] qsfp_txc_2_int; +// wire [63:0] qsfp_rxd_2_int = 64'h0707070707070707; +// wire [7:0] qsfp_rxc_2_int = 8'hff; +// wire [63:0] qsfp_txd_3_int; +// wire [7:0] qsfp_txc_3_int; +// wire [63:0] qsfp_rxd_3_int = 64'h0707070707070707; +// wire [7:0] qsfp_rxc_3_int = 8'hff; +// wire [63:0] qsfp_txd_4_int; +// wire [7:0] qsfp_txc_4_int; +// wire [63:0] qsfp_rxd_4_int = 64'h0707070707070707; +// wire [7:0] qsfp_rxc_4_int = 8'hff; + +// wire [535:0] configuration_vector; +// wire [447:0] status_vector; +// wire [7:0] core_status; + +// assign configuration_vector[0] = 1'b0; // PMA Loopback Enable +// assign configuration_vector[14:1] = 0; +// assign configuration_vector[15] = 1'b0; // PMA Reset +// assign configuration_vector[16] = 1'b0; // Global PMD TX Disable +// assign configuration_vector[109:17] = 0; +// assign configuration_vector[110] = 1'b0; // PCS Loopback Enable +// assign configuration_vector[111] = 1'b0; // PCS Reset +// assign configuration_vector[169:112] = 58'd0; // 10GBASE-R Test Pattern Seed A0-3 +// assign configuration_vector[175:170] = 0; +// assign configuration_vector[233:176] = 58'd0; // 10GBASE-R Test Pattern Seed B0-3 +// assign configuration_vector[239:234] = 0; +// assign configuration_vector[240] = 1'b0; // Data Pattern Select +// assign configuration_vector[241] = 1'b0; // Test Pattern Select +// assign configuration_vector[242] = 1'b0; // RX Test Pattern Checking Enable +// assign configuration_vector[243] = 1'b0; // TX Test Pattern Enable +// assign configuration_vector[244] = 1'b0; // PRBS31 TX Test Pattern Enable +// assign configuration_vector[245] = 1'b0; // PRBS31 RX Test Pattern Checking Enable +// assign configuration_vector[383:246] = 0; +// assign configuration_vector[399:384] = 16'h4C4B; // 125 us timer control +// assign configuration_vector[511:400] = 0; +// assign configuration_vector[512] = 1'b0; // Set PMA Link Status +// assign configuration_vector[513] = 1'b0; // Clear PMA/PMD Link Faults +// assign configuration_vector[515:514] = 0; +// assign configuration_vector[516] = 1'b0; // Set PCS Link Status +// assign configuration_vector[517] = 1'b0; // Clear PCS Link Faults +// assign configuration_vector[518] = 1'b0; // Clear 10GBASE-R Status 2 +// assign configuration_vector[519] = 1'b0; // Clear 10GBASE-R Test Pattern Error Counter +// assign configuration_vector[535:520] = 0; + +// wire drp_gnt; +// wire gt_drprdy; +// wire [15:0] gt_drpdo; +// wire gt_drpen; +// wire gt_drpwe; +// wire [15:0] gt_drpaddr; +// wire [15:0] gt_drpdi; + +// ten_gig_eth_pcs_pma_0 +// ten_gig_eth_pcs_pma_inst ( +// .refclk_p(qsfp_mgt_refclk_0_p), +// .refclk_n(qsfp_mgt_refclk_0_n), + +// .dclk(clk_125mhz_int), + +// .coreclk_out(), + +// //.reset(rst_125mhz_int | si570_i2c_init_busy), +// .reset(rst_125mhz_int), + +// .sim_speedup_control(1'b0), + +// .qpll0outclk_out(), +// .qpll0outrefclk_out(), +// .qpll0lock_out(), + +// .rxrecclk_out(), + +// .txusrclk_out(), +// .txusrclk2_out(clk_156mhz_int), + +// .gttxreset_out(), +// .gtrxreset_out(), + +// .txuserrdy_out(), + +// .areset_datapathclk_out(rst_156mhz_int), +// .areset_coreclk_out(), +// .reset_counter_done_out(), + +// .xgmii_txd(qsfp_txd_1_int), +// .xgmii_txc(qsfp_txc_1_int), +// .xgmii_rxd(qsfp_rxd_1_int), +// .xgmii_rxc(qsfp_rxc_1_int), + +// .txp(qsfp_tx1_p), +// .txn(qsfp_tx1_n), +// .rxp(qsfp_rx1_p), +// .rxn(qsfp_rx1_n), + +// .resetdone_out(), +// .signal_detect(1'b1), +// .tx_fault(1'b0), + +// .drp_req(drp_gnt), +// .drp_gnt(drp_gnt), + +// .core_to_gt_drprdy(gt_drprdy), +// .core_to_gt_drpdo(gt_drpdo), +// .core_to_gt_drpen(gt_drpen), +// .core_to_gt_drpwe(gt_drpwe), +// .core_to_gt_drpaddr(gt_drpaddr), +// .core_to_gt_drpdi(gt_drpdi), + +// .gt_drprdy(gt_drprdy), +// .gt_drpdo(gt_drpdo), +// .gt_drpen(gt_drpen), +// .gt_drpwe(gt_drpwe), +// .gt_drpaddr(gt_drpaddr), +// .gt_drpdi(gt_drpdi), + +// .tx_disable(), +// .configuration_vector(configuration_vector), +// .status_vector(status_vector), +// .pma_pmd_type(3'b101), +// .core_status(core_status) +// ); + +// SGMII interface to PHY +wire phy_gmii_clk_int; +wire phy_gmii_rst_int; +wire phy_gmii_clk_en_int; +wire [7:0] phy_gmii_txd_int; +wire phy_gmii_tx_en_int; +wire phy_gmii_tx_er_int; +wire [7:0] phy_gmii_rxd_int; +wire phy_gmii_rx_dv_int; +wire phy_gmii_rx_er_int; + +wire [15:0] gig_eth_pcspma_status_vector; + +wire gig_eth_pcspma_status_link_status = gig_eth_pcspma_status_vector[0]; +wire gig_eth_pcspma_status_link_synchronization = gig_eth_pcspma_status_vector[1]; +wire gig_eth_pcspma_status_rudi_c = gig_eth_pcspma_status_vector[2]; +wire gig_eth_pcspma_status_rudi_i = gig_eth_pcspma_status_vector[3]; +wire gig_eth_pcspma_status_rudi_invalid = gig_eth_pcspma_status_vector[4]; +wire gig_eth_pcspma_status_rxdisperr = gig_eth_pcspma_status_vector[5]; +wire gig_eth_pcspma_status_rxnotintable = gig_eth_pcspma_status_vector[6]; +wire gig_eth_pcspma_status_phy_link_status = gig_eth_pcspma_status_vector[7]; +wire [1:0] gig_eth_pcspma_status_remote_fault_encdg = gig_eth_pcspma_status_vector[9:8]; +wire [1:0] gig_eth_pcspma_status_speed = gig_eth_pcspma_status_vector[11:10]; +wire gig_eth_pcspma_status_duplex = gig_eth_pcspma_status_vector[12]; +wire gig_eth_pcspma_status_remote_fault = gig_eth_pcspma_status_vector[13]; +wire [1:0] gig_eth_pcspma_status_pause = gig_eth_pcspma_status_vector[15:14]; + +wire [4:0] gig_eth_pcspma_config_vector; + +assign gig_eth_pcspma_config_vector[4] = 1'b1; // autonegotiation enable +assign gig_eth_pcspma_config_vector[3] = 1'b0; // isolate +assign gig_eth_pcspma_config_vector[2] = 1'b0; // power down +assign gig_eth_pcspma_config_vector[1] = 1'b0; // loopback enable +assign gig_eth_pcspma_config_vector[0] = 1'b0; // unidirectional enable + +wire [15:0] gig_eth_pcspma_an_config_vector; + +assign gig_eth_pcspma_an_config_vector[15] = 1'b1; // SGMII link status +assign gig_eth_pcspma_an_config_vector[14] = 1'b1; // SGMII Acknowledge +assign gig_eth_pcspma_an_config_vector[13:12] = 2'b01; // full duplex +assign gig_eth_pcspma_an_config_vector[11:10] = 2'b10; // SGMII speed +assign gig_eth_pcspma_an_config_vector[9] = 1'b0; // reserved +assign gig_eth_pcspma_an_config_vector[8:7] = 2'b00; // pause frames - SGMII reserved +assign gig_eth_pcspma_an_config_vector[6] = 1'b0; // reserved +assign gig_eth_pcspma_an_config_vector[5] = 1'b0; // full duplex - SGMII reserved +assign gig_eth_pcspma_an_config_vector[4:1] = 4'b0000; // reserved +assign gig_eth_pcspma_an_config_vector[0] = 1'b1; // SGMII + +gig_ethernet_pcs_pma_0 +gig_eth_pcspma ( + // SGMII + .txp (phy_sgmii_tx_p), + .txn (phy_sgmii_tx_n), + .rxp (phy_sgmii_rx_p), + .rxn (phy_sgmii_rx_n), + + // Ref clock from PHY + .refclk625_p (phy_sgmii_clk_p), + .refclk625_n (phy_sgmii_clk_n), + + // async reset + .reset (rst_125mhz_int), + + // clock and reset outputs + .clk125_out (phy_gmii_clk_int), + .clk625_out (), + .clk312_out (), + .rst_125_out (phy_gmii_rst_int), + .idelay_rdy_out (), + .mmcm_locked_out (), + + // MAC clocking + .sgmii_clk_r (), + .sgmii_clk_f (), + .sgmii_clk_en (phy_gmii_clk_en_int), + + // Speed control + .speed_is_10_100 (gig_eth_pcspma_status_speed != 2'b10), + .speed_is_100 (gig_eth_pcspma_status_speed == 2'b01), + + // Internal GMII + .gmii_txd (phy_gmii_txd_int), + .gmii_tx_en (phy_gmii_tx_en_int), + .gmii_tx_er (phy_gmii_tx_er_int), + .gmii_rxd (phy_gmii_rxd_int), + .gmii_rx_dv (phy_gmii_rx_dv_int), + .gmii_rx_er (phy_gmii_rx_er_int), + .gmii_isolate (), + + // Configuration + .configuration_vector (gig_eth_pcspma_config_vector), + + .an_interrupt (), + .an_adv_config_vector (gig_eth_pcspma_an_config_vector), + .an_restart_config (1'b0), + + // Status + .status_vector (gig_eth_pcspma_status_vector), + .signal_detect (1'b1) +); + +wire [7:0] led_int; + +assign led = sw[0] ? {4'd0, qsfp_rx_block_lock_4, qsfp_rx_block_lock_3, qsfp_rx_block_lock_2, qsfp_rx_block_lock_1} : led_int; + +fpga_core +core_inst ( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + .clk(clk_156mhz_int), + .rst(rst_156mhz_int), + /* + * GPIO + */ + .btnu(btnu_int), + .btnl(btnl_int), + .btnd(btnd_int), + .btnr(btnr_int), + .btnc(btnc_int), + .sw(sw_int), + .led(led_int), + /* + * Ethernet: QSFP28 + */ + .qsfp_tx_clk_1(qsfp_tx_clk_1_int), + .qsfp_tx_rst_1(qsfp_tx_rst_1_int), + .qsfp_txd_1(qsfp_txd_1_int), + .qsfp_txc_1(qsfp_txc_1_int), + .qsfp_rx_clk_1(qsfp_rx_clk_1_int), + .qsfp_rx_rst_1(qsfp_rx_rst_1_int), + .qsfp_rxd_1(qsfp_rxd_1_int), + .qsfp_rxc_1(qsfp_rxc_1_int), + .qsfp_tx_clk_2(qsfp_tx_clk_2_int), + .qsfp_tx_rst_2(qsfp_tx_rst_2_int), + .qsfp_txd_2(qsfp_txd_2_int), + .qsfp_txc_2(qsfp_txc_2_int), + .qsfp_rx_clk_2(qsfp_rx_clk_2_int), + .qsfp_rx_rst_2(qsfp_rx_rst_2_int), + .qsfp_rxd_2(qsfp_rxd_2_int), + .qsfp_rxc_2(qsfp_rxc_2_int), + .qsfp_tx_clk_3(qsfp_tx_clk_3_int), + .qsfp_tx_rst_3(qsfp_tx_rst_3_int), + .qsfp_txd_3(qsfp_txd_3_int), + .qsfp_txc_3(qsfp_txc_3_int), + .qsfp_rx_clk_3(qsfp_rx_clk_3_int), + .qsfp_rx_rst_3(qsfp_rx_rst_3_int), + .qsfp_rxd_3(qsfp_rxd_3_int), + .qsfp_rxc_3(qsfp_rxc_3_int), + .qsfp_tx_clk_4(qsfp_tx_clk_4_int), + .qsfp_tx_rst_4(qsfp_tx_rst_4_int), + .qsfp_txd_4(qsfp_txd_4_int), + .qsfp_txc_4(qsfp_txc_4_int), + .qsfp_rx_clk_4(qsfp_rx_clk_4_int), + .qsfp_rx_rst_4(qsfp_rx_rst_4_int), + .qsfp_rxd_4(qsfp_rxd_4_int), + .qsfp_rxc_4(qsfp_rxc_4_int), + /* + * Ethernet: 1000BASE-T SGMII + */ + .phy_gmii_clk(phy_gmii_clk_int), + .phy_gmii_rst(phy_gmii_rst_int), + .phy_gmii_clk_en(phy_gmii_clk_en_int), + .phy_gmii_rxd(phy_gmii_rxd_int), + .phy_gmii_rx_dv(phy_gmii_rx_dv_int), + .phy_gmii_rx_er(phy_gmii_rx_er_int), + .phy_gmii_txd(phy_gmii_txd_int), + .phy_gmii_tx_en(phy_gmii_tx_en_int), + .phy_gmii_tx_er(phy_gmii_tx_er_int), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts_int) +); + +endmodule diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/rtl/fpga_core.v b/fpga/lib/eth/example/VCU108/fpga_10g/rtl/fpga_core.v new file mode 100644 index 000000000..16284a70f --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/rtl/fpga_core.v @@ -0,0 +1,867 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * Clock: 156.25MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + + /* + * 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: QSFP28 + */ + input wire qsfp_tx_clk_1, + input wire qsfp_tx_rst_1, + output wire [63:0] qsfp_txd_1, + output wire [7:0] qsfp_txc_1, + input wire qsfp_rx_clk_1, + input wire qsfp_rx_rst_1, + input wire [63:0] qsfp_rxd_1, + input wire [7:0] qsfp_rxc_1, + input wire qsfp_tx_clk_2, + input wire qsfp_tx_rst_2, + output wire [63:0] qsfp_txd_2, + output wire [7:0] qsfp_txc_2, + input wire qsfp_rx_clk_2, + input wire qsfp_rx_rst_2, + input wire [63:0] qsfp_rxd_2, + input wire [7:0] qsfp_rxc_2, + input wire qsfp_tx_clk_3, + input wire qsfp_tx_rst_3, + output wire [63:0] qsfp_txd_3, + output wire [7:0] qsfp_txc_3, + input wire qsfp_rx_clk_3, + input wire qsfp_rx_rst_3, + input wire [63:0] qsfp_rxd_3, + input wire [7:0] qsfp_rxc_3, + input wire qsfp_tx_clk_4, + input wire qsfp_tx_rst_4, + output wire [63:0] qsfp_txd_4, + output wire [7:0] qsfp_txc_4, + input wire qsfp_rx_clk_4, + input wire qsfp_rx_rst_4, + input wire [63:0] qsfp_rxd_4, + input wire [7:0] qsfp_rxc_4, + + /* + * Ethernet: 1000BASE-T SGMII + */ + input wire phy_gmii_clk, + input wire phy_gmii_rst, + input wire phy_gmii_clk_en, + input wire [7:0] phy_gmii_rxd, + input wire phy_gmii_rx_dv, + input wire phy_gmii_rx_er, + output wire [7:0] phy_gmii_txd, + output wire phy_gmii_tx_en, + output wire phy_gmii_tx_er, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 115200 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// AXI between MAC and Ethernet modules +wire [63:0] mac_rx_axis_tdata; +wire [7:0] mac_rx_axis_tkeep; +wire mac_rx_axis_tvalid; +wire mac_rx_axis_tready; +wire mac_rx_axis_tlast; +wire mac_rx_axis_tuser; + +wire [63:0] mac_tx_axis_tdata; +wire [7:0] mac_tx_axis_tkeep; +wire mac_tx_axis_tvalid; +wire mac_tx_axis_tready; +wire mac_tx_axis_tlast; +wire mac_tx_axis_tuser; + +wire [63:0] rx_axis_tdata; +wire [7:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tready; +wire rx_axis_tlast; +wire rx_axis_tuser; + +wire [63:0] tx_axis_tdata; +wire [7:0] tx_axis_tkeep; +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 [63:0] rx_eth_payload_axis_tdata; +wire [7:0] rx_eth_payload_axis_tkeep; +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 [63:0] tx_eth_payload_axis_tdata; +wire [7:0] tx_eth_payload_axis_tkeep; +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 [63:0] rx_ip_payload_axis_tdata; +wire [7:0] rx_ip_payload_axis_tkeep; +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 [63:0] tx_ip_payload_axis_tdata; +wire [7:0] tx_ip_payload_axis_tkeep; +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 [63:0] rx_udp_payload_axis_tdata; +wire [7:0] rx_udp_payload_axis_tkeep; +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 [63:0] tx_udp_payload_axis_tdata; +wire [7:0] tx_udp_payload_axis_tkeep; +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 [63:0] rx_fifo_udp_payload_axis_tdata; +wire [7:0] rx_fifo_udp_payload_axis_tkeep; +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 [63:0] tx_fifo_udp_payload_axis_tdata; +wire [7:0] tx_fifo_udp_payload_axis_tkeep; +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_tkeep = 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_tkeep = tx_fifo_udp_payload_axis_tkeep; +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_tkeep = rx_udp_payload_axis_tkeep; +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 + valid_last <= tx_udp_payload_axis_tvalid; + if (tx_udp_payload_axis_tvalid && !valid_last) begin + led_reg <= tx_udp_payload_axis_tdata; + end + end +end + +//assign led = sw; +assign led = led_reg; +assign phy_reset_n = !rst; + +assign qsfp_txd_2 = 64'h0707070707070707; +assign qsfp_txc_2 = 8'hff; +assign qsfp_txd_3 = 64'h0707070707070707; +assign qsfp_txc_3 = 8'hff; +assign qsfp_txd_4 = 64'h0707070707070707; +assign qsfp_txc_4 = 8'hff; + +eth_mac_10g_fifo #( + .ENABLE_PADDING(1), + .ENABLE_DIC(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(9), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(9), + .RX_FRAME_FIFO(1) +) +eth_mac_10g_fifo_inst ( + .rx_clk(qsfp_rx_clk_1), + .rx_rst(qsfp_rx_rst_1), + .tx_clk(qsfp_tx_clk_1), + .tx_rst(qsfp_tx_rst_1), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(mac_tx_axis_tdata), + .tx_axis_tkeep(mac_tx_axis_tkeep), + .tx_axis_tvalid(mac_tx_axis_tvalid), + .tx_axis_tready(mac_tx_axis_tready), + .tx_axis_tlast(mac_tx_axis_tlast), + .tx_axis_tuser(mac_tx_axis_tuser), + + .rx_axis_tdata(mac_rx_axis_tdata), + .rx_axis_tkeep(mac_rx_axis_tkeep), + .rx_axis_tvalid(mac_rx_axis_tvalid), + .rx_axis_tready(mac_rx_axis_tready), + .rx_axis_tlast(mac_rx_axis_tlast), + .rx_axis_tuser(mac_rx_axis_tuser), + + .xgmii_rxd(qsfp_rxd_1), + .xgmii_rxc(qsfp_rxc_1), + .xgmii_txd(qsfp_txd_1), + .xgmii_txc(qsfp_txc_1), + + .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(), + + .ifg_delay(8'd12) +); + +// 1G interface for debugging +wire [7:0] gig_rx_axis_tdata; +wire gig_rx_axis_tvalid; +wire gig_rx_axis_tready; +wire gig_rx_axis_tlast; +wire gig_rx_axis_tuser; + +wire [7:0] gig_tx_axis_tdata; +wire gig_tx_axis_tvalid; +wire gig_tx_axis_tready; +wire gig_tx_axis_tlast; +wire gig_tx_axis_tuser; + +wire [63:0] gig_rx_axis_tdata_64; +wire [7:0] gig_rx_axis_tkeep_64; +wire gig_rx_axis_tvalid_64; +wire gig_rx_axis_tready_64; +wire gig_rx_axis_tlast_64; +wire gig_rx_axis_tuser_64; + +wire [63:0] gig_tx_axis_tdata_64; +wire [7:0] gig_tx_axis_tkeep_64; +wire gig_tx_axis_tvalid_64; +wire gig_tx_axis_tready_64; +wire gig_tx_axis_tlast_64; +wire gig_tx_axis_tuser_64; + +eth_mac_1g_fifo #( + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_1g_inst ( + .rx_clk(phy_gmii_clk), + .rx_rst(phy_gmii_rst), + .tx_clk(phy_gmii_clk), + .tx_rst(phy_gmii_rst), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(gig_tx_axis_tdata), + .tx_axis_tvalid(gig_tx_axis_tvalid), + .tx_axis_tready(gig_tx_axis_tready), + .tx_axis_tlast(gig_tx_axis_tlast), + .tx_axis_tuser(gig_tx_axis_tuser), + + .rx_axis_tdata(gig_rx_axis_tdata), + .rx_axis_tvalid(gig_rx_axis_tvalid), + .rx_axis_tready(gig_rx_axis_tready), + .rx_axis_tlast(gig_rx_axis_tlast), + .rx_axis_tuser(gig_rx_axis_tuser), + + .gmii_rxd(phy_gmii_rxd), + .gmii_rx_dv(phy_gmii_rx_dv), + .gmii_rx_er(phy_gmii_rx_er), + .gmii_txd(phy_gmii_txd), + .gmii_tx_en(phy_gmii_tx_en), + .gmii_tx_er(phy_gmii_tx_er), + + .rx_clk_enable(phy_gmii_clk_en), + .tx_clk_enable(phy_gmii_clk_en), + .rx_mii_select(1'b0), + .tx_mii_select(1'b0), + + .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(), + + .ifg_delay(12) +); + +axis_adapter #( + .S_DATA_WIDTH(8), + .M_DATA_WIDTH(64), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1) +) +gig_rx_axis_adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(gig_rx_axis_tdata), + .s_axis_tkeep(1'b1), + .s_axis_tvalid(gig_rx_axis_tvalid), + .s_axis_tready(gig_rx_axis_tready), + .s_axis_tlast(gig_rx_axis_tlast), + .s_axis_tuser(gig_rx_axis_tuser), + // AXI output + .m_axis_tdata(gig_rx_axis_tdata_64), + .m_axis_tkeep(gig_rx_axis_tkeep_64), + .m_axis_tvalid(gig_rx_axis_tvalid_64), + .m_axis_tready(gig_rx_axis_tready_64), + .m_axis_tlast(gig_rx_axis_tlast_64), + .m_axis_tuser(gig_rx_axis_tuser_64) +); + +axis_adapter #( + .S_DATA_WIDTH(64), + .M_DATA_WIDTH(8), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1) +) +gig_tx_axis_adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(gig_tx_axis_tdata_64), + .s_axis_tkeep(gig_tx_axis_tkeep_64), + .s_axis_tvalid(gig_tx_axis_tvalid_64), + .s_axis_tready(gig_tx_axis_tready_64), + .s_axis_tlast(gig_tx_axis_tlast_64), + .s_axis_tuser(gig_tx_axis_tuser_64), + // AXI output + .m_axis_tdata(gig_tx_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(gig_tx_axis_tvalid), + .m_axis_tready(gig_tx_axis_tready), + .m_axis_tlast(gig_tx_axis_tlast), + .m_axis_tuser(gig_tx_axis_tuser) +); + +// tap port mux logic +// sw[3] enable +// sw[2] select 0 rx, 1 tx + +reg [1:0] mac_rx_tdest; +reg [1:0] tx_tdest; +reg [1:0] gig_rx_tdest; + +always @* begin + if (sw[3]) begin + if (sw[2]) begin + // Tap on TX path + // MAC RX out -> stack RX in + // stack TX out -> gig TX in + // gig RX out -> MAC TX in + mac_rx_tdest = 2'd1; + tx_tdest = 2'd2; + gig_rx_tdest = 2'd0; + end else begin + // Tap on RX path + // MAC RX out -> gig TX in + // stack TX out -> MAC TX in + // gig RX out -> stack RX in + mac_rx_tdest = 2'd2; + tx_tdest = 2'd0; + gig_rx_tdest = 2'd1; + end + end else begin + // Tap disabled + // MAC RX out -> stack RX in + // stack TX out -> MAC TX in + // gig RX out -> blackhole + mac_rx_tdest = 2'd1; + tx_tdest = 2'd0; + gig_rx_tdest = 2'd3; + end +end + +axis_switch #( + .S_COUNT(3), + .M_COUNT(3), + .DATA_WIDTH(64), + .KEEP_WIDTH(8), + .ID_ENABLE(0), + .DEST_WIDTH(2), + .USER_ENABLE(1), + .USER_WIDTH(1), + .M_BASE({2'd2, 2'd1, 2'd0}), + .M_TOP({2'd2, 2'd1, 2'd0}), + .M_CONNECT({3{3'b111}}), + .S_REG_TYPE(0), + .M_REG_TYPE(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +axis_switch_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ gig_rx_axis_tdata_64, tx_axis_tdata, mac_rx_axis_tdata}), + .s_axis_tkeep({ gig_rx_axis_tkeep_64, tx_axis_tkeep, mac_rx_axis_tkeep}), + .s_axis_tvalid({gig_rx_axis_tvalid_64, tx_axis_tvalid, mac_rx_axis_tvalid}), + .s_axis_tready({gig_rx_axis_tready_64, tx_axis_tready, mac_rx_axis_tready}), + .s_axis_tlast({ gig_rx_axis_tlast_64, tx_axis_tlast, mac_rx_axis_tlast}), + .s_axis_tid(0), + .s_axis_tdest({ gig_rx_tdest, tx_tdest, mac_rx_tdest}), + .s_axis_tuser({ gig_rx_axis_tuser_64, tx_axis_tuser, mac_rx_axis_tuser}), + // AXI outputs + .m_axis_tdata({ gig_tx_axis_tdata_64, rx_axis_tdata, mac_tx_axis_tdata}), + .m_axis_tkeep({ gig_tx_axis_tkeep_64, rx_axis_tkeep, mac_tx_axis_tkeep}), + .m_axis_tvalid({gig_tx_axis_tvalid_64, rx_axis_tvalid, mac_tx_axis_tvalid}), + .m_axis_tready({gig_tx_axis_tready_64, rx_axis_tready, mac_tx_axis_tready}), + .m_axis_tlast({ gig_tx_axis_tlast_64, rx_axis_tlast, mac_tx_axis_tlast}), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser({ gig_tx_axis_tuser_64, rx_axis_tuser, mac_tx_axis_tuser}) +); + +eth_axis_rx_64 +eth_axis_rx_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(rx_axis_tdata), + .s_axis_tkeep(rx_axis_tkeep), + .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_tkeep(rx_eth_payload_axis_tkeep), + .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_64 +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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_axis_tkeep), + .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_64 +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_tkeep(rx_eth_payload_axis_tkeep), + .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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_ip_payload_axis_tkeep), + .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_tkeep(rx_ip_payload_axis_tkeep), + .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_tkeep(tx_udp_payload_axis_tkeep), + .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_tkeep(rx_udp_payload_axis_tkeep), + .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(1'b0) +); + +axis_fifo #( + .ADDR_WIDTH(10), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .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(rx_fifo_udp_payload_axis_tkeep), + .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(tx_fifo_udp_payload_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/fpga/lib/eth/example/VCU108/fpga_10g/rtl/sync_reset.v b/fpga/lib/eth/example/VCU108/fpga_10g/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/rtl/sync_signal.v b/fpga/lib/eth/example/VCU108/fpga_10g/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/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/fpga/lib/eth/example/VCU108/fpga_10g/tb/arp_ep.py b/fpga/lib/eth/example/VCU108/fpga_10g/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/tb/axis_ep.py b/fpga/lib/eth/example/VCU108/fpga_10g/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/tb/eth_ep.py b/fpga/lib/eth/example/VCU108/fpga_10g/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/tb/gmii_ep.py b/fpga/lib/eth/example/VCU108/fpga_10g/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/tb/ip_ep.py b/fpga/lib/eth/example/VCU108/fpga_10g/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/tb/test_fpga_core.py b/fpga/lib/eth/example/VCU108/fpga_10g/tb/test_fpga_core.py new file mode 100755 index 000000000..92c8db291 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/tb/test_fpga_core.py @@ -0,0 +1,577 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import gmii_ep +import xgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_1g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/eth_mac_10g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_10g.v") +srcs.append("../lib/eth/rtl/axis_xgmii_rx_64.v") +srcs.append("../lib/eth/rtl/axis_xgmii_tx_64.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/eth_axis_rx_64.v") +srcs.append("../lib/eth/rtl/eth_axis_tx_64.v") +srcs.append("../lib/eth/rtl/udp_complete_64.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen_64.v") +srcs.append("../lib/eth/rtl/udp_64.v") +srcs.append("../lib/eth/rtl/udp_ip_rx_64.v") +srcs.append("../lib/eth/rtl/udp_ip_tx_64.v") +srcs.append("../lib/eth/rtl/ip_complete_64.v") +srcs.append("../lib/eth/rtl/ip_64.v") +srcs.append("../lib/eth/rtl/ip_eth_rx_64.v") +srcs.append("../lib/eth/rtl/ip_eth_tx_64.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp_64.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx_64.v") +srcs.append("../lib/eth/rtl/arp_eth_tx_64.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_adapter.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_switch.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_register.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[4:]) + qsfp_tx_clk_1 = Signal(bool(0)) + qsfp_tx_rst_1 = Signal(bool(0)) + qsfp_rx_clk_1 = Signal(bool(0)) + qsfp_rx_rst_1 = Signal(bool(0)) + qsfp_rxd_1 = Signal(intbv(0)[64:]) + qsfp_rxc_1 = Signal(intbv(0)[8:]) + qsfp_tx_clk_2 = Signal(bool(0)) + qsfp_tx_rst_2 = Signal(bool(0)) + qsfp_rx_clk_2 = Signal(bool(0)) + qsfp_rx_rst_2 = Signal(bool(0)) + qsfp_rxd_2 = Signal(intbv(0)[64:]) + qsfp_rxc_2 = Signal(intbv(0)[8:]) + qsfp_tx_clk_3 = Signal(bool(0)) + qsfp_tx_rst_3 = Signal(bool(0)) + qsfp_rx_clk_3 = Signal(bool(0)) + qsfp_rx_rst_3 = Signal(bool(0)) + qsfp_rxd_3 = Signal(intbv(0)[64:]) + qsfp_rxc_3 = Signal(intbv(0)[8:]) + qsfp_tx_clk_4 = Signal(bool(0)) + qsfp_tx_rst_4 = Signal(bool(0)) + qsfp_rx_clk_4 = Signal(bool(0)) + qsfp_rx_rst_4 = Signal(bool(0)) + qsfp_rxd_4 = Signal(intbv(0)[64:]) + qsfp_rxc_4 = Signal(intbv(0)[8:]) + phy_gmii_clk = Signal(bool(0)) + phy_gmii_rst = Signal(bool(0)) + phy_gmii_clk_en = Signal(bool(0)) + phy_gmii_rxd = Signal(intbv(0)[8:]) + phy_gmii_rx_dv = Signal(bool(0)) + phy_gmii_rx_er = Signal(bool(0)) + phy_int_n = Signal(bool(1)) + uart_rxd = Signal(bool(0)) + uart_cts = Signal(bool(0)) + + # Outputs + led = Signal(intbv(0)[8:]) + qsfp_txd_1 = Signal(intbv(0)[64:]) + qsfp_txc_1 = Signal(intbv(0)[8:]) + qsfp_txd_2 = Signal(intbv(0)[64:]) + qsfp_txc_2 = Signal(intbv(0)[8:]) + qsfp_txd_3 = Signal(intbv(0)[64:]) + qsfp_txc_3 = Signal(intbv(0)[8:]) + qsfp_txd_4 = Signal(intbv(0)[64:]) + qsfp_txc_4 = Signal(intbv(0)[8:]) + phy_gmii_txd = Signal(intbv(0)[8:]) + phy_gmii_tx_en = Signal(bool(0)) + phy_gmii_tx_er = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + + # sources and sinks + qsfp_1_source = xgmii_ep.XGMIISource() + qsfp_1_source_logic = qsfp_1_source.create_logic(qsfp_rx_clk_1, qsfp_rx_rst_1, txd=qsfp_rxd_1, txc=qsfp_rxc_1, name='qsfp_1_source') + + qsfp_1_sink = xgmii_ep.XGMIISink() + qsfp_1_sink_logic = qsfp_1_sink.create_logic(qsfp_tx_clk_1, qsfp_tx_rst_1, rxd=qsfp_txd_1, rxc=qsfp_txc_1, name='qsfp_1_sink') + + qsfp_2_source = xgmii_ep.XGMIISource() + qsfp_2_source_logic = qsfp_2_source.create_logic(qsfp_rx_clk_2, qsfp_rx_rst_2, txd=qsfp_rxd_2, txc=qsfp_rxc_2, name='qsfp_2_source') + + qsfp_2_sink = xgmii_ep.XGMIISink() + qsfp_2_sink_logic = qsfp_2_sink.create_logic(qsfp_tx_clk_2, qsfp_tx_rst_2, rxd=qsfp_txd_2, rxc=qsfp_txc_2, name='qsfp_2_sink') + + qsfp_3_source = xgmii_ep.XGMIISource() + qsfp_3_source_logic = qsfp_3_source.create_logic(qsfp_rx_clk_3, qsfp_rx_rst_3, txd=qsfp_rxd_3, txc=qsfp_rxc_3, name='qsfp_3_source') + + qsfp_3_sink = xgmii_ep.XGMIISink() + qsfp_3_sink_logic = qsfp_3_sink.create_logic(qsfp_tx_clk_3, qsfp_tx_rst_3, rxd=qsfp_txd_3, rxc=qsfp_txc_3, name='qsfp_3_sink') + + qsfp_4_source = xgmii_ep.XGMIISource() + qsfp_4_source_logic = qsfp_4_source.create_logic(qsfp_rx_clk_4, qsfp_rx_rst_4, txd=qsfp_rxd_4, txc=qsfp_rxc_4, name='qsfp_4_source') + + qsfp_4_sink = xgmii_ep.XGMIISink() + qsfp_4_sink_logic = qsfp_4_sink.create_logic(qsfp_tx_clk_4, qsfp_tx_rst_4, rxd=qsfp_txd_4, rxc=qsfp_txc_4, name='qsfp_4_sink') + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + phy_gmii_clk, + phy_gmii_rst, + txd=phy_gmii_rxd, + tx_en=phy_gmii_rx_dv, + tx_er=phy_gmii_rx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + phy_gmii_clk, + phy_gmii_rst, + rxd=phy_gmii_txd, + rx_dv=phy_gmii_tx_en, + rx_er=phy_gmii_tx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + led=led, + + qsfp_tx_clk_1=qsfp_tx_clk_1, + qsfp_tx_rst_1=qsfp_tx_rst_1, + qsfp_txd_1=qsfp_txd_1, + qsfp_txc_1=qsfp_txc_1, + qsfp_rx_clk_1=qsfp_rx_clk_1, + qsfp_rx_rst_1=qsfp_rx_rst_1, + qsfp_rxd_1=qsfp_rxd_1, + qsfp_rxc_1=qsfp_rxc_1, + qsfp_tx_clk_2=qsfp_tx_clk_2, + qsfp_tx_rst_2=qsfp_tx_rst_2, + qsfp_txd_2=qsfp_txd_2, + qsfp_txc_2=qsfp_txc_2, + qsfp_rx_clk_2=qsfp_rx_clk_2, + qsfp_rx_rst_2=qsfp_rx_rst_2, + qsfp_rxd_2=qsfp_rxd_2, + qsfp_rxc_2=qsfp_rxc_2, + qsfp_tx_clk_3=qsfp_tx_clk_3, + qsfp_tx_rst_3=qsfp_tx_rst_3, + qsfp_txd_3=qsfp_txd_3, + qsfp_txc_3=qsfp_txc_3, + qsfp_rx_clk_3=qsfp_rx_clk_3, + qsfp_rx_rst_3=qsfp_rx_rst_3, + qsfp_rxd_3=qsfp_rxd_3, + qsfp_rxc_3=qsfp_rxc_3, + qsfp_tx_clk_4=qsfp_tx_clk_4, + qsfp_tx_rst_4=qsfp_tx_rst_4, + qsfp_txd_4=qsfp_txd_4, + qsfp_txc_4=qsfp_txc_4, + qsfp_rx_clk_4=qsfp_rx_clk_4, + qsfp_rx_rst_4=qsfp_rx_rst_4, + qsfp_rxd_4=qsfp_rxd_4, + qsfp_rxc_4=qsfp_rxc_4, + + phy_gmii_clk=phy_gmii_clk, + phy_gmii_rst=phy_gmii_rst, + phy_gmii_clk_en=phy_gmii_clk_en, + phy_gmii_rxd=phy_gmii_rxd, + phy_gmii_rx_dv=phy_gmii_rx_dv, + phy_gmii_rx_er=phy_gmii_rx_er, + phy_gmii_txd=phy_gmii_txd, + phy_gmii_tx_en=phy_gmii_tx_en, + phy_gmii_tx_er=phy_gmii_tx_er, + phy_reset_n=phy_reset_n, + phy_int_n=phy_int_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd, + uart_rts=uart_rts, + uart_cts=uart_cts + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + qsfp_tx_clk_1.next = not qsfp_tx_clk_1 + qsfp_rx_clk_1.next = not qsfp_rx_clk_1 + qsfp_tx_clk_2.next = not qsfp_tx_clk_2 + qsfp_rx_clk_2.next = not qsfp_rx_clk_2 + qsfp_tx_clk_3.next = not qsfp_tx_clk_3 + qsfp_rx_clk_3.next = not qsfp_rx_clk_3 + qsfp_tx_clk_4.next = not qsfp_tx_clk_4 + qsfp_rx_clk_4.next = not qsfp_rx_clk_4 + phy_gmii_clk.next = not phy_gmii_clk + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + phy_gmii_clk_en.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + phy_gmii_clk_en.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + qsfp_tx_rst_1.next = 1 + qsfp_rx_rst_1.next = 1 + qsfp_tx_rst_2.next = 1 + qsfp_rx_rst_2.next = 1 + qsfp_tx_rst_3.next = 1 + qsfp_rx_rst_3.next = 1 + qsfp_tx_rst_4.next = 1 + qsfp_rx_rst_4.next = 1 + phy_gmii_rst.next = 1 + yield clk.posedge + rst.next = 0 + qsfp_tx_rst_1.next = 0 + qsfp_rx_rst_1.next = 0 + qsfp_tx_rst_2.next = 0 + qsfp_rx_rst_2.next = 0 + qsfp_tx_rst_3.next = 0 + qsfp_rx_rst_3.next = 0 + qsfp_tx_rst_4.next = 0 + qsfp_rx_rst_4.next = 0 + phy_gmii_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + qsfp_1_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while qsfp_1_sink.empty(): + yield clk.posedge + + rx_frame = qsfp_1_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + qsfp_1_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while qsfp_1_sink.empty(): + yield clk.posedge + + rx_frame = qsfp_1_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert qsfp_1_source.empty() + assert qsfp_1_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test gigabit tap") + current_test.next = 2 + + sw.next = 0x8 # enable tap on RX + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # loop packet back through on XGMII interface + while qsfp_1_sink.empty(): + yield clk.posedge + + qsfp_1_source.send(qsfp_1_sink.recv()) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + assert qsfp_1_source.empty() + assert qsfp_1_sink.empty() + + yield delay(100) + + sw.next = 0xc # enable tap on TX + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # loop packet back through on XGMII interface + while qsfp_1_sink.empty(): + yield clk.posedge + + qsfp_1_source.send(qsfp_1_sink.recv()) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + assert qsfp_1_source.empty() + assert qsfp_1_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/tb/test_fpga_core.v b/fpga/lib/eth/example/VCU108/fpga_10g/tb/test_fpga_core.v new file mode 100644 index 000000000..04b913f58 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/tb/test_fpga_core.v @@ -0,0 +1,228 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [3:0] sw = 0; +reg qsfp_tx_clk_1 = 0; +reg qsfp_tx_rst_1 = 0; +reg qsfp_rx_clk_1 = 0; +reg qsfp_rx_rst_1 = 0; +reg [63:0] qsfp_rxd_1 = 0; +reg [7:0] qsfp_rxc_1 = 0; +reg qsfp_tx_clk_2 = 0; +reg qsfp_tx_rst_2 = 0; +reg qsfp_rx_clk_2 = 0; +reg qsfp_rx_rst_2 = 0; +reg [63:0] qsfp_rxd_2 = 0; +reg [7:0] qsfp_rxc_2 = 0; +reg qsfp_tx_clk_3 = 0; +reg qsfp_tx_rst_3 = 0; +reg qsfp_rx_clk_3 = 0; +reg qsfp_rx_rst_3 = 0; +reg [63:0] qsfp_rxd_3 = 0; +reg [7:0] qsfp_rxc_3 = 0; +reg qsfp_tx_clk_4 = 0; +reg qsfp_tx_rst_4 = 0; +reg qsfp_rx_clk_4 = 0; +reg qsfp_rx_rst_4 = 0; +reg [63:0] qsfp_rxd_4 = 0; +reg [7:0] qsfp_rxc_4 = 0; +reg phy_gmii_clk = 0; +reg phy_gmii_rst = 0; +reg phy_gmii_clk_en = 0; +reg [7:0] phy_gmii_rxd = 0; +reg phy_gmii_rx_dv = 0; +reg phy_gmii_rx_er = 0; +reg phy_int_n = 1; +reg uart_rxd = 0; +reg uart_cts = 0; + +// Outputs +wire [7:0] led; +wire [63:0] qsfp_txd_1; +wire [7:0] qsfp_txc_1; +wire [63:0] qsfp_txd_2; +wire [7:0] qsfp_txc_2; +wire [63:0] qsfp_txd_3; +wire [7:0] qsfp_txc_3; +wire [63:0] qsfp_txd_4; +wire [7:0] qsfp_txc_4; +wire phy_tx_clk; +wire [7:0] phy_gmii_txd; +wire phy_gmii_tx_en; +wire phy_gmii_tx_er; +wire phy_reset_n; +wire uart_txd; +wire uart_rts; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + qsfp_tx_clk_1, + qsfp_tx_rst_1, + qsfp_rx_clk_1, + qsfp_rx_rst_1, + qsfp_rxd_1, + qsfp_rxc_1, + qsfp_tx_clk_2, + qsfp_tx_rst_2, + qsfp_rx_clk_2, + qsfp_rx_rst_2, + qsfp_rxd_2, + qsfp_rxc_2, + qsfp_tx_clk_3, + qsfp_tx_rst_3, + qsfp_rx_clk_3, + qsfp_rx_rst_3, + qsfp_rxd_3, + qsfp_rxc_3, + qsfp_tx_clk_4, + qsfp_tx_rst_4, + qsfp_rx_clk_4, + qsfp_rx_rst_4, + qsfp_rxd_4, + qsfp_rxc_4, + phy_gmii_clk, + phy_gmii_rst, + phy_gmii_clk_en, + phy_gmii_rxd, + phy_gmii_rx_dv, + phy_gmii_rx_er, + phy_int_n, + uart_rxd, + uart_cts + ); + $to_myhdl( + led, + qsfp_txd_1, + qsfp_txc_1, + qsfp_txd_2, + qsfp_txc_2, + qsfp_txd_3, + qsfp_txc_3, + qsfp_txd_4, + qsfp_txc_4, + phy_gmii_txd, + phy_gmii_tx_en, + phy_gmii_tx_er, + phy_reset_n, + uart_txd, + uart_rts + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .led(led), + .qsfp_tx_clk_1(qsfp_tx_clk_1), + .qsfp_tx_rst_1(qsfp_tx_rst_1), + .qsfp_txd_1(qsfp_txd_1), + .qsfp_txc_1(qsfp_txc_1), + .qsfp_rx_clk_1(qsfp_rx_clk_1), + .qsfp_rx_rst_1(qsfp_rx_rst_1), + .qsfp_rxd_1(qsfp_rxd_1), + .qsfp_rxc_1(qsfp_rxc_1), + .qsfp_tx_clk_2(qsfp_tx_clk_2), + .qsfp_tx_rst_2(qsfp_tx_rst_2), + .qsfp_txd_2(qsfp_txd_2), + .qsfp_txc_2(qsfp_txc_2), + .qsfp_rx_clk_2(qsfp_rx_clk_2), + .qsfp_rx_rst_2(qsfp_rx_rst_2), + .qsfp_rxd_2(qsfp_rxd_2), + .qsfp_rxc_2(qsfp_rxc_2), + .qsfp_tx_clk_3(qsfp_tx_clk_3), + .qsfp_tx_rst_3(qsfp_tx_rst_3), + .qsfp_txd_3(qsfp_txd_3), + .qsfp_txc_3(qsfp_txc_3), + .qsfp_rx_clk_3(qsfp_rx_clk_3), + .qsfp_rx_rst_3(qsfp_rx_rst_3), + .qsfp_rxd_3(qsfp_rxd_3), + .qsfp_rxc_3(qsfp_rxc_3), + .qsfp_tx_clk_4(qsfp_tx_clk_4), + .qsfp_tx_rst_4(qsfp_tx_rst_4), + .qsfp_txd_4(qsfp_txd_4), + .qsfp_txc_4(qsfp_txc_4), + .qsfp_rx_clk_4(qsfp_rx_clk_4), + .qsfp_rx_rst_4(qsfp_rx_rst_4), + .qsfp_rxd_4(qsfp_rxd_4), + .qsfp_rxc_4(qsfp_rxc_4), + .phy_gmii_clk(phy_gmii_clk), + .phy_gmii_rst(phy_gmii_rst), + .phy_gmii_clk_en(phy_gmii_clk_en), + .phy_gmii_rxd(phy_gmii_rxd), + .phy_gmii_rx_dv(phy_gmii_rx_dv), + .phy_gmii_rx_er(phy_gmii_rx_er), + .phy_gmii_txd(phy_gmii_txd), + .phy_gmii_tx_en(phy_gmii_tx_en), + .phy_gmii_tx_er(phy_gmii_tx_er), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts) +); + +endmodule diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/tb/udp_ep.py b/fpga/lib/eth/example/VCU108/fpga_10g/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_10g/tb/xgmii_ep.py b/fpga/lib/eth/example/VCU108/fpga_10g/tb/xgmii_ep.py new file mode 120000 index 000000000..63b6d3567 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_10g/tb/xgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/xgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/Makefile b/fpga/lib/eth/example/VCU108/fpga_1g/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/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/fpga/lib/eth/example/VCU108/fpga_1g/README.md b/fpga/lib/eth/example/VCU108/fpga_1g/README.md new file mode 100644 index 000000000..196c62926 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/README.md @@ -0,0 +1,25 @@ +# Verilog Ethernet VCU108 Example Design + +## Introduction + +This example design targets the Xilinx VCU108 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: xcvu095-ffva2104-2-e +PHY: Marvell M88E1111 + +## 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 VCU108 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. + + diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/common/vivado.mk b/fpga/lib/eth/example/VCU108/fpga_1g/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/VCU108/fpga_1g/eth.xdc b/fpga/lib/eth/example/VCU108/fpga_1g/eth.xdc new file mode 100644 index 000000000..35dd05b08 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/eth.xdc @@ -0,0 +1,4 @@ +# Ethernet constraints + +set_property LOC BITSLICE_RX_TX_X1Y35 [get_cells -hier -filter {name =~ */lvds_transceiver_mw/serdes_1_to_10_ser8_i/idelay_cal}] +#set_false_path -to [get_pins -hier -filter {name =~ *idelayctrl_inst/RST} -include_replicated_objects ] diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/fpga.xdc b/fpga/lib/eth/example/VCU108/fpga_1g/fpga.xdc new file mode 100644 index 000000000..652b31be7 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/fpga.xdc @@ -0,0 +1,77 @@ +# XDC constraints for the Xilinx VCU108 board +# part: xcvu095-ffva2104-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.BPI_SYNC_MODE Type1 [current_design] +set_property CONFIG_MODE BPI16 [current_design] + +# System clocks +# 300 MHz +#set_property -dict {LOC G31 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_1_p] +#set_property -dict {LOC F31 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_1_n] +#create_clock -period 3.333 -name clk_300mhz_1 [get_ports clk_300mhz_1_p] + +#set_property -dict {LOC G22 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_2_p] +#set_property -dict {LOC G21 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_2_n] +#create_clock -period 3.333 -name clk_300mhz_2 [get_ports clk_300mhz_2_p] + +# 125 MHz +set_property -dict {LOC BC9 IOSTANDARD LVDS} [get_ports clk_125mhz_p] +set_property -dict {LOC BC8 IOSTANDARD LVDS} [get_ports clk_125mhz_n] +create_clock -period 8.000 -name clk_125mhz [get_ports clk_125mhz_p] + +# 90 MHz +#set_property -dict {LOC AL20 IOSTANDARD LVCMOS18} [get_ports clk_90mhz] +#create_clock -period 11.111 -name clk_90mhz [get_ports clk_90mhz] + +# LEDs +set_property -dict {LOC AT32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[0]}] +set_property -dict {LOC AV34 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[1]}] +set_property -dict {LOC AY30 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[2]}] +set_property -dict {LOC BB32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[3]}] +set_property -dict {LOC BF32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[4]}] +set_property -dict {LOC AV36 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[5]}] +set_property -dict {LOC AY35 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[6]}] +set_property -dict {LOC BA37 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[7]}] + +# Reset button +set_property -dict {LOC E36 IOSTANDARD LVCMOS12} [get_ports reset] + +# Push buttons +set_property -dict {LOC E34 IOSTANDARD LVCMOS12} [get_ports btnu] +set_property -dict {LOC M22 IOSTANDARD LVCMOS12} [get_ports btnl] +set_property -dict {LOC D9 IOSTANDARD LVCMOS12} [get_ports btnd] +set_property -dict {LOC A10 IOSTANDARD LVCMOS12} [get_ports btnr] +set_property -dict {LOC AW27 IOSTANDARD LVCMOS12} [get_ports btnc] + +# DIP switches +set_property -dict {LOC BC40 IOSTANDARD LVCMOS12} [get_ports {sw[0]}] +set_property -dict {LOC L19 IOSTANDARD LVCMOS12} [get_ports {sw[1]}] +set_property -dict {LOC C37 IOSTANDARD LVCMOS12} [get_ports {sw[2]}] +set_property -dict {LOC C38 IOSTANDARD LVCMOS12} [get_ports {sw[3]}] + +# UART +set_property -dict {LOC BE24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_txd] +set_property -dict {LOC BC24 IOSTANDARD LVCMOS18} [get_ports uart_rxd] +set_property -dict {LOC BF24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_rts] +set_property -dict {LOC BD22 IOSTANDARD LVCMOS18} [get_ports uart_cts] + +# Gigabit Ethernet SGMII PHY +set_property -dict {LOC AR24 IOSTANDARD DIFF_HSTL_I_18} [get_ports phy_sgmii_rx_p] +set_property -dict {LOC AT24 IOSTANDARD DIFF_HSTL_I_18} [get_ports phy_sgmii_rx_n] +set_property -dict {LOC AR23 IOSTANDARD DIFF_HSTL_I_18} [get_ports phy_sgmii_tx_p] +set_property -dict {LOC AR22 IOSTANDARD DIFF_HSTL_I_18} [get_ports phy_sgmii_tx_n] +set_property -dict {LOC AT22 IOSTANDARD LVDS_25} [get_ports phy_sgmii_clk_p] +set_property -dict {LOC AU22 IOSTANDARD LVDS_25} [get_ports phy_sgmii_clk_n] +set_property -dict {LOC AU21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_reset_n] +set_property -dict {LOC AT21 IOSTANDARD LVCMOS18} [get_ports phy_int_n] +#set_property -dict {LOC AV24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_mdio] +#set_property -dict {LOC AV21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_mdc] + +# 625 MHz ref clock from SGMII PHY +create_clock -period 1.600 -name phy_sgmii_clk [get_ports phy_sgmii_clk_p] + diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/fpga/Makefile b/fpga/lib/eth/example/VCU108/fpga_1g/fpga/Makefile new file mode 100644 index 000000000..ba7eadc08 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/fpga/Makefile @@ -0,0 +1,99 @@ + +# FPGA settings +FPGA_PART = xcvu095-ffva2104-2-e +FPGA_TOP = fpga +FPGA_ARCH = VirtexUltrascale + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_fifo.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 + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += eth.xdc +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl + +# IP +XCI_FILES = ip/gig_ethernet_pcs_pma_0.xci + +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 + +%.mcs %.prm: %.bit + echo "write_cfgmem -force -format mcs -size 128 -interface BPIx16 -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 .mcs .prm; \ + do cp $*$$x rev/$*_rev$$COUNT$$x; \ + echo "Output: rev/$*_rev$$COUNT$$x"; done; + +flash: $(FPGA_TOP).mcs $(FPGA_TOP).prm + echo "open_hw" > 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 {mt28gu01gaax1e-bpi-x16}] 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 \"$(FPGA_TOP).mcs\"] [current_hw_cfgmem]" >> flash.tcl + echo "set_property PROGRAM.PRM_FILES [list \"$(FPGA_TOP).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.BPI_RS_PINS {none} [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 + diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/ip/gig_ethernet_pcs_pma_0.xci b/fpga/lib/eth/example/VCU108/fpga_1g/ip/gig_ethernet_pcs_pma_0.xci new file mode 100644 index 000000000..91b324db6 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/ip/gig_ethernet_pcs_pma_0.xci @@ -0,0 +1,360 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gig_ethernet_pcs_pma_0 + + + 1 + 1 + 1 + 1 + + + + 0 + + + + 0 + + + 0 + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + false + false + false + false + 0 + + + + 0 + + + + 0 + false + 100000000 + + + + 0 + + + + 0 + + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + false + false + false + false + 0 + 0 + + + + 100000000 + 0 + 0.000 + + + + 100000000 + 0 + 0.000 + false + false + false + + + + 100000000 + 0 + 0.000 + 0 + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + ACTIVE_LOW + ACTIVE_LOW + ACTIVE_LOW + ACTIVE_LOW + 1 + 0 + 0 + 0 + + 1 + 100000000 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 0.000 + AXI4LITE + READ_WRITE + 0 + 0 + 0 + 0 + 0 + + + 100000000 + 0 + 0.000 + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + true + 0 + 0 + true + false + DIFF_PAIR_0 + DIFF_PAIR_1 + false + DIFF_PAIR_0 + DIFF_PAIR_1 + virtexu + 0 + gig_ethernet_pcs_pma_0 + 50.0 + false + . + false + false + false + false + virtexu + 17 + 9 + X0Y0 + 7 + 4 + GTH + false + true + false + false + false + false + true + 1 + clk0 + 125 + TXOUTCLK + true + false + gig_ethernet_pcs_pma_0_gt + true + GTHE3 + false + 1 + true + false + false + xcvu095 + false + 1 + false + true + Sync + gig_ethernet_pcs_pma_0 + Custom + 50.0 + TEMAC + Custom + 0 + false + false + false + false + X0Y0 + GTH + false + false + 625 + Custom + false + 1G + 1 + LVDS + 125 + clk0 + TXOUTCLK + DIFF_PAIR_0 + DIFF_PAIR_1 + false + 10_100_1000 + false + SGMII + Include_Shared_Logic_in_Core + Time_of_day + false + DIFF_PAIR_0 + DIFF_PAIR_1 + 1 + false + virtexu + + + xcvu095 + ffva2104 + VERILOG + + MIXED + -2 + + E + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/lib/eth b/fpga/lib/eth/example/VCU108/fpga_1g/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/rtl/debounce_switch.v b/fpga/lib/eth/example/VCU108/fpga_1g/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/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 + +`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/fpga/lib/eth/example/VCU108/fpga_1g/rtl/fpga.v b/fpga/lib/eth/example/VCU108/fpga_1g/rtl/fpga.v new file mode 100644 index 000000000..87a59a66e --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/rtl/fpga.v @@ -0,0 +1,361 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 125MHz LVDS + * Reset: Push button, active low + */ + input wire clk_125mhz_p, + input wire clk_125mhz_n, + input wire reset, + + /* + * 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 SGMII + */ + input wire phy_sgmii_rx_p, + input wire phy_sgmii_rx_n, + output wire phy_sgmii_tx_p, + output wire phy_sgmii_tx_n, + input wire phy_sgmii_clk_p, + input wire phy_sgmii_clk_n, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 500000 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// Clock and reset + +wire clk_125mhz_ibufg; +wire clk_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire rst_125mhz_int; + +wire mmcm_rst = reset; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS #( + .DIFF_TERM("FALSE"), + .IBUF_LOW_PWR("FALSE") +) +clk_125mhz_ibufg_inst ( + .O (clk_125mhz_ibufg), + .I (clk_125mhz_p), + .IB (clk_125mhz_n) +); + +// MMCM instance +// 125 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 = 625 MHz (in range) +// Divide by 5 to get output frequency of 125 MHz +MMCME3_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(5), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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(8.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_125mhz_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_125mhz_bufg_inst ( + .I(clk_125mhz_mmcm_out), + .O(clk_125mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_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_125mhz_int), + .rst(rst_125mhz_int), + .in({btnu, + btnl, + btnd, + btnr, + btnc, + sw}), + .out({btnu_int, + btnl_int, + btnd_int, + btnr_int, + btnc_int, + sw_int}) +); + +wire uart_rxd_int; +wire uart_cts_int; + +sync_signal #( + .WIDTH(2), + .N(2) +) +sync_signal_inst ( + .clk(clk_125mhz_int), + .in({uart_rxd, uart_cts}), + .out({uart_rxd_int, uart_cts_int}) +); + +// SGMII interface to PHY +wire phy_gmii_clk_int; +wire phy_gmii_rst_int; +wire phy_gmii_clk_en_int; +wire [7:0] phy_gmii_txd_int; +wire phy_gmii_tx_en_int; +wire phy_gmii_tx_er_int; +wire [7:0] phy_gmii_rxd_int; +wire phy_gmii_rx_dv_int; +wire phy_gmii_rx_er_int; + +wire [15:0] pcspma_status_vector; + +wire pcspma_status_link_status = pcspma_status_vector[0]; +wire pcspma_status_link_synchronization = pcspma_status_vector[1]; +wire pcspma_status_rudi_c = pcspma_status_vector[2]; +wire pcspma_status_rudi_i = pcspma_status_vector[3]; +wire pcspma_status_rudi_invalid = pcspma_status_vector[4]; +wire pcspma_status_rxdisperr = pcspma_status_vector[5]; +wire pcspma_status_rxnotintable = pcspma_status_vector[6]; +wire pcspma_status_phy_link_status = pcspma_status_vector[7]; +wire [1:0] pcspma_status_remote_fault_encdg = pcspma_status_vector[9:8]; +wire [1:0] pcspma_status_speed = pcspma_status_vector[11:10]; +wire pcspma_status_duplex = pcspma_status_vector[12]; +wire pcspma_status_remote_fault = pcspma_status_vector[13]; +wire [1:0] pcspma_status_pause = pcspma_status_vector[15:14]; + +wire [4:0] pcspma_config_vector; + +assign pcspma_config_vector[4] = 1'b1; // autonegotiation enable +assign pcspma_config_vector[3] = 1'b0; // isolate +assign pcspma_config_vector[2] = 1'b0; // power down +assign pcspma_config_vector[1] = 1'b0; // loopback enable +assign pcspma_config_vector[0] = 1'b0; // unidirectional enable + +wire [15:0] pcspma_an_config_vector; + +assign pcspma_an_config_vector[15] = 1'b1; // SGMII link status +assign pcspma_an_config_vector[14] = 1'b1; // SGMII Acknowledge +assign pcspma_an_config_vector[13:12] = 2'b01; // full duplex +assign pcspma_an_config_vector[11:10] = 2'b10; // SGMII speed +assign pcspma_an_config_vector[9] = 1'b0; // reserved +assign pcspma_an_config_vector[8:7] = 2'b00; // pause frames - SGMII reserved +assign pcspma_an_config_vector[6] = 1'b0; // reserved +assign pcspma_an_config_vector[5] = 1'b0; // full duplex - SGMII reserved +assign pcspma_an_config_vector[4:1] = 4'b0000; // reserved +assign pcspma_an_config_vector[0] = 1'b1; // SGMII + +gig_ethernet_pcs_pma_0 +eth_pcspma ( + // SGMII + .txp (phy_sgmii_tx_p), + .txn (phy_sgmii_tx_n), + .rxp (phy_sgmii_rx_p), + .rxn (phy_sgmii_rx_n), + + // Ref clock from PHY + .refclk625_p (phy_sgmii_clk_p), + .refclk625_n (phy_sgmii_clk_n), + + // async reset + .reset (rst_125mhz_int), + + // clock and reset outputs + .clk125_out (phy_gmii_clk_int), + .clk625_out (), + .clk312_out (), + .rst_125_out (phy_gmii_rst_int), + .idelay_rdy_out (), + .mmcm_locked_out (), + + // MAC clocking + .sgmii_clk_r (), + .sgmii_clk_f (), + .sgmii_clk_en (phy_gmii_clk_en_int), + + // Speed control + .speed_is_10_100 (pcspma_status_speed != 2'b10), + .speed_is_100 (pcspma_status_speed == 2'b01), + + // Internal GMII + .gmii_txd (phy_gmii_txd_int), + .gmii_tx_en (phy_gmii_tx_en_int), + .gmii_tx_er (phy_gmii_tx_er_int), + .gmii_rxd (phy_gmii_rxd_int), + .gmii_rx_dv (phy_gmii_rx_dv_int), + .gmii_rx_er (phy_gmii_rx_er_int), + .gmii_isolate (), + + // Configuration + .configuration_vector (pcspma_config_vector), + + .an_interrupt (), + .an_adv_config_vector (pcspma_an_config_vector), + .an_restart_config (1'b0), + + // Status + .status_vector (pcspma_status_vector), + .signal_detect (1'b1) +); + +wire [7:0] led_int; + +// SGMII interface debug: +// SW12:4 (sw[0]) off for payload byte, on for status vector +// SW12:3 (sw[1]) off for LSB of status vector, on for MSB +assign led = sw[0] ? (sw[1] ? pcspma_status_vector[15:8] : pcspma_status_vector[7:0]) : led_int; + +fpga_core +core_inst ( + /* + * Clock: 125MHz + * Synchronous reset + */ + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + /* + * GPIO + */ + .btnu(btnu_int), + .btnl(btnl_int), + .btnd(btnd_int), + .btnr(btnr_int), + .btnc(btnc_int), + .sw(sw_int), + .led(led_int), + /* + * Ethernet: 1000BASE-T SGMII + */ + .phy_gmii_clk(phy_gmii_clk_int), + .phy_gmii_rst(phy_gmii_rst_int), + .phy_gmii_clk_en(phy_gmii_clk_en_int), + .phy_gmii_rxd(phy_gmii_rxd_int), + .phy_gmii_rx_dv(phy_gmii_rx_dv_int), + .phy_gmii_rx_er(phy_gmii_rx_er_int), + .phy_gmii_txd(phy_gmii_txd_int), + .phy_gmii_tx_en(phy_gmii_tx_en_int), + .phy_gmii_tx_er(phy_gmii_tx_er_int), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts_int) +); + +endmodule diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/rtl/fpga_core.v b/fpga/lib/eth/example/VCU108/fpga_1g/rtl/fpga_core.v new file mode 100644 index 000000000..f2cf9cc56 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/rtl/fpga_core.v @@ -0,0 +1,584 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core +( + /* + * Clock: 125MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + + /* + * 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 SGMII + */ + input wire phy_gmii_clk, + input wire phy_gmii_rst, + input wire phy_gmii_clk_en, + input wire [7:0] phy_gmii_rxd, + input wire phy_gmii_rx_dv, + input wire phy_gmii_rx_er, + output wire [7:0] phy_gmii_txd, + output wire phy_gmii_tx_en, + output wire phy_gmii_tx_er, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 115200 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// 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; +assign uart_rts = 0; + +eth_mac_1g_fifo #( + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_inst ( + .rx_clk(phy_gmii_clk), + .rx_rst(phy_gmii_rst), + .tx_clk(phy_gmii_clk), + .tx_rst(phy_gmii_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), + + .gmii_rxd(phy_gmii_rxd), + .gmii_rx_dv(phy_gmii_rx_dv), + .gmii_rx_er(phy_gmii_rx_er), + .gmii_txd(phy_gmii_txd), + .gmii_tx_en(phy_gmii_tx_en), + .gmii_tx_er(phy_gmii_tx_er), + + .rx_clk_enable(phy_gmii_clk_en), + .tx_clk_enable(phy_gmii_clk_en), + .rx_mii_select(1'b0), + .tx_mii_select(1'b0), + + .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(), + + .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 #( + .ADDR_WIDTH(12), + .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/fpga/lib/eth/example/VCU108/fpga_1g/rtl/sync_reset.v b/fpga/lib/eth/example/VCU108/fpga_1g/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/rtl/sync_signal.v b/fpga/lib/eth/example/VCU108/fpga_1g/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/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/fpga/lib/eth/example/VCU108/fpga_1g/tb/arp_ep.py b/fpga/lib/eth/example/VCU108/fpga_1g/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/tb/axis_ep.py b/fpga/lib/eth/example/VCU108/fpga_1g/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/tb/eth_ep.py b/fpga/lib/eth/example/VCU108/fpga_1g/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/tb/gmii_ep.py b/fpga/lib/eth/example/VCU108/fpga_1g/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/tb/ip_ep.py b/fpga/lib/eth/example/VCU108/fpga_1g/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/tb/test_fpga_core.py b/fpga/lib/eth/example/VCU108/fpga_1g/tb/test_fpga_core.py new file mode 100755 index 000000000..97523fc5a --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/tb/test_fpga_core.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import gmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_1g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/udp_complete.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen.v") +srcs.append("../lib/eth/rtl/udp.v") +srcs.append("../lib/eth/rtl/udp_ip_rx.v") +srcs.append("../lib/eth/rtl/udp_ip_tx.v") +srcs.append("../lib/eth/rtl/ip_complete.v") +srcs.append("../lib/eth/rtl/ip.v") +srcs.append("../lib/eth/rtl/ip_eth_rx.v") +srcs.append("../lib/eth/rtl/ip_eth_tx.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx.v") +srcs.append("../lib/eth/rtl/arp_eth_tx.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[4:]) + phy_gmii_clk = Signal(bool(0)) + phy_gmii_rst = Signal(bool(0)) + phy_gmii_clk_en = Signal(bool(0)) + phy_gmii_rxd = Signal(intbv(0)[8:]) + phy_gmii_rx_dv = Signal(bool(0)) + phy_gmii_rx_er = Signal(bool(0)) + phy_int_n = Signal(bool(1)) + uart_rxd = Signal(bool(0)) + uart_cts = Signal(bool(0)) + + # Outputs + led = Signal(intbv(0)[8:]) + phy_gmii_txd = Signal(intbv(0)[8:]) + phy_gmii_tx_en = Signal(bool(0)) + phy_gmii_tx_er = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + + # sources and sinks + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + phy_gmii_clk, + phy_gmii_rst, + txd=phy_gmii_rxd, + tx_en=phy_gmii_rx_dv, + tx_er=phy_gmii_rx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + phy_gmii_clk, + phy_gmii_rst, + rxd=phy_gmii_txd, + rx_dv=phy_gmii_tx_en, + rx_er=phy_gmii_tx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + led=led, + + phy_gmii_clk=phy_gmii_clk, + phy_gmii_rst=phy_gmii_rst, + phy_gmii_clk_en=phy_gmii_clk_en, + phy_gmii_rxd=phy_gmii_rxd, + phy_gmii_rx_dv=phy_gmii_rx_dv, + phy_gmii_rx_er=phy_gmii_rx_er, + phy_gmii_txd=phy_gmii_txd, + phy_gmii_tx_en=phy_gmii_tx_en, + phy_gmii_tx_er=phy_gmii_tx_er, + phy_reset_n=phy_reset_n, + phy_int_n=phy_int_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd, + uart_rts=uart_rts, + uart_cts=uart_cts + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + phy_gmii_clk.next = not phy_gmii_clk + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + phy_gmii_clk_en.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + phy_gmii_clk_en.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + phy_gmii_rst.next = 1 + yield clk.posedge + rst.next = 0 + phy_gmii_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/tb/test_fpga_core.v b/fpga/lib/eth/example/VCU108/fpga_1g/tb/test_fpga_core.v new file mode 100644 index 000000000..e3de43465 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/tb/test_fpga_core.v @@ -0,0 +1,131 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [3:0] sw = 0; +reg phy_gmii_clk = 0; +reg phy_gmii_rst = 0; +reg phy_gmii_clk_en = 0; +reg [7:0] phy_gmii_rxd = 0; +reg phy_gmii_rx_dv = 0; +reg phy_gmii_rx_er = 0; +reg phy_int_n = 1; +reg uart_rxd = 0; +reg uart_cts = 0; + +// Outputs +wire [7:0] led; +wire [7:0] phy_gmii_txd; +wire phy_gmii_tx_en; +wire phy_gmii_tx_er; +wire phy_reset_n; +wire uart_txd; +wire uart_rts; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + phy_gmii_clk, + phy_gmii_rst, + phy_gmii_clk_en, + phy_gmii_rxd, + phy_gmii_rx_dv, + phy_gmii_rx_er, + phy_int_n, + uart_rxd, + uart_cts + ); + $to_myhdl( + led, + phy_gmii_txd, + phy_gmii_tx_en, + phy_gmii_tx_er, + phy_reset_n, + uart_txd, + uart_rts + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .led(led), + .phy_gmii_clk(phy_gmii_clk), + .phy_gmii_rst(phy_gmii_rst), + .phy_gmii_clk_en(phy_gmii_clk_en), + .phy_gmii_rxd(phy_gmii_rxd), + .phy_gmii_rx_dv(phy_gmii_rx_dv), + .phy_gmii_rx_er(phy_gmii_rx_er), + .phy_gmii_txd(phy_gmii_txd), + .phy_gmii_tx_en(phy_gmii_tx_en), + .phy_gmii_tx_er(phy_gmii_tx_er), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts) +); + +endmodule diff --git a/fpga/lib/eth/example/VCU108/fpga_1g/tb/udp_ep.py b/fpga/lib/eth/example/VCU108/fpga_1g/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/VCU108/fpga_1g/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/Makefile b/fpga/lib/eth/example/VCU118/fpga_10g/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/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/fpga/lib/eth/example/VCU118/fpga_10g/README.md b/fpga/lib/eth/example/VCU118/fpga_10g/README.md new file mode 100644 index 000000000..c8beebcaf --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/README.md @@ -0,0 +1,33 @@ +# Verilog Ethernet VCU118 Example Design + +## Introduction + +This example design targets the Xilinx VCU118 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. The design also enables the gigabit Ethernet interface for +testing with a QSFP loopback adapter. + +FPGA: xcvu9p-flga2104-2L-e +PHY: 10G BASE-R PHY IP core and internal GTY transceiver + +## 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 VCU118 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. + +Note that the gigabit PHY is also enabled for debugging. The gigabit port can +be inserted into the 10G data path between the 10G MAC and 10G PHY so that the +10G interface can be tested with a QSFP loopback adapter. Turn on SW12.1 to +insert the gigabit port into the 10G data path, or off to bypass the gigabit +port. Turn on SW12.2 to place the port in the TX path or off to place the +port in the RX path. + + diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/common/vivado.mk b/fpga/lib/eth/example/VCU118/fpga_10g/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/VCU118/fpga_10g/fpga.xdc b/fpga/lib/eth/example/VCU118/fpga_10g/fpga.xdc new file mode 100644 index 000000000..efbf0cc00 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/fpga.xdc @@ -0,0 +1,151 @@ +# XDC constraints for the Xilinx VCU118 board +# part: xcvu9p-flga2104-2L-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] + +# System clocks +# 300 MHz +#set_property -dict {LOC G31 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_p] +#set_property -dict {LOC F31 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_n] +#create_clock -period 3.333 -name clk_300mhz [get_ports clk_300mhz_p] + +# 250 MHz +#set_property -dict {LOC E12 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_1_p] +#set_property -dict {LOC D12 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_1_n] +#create_clock -period 4 -name clk_250mhz_1 [get_ports clk_250mhz_1_p] + +#set_property -dict {LOC AW26 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_2_p] +#set_property -dict {LOC AW27 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_2_n] +#create_clock -period 4 -name clk_250mhz_2 [get_ports clk_250mhz_2_p] + +# 125 MHz +set_property -dict {LOC AY24 IOSTANDARD LVDS} [get_ports clk_125mhz_p] +set_property -dict {LOC AY23 IOSTANDARD LVDS} [get_ports clk_125mhz_n] +create_clock -period 8.000 -name clk_125mhz [get_ports clk_125mhz_p] + +# 90 MHz +#set_property -dict {LOC AL20 IOSTANDARD LVCMOS18} [get_ports clk_90mhz] +#create_clock -period 11.111 -name clk_90mhz [get_ports clk_90mhz] + +# LEDs +set_property -dict {LOC AT32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[0]}] +set_property -dict {LOC AV34 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[1]}] +set_property -dict {LOC AY30 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[2]}] +set_property -dict {LOC BB32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[3]}] +set_property -dict {LOC BF32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[4]}] +set_property -dict {LOC AU37 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[5]}] +set_property -dict {LOC AV36 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[6]}] +set_property -dict {LOC BA37 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[7]}] + +# Reset button +set_property -dict {LOC L19 IOSTANDARD LVCMOS12} [get_ports reset] + +# Push buttons +set_property -dict {LOC BB24 IOSTANDARD LVCMOS18} [get_ports btnu] +set_property -dict {LOC BF22 IOSTANDARD LVCMOS18} [get_ports btnl] +set_property -dict {LOC BE22 IOSTANDARD LVCMOS18} [get_ports btnd] +set_property -dict {LOC BE23 IOSTANDARD LVCMOS18} [get_ports btnr] +set_property -dict {LOC BD23 IOSTANDARD LVCMOS18} [get_ports btnc] + +# DIP switches +set_property -dict {LOC B17 IOSTANDARD LVCMOS12} [get_ports {sw[0]}] +set_property -dict {LOC G16 IOSTANDARD LVCMOS12} [get_ports {sw[1]}] +set_property -dict {LOC J16 IOSTANDARD LVCMOS12} [get_ports {sw[2]}] +set_property -dict {LOC D21 IOSTANDARD LVCMOS12} [get_ports {sw[3]}] + +# UART +set_property -dict {LOC BB21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_txd] +set_property -dict {LOC AW25 IOSTANDARD LVCMOS18} [get_ports uart_rxd] +set_property -dict {LOC BB22 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_rts] +set_property -dict {LOC AY25 IOSTANDARD LVCMOS18} [get_ports uart_cts] + +# Gigabit Ethernet SGMII PHY +set_property -dict {LOC AU24 IOSTANDARD LVDS} [get_ports phy_sgmii_rx_p] +set_property -dict {LOC AV24 IOSTANDARD LVDS} [get_ports phy_sgmii_rx_n] +set_property -dict {LOC AU21 IOSTANDARD LVDS} [get_ports phy_sgmii_tx_p] +set_property -dict {LOC AV21 IOSTANDARD LVDS} [get_ports phy_sgmii_tx_n] +set_property -dict {LOC AT22 IOSTANDARD LVDS} [get_ports phy_sgmii_clk_p] +set_property -dict {LOC AU22 IOSTANDARD LVDS} [get_ports phy_sgmii_clk_n] +set_property -dict {LOC BA21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_reset_n] +set_property -dict {LOC AR24 IOSTANDARD LVCMOS18} [get_ports phy_int_n] +set_property -dict {LOC AR23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_mdio] +set_property -dict {LOC AV23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_mdc] + +# 625 MHz ref clock from SGMII PHY +#create_clock -period 1.600 -name phy_sgmii_clk [get_ports phy_sgmii_clk_p] + +# QSFP28 Interfaces +set_property -dict {LOC V7 } [get_ports qsfp1_tx1_p] ;# MGTYTXN0_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC V6 } [get_ports qsfp1_tx1_n] ;# MGTYTXP0_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC Y2 } [get_ports qsfp1_rx1_p] ;# MGTYTXN1_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC Y1 } [get_ports qsfp1_rx1_n] ;# MGTYTXP1_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC T7 } [get_ports qsfp1_tx2_p] ;# MGTYTXN2_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC T6 } [get_ports qsfp1_tx2_n] ;# MGTYTXP2_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC W4 } [get_ports qsfp1_rx2_p] ;# MGTYTXN3_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC W3 } [get_ports qsfp1_rx2_n] ;# MGTYTXP3_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC P7 } [get_ports qsfp1_tx3_p] ;# MGTYTXN0_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC P6 } [get_ports qsfp1_tx3_n] ;# MGTYTXP0_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC V2 } [get_ports qsfp1_rx3_p] ;# MGTYTXN1_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC V1 } [get_ports qsfp1_rx3_n] ;# MGTYTXP1_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC M7 } [get_ports qsfp1_tx4_p] ;# MGTYTXN2_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC M6 } [get_ports qsfp1_tx4_n] ;# MGTYTXP2_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC U4 } [get_ports qsfp1_rx4_p] ;# MGTYTXN3_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC U3 } [get_ports qsfp1_rx4_n] ;# MGTYTXP3_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC W9 } [get_ports qsfp1_mgt_refclk_0_p] ;# MGTREFCLK0P_231 from U38.4 +#set_property -dict {LOC W8 } [get_ports qsfp1_mgt_refclk_0_n] ;# MGTREFCLK0N_231 from U38.5 +#set_property -dict {LOC U9 } [get_ports qsfp1_mgt_refclk_1_p] ;# MGTREFCLK1P_231 from U57.28 +#set_property -dict {LOC U8 } [get_ports qsfp1_mgt_refclk_1_n] ;# MGTREFCLK1N_231 from U57.29 +#set_property -dict {LOC AM23 IOSTANDARD LVDS} [get_ports qsfp1_recclk_p] ;# to U57.16 +#set_property -dict {LOC AM22 IOSTANDARD LVDS} [get_ports qsfp1_recclk_n] ;# to U57.17 +set_property -dict {LOC AM21 IOSTANDARD LVCMOS18} [get_ports qsfp1_modsell] +set_property -dict {LOC BA22 IOSTANDARD LVCMOS18} [get_ports qsfp1_resetl] +set_property -dict {LOC AL21 IOSTANDARD LVCMOS18} [get_ports qsfp1_modprsl] +set_property -dict {LOC AP21 IOSTANDARD LVCMOS18} [get_ports qsfp1_intl] +set_property -dict {LOC AN21 IOSTANDARD LVCMOS18} [get_ports qsfp1_lpmode] + +# 156.25 MHz MGT reference clock +create_clock -period 6.400 -name qsfp1_mgt_refclk_0 [get_ports qsfp1_mgt_refclk_0_p] + +set_property -dict {LOC L5 } [get_ports qsfp2_tx1_p] ;# MGTYTXN0_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC L4 } [get_ports qsfp2_tx1_n] ;# MGTYTXP0_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC T2 } [get_ports qsfp2_rx1_p] ;# MGTYTXN1_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC T1 } [get_ports qsfp2_rx1_n] ;# MGTYTXP1_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC K7 } [get_ports qsfp2_tx2_p] ;# MGTYTXN2_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC K6 } [get_ports qsfp2_tx2_n] ;# MGTYTXP2_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC R4 } [get_ports qsfp2_rx2_p] ;# MGTYTXN3_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC R3 } [get_ports qsfp2_rx2_n] ;# MGTYTXP3_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC J5 } [get_ports qsfp2_tx3_p] ;# MGTYTXN0_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC J4 } [get_ports qsfp2_tx3_n] ;# MGTYTXP0_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC P2 } [get_ports qsfp2_rx3_p] ;# MGTYTXN1_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC P1 } [get_ports qsfp2_rx3_n] ;# MGTYTXP1_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC H7 } [get_ports qsfp2_tx4_p] ;# MGTYTXN2_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC H6 } [get_ports qsfp2_tx4_n] ;# MGTYTXP2_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC M2 } [get_ports qsfp2_rx4_p] ;# MGTYTXN3_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC M1 } [get_ports qsfp2_rx4_n] ;# MGTYTXP3_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC R9 } [get_ports qsfp2_mgt_refclk_0_p] ;# MGTREFCLK0P_232 from U104.13 +#set_property -dict {LOC R8 } [get_ports qsfp2_mgt_refclk_0_n] ;# MGTREFCLK0N_232 from U104.14 +#set_property -dict {LOC N9 } [get_ports qsfp2_mgt_refclk_1_p] ;# MGTREFCLK1P_232 from U57.35 +#set_property -dict {LOC N8 } [get_ports qsfp2_mgt_refclk_1_n] ;# MGTREFCLK1N_232 from U57.34 +#set_property -dict {LOC AP23 IOSTANDARD LVDS} [get_ports qsfp2_recclk_p] ;# to U57.12 +#set_property -dict {LOC AP22 IOSTANDARD LVDS} [get_ports qsfp2_recclk_n] ;# to U57.13 +set_property -dict {LOC AN23 IOSTANDARD LVCMOS18} [get_ports qsfp2_modsell] +set_property -dict {LOC AY22 IOSTANDARD LVCMOS18} [get_ports qsfp2_resetl] +set_property -dict {LOC AN24 IOSTANDARD LVCMOS18} [get_ports qsfp2_modprsl] +set_property -dict {LOC AT21 IOSTANDARD LVCMOS18} [get_ports qsfp2_intl] +set_property -dict {LOC AT24 IOSTANDARD LVCMOS18} [get_ports qsfp2_lpmode] + +# 156.25 MHz MGT reference clock +#create_clock -period 6.400 -name qsfp2_mgt_refclk_0 [get_ports qsfp2_mgt_refclk_0_p] + +# I2C interface +set_property -dict {LOC AM24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_scl] +set_property -dict {LOC AL24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_sda] + + diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/fpga/Makefile b/fpga/lib/eth/example/VCU118/fpga_10g/fpga/Makefile new file mode 100644 index 000000000..807cc1ed1 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/fpga/Makefile @@ -0,0 +1,115 @@ + +# FPGA settings +FPGA_PART = xcvu9p-flga2104-2L-e +FPGA_TOP = fpga +FPGA_ARCH = virtexuplus + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += rtl/mdio_master.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_fifo.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/eth_mac_10g_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_10g.v +SYN_FILES += lib/eth/rtl/axis_xgmii_rx_64.v +SYN_FILES += lib/eth/rtl/axis_xgmii_tx_64.v +SYN_FILES += lib/eth/rtl/eth_phy_10g.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_if.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_frame_sync.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_ber_mon.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx_if.v +SYN_FILES += lib/eth/rtl/xgmii_baser_dec_64.v +SYN_FILES += lib/eth/rtl/xgmii_baser_enc_64.v +SYN_FILES += lib/eth/rtl/lfsr.v +SYN_FILES += lib/eth/rtl/eth_axis_rx_64.v +SYN_FILES += lib/eth/rtl/eth_axis_tx_64.v +SYN_FILES += lib/eth/rtl/udp_complete_64.v +SYN_FILES += lib/eth/rtl/udp_checksum_gen_64.v +SYN_FILES += lib/eth/rtl/udp_64.v +SYN_FILES += lib/eth/rtl/udp_ip_rx_64.v +SYN_FILES += lib/eth/rtl/udp_ip_tx_64.v +SYN_FILES += lib/eth/rtl/ip_complete_64.v +SYN_FILES += lib/eth/rtl/ip_64.v +SYN_FILES += lib/eth/rtl/ip_eth_rx_64.v +SYN_FILES += lib/eth/rtl/ip_eth_tx_64.v +SYN_FILES += lib/eth/rtl/ip_arb_mux.v +SYN_FILES += lib/eth/rtl/arp_64.v +SYN_FILES += lib/eth/rtl/arp_cache.v +SYN_FILES += lib/eth/rtl/arp_eth_rx_64.v +SYN_FILES += lib/eth/rtl/arp_eth_tx_64.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_adapter.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_fifo.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_switch.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_register.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_async_fifo.v + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl + +# IP +XCI_FILES = ip/gig_ethernet_pcs_pma_0.xci +XCI_FILES += ip/gtwizard_ultrascale_0.xci + +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 + +%_primary.mcs %_secondary.mcs %_primary.prm %_secondary.prm: %.bit + echo "write_cfgmem -force -format mcs -size 256 -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: $(FPGA_TOP)_primary.mcs $(FPGA_TOP)_secondary.mcs $(FPGA_TOP)_primary.prm $(FPGA_TOP)_secondary.prm + echo "open_hw" > 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 {mt25qu01g-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 \"$(FPGA_TOP)_primary.mcs\" \"$(FPGA_TOP)_secondary.mcs\"] [current_hw_cfgmem]" >> flash.tcl + echo "set_property PROGRAM.PRM_FILES [list \"$(FPGA_TOP)_primary.prm\" \"$(FPGA_TOP)_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 + diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/ip/gig_ethernet_pcs_pma_0.xci b/fpga/lib/eth/example/VCU118/fpga_10g/ip/gig_ethernet_pcs_pma_0.xci new file mode 100644 index 000000000..1e578e860 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/ip/gig_ethernet_pcs_pma_0.xci @@ -0,0 +1,365 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gig_ethernet_pcs_pma_0 + + + 1 + 1 + 1 + 1 + + + + 0 + + + + 0 + + + 0 + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + false + false + false + false + 0 + + + + 0 + + + + 0 + false + 100000000 + + + + 0 + + + + 0 + + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + false + false + false + false + 0 + 0 + + + + 100000000 + 0 + 0.000 + + + + 100000000 + 0 + 0.000 + false + false + false + + + + 100000000 + 0 + 0.000 + 0 + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + ACTIVE_LOW + ACTIVE_LOW + ACTIVE_LOW + ACTIVE_LOW + 1 + 0 + 0 + 0 + + 1 + 100000000 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 0.000 + AXI4LITE + READ_WRITE + 0 + 0 + 0 + 0 + 0 + + + 100000000 + 0 + 0.000 + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + true + 0 + 0 + true + false + DIFF_PAIR_0 + DIFF_PAIR_1 + false + DIFF_PAIR_2 + DIFF_PAIR_1 + virtexuplus + 0 + gig_ethernet_pcs_pma_0 + 50.0 + false + . + true + false + false + true + virtexuplus + 16 + 10 + X0Y4 + 8 + 5 + GTH + false + true + false + false + false + false + true + 1 + clk0 + 125 + TXOUTCLK + true + false + gig_ethernet_pcs_pma_0_gt + true + GTHE4 + false + 0 + true + false + false + xcvu9p + false + 1 + false + true + Sync + gig_ethernet_pcs_pma_0 + Custom + 50.0 + TEMAC + Custom + 0 + false + false + false + false + X0Y4 + GTH + false + false + 625 + Custom + false + 1G + 1 + LVDS + 125 + clk0 + TXOUTCLK + DIFF_PAIR_0 + DIFF_PAIR_1 + false + 10_100_1000 + false + SGMII + Include_Shared_Logic_in_Core + Time_of_day + false + DIFF_PAIR_2 + DIFF_PAIR_1 + 0 + false + virtexuplus + + + xcvu9p + flga2104 + VERILOG + + MIXED + -2L + + E + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/ip/gtwizard_ultrascale_0.xci b/fpga/lib/eth/example/VCU118/fpga_10g/ip/gtwizard_ultrascale_0.xci new file mode 100644 index 000000000..31eeac690 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/ip/gtwizard_ultrascale_0.xci @@ -0,0 +1,1409 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gtwizard_ultrascale_0 + + + "000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + 2 + 2578.125 + 0 + 0 + 125 + 67 + 3 + 2 + 0 + 2 + 0 + 0 + 1 + 0 + 1 + 1 + 250 + 0 + 0 + 0 + 0 + 0 + 1 + "00000000" + "00000000" + 1 + 4 + 0 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000" + 0 + "00000000" + 1 + 0 + 5000 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + 0 + "1010000011" + 0 + "0101111100" + 4 + 1 + 64 + 10.3125 + 144 + 1 + 156.2500000 + 4 + 0 + 0x000000000000000000000000000000000000000000000000 + 156.25 + 0 + 0 + 0 + 1 + 1 + 0 + 64 + 156.2500000 + 156.2500000 + 0 + 257.8125 + 0 + 8 + 2 + 0 + 0 + 0 + 156.25 + 0 + 0 + 1 + 4 + 1 + 64 + 10.3125 + 144 + 1 + 156.2500000 + 4 + 0 + 156.25 + 0 + 0 + 1 + 1 + 0 + 64 + 156.2500000 + 156.2500000 + 1 + X1Y55 X1Y54 X1Y53 X1Y52 X1Y51 X1Y50 X1Y49 X1Y48 + gtwizard_ultrascale_0 + 0 + 0 + + 125 + BOTH + 0 + GTY + 2 + 20 + 96 + 1 + gtye4 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + -1 + -1 + -1 + -1 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + 1 + 1 + 0 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + -1 + 0 + 0 + 0 + -1 + 0 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 14 + 0 + 10GBASE-R + 5 + 156.2500000 + 8 + 2 + 156.2500000 + false + CORE + NONE + CORE + CORE + EXAMPLE_DESIGN + CORE + EXAMPLE_DESIGN + EXAMPLE_DESIGN + false + NAME + false + 250 + false + false + 250 + GTY-10GBASE-R + 0 + MULTI + 1 + ENABLE + DISABLE + ENABLE + 00000000 + false + false + false + false + false + false + false + false + 00000000 + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 4 + 1 + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + false + false + false + false + false + false + false + false + 00000000 + DISABLE + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 0 + 5000 + ENABLE + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 1 + false + 0000000000 + false + 1010000011 + NONE + false + 0101111100 + true + 0 + AC + 64B66B_ASYNC + true + AUTO + 64 + 6.1862627 + -20 + 10.3125 + X1Y48 + RXPROGDIVCLK + QPLL0 + 200 + 0 + + 156.25 + X1Y55 clk0-1 X1Y54 clk0-1 X1Y53 clk0-1 X1Y52 clk0-1 + OFF + 0 + PROGRAMMABLE + 800 + 64 + 15 + false + 0 + 10.3125 + 257.8125 + 0 + false + QPLL0 + 156.25 + 1 + ENABLE + 64B66B_ASYNC + CUSTOM + true + 64 + 10.3125 + X1Y48 + TXPROGDIVCLK + QPLL0 + 0 + 156.25 + X1Y55 clk0-1 X1Y54 clk0-1 X1Y53 clk0-1 X1Y52 clk0-1 + 64 + false + 1 + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + true + true + false + true + true + true + false + true + true + true + false + false + false + false + false + true + false + false + false + false + false + false + false + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + virtexuplus + + + xcvu9p + flga2104 + VERILOG + + MIXED + -2L + + E + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/lib/eth b/fpga/lib/eth/example/VCU118/fpga_10g/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/rtl/debounce_switch.v b/fpga/lib/eth/example/VCU118/fpga_10g/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/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 + +`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/fpga/lib/eth/example/VCU118/fpga_10g/rtl/fpga.v b/fpga/lib/eth/example/VCU118/fpga_10g/rtl/fpga.v new file mode 100644 index 000000000..4009e488f --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/rtl/fpga.v @@ -0,0 +1,1290 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 125MHz LVDS + * Reset: Push button, active low + */ + input wire clk_125mhz_p, + input wire clk_125mhz_n, + input wire reset, + + /* + * 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, + + /* + * I2C for board management + */ + inout wire i2c_scl, + inout wire i2c_sda, + + /* + * Ethernet: QSFP28 + */ + output wire qsfp1_tx1_p, + output wire qsfp1_tx1_n, + input wire qsfp1_rx1_p, + input wire qsfp1_rx1_n, + output wire qsfp1_tx2_p, + output wire qsfp1_tx2_n, + input wire qsfp1_rx2_p, + input wire qsfp1_rx2_n, + output wire qsfp1_tx3_p, + output wire qsfp1_tx3_n, + input wire qsfp1_rx3_p, + input wire qsfp1_rx3_n, + output wire qsfp1_tx4_p, + output wire qsfp1_tx4_n, + input wire qsfp1_rx4_p, + input wire qsfp1_rx4_n, + input wire qsfp1_mgt_refclk_0_p, + input wire qsfp1_mgt_refclk_0_n, + // input wire qsfp1_mgt_refclk_1_p, + // input wire qsfp1_mgt_refclk_1_n, + // output wire qsfp1_recclk_p, + // output wire qsfp1_recclk_n, + output wire qsfp1_modsell, + output wire qsfp1_resetl, + input wire qsfp1_modprsl, + input wire qsfp1_intl, + output wire qsfp1_lpmode, + + output wire qsfp2_tx1_p, + output wire qsfp2_tx1_n, + input wire qsfp2_rx1_p, + input wire qsfp2_rx1_n, + output wire qsfp2_tx2_p, + output wire qsfp2_tx2_n, + input wire qsfp2_rx2_p, + input wire qsfp2_rx2_n, + output wire qsfp2_tx3_p, + output wire qsfp2_tx3_n, + input wire qsfp2_rx3_p, + input wire qsfp2_rx3_n, + output wire qsfp2_tx4_p, + output wire qsfp2_tx4_n, + input wire qsfp2_rx4_p, + input wire qsfp2_rx4_n, + // input wire qsfp2_mgt_refclk_0_p, + // input wire qsfp2_mgt_refclk_0_n, + // input wire qsfp2_mgt_refclk_1_p, + // input wire qsfp2_mgt_refclk_1_n, + // output wire qsfp2_recclk_p, + // output wire qsfp2_recclk_n, + output wire qsfp2_modsell, + output wire qsfp2_resetl, + input wire qsfp2_modprsl, + input wire qsfp2_intl, + output wire qsfp2_lpmode, + + /* + * Ethernet: 1000BASE-T SGMII + */ + input wire phy_sgmii_rx_p, + input wire phy_sgmii_rx_n, + output wire phy_sgmii_tx_p, + output wire phy_sgmii_tx_n, + input wire phy_sgmii_clk_p, + input wire phy_sgmii_clk_n, + output wire phy_reset_n, + input wire phy_int_n, + inout wire phy_mdio, + output wire phy_mdc, + + /* + * UART: 500000 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// Clock and reset + +wire clk_125mhz_ibufg; +wire clk_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire rst_125mhz_int; + +// Internal 156.25 MHz clock +wire clk_156mhz_int; +wire rst_156mhz_int; + +wire mmcm_rst = reset; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS #( + .DIFF_TERM("FALSE"), + .IBUF_LOW_PWR("FALSE") +) +clk_125mhz_ibufg_inst ( + .O (clk_125mhz_ibufg), + .I (clk_125mhz_p), + .IB (clk_125mhz_n) +); + +// MMCM instance +// 125 MHz in, 125 MHz out +// PFD range: 10 MHz to 500 MHz +// VCO range: 800 MHz to 1600 MHz +// M = 8, D = 1 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +MMCME3_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(8), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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(8), + .CLKFBOUT_PHASE(0), + .DIVCLK_DIVIDE(1), + .REF_JITTER1(0.010), + .CLKIN1_PERIOD(8.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_125mhz_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_125mhz_bufg_inst ( + .I(clk_125mhz_mmcm_out), + .O(clk_125mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_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(156000) +) +debounce_switch_inst ( + .clk(clk_156mhz_int), + .rst(rst_156mhz_int), + .in({btnu, + btnl, + btnd, + btnr, + btnc, + sw}), + .out({btnu_int, + btnl_int, + btnd_int, + btnr_int, + btnc_int, + sw_int}) +); + +wire uart_rxd_int; +wire uart_cts_int; + +sync_signal #( + .WIDTH(2), + .N(2) +) +sync_signal_inst ( + .clk(clk_156mhz_int), + .in({uart_rxd, uart_cts}), + .out({uart_rxd_int, uart_cts_int}) +); + +// SI570 I2C +wire i2c_scl_i; +wire i2c_scl_o = 1'b1; +wire i2c_scl_t = 1'b1; +wire i2c_sda_i; +wire i2c_sda_o = 1'b1; +wire i2c_sda_t = 1'b1; + +assign i2c_scl_i = i2c_scl; +assign i2c_scl = i2c_scl_t ? 1'bz : i2c_scl_o; +assign i2c_sda_i = i2c_sda; +assign i2c_sda = i2c_sda_t ? 1'bz : i2c_sda_o; + +// XGMII 10G PHY +assign qsfp1_modsell = 1'b0; +assign qsfp1_resetl = 1'b1; +assign qsfp1_lpmode = 1'b0; + +wire qsfp1_tx_clk_1_int; +wire qsfp1_tx_rst_1_int; +wire [63:0] qsfp1_txd_1_int; +wire [7:0] qsfp1_txc_1_int; +wire qsfp1_rx_clk_1_int; +wire qsfp1_rx_rst_1_int; +wire [63:0] qsfp1_rxd_1_int; +wire [7:0] qsfp1_rxc_1_int; +wire qsfp1_tx_clk_2_int; +wire qsfp1_tx_rst_2_int; +wire [63:0] qsfp1_txd_2_int; +wire [7:0] qsfp1_txc_2_int; +wire qsfp1_rx_clk_2_int; +wire qsfp1_rx_rst_2_int; +wire [63:0] qsfp1_rxd_2_int; +wire [7:0] qsfp1_rxc_2_int; +wire qsfp1_tx_clk_3_int; +wire qsfp1_tx_rst_3_int; +wire [63:0] qsfp1_txd_3_int; +wire [7:0] qsfp1_txc_3_int; +wire qsfp1_rx_clk_3_int; +wire qsfp1_rx_rst_3_int; +wire [63:0] qsfp1_rxd_3_int; +wire [7:0] qsfp1_rxc_3_int; +wire qsfp1_tx_clk_4_int; +wire qsfp1_tx_rst_4_int; +wire [63:0] qsfp1_txd_4_int; +wire [7:0] qsfp1_txc_4_int; +wire qsfp1_rx_clk_4_int; +wire qsfp1_rx_rst_4_int; +wire [63:0] qsfp1_rxd_4_int; +wire [7:0] qsfp1_rxc_4_int; + +assign qsfp2_modsell = 1'b0; +assign qsfp2_resetl = 1'b1; +assign qsfp2_lpmode = 1'b0; + +wire qsfp2_tx_clk_1_int; +wire qsfp2_tx_rst_1_int; +wire [63:0] qsfp2_txd_1_int; +wire [7:0] qsfp2_txc_1_int; +wire qsfp2_rx_clk_1_int; +wire qsfp2_rx_rst_1_int; +wire [63:0] qsfp2_rxd_1_int; +wire [7:0] qsfp2_rxc_1_int; +wire qsfp2_tx_clk_2_int; +wire qsfp2_tx_rst_2_int; +wire [63:0] qsfp2_txd_2_int; +wire [7:0] qsfp2_txc_2_int; +wire qsfp2_rx_clk_2_int; +wire qsfp2_rx_rst_2_int; +wire [63:0] qsfp2_rxd_2_int; +wire [7:0] qsfp2_rxc_2_int; +wire qsfp2_tx_clk_3_int; +wire qsfp2_tx_rst_3_int; +wire [63:0] qsfp2_txd_3_int; +wire [7:0] qsfp2_txc_3_int; +wire qsfp2_rx_clk_3_int; +wire qsfp2_rx_rst_3_int; +wire [63:0] qsfp2_rxd_3_int; +wire [7:0] qsfp2_rxc_3_int; +wire qsfp2_tx_clk_4_int; +wire qsfp2_tx_rst_4_int; +wire [63:0] qsfp2_txd_4_int; +wire [7:0] qsfp2_txc_4_int; +wire qsfp2_rx_clk_4_int; +wire qsfp2_rx_rst_4_int; +wire [63:0] qsfp2_rxd_4_int; +wire [7:0] qsfp2_rxc_4_int; + +wire qsfp1_rx_block_lock_1; +wire qsfp1_rx_block_lock_2; +wire qsfp1_rx_block_lock_3; +wire qsfp1_rx_block_lock_4; + +wire qsfp2_rx_block_lock_1; +wire qsfp2_rx_block_lock_2; +wire qsfp2_rx_block_lock_3; +wire qsfp2_rx_block_lock_4; + +wire qsfp1_mgt_refclk_0; + +wire [7:0] gt_txclkout; +wire gt_txusrclk; + +wire [7:0] gt_rxclkout; +wire [7:0] gt_rxusrclk; + +wire gt_reset_tx_done; +wire gt_reset_rx_done; + +wire [7:0] gt_txprgdivresetdone; +wire [7:0] gt_txpmaresetdone; +wire [7:0] gt_rxprgdivresetdone; +wire [7:0] gt_rxpmaresetdone; + +wire gt_tx_reset = ~((>_txprgdivresetdone) & (>_txpmaresetdone)); +wire gt_rx_reset = ~>_rxpmaresetdone; + +reg gt_userclk_tx_active = 1'b0; +reg [7:0] gt_userclk_rx_active = 1'b0; + +IBUFDS_GTE4 ibufds_gte4_qsfp1_mgt_refclk_0_inst ( + .I (qsfp1_mgt_refclk_0_p), + .IB (qsfp1_mgt_refclk_0_n), + .CEB (1'b0), + .O (qsfp1_mgt_refclk_0), + .ODIV2 () +); + + +BUFG_GT bufg_gt_tx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_tx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_txclkout[0]), + .O (gt_txusrclk) +); + +assign clk_156mhz_int = gt_txusrclk; + +always @(posedge gt_txusrclk, posedge gt_tx_reset) begin + if (gt_tx_reset) begin + gt_userclk_tx_active <= 1'b0; + end else begin + gt_userclk_tx_active <= 1'b1; + end +end + +genvar n; + +generate + +for (n = 0; n < 8; n = n + 1) begin + + BUFG_GT bufg_gt_rx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_rx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_rxclkout[n]), + .O (gt_rxusrclk[n]) + ); + + always @(posedge gt_rxusrclk[n], posedge gt_rx_reset) begin + if (gt_rx_reset) begin + gt_userclk_rx_active[n] <= 1'b0; + end else begin + gt_userclk_rx_active[n] <= 1'b1; + end + end + +end + +endgenerate + +sync_reset #( + .N(4) +) +sync_reset_156mhz_inst ( + .clk(clk_156mhz_int), + .rst(~gt_reset_tx_done), + .sync_reset_out(rst_156mhz_int) +); + +wire [5:0] qsfp1_gt_txheader_1; +wire [127:0] qsfp1_gt_txdata_1; +wire qsfp1_gt_rxgearboxslip_1; +wire [5:0] qsfp1_gt_rxheader_1; +wire [1:0] qsfp1_gt_rxheadervalid_1; +wire [127:0] qsfp1_gt_rxdata_1; +wire [1:0] qsfp1_gt_rxdatavalid_1; + +wire [5:0] qsfp1_gt_txheader_2; +wire [127:0] qsfp1_gt_txdata_2; +wire qsfp1_gt_rxgearboxslip_2; +wire [5:0] qsfp1_gt_rxheader_2; +wire [1:0] qsfp1_gt_rxheadervalid_2; +wire [127:0] qsfp1_gt_rxdata_2; +wire [1:0] qsfp1_gt_rxdatavalid_2; + +wire [5:0] qsfp1_gt_txheader_3; +wire [127:0] qsfp1_gt_txdata_3; +wire qsfp1_gt_rxgearboxslip_3; +wire [5:0] qsfp1_gt_rxheader_3; +wire [1:0] qsfp1_gt_rxheadervalid_3; +wire [127:0] qsfp1_gt_rxdata_3; +wire [1:0] qsfp1_gt_rxdatavalid_3; + +wire [5:0] qsfp1_gt_txheader_4; +wire [127:0] qsfp1_gt_txdata_4; +wire qsfp1_gt_rxgearboxslip_4; +wire [5:0] qsfp1_gt_rxheader_4; +wire [1:0] qsfp1_gt_rxheadervalid_4; +wire [127:0] qsfp1_gt_rxdata_4; +wire [1:0] qsfp1_gt_rxdatavalid_4; + +wire [5:0] qsfp2_gt_txheader_1; +wire [127:0] qsfp2_gt_txdata_1; +wire qsfp2_gt_rxgearboxslip_1; +wire [5:0] qsfp2_gt_rxheader_1; +wire [1:0] qsfp2_gt_rxheadervalid_1; +wire [127:0] qsfp2_gt_rxdata_1; +wire [1:0] qsfp2_gt_rxdatavalid_1; + +wire [5:0] qsfp2_gt_txheader_2; +wire [127:0] qsfp2_gt_txdata_2; +wire qsfp2_gt_rxgearboxslip_2; +wire [5:0] qsfp2_gt_rxheader_2; +wire [1:0] qsfp2_gt_rxheadervalid_2; +wire [127:0] qsfp2_gt_rxdata_2; +wire [1:0] qsfp2_gt_rxdatavalid_2; + +wire [5:0] qsfp2_gt_txheader_3; +wire [127:0] qsfp2_gt_txdata_3; +wire qsfp2_gt_rxgearboxslip_3; +wire [5:0] qsfp2_gt_rxheader_3; +wire [1:0] qsfp2_gt_rxheadervalid_3; +wire [127:0] qsfp2_gt_rxdata_3; +wire [1:0] qsfp2_gt_rxdatavalid_3; + +wire [5:0] qsfp2_gt_txheader_4; +wire [127:0] qsfp2_gt_txdata_4; +wire qsfp2_gt_rxgearboxslip_4; +wire [5:0] qsfp2_gt_rxheader_4; +wire [1:0] qsfp2_gt_rxheadervalid_4; +wire [127:0] qsfp2_gt_rxdata_4; +wire [1:0] qsfp2_gt_rxdatavalid_4; + +gtwizard_ultrascale_0 +qsfp_gty_inst ( + .gtwiz_userclk_tx_active_in(>_userclk_tx_active), + .gtwiz_userclk_rx_active_in(>_userclk_rx_active), + + .gtwiz_reset_clk_freerun_in(clk_125mhz_int), + .gtwiz_reset_all_in(rst_125mhz_int), + + .gtwiz_reset_tx_pll_and_datapath_in(1'b0), + .gtwiz_reset_tx_datapath_in(1'b0), + + .gtwiz_reset_rx_pll_and_datapath_in(1'b0), + .gtwiz_reset_rx_datapath_in(1'b0), + + .gtwiz_reset_rx_cdr_stable_out(), + + .gtwiz_reset_tx_done_out(gt_reset_tx_done), + .gtwiz_reset_rx_done_out(gt_reset_rx_done), + + .gtrefclk00_in({2{qsfp1_mgt_refclk_0}}), + + .qpll0outclk_out(), + .qpll0outrefclk_out(), + + .gtyrxn_in({qsfp2_rx4_n, qsfp2_rx3_n, qsfp2_rx2_n, qsfp2_rx1_n, qsfp1_rx4_n, qsfp1_rx3_n, qsfp1_rx2_n, qsfp1_rx1_n}), + .gtyrxp_in({qsfp2_rx4_p, qsfp2_rx3_p, qsfp2_rx2_p, qsfp2_rx1_p, qsfp1_rx4_p, qsfp1_rx3_p, qsfp1_rx2_p, qsfp1_rx1_p}), + + .rxusrclk_in(gt_rxusrclk), + .rxusrclk2_in(gt_rxusrclk), + + .txdata_in({qsfp2_gt_txdata_4, qsfp2_gt_txdata_3, qsfp2_gt_txdata_2, qsfp2_gt_txdata_1, qsfp1_gt_txdata_4, qsfp1_gt_txdata_3, qsfp1_gt_txdata_2, qsfp1_gt_txdata_1}), + .txheader_in({qsfp2_gt_txheader_4, qsfp2_gt_txheader_3, qsfp2_gt_txheader_2, qsfp2_gt_txheader_1, qsfp1_gt_txheader_4, qsfp1_gt_txheader_3, qsfp1_gt_txheader_2, qsfp1_gt_txheader_1}), + .txsequence_in({8{1'b0}}), + + .txusrclk_in({8{gt_txusrclk}}), + .txusrclk2_in({8{gt_txusrclk}}), + + .gtpowergood_out(), + + .gtytxn_out({qsfp2_tx4_n, qsfp2_tx3_n, qsfp2_tx2_n, qsfp2_tx1_n, qsfp1_tx4_n, qsfp1_tx3_n, qsfp1_tx2_n, qsfp1_tx1_n}), + .gtytxp_out({qsfp2_tx4_p, qsfp2_tx3_p, qsfp2_tx2_p, qsfp2_tx1_p, qsfp1_tx4_p, qsfp1_tx3_p, qsfp1_tx2_p, qsfp1_tx1_p}), + + .rxgearboxslip_in({qsfp2_gt_rxgearboxslip_4, qsfp2_gt_rxgearboxslip_3, qsfp2_gt_rxgearboxslip_2, qsfp2_gt_rxgearboxslip_1, qsfp1_gt_rxgearboxslip_4, qsfp1_gt_rxgearboxslip_3, qsfp1_gt_rxgearboxslip_2, qsfp1_gt_rxgearboxslip_1}), + .rxdata_out({qsfp2_gt_rxdata_4, qsfp2_gt_rxdata_3, qsfp2_gt_rxdata_2, qsfp2_gt_rxdata_1, qsfp1_gt_rxdata_4, qsfp1_gt_rxdata_3, qsfp1_gt_rxdata_2, qsfp1_gt_rxdata_1}), + .rxdatavalid_out({qsfp2_gt_rxdatavalid_4, qsfp2_gt_rxdatavalid_3, qsfp2_gt_rxdatavalid_2, qsfp2_gt_rxdatavalid_1, qsfp1_gt_rxdatavalid_4, qsfp1_gt_rxdatavalid_3, qsfp1_gt_rxdatavalid_2, qsfp1_gt_rxdatavalid_1}), + .rxheader_out({qsfp2_gt_rxheader_4, qsfp2_gt_rxheader_3, qsfp2_gt_rxheader_2, qsfp2_gt_rxheader_1, qsfp1_gt_rxheader_4, qsfp1_gt_rxheader_3, qsfp1_gt_rxheader_2, qsfp1_gt_rxheader_1}), + .rxheadervalid_out({qsfp2_gt_rxheadervalid_4, qsfp2_gt_rxheadervalid_3, qsfp2_gt_rxheadervalid_2, qsfp2_gt_rxheadervalid_1, qsfp1_gt_rxheadervalid_4, qsfp1_gt_rxheadervalid_3, qsfp1_gt_rxheadervalid_2, qsfp1_gt_rxheadervalid_1}), + .rxoutclk_out(gt_rxclkout), + .rxpmaresetdone_out(gt_rxpmaresetdone), + .rxprgdivresetdone_out(gt_rxprgdivresetdone), + .rxstartofseq_out(), + + .txoutclk_out(gt_txclkout), + .txpmaresetdone_out(gt_txpmaresetdone), + .txprgdivresetdone_out(gt_txprgdivresetdone) +); + +assign qsfp1_tx_clk_1_int = clk_156mhz_int; +assign qsfp1_tx_rst_1_int = rst_156mhz_int; + +assign qsfp1_rx_clk_1_int = gt_rxusrclk[0]; + +sync_reset #( + .N(4) +) +qsfp1_rx_rst_1_reset_sync_inst ( + .clk(qsfp1_rx_clk_1_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp1_rx_rst_1_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp1_phy_1_inst ( + .tx_clk(qsfp1_tx_clk_1_int), + .tx_rst(qsfp1_tx_rst_1_int), + .rx_clk(qsfp1_rx_clk_1_int), + .rx_rst(qsfp1_rx_rst_1_int), + .xgmii_txd(qsfp1_txd_1_int), + .xgmii_txc(qsfp1_txc_1_int), + .xgmii_rxd(qsfp1_rxd_1_int), + .xgmii_rxc(qsfp1_rxc_1_int), + .serdes_tx_data(qsfp1_gt_txdata_1), + .serdes_tx_hdr(qsfp1_gt_txheader_1), + .serdes_rx_data(qsfp1_gt_rxdata_1), + .serdes_rx_hdr(qsfp1_gt_rxheader_1), + .serdes_rx_bitslip(qsfp1_gt_rxgearboxslip_1), + .rx_block_lock(qsfp1_rx_block_lock_1), + .rx_high_ber() +); + +assign qsfp1_tx_clk_2_int = clk_156mhz_int; +assign qsfp1_tx_rst_2_int = rst_156mhz_int; + +assign qsfp1_rx_clk_2_int = gt_rxusrclk[1]; + +sync_reset #( + .N(4) +) +qsfp1_rx_rst_2_reset_sync_inst ( + .clk(qsfp1_rx_clk_2_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp1_rx_rst_2_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp1_phy_2_inst ( + .tx_clk(qsfp1_tx_clk_2_int), + .tx_rst(qsfp1_tx_rst_2_int), + .rx_clk(qsfp1_rx_clk_2_int), + .rx_rst(qsfp1_rx_rst_2_int), + .xgmii_txd(qsfp1_txd_2_int), + .xgmii_txc(qsfp1_txc_2_int), + .xgmii_rxd(qsfp1_rxd_2_int), + .xgmii_rxc(qsfp1_rxc_2_int), + .serdes_tx_data(qsfp1_gt_txdata_2), + .serdes_tx_hdr(qsfp1_gt_txheader_2), + .serdes_rx_data(qsfp1_gt_rxdata_2), + .serdes_rx_hdr(qsfp1_gt_rxheader_2), + .serdes_rx_bitslip(qsfp1_gt_rxgearboxslip_2), + .rx_block_lock(qsfp1_rx_block_lock_2), + .rx_high_ber() +); + +assign qsfp1_tx_clk_3_int = clk_156mhz_int; +assign qsfp1_tx_rst_3_int = rst_156mhz_int; + +assign qsfp1_rx_clk_3_int = gt_rxusrclk[2]; + +sync_reset #( + .N(4) +) +qsfp1_rx_rst_3_reset_sync_inst ( + .clk(qsfp1_rx_clk_3_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp1_rx_rst_3_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp1_phy_3_inst ( + .tx_clk(qsfp1_tx_clk_3_int), + .tx_rst(qsfp1_tx_rst_3_int), + .rx_clk(qsfp1_rx_clk_3_int), + .rx_rst(qsfp1_rx_rst_3_int), + .xgmii_txd(qsfp1_txd_3_int), + .xgmii_txc(qsfp1_txc_3_int), + .xgmii_rxd(qsfp1_rxd_3_int), + .xgmii_rxc(qsfp1_rxc_3_int), + .serdes_tx_data(qsfp1_gt_txdata_3), + .serdes_tx_hdr(qsfp1_gt_txheader_3), + .serdes_rx_data(qsfp1_gt_rxdata_3), + .serdes_rx_hdr(qsfp1_gt_rxheader_3), + .serdes_rx_bitslip(qsfp1_gt_rxgearboxslip_3), + .rx_block_lock(qsfp1_rx_block_lock_3), + .rx_high_ber() +); + +assign qsfp1_tx_clk_4_int = clk_156mhz_int; +assign qsfp1_tx_rst_4_int = rst_156mhz_int; + +assign qsfp1_rx_clk_4_int = gt_rxusrclk[3]; + +sync_reset #( + .N(4) +) +qsfp1_rx_rst_4_reset_sync_inst ( + .clk(qsfp1_rx_clk_4_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp1_rx_rst_4_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp1_phy_4_inst ( + .tx_clk(qsfp1_tx_clk_4_int), + .tx_rst(qsfp1_tx_rst_4_int), + .rx_clk(qsfp1_rx_clk_4_int), + .rx_rst(qsfp1_rx_rst_4_int), + .xgmii_txd(qsfp1_txd_4_int), + .xgmii_txc(qsfp1_txc_4_int), + .xgmii_rxd(qsfp1_rxd_4_int), + .xgmii_rxc(qsfp1_rxc_4_int), + .serdes_tx_data(qsfp1_gt_txdata_4), + .serdes_tx_hdr(qsfp1_gt_txheader_4), + .serdes_rx_data(qsfp1_gt_rxdata_4), + .serdes_rx_hdr(qsfp1_gt_rxheader_4), + .serdes_rx_bitslip(qsfp1_gt_rxgearboxslip_4), + .rx_block_lock(qsfp1_rx_block_lock_4), + .rx_high_ber() +); + +assign qsfp2_tx_clk_1_int = clk_156mhz_int; +assign qsfp2_tx_rst_1_int = rst_156mhz_int; + +assign qsfp2_rx_clk_1_int = gt_rxusrclk[4]; + +sync_reset #( + .N(4) +) +qsfp2_rx_rst_1_reset_sync_inst ( + .clk(qsfp2_rx_clk_1_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp2_rx_rst_1_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp2_phy_1_inst ( + .tx_clk(qsfp2_tx_clk_1_int), + .tx_rst(qsfp2_tx_rst_1_int), + .rx_clk(qsfp2_rx_clk_1_int), + .rx_rst(qsfp2_rx_rst_1_int), + .xgmii_txd(qsfp2_txd_1_int), + .xgmii_txc(qsfp2_txc_1_int), + .xgmii_rxd(qsfp2_rxd_1_int), + .xgmii_rxc(qsfp2_rxc_1_int), + .serdes_tx_data(qsfp2_gt_txdata_1), + .serdes_tx_hdr(qsfp2_gt_txheader_1), + .serdes_rx_data(qsfp2_gt_rxdata_1), + .serdes_rx_hdr(qsfp2_gt_rxheader_1), + .serdes_rx_bitslip(qsfp2_gt_rxgearboxslip_1), + .rx_block_lock(qsfp2_rx_block_lock_1), + .rx_high_ber() +); + +assign qsfp2_tx_clk_2_int = clk_156mhz_int; +assign qsfp2_tx_rst_2_int = rst_156mhz_int; + +assign qsfp2_rx_clk_2_int = gt_rxusrclk[5]; + +sync_reset #( + .N(4) +) +qsfp2_rx_rst_2_reset_sync_inst ( + .clk(qsfp2_rx_clk_2_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp2_rx_rst_2_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp2_phy_2_inst ( + .tx_clk(qsfp2_tx_clk_2_int), + .tx_rst(qsfp2_tx_rst_2_int), + .rx_clk(qsfp2_rx_clk_2_int), + .rx_rst(qsfp2_rx_rst_2_int), + .xgmii_txd(qsfp2_txd_2_int), + .xgmii_txc(qsfp2_txc_2_int), + .xgmii_rxd(qsfp2_rxd_2_int), + .xgmii_rxc(qsfp2_rxc_2_int), + .serdes_tx_data(qsfp2_gt_txdata_2), + .serdes_tx_hdr(qsfp2_gt_txheader_2), + .serdes_rx_data(qsfp2_gt_rxdata_2), + .serdes_rx_hdr(qsfp2_gt_rxheader_2), + .serdes_rx_bitslip(qsfp2_gt_rxgearboxslip_2), + .rx_block_lock(qsfp2_rx_block_lock_2), + .rx_high_ber() +); + +assign qsfp2_tx_clk_3_int = clk_156mhz_int; +assign qsfp2_tx_rst_3_int = rst_156mhz_int; + +assign qsfp2_rx_clk_3_int = gt_rxusrclk[6]; + +sync_reset #( + .N(4) +) +qsfp2_rx_rst_3_reset_sync_inst ( + .clk(qsfp2_rx_clk_3_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp2_rx_rst_3_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp2_phy_3_inst ( + .tx_clk(qsfp2_tx_clk_3_int), + .tx_rst(qsfp2_tx_rst_3_int), + .rx_clk(qsfp2_rx_clk_3_int), + .rx_rst(qsfp2_rx_rst_3_int), + .xgmii_txd(qsfp2_txd_3_int), + .xgmii_txc(qsfp2_txc_3_int), + .xgmii_rxd(qsfp2_rxd_3_int), + .xgmii_rxc(qsfp2_rxc_3_int), + .serdes_tx_data(qsfp2_gt_txdata_3), + .serdes_tx_hdr(qsfp2_gt_txheader_3), + .serdes_rx_data(qsfp2_gt_rxdata_3), + .serdes_rx_hdr(qsfp2_gt_rxheader_3), + .serdes_rx_bitslip(qsfp2_gt_rxgearboxslip_3), + .rx_block_lock(qsfp2_rx_block_lock_3), + .rx_high_ber() +); + +assign qsfp2_tx_clk_4_int = clk_156mhz_int; +assign qsfp2_tx_rst_4_int = rst_156mhz_int; + +assign qsfp2_rx_clk_4_int = gt_rxusrclk[7]; + +sync_reset #( + .N(4) +) +qsfp2_rx_rst_4_reset_sync_inst ( + .clk(qsfp2_rx_clk_4_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp2_rx_rst_4_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1) +) +qsfp2_phy_4_inst ( + .tx_clk(qsfp2_tx_clk_4_int), + .tx_rst(qsfp2_tx_rst_4_int), + .rx_clk(qsfp2_rx_clk_4_int), + .rx_rst(qsfp2_rx_rst_4_int), + .xgmii_txd(qsfp2_txd_4_int), + .xgmii_txc(qsfp2_txc_4_int), + .xgmii_rxd(qsfp2_rxd_4_int), + .xgmii_rxc(qsfp2_rxc_4_int), + .serdes_tx_data(qsfp2_gt_txdata_4), + .serdes_tx_hdr(qsfp2_gt_txheader_4), + .serdes_rx_data(qsfp2_gt_rxdata_4), + .serdes_rx_hdr(qsfp2_gt_rxheader_4), + .serdes_rx_bitslip(qsfp2_gt_rxgearboxslip_4), + .rx_block_lock(qsfp2_rx_block_lock_4), + .rx_high_ber() +); + +// SGMII interface to PHY +wire phy_gmii_clk_int; +wire phy_gmii_rst_int; +wire phy_gmii_clk_en_int; +wire [7:0] phy_gmii_txd_int; +wire phy_gmii_tx_en_int; +wire phy_gmii_tx_er_int; +wire [7:0] phy_gmii_rxd_int; +wire phy_gmii_rx_dv_int; +wire phy_gmii_rx_er_int; + +wire [15:0] gig_eth_pcspma_status_vector; + +wire gig_eth_pcspma_status_link_status = gig_eth_pcspma_status_vector[0]; +wire gig_eth_pcspma_status_link_synchronization = gig_eth_pcspma_status_vector[1]; +wire gig_eth_pcspma_status_rudi_c = gig_eth_pcspma_status_vector[2]; +wire gig_eth_pcspma_status_rudi_i = gig_eth_pcspma_status_vector[3]; +wire gig_eth_pcspma_status_rudi_invalid = gig_eth_pcspma_status_vector[4]; +wire gig_eth_pcspma_status_rxdisperr = gig_eth_pcspma_status_vector[5]; +wire gig_eth_pcspma_status_rxnotintable = gig_eth_pcspma_status_vector[6]; +wire gig_eth_pcspma_status_phy_link_status = gig_eth_pcspma_status_vector[7]; +wire [1:0] gig_eth_pcspma_status_remote_fault_encdg = gig_eth_pcspma_status_vector[9:8]; +wire [1:0] gig_eth_pcspma_status_speed = gig_eth_pcspma_status_vector[11:10]; +wire gig_eth_pcspma_status_duplex = gig_eth_pcspma_status_vector[12]; +wire gig_eth_pcspma_status_remote_fault = gig_eth_pcspma_status_vector[13]; +wire [1:0] gig_eth_pcspma_status_pause = gig_eth_pcspma_status_vector[15:14]; + +wire [4:0] gig_eth_pcspma_config_vector; + +assign gig_eth_pcspma_config_vector[4] = 1'b1; // autonegotiation enable +assign gig_eth_pcspma_config_vector[3] = 1'b0; // isolate +assign gig_eth_pcspma_config_vector[2] = 1'b0; // power down +assign gig_eth_pcspma_config_vector[1] = 1'b0; // loopback enable +assign gig_eth_pcspma_config_vector[0] = 1'b0; // unidirectional enable + +wire [15:0] gig_eth_pcspma_an_config_vector; + +assign gig_eth_pcspma_an_config_vector[15] = 1'b1; // SGMII link status +assign gig_eth_pcspma_an_config_vector[14] = 1'b1; // SGMII Acknowledge +assign gig_eth_pcspma_an_config_vector[13:12] = 2'b01; // full duplex +assign gig_eth_pcspma_an_config_vector[11:10] = 2'b10; // SGMII speed +assign gig_eth_pcspma_an_config_vector[9] = 1'b0; // reserved +assign gig_eth_pcspma_an_config_vector[8:7] = 2'b00; // pause frames - SGMII reserved +assign gig_eth_pcspma_an_config_vector[6] = 1'b0; // reserved +assign gig_eth_pcspma_an_config_vector[5] = 1'b0; // full duplex - SGMII reserved +assign gig_eth_pcspma_an_config_vector[4:1] = 4'b0000; // reserved +assign gig_eth_pcspma_an_config_vector[0] = 1'b1; // SGMII + +gig_ethernet_pcs_pma_0 +eth_pcspma ( + // SGMII + .txp_0 (phy_sgmii_tx_p), + .txn_0 (phy_sgmii_tx_n), + .rxp_0 (phy_sgmii_rx_p), + .rxn_0 (phy_sgmii_rx_n), + + // Ref clock from PHY + .refclk625_p (phy_sgmii_clk_p), + .refclk625_n (phy_sgmii_clk_n), + + // async reset + .reset (rst_125mhz_int), + + // clock and reset outputs + .clk125_out (phy_gmii_clk_int), + .clk312_out (), + .rst_125_out (phy_gmii_rst_int), + .tx_logic_reset (), + .rx_logic_reset (), + .tx_locked (), + .rx_locked (), + .tx_pll_clk_out (), + .rx_pll_clk_out (), + + // MAC clocking + .sgmii_clk_r_0 (), + .sgmii_clk_f_0 (), + .sgmii_clk_en_0 (phy_gmii_clk_en_int), + + // Speed control + .speed_is_10_100_0 (gig_eth_pcspma_status_speed != 2'b10), + .speed_is_100_0 (gig_eth_pcspma_status_speed == 2'b01), + + // Internal GMII + .gmii_txd_0 (phy_gmii_txd_int), + .gmii_tx_en_0 (phy_gmii_tx_en_int), + .gmii_tx_er_0 (phy_gmii_tx_er_int), + .gmii_rxd_0 (phy_gmii_rxd_int), + .gmii_rx_dv_0 (phy_gmii_rx_dv_int), + .gmii_rx_er_0 (phy_gmii_rx_er_int), + .gmii_isolate_0 (), + + // Configuration + .configuration_vector_0 (gig_eth_pcspma_config_vector), + + .an_interrupt_0 (), + .an_adv_config_vector_0 (gig_eth_pcspma_an_config_vector), + .an_restart_config_0 (1'b0), + + // Status + .status_vector_0 (gig_eth_pcspma_status_vector), + .signal_detect_0 (1'b1), + + // Cascade + .tx_bsc_rst_out (), + .rx_bsc_rst_out (), + .tx_bs_rst_out (), + .rx_bs_rst_out (), + .tx_rst_dly_out (), + .rx_rst_dly_out (), + .tx_bsc_en_vtc_out (), + .rx_bsc_en_vtc_out (), + .tx_bs_en_vtc_out (), + .rx_bs_en_vtc_out (), + .riu_clk_out (), + .riu_addr_out (), + .riu_wr_data_out (), + .riu_wr_en_out (), + .riu_nibble_sel_out (), + .riu_rddata_1 (16'b0), + .riu_valid_1 (1'b0), + .riu_prsnt_1 (1'b0), + .riu_rddata_2 (16'b0), + .riu_valid_2 (1'b0), + .riu_prsnt_2 (1'b0), + .riu_rddata_3 (16'b0), + .riu_valid_3 (1'b0), + .riu_prsnt_3 (1'b0), + .rx_btval_1 (), + .rx_btval_2 (), + .rx_btval_3 (), + .tx_dly_rdy_1 (1'b1), + .rx_dly_rdy_1 (1'b1), + .rx_vtc_rdy_1 (1'b1), + .tx_vtc_rdy_1 (1'b1), + .tx_dly_rdy_2 (1'b1), + .rx_dly_rdy_2 (1'b1), + .rx_vtc_rdy_2 (1'b1), + .tx_vtc_rdy_2 (1'b1), + .tx_dly_rdy_3 (1'b1), + .rx_dly_rdy_3 (1'b1), + .rx_vtc_rdy_3 (1'b1), + .tx_vtc_rdy_3 (1'b1), + .tx_rdclk_out () +); + +reg [19:0] delay_reg = 20'hfffff; + +reg [4:0] mdio_cmd_phy_addr = 5'h03; +reg [4:0] mdio_cmd_reg_addr = 5'h00; +reg [15:0] mdio_cmd_data = 16'd0; +reg [1:0] mdio_cmd_opcode = 2'b01; +reg mdio_cmd_valid = 1'b0; +wire mdio_cmd_ready; + +reg [3:0] state_reg = 0; + +always @(posedge clk_125mhz_int) begin + if (rst_125mhz_int) begin + state_reg <= 0; + delay_reg <= 20'hfffff; + mdio_cmd_reg_addr <= 5'h00; + mdio_cmd_data <= 16'd0; + mdio_cmd_valid <= 1'b0; + end else begin + mdio_cmd_valid <= mdio_cmd_valid & !mdio_cmd_ready; + if (delay_reg > 0) begin + delay_reg <= delay_reg - 1; + end else if (!mdio_cmd_ready) begin + // wait for ready + state_reg <= state_reg; + end else begin + mdio_cmd_valid <= 1'b0; + case (state_reg) + // set SGMII autonegotiation timer to 11 ms + // write 0x0070 to CFG4 (0x0031) + 4'd0: begin + // write to REGCR to load address + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h001F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd1; + end + 4'd1: begin + // write address of CFG4 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h0031; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd2; + end + 4'd2: begin + // write to REGCR to load data + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h401F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd3; + end + 4'd3: begin + // write data for CFG4 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h0070; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd4; + end + // enable SGMII clock output + // write 0x4000 to SGMIICTL1 (0x00D3) + 4'd4: begin + // write to REGCR to load address + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h001F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd5; + end + 4'd5: begin + // write address of SGMIICTL1 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h00D3; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd6; + end + 4'd6: begin + // write to REGCR to load data + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h401F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd7; + end + 4'd7: begin + // write data for SGMIICTL1 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h4000; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd8; + end + // enable 10Mbps operation + // write 0x0015 to 10M_SGMII_CFG (0x016F) + 4'd8: begin + // write to REGCR to load address + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h001F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd9; + end + 4'd9: begin + // write address of 10M_SGMII_CFG to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h016F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd10; + end + 4'd10: begin + // write to REGCR to load data + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h401F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd11; + end + 4'd11: begin + // write data for 10M_SGMII_CFG to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h0015; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd12; + end + 4'd12: begin + // done + state_reg <= 4'd12; + end + endcase + end + end +end + +wire mdc; +wire mdio_i; +wire mdio_o; +wire mdio_t; + +mdio_master +mdio_master_inst ( + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + + .cmd_phy_addr(mdio_cmd_phy_addr), + .cmd_reg_addr(mdio_cmd_reg_addr), + .cmd_data(mdio_cmd_data), + .cmd_opcode(mdio_cmd_opcode), + .cmd_valid(mdio_cmd_valid), + .cmd_ready(mdio_cmd_ready), + + .data_out(), + .data_out_valid(), + .data_out_ready(1'b1), + + .mdc_o(mdc), + .mdio_i(mdio_i), + .mdio_o(mdio_o), + .mdio_t(mdio_t), + + .busy(), + + .prescale(8'd3) +); + +assign phy_mdc = mdc; +assign mdio_i = phy_mdio; +assign phy_mdio = mdio_t ? 1'bz : mdio_o; + +wire [7:0] led_int; + +assign led = sw[0] ? {qsfp2_rx_block_lock_4, qsfp2_rx_block_lock_3, qsfp2_rx_block_lock_2, qsfp2_rx_block_lock_1, qsfp1_rx_block_lock_4, qsfp1_rx_block_lock_3, qsfp1_rx_block_lock_2, qsfp1_rx_block_lock_1} : led_int; + +fpga_core +core_inst ( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + .clk(clk_156mhz_int), + .rst(rst_156mhz_int), + /* + * GPIO + */ + .btnu(btnu_int), + .btnl(btnl_int), + .btnd(btnd_int), + .btnr(btnr_int), + .btnc(btnc_int), + .sw(sw_int), + .led(led_int), + /* + * Ethernet: QSFP28 + */ + .qsfp1_tx_clk_1(qsfp1_tx_clk_1_int), + .qsfp1_tx_rst_1(qsfp1_tx_rst_1_int), + .qsfp1_txd_1(qsfp1_txd_1_int), + .qsfp1_txc_1(qsfp1_txc_1_int), + .qsfp1_rx_clk_1(qsfp1_rx_clk_1_int), + .qsfp1_rx_rst_1(qsfp1_rx_rst_1_int), + .qsfp1_rxd_1(qsfp1_rxd_1_int), + .qsfp1_rxc_1(qsfp1_rxc_1_int), + .qsfp1_tx_clk_2(qsfp1_tx_clk_2_int), + .qsfp1_tx_rst_2(qsfp1_tx_rst_2_int), + .qsfp1_txd_2(qsfp1_txd_2_int), + .qsfp1_txc_2(qsfp1_txc_2_int), + .qsfp1_rx_clk_2(qsfp1_rx_clk_2_int), + .qsfp1_rx_rst_2(qsfp1_rx_rst_2_int), + .qsfp1_rxd_2(qsfp1_rxd_2_int), + .qsfp1_rxc_2(qsfp1_rxc_2_int), + .qsfp1_tx_clk_3(qsfp1_tx_clk_3_int), + .qsfp1_tx_rst_3(qsfp1_tx_rst_3_int), + .qsfp1_txd_3(qsfp1_txd_3_int), + .qsfp1_txc_3(qsfp1_txc_3_int), + .qsfp1_rx_clk_3(qsfp1_rx_clk_3_int), + .qsfp1_rx_rst_3(qsfp1_rx_rst_3_int), + .qsfp1_rxd_3(qsfp1_rxd_3_int), + .qsfp1_rxc_3(qsfp1_rxc_3_int), + .qsfp1_tx_clk_4(qsfp1_tx_clk_4_int), + .qsfp1_tx_rst_4(qsfp1_tx_rst_4_int), + .qsfp1_txd_4(qsfp1_txd_4_int), + .qsfp1_txc_4(qsfp1_txc_4_int), + .qsfp1_rx_clk_4(qsfp1_rx_clk_4_int), + .qsfp1_rx_rst_4(qsfp1_rx_rst_4_int), + .qsfp1_rxd_4(qsfp1_rxd_4_int), + .qsfp1_rxc_4(qsfp1_rxc_4_int), + .qsfp2_tx_clk_1(qsfp2_tx_clk_1_int), + .qsfp2_tx_rst_1(qsfp2_tx_rst_1_int), + .qsfp2_txd_1(qsfp2_txd_1_int), + .qsfp2_txc_1(qsfp2_txc_1_int), + .qsfp2_rx_clk_1(qsfp2_rx_clk_1_int), + .qsfp2_rx_rst_1(qsfp2_rx_rst_1_int), + .qsfp2_rxd_1(qsfp2_rxd_1_int), + .qsfp2_rxc_1(qsfp2_rxc_1_int), + .qsfp2_tx_clk_2(qsfp2_tx_clk_2_int), + .qsfp2_tx_rst_2(qsfp2_tx_rst_2_int), + .qsfp2_txd_2(qsfp2_txd_2_int), + .qsfp2_txc_2(qsfp2_txc_2_int), + .qsfp2_rx_clk_2(qsfp2_rx_clk_2_int), + .qsfp2_rx_rst_2(qsfp2_rx_rst_2_int), + .qsfp2_rxd_2(qsfp2_rxd_2_int), + .qsfp2_rxc_2(qsfp2_rxc_2_int), + .qsfp2_tx_clk_3(qsfp2_tx_clk_3_int), + .qsfp2_tx_rst_3(qsfp2_tx_rst_3_int), + .qsfp2_txd_3(qsfp2_txd_3_int), + .qsfp2_txc_3(qsfp2_txc_3_int), + .qsfp2_rx_clk_3(qsfp2_rx_clk_3_int), + .qsfp2_rx_rst_3(qsfp2_rx_rst_3_int), + .qsfp2_rxd_3(qsfp2_rxd_3_int), + .qsfp2_rxc_3(qsfp2_rxc_3_int), + .qsfp2_tx_clk_4(qsfp2_tx_clk_4_int), + .qsfp2_tx_rst_4(qsfp2_tx_rst_4_int), + .qsfp2_txd_4(qsfp2_txd_4_int), + .qsfp2_txc_4(qsfp2_txc_4_int), + .qsfp2_rx_clk_4(qsfp2_rx_clk_4_int), + .qsfp2_rx_rst_4(qsfp2_rx_rst_4_int), + .qsfp2_rxd_4(qsfp2_rxd_4_int), + .qsfp2_rxc_4(qsfp2_rxc_4_int), + /* + * Ethernet: 1000BASE-T SGMII + */ + .phy_gmii_clk(phy_gmii_clk_int), + .phy_gmii_rst(phy_gmii_rst_int), + .phy_gmii_clk_en(phy_gmii_clk_en_int), + .phy_gmii_rxd(phy_gmii_rxd_int), + .phy_gmii_rx_dv(phy_gmii_rx_dv_int), + .phy_gmii_rx_er(phy_gmii_rx_er_int), + .phy_gmii_txd(phy_gmii_txd_int), + .phy_gmii_tx_en(phy_gmii_tx_en_int), + .phy_gmii_tx_er(phy_gmii_tx_er_int), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts_int) +); + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/rtl/fpga_core.v b/fpga/lib/eth/example/VCU118/fpga_10g/rtl/fpga_core.v new file mode 100644 index 000000000..a523e3afd --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/rtl/fpga_core.v @@ -0,0 +1,908 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * Clock: 156.25MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + + /* + * 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: QSFP28 + */ + input wire qsfp1_tx_clk_1, + input wire qsfp1_tx_rst_1, + output wire [63:0] qsfp1_txd_1, + output wire [7:0] qsfp1_txc_1, + input wire qsfp1_rx_clk_1, + input wire qsfp1_rx_rst_1, + input wire [63:0] qsfp1_rxd_1, + input wire [7:0] qsfp1_rxc_1, + input wire qsfp1_tx_clk_2, + input wire qsfp1_tx_rst_2, + output wire [63:0] qsfp1_txd_2, + output wire [7:0] qsfp1_txc_2, + input wire qsfp1_rx_clk_2, + input wire qsfp1_rx_rst_2, + input wire [63:0] qsfp1_rxd_2, + input wire [7:0] qsfp1_rxc_2, + input wire qsfp1_tx_clk_3, + input wire qsfp1_tx_rst_3, + output wire [63:0] qsfp1_txd_3, + output wire [7:0] qsfp1_txc_3, + input wire qsfp1_rx_clk_3, + input wire qsfp1_rx_rst_3, + input wire [63:0] qsfp1_rxd_3, + input wire [7:0] qsfp1_rxc_3, + input wire qsfp1_tx_clk_4, + input wire qsfp1_tx_rst_4, + output wire [63:0] qsfp1_txd_4, + output wire [7:0] qsfp1_txc_4, + input wire qsfp1_rx_clk_4, + input wire qsfp1_rx_rst_4, + input wire [63:0] qsfp1_rxd_4, + input wire [7:0] qsfp1_rxc_4, + input wire qsfp2_tx_clk_1, + input wire qsfp2_tx_rst_1, + output wire [63:0] qsfp2_txd_1, + output wire [7:0] qsfp2_txc_1, + input wire qsfp2_rx_clk_1, + input wire qsfp2_rx_rst_1, + input wire [63:0] qsfp2_rxd_1, + input wire [7:0] qsfp2_rxc_1, + input wire qsfp2_tx_clk_2, + input wire qsfp2_tx_rst_2, + output wire [63:0] qsfp2_txd_2, + output wire [7:0] qsfp2_txc_2, + input wire qsfp2_rx_clk_2, + input wire qsfp2_rx_rst_2, + input wire [63:0] qsfp2_rxd_2, + input wire [7:0] qsfp2_rxc_2, + input wire qsfp2_tx_clk_3, + input wire qsfp2_tx_rst_3, + output wire [63:0] qsfp2_txd_3, + output wire [7:0] qsfp2_txc_3, + input wire qsfp2_rx_clk_3, + input wire qsfp2_rx_rst_3, + input wire [63:0] qsfp2_rxd_3, + input wire [7:0] qsfp2_rxc_3, + input wire qsfp2_tx_clk_4, + input wire qsfp2_tx_rst_4, + output wire [63:0] qsfp2_txd_4, + output wire [7:0] qsfp2_txc_4, + input wire qsfp2_rx_clk_4, + input wire qsfp2_rx_rst_4, + input wire [63:0] qsfp2_rxd_4, + input wire [7:0] qsfp2_rxc_4, + + /* + * Ethernet: 1000BASE-T SGMII + */ + input wire phy_gmii_clk, + input wire phy_gmii_rst, + input wire phy_gmii_clk_en, + input wire [7:0] phy_gmii_rxd, + input wire phy_gmii_rx_dv, + input wire phy_gmii_rx_er, + output wire [7:0] phy_gmii_txd, + output wire phy_gmii_tx_en, + output wire phy_gmii_tx_er, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 115200 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// AXI between MAC and Ethernet modules +wire [63:0] mac_rx_axis_tdata; +wire [7:0] mac_rx_axis_tkeep; +wire mac_rx_axis_tvalid; +wire mac_rx_axis_tready; +wire mac_rx_axis_tlast; +wire mac_rx_axis_tuser; + +wire [63:0] mac_tx_axis_tdata; +wire [7:0] mac_tx_axis_tkeep; +wire mac_tx_axis_tvalid; +wire mac_tx_axis_tready; +wire mac_tx_axis_tlast; +wire mac_tx_axis_tuser; + +wire [63:0] rx_axis_tdata; +wire [7:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tready; +wire rx_axis_tlast; +wire rx_axis_tuser; + +wire [63:0] tx_axis_tdata; +wire [7:0] tx_axis_tkeep; +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 [63:0] rx_eth_payload_axis_tdata; +wire [7:0] rx_eth_payload_axis_tkeep; +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 [63:0] tx_eth_payload_axis_tdata; +wire [7:0] tx_eth_payload_axis_tkeep; +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 [63:0] rx_ip_payload_axis_tdata; +wire [7:0] rx_ip_payload_axis_tkeep; +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 [63:0] tx_ip_payload_axis_tdata; +wire [7:0] tx_ip_payload_axis_tkeep; +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 [63:0] rx_udp_payload_axis_tdata; +wire [7:0] rx_udp_payload_axis_tkeep; +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 [63:0] tx_udp_payload_axis_tdata; +wire [7:0] tx_udp_payload_axis_tkeep; +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 [63:0] rx_fifo_udp_payload_axis_tdata; +wire [7:0] rx_fifo_udp_payload_axis_tkeep; +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 [63:0] tx_fifo_udp_payload_axis_tdata; +wire [7:0] tx_fifo_udp_payload_axis_tkeep; +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_tkeep = 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_tkeep = tx_fifo_udp_payload_axis_tkeep; +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_tkeep = rx_udp_payload_axis_tkeep; +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 + valid_last <= tx_udp_payload_axis_tvalid; + if (tx_udp_payload_axis_tvalid && !valid_last) begin + led_reg <= tx_udp_payload_axis_tdata; + end + end +end + +//assign led = sw; +assign led = led_reg; +assign phy_reset_n = !rst; + +assign qsfp1_txd_2 = 64'h0707070707070707; +assign qsfp1_txc_2 = 8'hff; +assign qsfp1_txd_3 = 64'h0707070707070707; +assign qsfp1_txc_3 = 8'hff; +assign qsfp1_txd_4 = 64'h0707070707070707; +assign qsfp1_txc_4 = 8'hff; + +assign qsfp2_txd_1 = 64'h0707070707070707; +assign qsfp2_txc_1 = 8'hff; +assign qsfp2_txd_2 = 64'h0707070707070707; +assign qsfp2_txc_2 = 8'hff; +assign qsfp2_txd_3 = 64'h0707070707070707; +assign qsfp2_txc_3 = 8'hff; +assign qsfp2_txd_4 = 64'h0707070707070707; +assign qsfp2_txc_4 = 8'hff; + +eth_mac_10g_fifo #( + .ENABLE_PADDING(1), + .ENABLE_DIC(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(9), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(9), + .RX_FRAME_FIFO(1) +) +eth_mac_10g_fifo_inst ( + .rx_clk(qsfp1_rx_clk_1), + .rx_rst(qsfp1_rx_rst_1), + .tx_clk(qsfp1_tx_clk_1), + .tx_rst(qsfp1_tx_rst_1), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(mac_tx_axis_tdata), + .tx_axis_tkeep(mac_tx_axis_tkeep), + .tx_axis_tvalid(mac_tx_axis_tvalid), + .tx_axis_tready(mac_tx_axis_tready), + .tx_axis_tlast(mac_tx_axis_tlast), + .tx_axis_tuser(mac_tx_axis_tuser), + + .rx_axis_tdata(mac_rx_axis_tdata), + .rx_axis_tkeep(mac_rx_axis_tkeep), + .rx_axis_tvalid(mac_rx_axis_tvalid), + .rx_axis_tready(mac_rx_axis_tready), + .rx_axis_tlast(mac_rx_axis_tlast), + .rx_axis_tuser(mac_rx_axis_tuser), + + .xgmii_rxd(qsfp1_rxd_1), + .xgmii_rxc(qsfp1_rxc_1), + .xgmii_txd(qsfp1_txd_1), + .xgmii_txc(qsfp1_txc_1), + + .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(), + + .ifg_delay(8'd12) +); + +// 1G interface for debugging +wire [7:0] gig_rx_axis_tdata; +wire gig_rx_axis_tvalid; +wire gig_rx_axis_tready; +wire gig_rx_axis_tlast; +wire gig_rx_axis_tuser; + +wire [7:0] gig_tx_axis_tdata; +wire gig_tx_axis_tvalid; +wire gig_tx_axis_tready; +wire gig_tx_axis_tlast; +wire gig_tx_axis_tuser; + +wire [63:0] gig_rx_axis_tdata_64; +wire [7:0] gig_rx_axis_tkeep_64; +wire gig_rx_axis_tvalid_64; +wire gig_rx_axis_tready_64; +wire gig_rx_axis_tlast_64; +wire gig_rx_axis_tuser_64; + +wire [63:0] gig_tx_axis_tdata_64; +wire [7:0] gig_tx_axis_tkeep_64; +wire gig_tx_axis_tvalid_64; +wire gig_tx_axis_tready_64; +wire gig_tx_axis_tlast_64; +wire gig_tx_axis_tuser_64; + +eth_mac_1g_fifo #( + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_1g_inst ( + .rx_clk(phy_gmii_clk), + .rx_rst(phy_gmii_rst), + .tx_clk(phy_gmii_clk), + .tx_rst(phy_gmii_rst), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(gig_tx_axis_tdata), + .tx_axis_tvalid(gig_tx_axis_tvalid), + .tx_axis_tready(gig_tx_axis_tready), + .tx_axis_tlast(gig_tx_axis_tlast), + .tx_axis_tuser(gig_tx_axis_tuser), + + .rx_axis_tdata(gig_rx_axis_tdata), + .rx_axis_tvalid(gig_rx_axis_tvalid), + .rx_axis_tready(gig_rx_axis_tready), + .rx_axis_tlast(gig_rx_axis_tlast), + .rx_axis_tuser(gig_rx_axis_tuser), + + .gmii_rxd(phy_gmii_rxd), + .gmii_rx_dv(phy_gmii_rx_dv), + .gmii_rx_er(phy_gmii_rx_er), + .gmii_txd(phy_gmii_txd), + .gmii_tx_en(phy_gmii_tx_en), + .gmii_tx_er(phy_gmii_tx_er), + + .rx_clk_enable(phy_gmii_clk_en), + .tx_clk_enable(phy_gmii_clk_en), + .rx_mii_select(1'b0), + .tx_mii_select(1'b0), + + .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(), + + .ifg_delay(12) +); + +axis_adapter #( + .S_DATA_WIDTH(8), + .M_DATA_WIDTH(64), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1) +) +gig_rx_axis_adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(gig_rx_axis_tdata), + .s_axis_tkeep(1'b1), + .s_axis_tvalid(gig_rx_axis_tvalid), + .s_axis_tready(gig_rx_axis_tready), + .s_axis_tlast(gig_rx_axis_tlast), + .s_axis_tuser(gig_rx_axis_tuser), + // AXI output + .m_axis_tdata(gig_rx_axis_tdata_64), + .m_axis_tkeep(gig_rx_axis_tkeep_64), + .m_axis_tvalid(gig_rx_axis_tvalid_64), + .m_axis_tready(gig_rx_axis_tready_64), + .m_axis_tlast(gig_rx_axis_tlast_64), + .m_axis_tuser(gig_rx_axis_tuser_64) +); + +axis_adapter #( + .S_DATA_WIDTH(64), + .M_DATA_WIDTH(8), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1) +) +gig_tx_axis_adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(gig_tx_axis_tdata_64), + .s_axis_tkeep(gig_tx_axis_tkeep_64), + .s_axis_tvalid(gig_tx_axis_tvalid_64), + .s_axis_tready(gig_tx_axis_tready_64), + .s_axis_tlast(gig_tx_axis_tlast_64), + .s_axis_tuser(gig_tx_axis_tuser_64), + // AXI output + .m_axis_tdata(gig_tx_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(gig_tx_axis_tvalid), + .m_axis_tready(gig_tx_axis_tready), + .m_axis_tlast(gig_tx_axis_tlast), + .m_axis_tuser(gig_tx_axis_tuser) +); + +// tap port mux logic +// sw[3] enable +// sw[2] select 0 rx, 1 tx + +reg [1:0] mac_rx_tdest; +reg [1:0] tx_tdest; +reg [1:0] gig_rx_tdest; + +always @* begin + if (sw[3]) begin + if (sw[2]) begin + // Tap on TX path + // MAC RX out -> stack RX in + // stack TX out -> gig TX in + // gig RX out -> MAC TX in + mac_rx_tdest = 2'd1; + tx_tdest = 2'd2; + gig_rx_tdest = 2'd0; + end else begin + // Tap on RX path + // MAC RX out -> gig TX in + // stack TX out -> MAC TX in + // gig RX out -> stack RX in + mac_rx_tdest = 2'd2; + tx_tdest = 2'd0; + gig_rx_tdest = 2'd1; + end + end else begin + // Tap disabled + // MAC RX out -> stack RX in + // stack TX out -> MAC TX in + // gig RX out -> blackhole + mac_rx_tdest = 2'd1; + tx_tdest = 2'd0; + gig_rx_tdest = 2'd3; + end +end + +axis_switch #( + .S_COUNT(3), + .M_COUNT(3), + .DATA_WIDTH(64), + .KEEP_WIDTH(8), + .ID_ENABLE(0), + .DEST_WIDTH(2), + .USER_ENABLE(1), + .USER_WIDTH(1), + .M_BASE({2'd2, 2'd1, 2'd0}), + .M_TOP({2'd2, 2'd1, 2'd0}), + .M_CONNECT({3{3'b111}}), + .S_REG_TYPE(0), + .M_REG_TYPE(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +axis_switch_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ gig_rx_axis_tdata_64, tx_axis_tdata, mac_rx_axis_tdata}), + .s_axis_tkeep({ gig_rx_axis_tkeep_64, tx_axis_tkeep, mac_rx_axis_tkeep}), + .s_axis_tvalid({gig_rx_axis_tvalid_64, tx_axis_tvalid, mac_rx_axis_tvalid}), + .s_axis_tready({gig_rx_axis_tready_64, tx_axis_tready, mac_rx_axis_tready}), + .s_axis_tlast({ gig_rx_axis_tlast_64, tx_axis_tlast, mac_rx_axis_tlast}), + .s_axis_tid(0), + .s_axis_tdest({ gig_rx_tdest, tx_tdest, mac_rx_tdest}), + .s_axis_tuser({ gig_rx_axis_tuser_64, tx_axis_tuser, mac_rx_axis_tuser}), + // AXI outputs + .m_axis_tdata({ gig_tx_axis_tdata_64, rx_axis_tdata, mac_tx_axis_tdata}), + .m_axis_tkeep({ gig_tx_axis_tkeep_64, rx_axis_tkeep, mac_tx_axis_tkeep}), + .m_axis_tvalid({gig_tx_axis_tvalid_64, rx_axis_tvalid, mac_tx_axis_tvalid}), + .m_axis_tready({gig_tx_axis_tready_64, rx_axis_tready, mac_tx_axis_tready}), + .m_axis_tlast({ gig_tx_axis_tlast_64, rx_axis_tlast, mac_tx_axis_tlast}), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser({ gig_tx_axis_tuser_64, rx_axis_tuser, mac_tx_axis_tuser}) +); + +eth_axis_rx_64 +eth_axis_rx_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(rx_axis_tdata), + .s_axis_tkeep(rx_axis_tkeep), + .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_tkeep(rx_eth_payload_axis_tkeep), + .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_64 +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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_axis_tkeep), + .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_64 +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_tkeep(rx_eth_payload_axis_tkeep), + .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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_ip_payload_axis_tkeep), + .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_tkeep(rx_ip_payload_axis_tkeep), + .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_tkeep(tx_udp_payload_axis_tkeep), + .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_tkeep(rx_udp_payload_axis_tkeep), + .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(1'b0) +); + +axis_fifo #( + .ADDR_WIDTH(10), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .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(rx_fifo_udp_payload_axis_tkeep), + .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(tx_fifo_udp_payload_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/fpga/lib/eth/example/VCU118/fpga_10g/rtl/mdio_master.v b/fpga/lib/eth/example/VCU118/fpga_10g/rtl/mdio_master.v new file mode 100644 index 000000000..1dc56a8cf --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/rtl/mdio_master.v @@ -0,0 +1,225 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * MDIO master + */ +module mdio_master ( + input wire clk, + input wire rst, + + /* + * Host interface + */ + input wire [4:0] cmd_phy_addr, + input wire [4:0] cmd_reg_addr, + input wire [15:0] cmd_data, + input wire [1:0] cmd_opcode, + input wire cmd_valid, + output wire cmd_ready, + + output wire [15:0] data_out, + output wire data_out_valid, + input wire data_out_ready, + + /* + * MDIO to PHY + */ + output wire mdc_o, + input wire mdio_i, + output wire mdio_o, + output wire mdio_t, + + /* + * Status + */ + output wire busy, + + /* + * Configuration + */ + input wire [7:0] prescale +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PREAMBLE = 2'd1, + STATE_TRANSFER = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [16:0] count_reg = 16'd0, count_next; +reg [6:0] bit_count_reg = 6'd0, bit_count_next; +reg cycle_reg = 1'b0, cycle_next; + +reg [31:0] data_reg = 32'd0, data_next; + +reg [1:0] op_reg = 2'b00, op_next; + +reg cmd_ready_reg = 1'b0, cmd_ready_next; + +reg [15:0] data_out_reg = 15'd0, data_out_next; +reg data_out_valid_reg = 1'b0, data_out_valid_next; + +reg mdio_i_reg = 1'b1; + +reg mdc_o_reg = 1'b0, mdc_o_next; +reg mdio_o_reg = 1'b0, mdio_o_next; +reg mdio_t_reg = 1'b1, mdio_t_next; + +reg busy_reg = 1'b0; + +assign cmd_ready = cmd_ready_reg; + +assign data_out = data_out_reg; +assign data_out_valid = data_out_valid_reg; + +assign mdc_o = mdc_o_reg; +assign mdio_o = mdio_o_reg; +assign mdio_t = mdio_t_reg; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + count_next = count_reg; + bit_count_next = bit_count_reg; + cycle_next = cycle_reg; + + data_next = data_reg; + + op_next = op_reg; + + cmd_ready_next = 1'b0; + + data_out_next = data_out_reg; + data_out_valid_next = data_out_valid_reg & ~data_out_ready; + + mdc_o_next = mdc_o_reg; + mdio_o_next = mdio_o_reg; + mdio_t_next = mdio_t_reg; + + if (count_reg > 16'd0) begin + count_next = count_reg - 16'd1; + state_next = state_reg; + end else if (cycle_reg) begin + cycle_next = 1'b0; + mdc_o_next = 1'b1; + count_next = prescale; + state_next = state_reg; + end else begin + mdc_o_next = 1'b0; + case (state_reg) + STATE_IDLE: begin + // idle - accept new command + cmd_ready_next = ~data_out_valid; + + if (cmd_ready & cmd_valid) begin + cmd_ready_next = 1'b0; + data_next = {2'b01, cmd_opcode, cmd_phy_addr, cmd_reg_addr, 2'b10, cmd_data}; + op_next = cmd_opcode; + mdio_t_next = 1'b0; + mdio_o_next = 1'b1; + bit_count_next = 6'd32; + cycle_next = 1'b1; + count_next = prescale; + state_next = STATE_PREAMBLE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + cycle_next = 1'b1; + count_next = prescale; + if (bit_count_reg > 6'd1) begin + bit_count_next = bit_count_reg - 6'd1; + state_next = STATE_PREAMBLE; + end else begin + bit_count_next = 6'd32; + {mdio_o_next, data_next} = {data_reg, mdio_i_reg}; + state_next = STATE_TRANSFER; + end + end + STATE_TRANSFER: begin + cycle_next = 1'b1; + count_next = prescale; + if ((op_reg == 2'b10 || op_reg == 2'b11) && bit_count_reg == 6'd19) begin + mdio_t_next = 1'b1; + end + if (bit_count_reg > 6'd1) begin + bit_count_next = bit_count_reg - 6'd1; + {mdio_o_next, data_next} = {data_reg, mdio_i_reg}; + state_next = STATE_TRANSFER; + end else begin + if (op_reg == 2'b10 || op_reg == 2'b11) begin + data_out_next = data_reg[15:0]; + data_out_valid_next = 1'b1; + end + mdio_t_next = 1'b1; + state_next = STATE_IDLE; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + count_reg <= 16'd0; + bit_count_reg <= 6'd0; + cycle_reg <= 1'b0; + cmd_ready_reg <= 1'b0; + data_out_valid_reg <= 1'b0; + mdc_o_reg <= 1'b0; + mdio_o_reg <= 1'b0; + mdio_t_reg <= 1'b1; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + count_reg <= count_next; + bit_count_reg <= bit_count_next; + cycle_reg <= cycle_next; + cmd_ready_reg <= cmd_ready_next; + data_out_valid_reg <= data_out_valid_next; + mdc_o_reg <= mdc_o_next; + mdio_o_reg <= mdio_o_next; + mdio_t_reg <= mdio_t_next; + busy_reg <= (state_next != STATE_IDLE || count_reg != 0 || cycle_reg || mdc_o); + end + + data_reg <= data_next; + op_reg <= op_next; + + data_out_reg <= data_out_next; + + mdio_i_reg <= mdio_i; +end + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/rtl/sync_reset.v b/fpga/lib/eth/example/VCU118/fpga_10g/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/rtl/sync_signal.v b/fpga/lib/eth/example/VCU118/fpga_10g/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/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/fpga/lib/eth/example/VCU118/fpga_10g/tb/arp_ep.py b/fpga/lib/eth/example/VCU118/fpga_10g/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/tb/axis_ep.py b/fpga/lib/eth/example/VCU118/fpga_10g/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/tb/eth_ep.py b/fpga/lib/eth/example/VCU118/fpga_10g/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/tb/gmii_ep.py b/fpga/lib/eth/example/VCU118/fpga_10g/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/tb/ip_ep.py b/fpga/lib/eth/example/VCU118/fpga_10g/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/tb/test_fpga_core.py b/fpga/lib/eth/example/VCU118/fpga_10g/tb/test_fpga_core.py new file mode 100755 index 000000000..00ed0dd0c --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/tb/test_fpga_core.py @@ -0,0 +1,689 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import gmii_ep +import xgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_1g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/eth_mac_10g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_10g.v") +srcs.append("../lib/eth/rtl/axis_xgmii_rx_64.v") +srcs.append("../lib/eth/rtl/axis_xgmii_tx_64.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/eth_axis_rx_64.v") +srcs.append("../lib/eth/rtl/eth_axis_tx_64.v") +srcs.append("../lib/eth/rtl/udp_complete_64.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen_64.v") +srcs.append("../lib/eth/rtl/udp_64.v") +srcs.append("../lib/eth/rtl/udp_ip_rx_64.v") +srcs.append("../lib/eth/rtl/udp_ip_tx_64.v") +srcs.append("../lib/eth/rtl/ip_complete_64.v") +srcs.append("../lib/eth/rtl/ip_64.v") +srcs.append("../lib/eth/rtl/ip_eth_rx_64.v") +srcs.append("../lib/eth/rtl/ip_eth_tx_64.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp_64.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx_64.v") +srcs.append("../lib/eth/rtl/arp_eth_tx_64.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_adapter.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_switch.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_register.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[4:]) + qsfp1_tx_clk_1 = Signal(bool(0)) + qsfp1_tx_rst_1 = Signal(bool(0)) + qsfp1_rx_clk_1 = Signal(bool(0)) + qsfp1_rx_rst_1 = Signal(bool(0)) + qsfp1_rxd_1 = Signal(intbv(0)[64:]) + qsfp1_rxc_1 = Signal(intbv(0)[8:]) + qsfp1_tx_clk_2 = Signal(bool(0)) + qsfp1_tx_rst_2 = Signal(bool(0)) + qsfp1_rx_clk_2 = Signal(bool(0)) + qsfp1_rx_rst_2 = Signal(bool(0)) + qsfp1_rxd_2 = Signal(intbv(0)[64:]) + qsfp1_rxc_2 = Signal(intbv(0)[8:]) + qsfp1_tx_clk_3 = Signal(bool(0)) + qsfp1_tx_rst_3 = Signal(bool(0)) + qsfp1_rx_clk_3 = Signal(bool(0)) + qsfp1_rx_rst_3 = Signal(bool(0)) + qsfp1_rxd_3 = Signal(intbv(0)[64:]) + qsfp1_rxc_3 = Signal(intbv(0)[8:]) + qsfp1_tx_clk_4 = Signal(bool(0)) + qsfp1_tx_rst_4 = Signal(bool(0)) + qsfp1_rx_clk_4 = Signal(bool(0)) + qsfp1_rx_rst_4 = Signal(bool(0)) + qsfp1_rxd_4 = Signal(intbv(0)[64:]) + qsfp1_rxc_4 = Signal(intbv(0)[8:]) + qsfp2_tx_clk_1 = Signal(bool(0)) + qsfp2_tx_rst_1 = Signal(bool(0)) + qsfp2_rx_clk_1 = Signal(bool(0)) + qsfp2_rx_rst_1 = Signal(bool(0)) + qsfp2_rxd_1 = Signal(intbv(0)[64:]) + qsfp2_rxc_1 = Signal(intbv(0)[8:]) + qsfp2_tx_clk_2 = Signal(bool(0)) + qsfp2_tx_rst_2 = Signal(bool(0)) + qsfp2_rx_clk_2 = Signal(bool(0)) + qsfp2_rx_rst_2 = Signal(bool(0)) + qsfp2_rxd_2 = Signal(intbv(0)[64:]) + qsfp2_rxc_2 = Signal(intbv(0)[8:]) + qsfp2_tx_clk_3 = Signal(bool(0)) + qsfp2_tx_rst_3 = Signal(bool(0)) + qsfp2_rx_clk_3 = Signal(bool(0)) + qsfp2_rx_rst_3 = Signal(bool(0)) + qsfp2_rxd_3 = Signal(intbv(0)[64:]) + qsfp2_rxc_3 = Signal(intbv(0)[8:]) + qsfp2_tx_clk_4 = Signal(bool(0)) + qsfp2_tx_rst_4 = Signal(bool(0)) + qsfp2_rx_clk_4 = Signal(bool(0)) + qsfp2_rx_rst_4 = Signal(bool(0)) + qsfp2_rxd_4 = Signal(intbv(0)[64:]) + qsfp2_rxc_4 = Signal(intbv(0)[8:]) + phy_gmii_clk = Signal(bool(0)) + phy_gmii_rst = Signal(bool(0)) + phy_gmii_clk_en = Signal(bool(0)) + phy_gmii_rxd = Signal(intbv(0)[8:]) + phy_gmii_rx_dv = Signal(bool(0)) + phy_gmii_rx_er = Signal(bool(0)) + phy_int_n = Signal(bool(1)) + uart_rxd = Signal(bool(0)) + uart_cts = Signal(bool(0)) + + # Outputs + led = Signal(intbv(0)[8:]) + qsfp1_txd_1 = Signal(intbv(0)[64:]) + qsfp1_txc_1 = Signal(intbv(0)[8:]) + qsfp1_txd_2 = Signal(intbv(0)[64:]) + qsfp1_txc_2 = Signal(intbv(0)[8:]) + qsfp1_txd_3 = Signal(intbv(0)[64:]) + qsfp1_txc_3 = Signal(intbv(0)[8:]) + qsfp1_txd_4 = Signal(intbv(0)[64:]) + qsfp1_txc_4 = Signal(intbv(0)[8:]) + qsfp2_txd_1 = Signal(intbv(0)[64:]) + qsfp2_txc_1 = Signal(intbv(0)[8:]) + qsfp2_txd_2 = Signal(intbv(0)[64:]) + qsfp2_txc_2 = Signal(intbv(0)[8:]) + qsfp2_txd_3 = Signal(intbv(0)[64:]) + qsfp2_txc_3 = Signal(intbv(0)[8:]) + qsfp2_txd_4 = Signal(intbv(0)[64:]) + qsfp2_txc_4 = Signal(intbv(0)[8:]) + phy_gmii_txd = Signal(intbv(0)[8:]) + phy_gmii_tx_en = Signal(bool(0)) + phy_gmii_tx_er = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + + # sources and sinks + qsfp1_1_source = xgmii_ep.XGMIISource() + qsfp1_1_source_logic = qsfp1_1_source.create_logic(qsfp1_rx_clk_1, qsfp1_rx_rst_1, txd=qsfp1_rxd_1, txc=qsfp1_rxc_1, name='qsfp1_1_source') + + qsfp1_1_sink = xgmii_ep.XGMIISink() + qsfp1_1_sink_logic = qsfp1_1_sink.create_logic(qsfp1_tx_clk_1, qsfp1_tx_rst_1, rxd=qsfp1_txd_1, rxc=qsfp1_txc_1, name='qsfp1_1_sink') + + qsfp1_2_source = xgmii_ep.XGMIISource() + qsfp1_2_source_logic = qsfp1_2_source.create_logic(qsfp1_rx_clk_2, qsfp1_rx_rst_2, txd=qsfp1_rxd_2, txc=qsfp1_rxc_2, name='qsfp1_2_source') + + qsfp1_2_sink = xgmii_ep.XGMIISink() + qsfp1_2_sink_logic = qsfp1_2_sink.create_logic(qsfp1_tx_clk_2, qsfp1_tx_rst_2, rxd=qsfp1_txd_2, rxc=qsfp1_txc_2, name='qsfp1_2_sink') + + qsfp1_3_source = xgmii_ep.XGMIISource() + qsfp1_3_source_logic = qsfp1_3_source.create_logic(qsfp1_rx_clk_3, qsfp1_rx_rst_3, txd=qsfp1_rxd_3, txc=qsfp1_rxc_3, name='qsfp1_3_source') + + qsfp1_3_sink = xgmii_ep.XGMIISink() + qsfp1_3_sink_logic = qsfp1_3_sink.create_logic(qsfp1_tx_clk_3, qsfp1_tx_rst_3, rxd=qsfp1_txd_3, rxc=qsfp1_txc_3, name='qsfp1_3_sink') + + qsfp1_4_source = xgmii_ep.XGMIISource() + qsfp1_4_source_logic = qsfp1_4_source.create_logic(qsfp1_rx_clk_4, qsfp1_rx_rst_4, txd=qsfp1_rxd_4, txc=qsfp1_rxc_4, name='qsfp1_4_source') + + qsfp1_4_sink = xgmii_ep.XGMIISink() + qsfp1_4_sink_logic = qsfp1_4_sink.create_logic(qsfp1_tx_clk_4, qsfp1_tx_rst_4, rxd=qsfp1_txd_4, rxc=qsfp1_txc_4, name='qsfp1_4_sink') + + qsfp2_1_source = xgmii_ep.XGMIISource() + qsfp2_1_source_logic = qsfp2_1_source.create_logic(qsfp2_rx_clk_1, qsfp2_rx_rst_1, txd=qsfp2_rxd_1, txc=qsfp2_rxc_1, name='qsfp2_1_source') + + qsfp2_1_sink = xgmii_ep.XGMIISink() + qsfp2_1_sink_logic = qsfp2_1_sink.create_logic(qsfp2_tx_clk_1, qsfp2_tx_rst_1, rxd=qsfp2_txd_1, rxc=qsfp2_txc_1, name='qsfp2_1_sink') + + qsfp2_2_source = xgmii_ep.XGMIISource() + qsfp2_2_source_logic = qsfp2_2_source.create_logic(qsfp2_rx_clk_2, qsfp2_rx_rst_2, txd=qsfp2_rxd_2, txc=qsfp2_rxc_2, name='qsfp2_2_source') + + qsfp2_2_sink = xgmii_ep.XGMIISink() + qsfp2_2_sink_logic = qsfp2_2_sink.create_logic(qsfp2_tx_clk_2, qsfp2_tx_rst_2, rxd=qsfp2_txd_2, rxc=qsfp2_txc_2, name='qsfp2_2_sink') + + qsfp2_3_source = xgmii_ep.XGMIISource() + qsfp2_3_source_logic = qsfp2_3_source.create_logic(qsfp2_rx_clk_3, qsfp2_rx_rst_3, txd=qsfp2_rxd_3, txc=qsfp2_rxc_3, name='qsfp2_3_source') + + qsfp2_3_sink = xgmii_ep.XGMIISink() + qsfp2_3_sink_logic = qsfp2_3_sink.create_logic(qsfp2_tx_clk_3, qsfp2_tx_rst_3, rxd=qsfp2_txd_3, rxc=qsfp2_txc_3, name='qsfp2_3_sink') + + qsfp2_4_source = xgmii_ep.XGMIISource() + qsfp2_4_source_logic = qsfp2_4_source.create_logic(qsfp2_rx_clk_4, qsfp2_rx_rst_4, txd=qsfp2_rxd_4, txc=qsfp2_rxc_4, name='qsfp2_4_source') + + qsfp2_4_sink = xgmii_ep.XGMIISink() + qsfp2_4_sink_logic = qsfp2_4_sink.create_logic(qsfp2_tx_clk_4, qsfp2_tx_rst_4, rxd=qsfp2_txd_4, rxc=qsfp2_txc_4, name='qsfp2_4_sink') + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + phy_gmii_clk, + phy_gmii_rst, + txd=phy_gmii_rxd, + tx_en=phy_gmii_rx_dv, + tx_er=phy_gmii_rx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + phy_gmii_clk, + phy_gmii_rst, + rxd=phy_gmii_txd, + rx_dv=phy_gmii_tx_en, + rx_er=phy_gmii_tx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + led=led, + + qsfp1_tx_clk_1=qsfp1_tx_clk_1, + qsfp1_tx_rst_1=qsfp1_tx_rst_1, + qsfp1_txd_1=qsfp1_txd_1, + qsfp1_txc_1=qsfp1_txc_1, + qsfp1_rx_clk_1=qsfp1_rx_clk_1, + qsfp1_rx_rst_1=qsfp1_rx_rst_1, + qsfp1_rxd_1=qsfp1_rxd_1, + qsfp1_rxc_1=qsfp1_rxc_1, + qsfp1_tx_clk_2=qsfp1_tx_clk_2, + qsfp1_tx_rst_2=qsfp1_tx_rst_2, + qsfp1_txd_2=qsfp1_txd_2, + qsfp1_txc_2=qsfp1_txc_2, + qsfp1_rx_clk_2=qsfp1_rx_clk_2, + qsfp1_rx_rst_2=qsfp1_rx_rst_2, + qsfp1_rxd_2=qsfp1_rxd_2, + qsfp1_rxc_2=qsfp1_rxc_2, + qsfp1_tx_clk_3=qsfp1_tx_clk_3, + qsfp1_tx_rst_3=qsfp1_tx_rst_3, + qsfp1_txd_3=qsfp1_txd_3, + qsfp1_txc_3=qsfp1_txc_3, + qsfp1_rx_clk_3=qsfp1_rx_clk_3, + qsfp1_rx_rst_3=qsfp1_rx_rst_3, + qsfp1_rxd_3=qsfp1_rxd_3, + qsfp1_rxc_3=qsfp1_rxc_3, + qsfp1_tx_clk_4=qsfp1_tx_clk_4, + qsfp1_tx_rst_4=qsfp1_tx_rst_4, + qsfp1_txd_4=qsfp1_txd_4, + qsfp1_txc_4=qsfp1_txc_4, + qsfp1_rx_clk_4=qsfp1_rx_clk_4, + qsfp1_rx_rst_4=qsfp1_rx_rst_4, + qsfp1_rxd_4=qsfp1_rxd_4, + qsfp1_rxc_4=qsfp1_rxc_4, + qsfp2_tx_clk_1=qsfp2_tx_clk_1, + qsfp2_tx_rst_1=qsfp2_tx_rst_1, + qsfp2_txd_1=qsfp2_txd_1, + qsfp2_txc_1=qsfp2_txc_1, + qsfp2_rx_clk_1=qsfp2_rx_clk_1, + qsfp2_rx_rst_1=qsfp2_rx_rst_1, + qsfp2_rxd_1=qsfp2_rxd_1, + qsfp2_rxc_1=qsfp2_rxc_1, + qsfp2_tx_clk_2=qsfp2_tx_clk_2, + qsfp2_tx_rst_2=qsfp2_tx_rst_2, + qsfp2_txd_2=qsfp2_txd_2, + qsfp2_txc_2=qsfp2_txc_2, + qsfp2_rx_clk_2=qsfp2_rx_clk_2, + qsfp2_rx_rst_2=qsfp2_rx_rst_2, + qsfp2_rxd_2=qsfp2_rxd_2, + qsfp2_rxc_2=qsfp2_rxc_2, + qsfp2_tx_clk_3=qsfp2_tx_clk_3, + qsfp2_tx_rst_3=qsfp2_tx_rst_3, + qsfp2_txd_3=qsfp2_txd_3, + qsfp2_txc_3=qsfp2_txc_3, + qsfp2_rx_clk_3=qsfp2_rx_clk_3, + qsfp2_rx_rst_3=qsfp2_rx_rst_3, + qsfp2_rxd_3=qsfp2_rxd_3, + qsfp2_rxc_3=qsfp2_rxc_3, + qsfp2_tx_clk_4=qsfp2_tx_clk_4, + qsfp2_tx_rst_4=qsfp2_tx_rst_4, + qsfp2_txd_4=qsfp2_txd_4, + qsfp2_txc_4=qsfp2_txc_4, + qsfp2_rx_clk_4=qsfp2_rx_clk_4, + qsfp2_rx_rst_4=qsfp2_rx_rst_4, + qsfp2_rxd_4=qsfp2_rxd_4, + qsfp2_rxc_4=qsfp2_rxc_4, + + phy_gmii_clk=phy_gmii_clk, + phy_gmii_rst=phy_gmii_rst, + phy_gmii_clk_en=phy_gmii_clk_en, + phy_gmii_rxd=phy_gmii_rxd, + phy_gmii_rx_dv=phy_gmii_rx_dv, + phy_gmii_rx_er=phy_gmii_rx_er, + phy_gmii_txd=phy_gmii_txd, + phy_gmii_tx_en=phy_gmii_tx_en, + phy_gmii_tx_er=phy_gmii_tx_er, + phy_reset_n=phy_reset_n, + phy_int_n=phy_int_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd, + uart_rts=uart_rts, + uart_cts=uart_cts + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + qsfp1_tx_clk_1.next = not qsfp1_tx_clk_1 + qsfp1_rx_clk_1.next = not qsfp1_rx_clk_1 + qsfp1_tx_clk_2.next = not qsfp1_tx_clk_2 + qsfp1_rx_clk_2.next = not qsfp1_rx_clk_2 + qsfp1_tx_clk_3.next = not qsfp1_tx_clk_3 + qsfp1_rx_clk_3.next = not qsfp1_rx_clk_3 + qsfp1_tx_clk_4.next = not qsfp1_tx_clk_4 + qsfp1_rx_clk_4.next = not qsfp1_rx_clk_4 + qsfp2_tx_clk_1.next = not qsfp2_tx_clk_1 + qsfp2_rx_clk_1.next = not qsfp2_rx_clk_1 + qsfp2_tx_clk_2.next = not qsfp2_tx_clk_2 + qsfp2_rx_clk_2.next = not qsfp2_rx_clk_2 + qsfp2_tx_clk_3.next = not qsfp2_tx_clk_3 + qsfp2_rx_clk_3.next = not qsfp2_rx_clk_3 + qsfp2_tx_clk_4.next = not qsfp2_tx_clk_4 + qsfp2_rx_clk_4.next = not qsfp2_rx_clk_4 + phy_gmii_clk.next = not phy_gmii_clk + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + phy_gmii_clk_en.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + phy_gmii_clk_en.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + qsfp1_tx_rst_1.next = 1 + qsfp1_rx_rst_1.next = 1 + qsfp1_tx_rst_2.next = 1 + qsfp1_rx_rst_2.next = 1 + qsfp1_tx_rst_3.next = 1 + qsfp1_rx_rst_3.next = 1 + qsfp1_tx_rst_4.next = 1 + qsfp1_rx_rst_4.next = 1 + qsfp2_tx_rst_1.next = 1 + qsfp2_rx_rst_1.next = 1 + qsfp2_tx_rst_2.next = 1 + qsfp2_rx_rst_2.next = 1 + qsfp2_tx_rst_3.next = 1 + qsfp2_rx_rst_3.next = 1 + qsfp2_tx_rst_4.next = 1 + qsfp2_rx_rst_4.next = 1 + phy_gmii_rst.next = 1 + yield clk.posedge + rst.next = 0 + qsfp1_tx_rst_1.next = 0 + qsfp1_rx_rst_1.next = 0 + qsfp1_tx_rst_2.next = 0 + qsfp1_rx_rst_2.next = 0 + qsfp1_tx_rst_3.next = 0 + qsfp1_rx_rst_3.next = 0 + qsfp1_tx_rst_4.next = 0 + qsfp1_rx_rst_4.next = 0 + qsfp2_tx_rst_1.next = 0 + qsfp2_rx_rst_1.next = 0 + qsfp2_tx_rst_2.next = 0 + qsfp2_rx_rst_2.next = 0 + qsfp2_tx_rst_3.next = 0 + qsfp2_rx_rst_3.next = 0 + qsfp2_tx_rst_4.next = 0 + qsfp2_rx_rst_4.next = 0 + phy_gmii_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + qsfp1_1_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while qsfp1_1_sink.empty(): + yield clk.posedge + + rx_frame = qsfp1_1_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + qsfp1_1_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while qsfp1_1_sink.empty(): + yield clk.posedge + + rx_frame = qsfp1_1_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert qsfp1_1_source.empty() + assert qsfp1_1_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test gigabit tap") + current_test.next = 2 + + sw.next = 0x8 # enable tap on RX + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # loop packet back through on XGMII interface + while qsfp1_1_sink.empty(): + yield clk.posedge + + qsfp1_1_source.send(qsfp1_1_sink.recv()) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + assert qsfp1_1_source.empty() + assert qsfp1_1_sink.empty() + + yield delay(100) + + sw.next = 0xc # enable tap on TX + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # loop packet back through on XGMII interface + while qsfp1_1_sink.empty(): + yield clk.posedge + + qsfp1_1_source.send(qsfp1_1_sink.recv()) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + assert qsfp1_1_source.empty() + assert qsfp1_1_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/tb/test_fpga_core.v b/fpga/lib/eth/example/VCU118/fpga_10g/tb/test_fpga_core.v new file mode 100644 index 000000000..24db5dc0c --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/tb/test_fpga_core.v @@ -0,0 +1,324 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [3:0] sw = 0; +reg qsfp1_tx_clk_1 = 0; +reg qsfp1_tx_rst_1 = 0; +reg qsfp1_rx_clk_1 = 0; +reg qsfp1_rx_rst_1 = 0; +reg [63:0] qsfp1_rxd_1 = 0; +reg [7:0] qsfp1_rxc_1 = 0; +reg qsfp1_tx_clk_2 = 0; +reg qsfp1_tx_rst_2 = 0; +reg qsfp1_rx_clk_2 = 0; +reg qsfp1_rx_rst_2 = 0; +reg [63:0] qsfp1_rxd_2 = 0; +reg [7:0] qsfp1_rxc_2 = 0; +reg qsfp1_tx_clk_3 = 0; +reg qsfp1_tx_rst_3 = 0; +reg qsfp1_rx_clk_3 = 0; +reg qsfp1_rx_rst_3 = 0; +reg [63:0] qsfp1_rxd_3 = 0; +reg [7:0] qsfp1_rxc_3 = 0; +reg qsfp1_tx_clk_4 = 0; +reg qsfp1_tx_rst_4 = 0; +reg qsfp1_rx_clk_4 = 0; +reg qsfp1_rx_rst_4 = 0; +reg [63:0] qsfp1_rxd_4 = 0; +reg [7:0] qsfp1_rxc_4 = 0; +reg qsfp2_tx_clk_1 = 0; +reg qsfp2_tx_rst_1 = 0; +reg qsfp2_rx_clk_1 = 0; +reg qsfp2_rx_rst_1 = 0; +reg [63:0] qsfp2_rxd_1 = 0; +reg [7:0] qsfp2_rxc_1 = 0; +reg qsfp2_tx_clk_2 = 0; +reg qsfp2_tx_rst_2 = 0; +reg qsfp2_rx_clk_2 = 0; +reg qsfp2_rx_rst_2 = 0; +reg [63:0] qsfp2_rxd_2 = 0; +reg [7:0] qsfp2_rxc_2 = 0; +reg qsfp2_tx_clk_3 = 0; +reg qsfp2_tx_rst_3 = 0; +reg qsfp2_rx_clk_3 = 0; +reg qsfp2_rx_rst_3 = 0; +reg [63:0] qsfp2_rxd_3 = 0; +reg [7:0] qsfp2_rxc_3 = 0; +reg qsfp2_tx_clk_4 = 0; +reg qsfp2_tx_rst_4 = 0; +reg qsfp2_rx_clk_4 = 0; +reg qsfp2_rx_rst_4 = 0; +reg [63:0] qsfp2_rxd_4 = 0; +reg [7:0] qsfp2_rxc_4 = 0; +reg phy_gmii_clk = 0; +reg phy_gmii_rst = 0; +reg phy_gmii_clk_en = 0; +reg [7:0] phy_gmii_rxd = 0; +reg phy_gmii_rx_dv = 0; +reg phy_gmii_rx_er = 0; +reg phy_int_n = 1; +reg uart_rxd = 0; +reg uart_cts = 0; + +// Outputs +wire [7:0] led; +wire [63:0] qsfp1_txd_1; +wire [7:0] qsfp1_txc_1; +wire [63:0] qsfp1_txd_2; +wire [7:0] qsfp1_txc_2; +wire [63:0] qsfp1_txd_3; +wire [7:0] qsfp1_txc_3; +wire [63:0] qsfp1_txd_4; +wire [7:0] qsfp1_txc_4; +wire [63:0] qsfp2_txd_1; +wire [7:0] qsfp2_txc_1; +wire [63:0] qsfp2_txd_2; +wire [7:0] qsfp2_txc_2; +wire [63:0] qsfp2_txd_3; +wire [7:0] qsfp2_txc_3; +wire [63:0] qsfp2_txd_4; +wire [7:0] qsfp2_txc_4; +wire phy_tx_clk; +wire [7:0] phy_gmii_txd; +wire phy_gmii_tx_en; +wire phy_gmii_tx_er; +wire phy_reset_n; +wire uart_txd; +wire uart_rts; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + qsfp1_tx_clk_1, + qsfp1_tx_rst_1, + qsfp1_rx_clk_1, + qsfp1_rx_rst_1, + qsfp1_rxd_1, + qsfp1_rxc_1, + qsfp1_tx_clk_2, + qsfp1_tx_rst_2, + qsfp1_rx_clk_2, + qsfp1_rx_rst_2, + qsfp1_rxd_2, + qsfp1_rxc_2, + qsfp1_tx_clk_3, + qsfp1_tx_rst_3, + qsfp1_rx_clk_3, + qsfp1_rx_rst_3, + qsfp1_rxd_3, + qsfp1_rxc_3, + qsfp1_tx_clk_4, + qsfp1_tx_rst_4, + qsfp1_rx_clk_4, + qsfp1_rx_rst_4, + qsfp1_rxd_4, + qsfp1_rxc_4, + qsfp2_tx_clk_1, + qsfp2_tx_rst_1, + qsfp2_rx_clk_1, + qsfp2_rx_rst_1, + qsfp2_rxd_1, + qsfp2_rxc_1, + qsfp2_tx_clk_2, + qsfp2_tx_rst_2, + qsfp2_rx_clk_2, + qsfp2_rx_rst_2, + qsfp2_rxd_2, + qsfp2_rxc_2, + qsfp2_tx_clk_3, + qsfp2_tx_rst_3, + qsfp2_rx_clk_3, + qsfp2_rx_rst_3, + qsfp2_rxd_3, + qsfp2_rxc_3, + qsfp2_tx_clk_4, + qsfp2_tx_rst_4, + qsfp2_rx_clk_4, + qsfp2_rx_rst_4, + qsfp2_rxd_4, + qsfp2_rxc_4, + phy_gmii_clk, + phy_gmii_rst, + phy_gmii_clk_en, + phy_gmii_rxd, + phy_gmii_rx_dv, + phy_gmii_rx_er, + phy_int_n, + uart_rxd, + uart_cts + ); + $to_myhdl( + led, + qsfp1_txd_1, + qsfp1_txc_1, + qsfp1_txd_2, + qsfp1_txc_2, + qsfp1_txd_3, + qsfp1_txc_3, + qsfp1_txd_4, + qsfp1_txc_4, + qsfp2_txd_1, + qsfp2_txc_1, + qsfp2_txd_2, + qsfp2_txc_2, + qsfp2_txd_3, + qsfp2_txc_3, + qsfp2_txd_4, + qsfp2_txc_4, + phy_gmii_txd, + phy_gmii_tx_en, + phy_gmii_tx_er, + phy_reset_n, + uart_txd, + uart_rts + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .led(led), + .qsfp1_tx_clk_1(qsfp1_tx_clk_1), + .qsfp1_tx_rst_1(qsfp1_tx_rst_1), + .qsfp1_txd_1(qsfp1_txd_1), + .qsfp1_txc_1(qsfp1_txc_1), + .qsfp1_rx_clk_1(qsfp1_rx_clk_1), + .qsfp1_rx_rst_1(qsfp1_rx_rst_1), + .qsfp1_rxd_1(qsfp1_rxd_1), + .qsfp1_rxc_1(qsfp1_rxc_1), + .qsfp1_tx_clk_2(qsfp1_tx_clk_2), + .qsfp1_tx_rst_2(qsfp1_tx_rst_2), + .qsfp1_txd_2(qsfp1_txd_2), + .qsfp1_txc_2(qsfp1_txc_2), + .qsfp1_rx_clk_2(qsfp1_rx_clk_2), + .qsfp1_rx_rst_2(qsfp1_rx_rst_2), + .qsfp1_rxd_2(qsfp1_rxd_2), + .qsfp1_rxc_2(qsfp1_rxc_2), + .qsfp1_tx_clk_3(qsfp1_tx_clk_3), + .qsfp1_tx_rst_3(qsfp1_tx_rst_3), + .qsfp1_txd_3(qsfp1_txd_3), + .qsfp1_txc_3(qsfp1_txc_3), + .qsfp1_rx_clk_3(qsfp1_rx_clk_3), + .qsfp1_rx_rst_3(qsfp1_rx_rst_3), + .qsfp1_rxd_3(qsfp1_rxd_3), + .qsfp1_rxc_3(qsfp1_rxc_3), + .qsfp1_tx_clk_4(qsfp1_tx_clk_4), + .qsfp1_tx_rst_4(qsfp1_tx_rst_4), + .qsfp1_txd_4(qsfp1_txd_4), + .qsfp1_txc_4(qsfp1_txc_4), + .qsfp1_rx_clk_4(qsfp1_rx_clk_4), + .qsfp1_rx_rst_4(qsfp1_rx_rst_4), + .qsfp1_rxd_4(qsfp1_rxd_4), + .qsfp1_rxc_4(qsfp1_rxc_4), + .qsfp2_tx_clk_1(qsfp2_tx_clk_1), + .qsfp2_tx_rst_1(qsfp2_tx_rst_1), + .qsfp2_txd_1(qsfp2_txd_1), + .qsfp2_txc_1(qsfp2_txc_1), + .qsfp2_rx_clk_1(qsfp2_rx_clk_1), + .qsfp2_rx_rst_1(qsfp2_rx_rst_1), + .qsfp2_rxd_1(qsfp2_rxd_1), + .qsfp2_rxc_1(qsfp2_rxc_1), + .qsfp2_tx_clk_2(qsfp2_tx_clk_2), + .qsfp2_tx_rst_2(qsfp2_tx_rst_2), + .qsfp2_txd_2(qsfp2_txd_2), + .qsfp2_txc_2(qsfp2_txc_2), + .qsfp2_rx_clk_2(qsfp2_rx_clk_2), + .qsfp2_rx_rst_2(qsfp2_rx_rst_2), + .qsfp2_rxd_2(qsfp2_rxd_2), + .qsfp2_rxc_2(qsfp2_rxc_2), + .qsfp2_tx_clk_3(qsfp2_tx_clk_3), + .qsfp2_tx_rst_3(qsfp2_tx_rst_3), + .qsfp2_txd_3(qsfp2_txd_3), + .qsfp2_txc_3(qsfp2_txc_3), + .qsfp2_rx_clk_3(qsfp2_rx_clk_3), + .qsfp2_rx_rst_3(qsfp2_rx_rst_3), + .qsfp2_rxd_3(qsfp2_rxd_3), + .qsfp2_rxc_3(qsfp2_rxc_3), + .qsfp2_tx_clk_4(qsfp2_tx_clk_4), + .qsfp2_tx_rst_4(qsfp2_tx_rst_4), + .qsfp2_txd_4(qsfp2_txd_4), + .qsfp2_txc_4(qsfp2_txc_4), + .qsfp2_rx_clk_4(qsfp2_rx_clk_4), + .qsfp2_rx_rst_4(qsfp2_rx_rst_4), + .qsfp2_rxd_4(qsfp2_rxd_4), + .qsfp2_rxc_4(qsfp2_rxc_4), + .phy_gmii_clk(phy_gmii_clk), + .phy_gmii_rst(phy_gmii_rst), + .phy_gmii_clk_en(phy_gmii_clk_en), + .phy_gmii_rxd(phy_gmii_rxd), + .phy_gmii_rx_dv(phy_gmii_rx_dv), + .phy_gmii_rx_er(phy_gmii_rx_er), + .phy_gmii_txd(phy_gmii_txd), + .phy_gmii_tx_en(phy_gmii_tx_en), + .phy_gmii_tx_er(phy_gmii_tx_er), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts) +); + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/tb/udp_ep.py b/fpga/lib/eth/example/VCU118/fpga_10g/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_10g/tb/xgmii_ep.py b/fpga/lib/eth/example/VCU118/fpga_10g/tb/xgmii_ep.py new file mode 120000 index 000000000..63b6d3567 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_10g/tb/xgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/xgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/Makefile b/fpga/lib/eth/example/VCU118/fpga_1g/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/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/fpga/lib/eth/example/VCU118/fpga_1g/README.md b/fpga/lib/eth/example/VCU118/fpga_1g/README.md new file mode 100644 index 000000000..66172f75f --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/README.md @@ -0,0 +1,25 @@ +# Verilog Ethernet VCU118 Example Design + +## Introduction + +This example design targets the Xilinx VCU118 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: xcvu9p-flga2104-2L-e +PHY: TI DP83867ISRGZ + +## 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 VCU108 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. + + diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/common/vivado.mk b/fpga/lib/eth/example/VCU118/fpga_1g/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/VCU118/fpga_1g/fpga.xdc b/fpga/lib/eth/example/VCU118/fpga_1g/fpga.xdc new file mode 100644 index 000000000..65c32a917 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/fpga.xdc @@ -0,0 +1,150 @@ +# XDC constraints for the Xilinx VCU118 board +# part: xcvu9p-flga2104-2L-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] + +# System clocks +# 300 MHz +#set_property -dict {LOC G31 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_p] +#set_property -dict {LOC F31 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_n] +#create_clock -period 3.333 -name clk_300mhz [get_ports clk_300mhz_p] + +# 250 MHz +#set_property -dict {LOC E12 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_1_p] +#set_property -dict {LOC D12 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_1_n] +#create_clock -period 4 -name clk_250mhz_1 [get_ports clk_250mhz_1_p] + +#set_property -dict {LOC AW26 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_2_p] +#set_property -dict {LOC AW27 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_2_n] +#create_clock -period 4 -name clk_250mhz_2 [get_ports clk_250mhz_2_p] + +# 125 MHz +set_property -dict {LOC AY24 IOSTANDARD LVDS} [get_ports clk_125mhz_p] +set_property -dict {LOC AY23 IOSTANDARD LVDS} [get_ports clk_125mhz_n] +create_clock -period 8.000 -name clk_125mhz [get_ports clk_125mhz_p] + +# 90 MHz +#set_property -dict {LOC AL20 IOSTANDARD LVCMOS18} [get_ports clk_90mhz] +#create_clock -period 11.111 -name clk_90mhz [get_ports clk_90mhz] + +# LEDs +set_property -dict {LOC AT32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[0]}] +set_property -dict {LOC AV34 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[1]}] +set_property -dict {LOC AY30 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[2]}] +set_property -dict {LOC BB32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[3]}] +set_property -dict {LOC BF32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[4]}] +set_property -dict {LOC AU37 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[5]}] +set_property -dict {LOC AV36 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[6]}] +set_property -dict {LOC BA37 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[7]}] + +# Reset button +set_property -dict {LOC L19 IOSTANDARD LVCMOS12} [get_ports reset] + +# Push buttons +set_property -dict {LOC BB24 IOSTANDARD LVCMOS18} [get_ports btnu] +set_property -dict {LOC BF22 IOSTANDARD LVCMOS18} [get_ports btnl] +set_property -dict {LOC BE22 IOSTANDARD LVCMOS18} [get_ports btnd] +set_property -dict {LOC BE23 IOSTANDARD LVCMOS18} [get_ports btnr] +set_property -dict {LOC BD23 IOSTANDARD LVCMOS18} [get_ports btnc] + +# DIP switches +set_property -dict {LOC B17 IOSTANDARD LVCMOS12} [get_ports {sw[0]}] +set_property -dict {LOC G16 IOSTANDARD LVCMOS12} [get_ports {sw[1]}] +set_property -dict {LOC J16 IOSTANDARD LVCMOS12} [get_ports {sw[2]}] +set_property -dict {LOC D21 IOSTANDARD LVCMOS12} [get_ports {sw[3]}] + +# UART +set_property -dict {LOC BB21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_txd] +set_property -dict {LOC AW25 IOSTANDARD LVCMOS18} [get_ports uart_rxd] +set_property -dict {LOC BB22 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_rts] +set_property -dict {LOC AY25 IOSTANDARD LVCMOS18} [get_ports uart_cts] + +# Gigabit Ethernet SGMII PHY +set_property -dict {LOC AU24 IOSTANDARD LVDS} [get_ports phy_sgmii_rx_p] +set_property -dict {LOC AV24 IOSTANDARD LVDS} [get_ports phy_sgmii_rx_n] +set_property -dict {LOC AU21 IOSTANDARD LVDS} [get_ports phy_sgmii_tx_p] +set_property -dict {LOC AV21 IOSTANDARD LVDS} [get_ports phy_sgmii_tx_n] +set_property -dict {LOC AT22 IOSTANDARD LVDS} [get_ports phy_sgmii_clk_p] +set_property -dict {LOC AU22 IOSTANDARD LVDS} [get_ports phy_sgmii_clk_n] +set_property -dict {LOC BA21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_reset_n] +set_property -dict {LOC AR24 IOSTANDARD LVCMOS18} [get_ports phy_int_n] +set_property -dict {LOC AR23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_mdio] +set_property -dict {LOC AV23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_mdc] + +# 625 MHz ref clock from SGMII PHY +#create_clock -period 1.600 -name phy_sgmii_clk [get_ports phy_sgmii_clk_p] + +# QSFP28 Interfaces +#set_property -dict {LOC V7 } [get_ports qsfp1_tx1_p] ;# MGTYTXN0_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC V6 } [get_ports qsfp1_tx1_n] ;# MGTYTXP0_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC Y2 } [get_ports qsfp1_rx1_p] ;# MGTYTXN1_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC Y1 } [get_ports qsfp1_rx1_n] ;# MGTYTXP1_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC T7 } [get_ports qsfp1_tx2_p] ;# MGTYTXN2_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC T6 } [get_ports qsfp1_tx2_n] ;# MGTYTXP2_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC W4 } [get_ports qsfp1_rx2_p] ;# MGTYTXN3_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC W3 } [get_ports qsfp1_rx2_n] ;# MGTYTXP3_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC P7 } [get_ports qsfp1_tx3_p] ;# MGTYTXN0_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC P6 } [get_ports qsfp1_tx3_n] ;# MGTYTXP0_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC V2 } [get_ports qsfp1_rx3_p] ;# MGTYTXN1_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC V1 } [get_ports qsfp1_rx3_n] ;# MGTYTXP1_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC M7 } [get_ports qsfp1_tx4_p] ;# MGTYTXN2_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC M6 } [get_ports qsfp1_tx4_n] ;# MGTYTXP2_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC U4 } [get_ports qsfp1_rx4_p] ;# MGTYTXN3_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC U3 } [get_ports qsfp1_rx4_n] ;# MGTYTXP3_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC W9 } [get_ports qsfp1_mgt_refclk_0_p] ;# MGTREFCLK0P_231 from U38.4 +#set_property -dict {LOC W8 } [get_ports qsfp1_mgt_refclk_0_n] ;# MGTREFCLK0N_231 from U38.5 +#set_property -dict {LOC U9 } [get_ports qsfp1_mgt_refclk_1_p] ;# MGTREFCLK1P_231 from U57.28 +#set_property -dict {LOC U8 } [get_ports qsfp1_mgt_refclk_1_n] ;# MGTREFCLK1N_231 from U57.29 +#set_property -dict {LOC AM23 IOSTANDARD LVDS} [get_ports qsfp1_recclk_p] ;# to U57.16 +#set_property -dict {LOC AM22 IOSTANDARD LVDS} [get_ports qsfp1_recclk_n] ;# to U57.17 +#set_property -dict {LOC AM21 IOSTANDARD LVCMOS18} [get_ports qsfp1_modsell] +#set_property -dict {LOC BA22 IOSTANDARD LVCMOS18} [get_ports qsfp1_resetl] +#set_property -dict {LOC AL21 IOSTANDARD LVCMOS18} [get_ports qsfp1_modprsl] +#set_property -dict {LOC AP21 IOSTANDARD LVCMOS18} [get_ports qsfp1_intl] +#set_property -dict {LOC AN21 IOSTANDARD LVCMOS18} [get_ports qsfp1_lpmode] + +# 156.25 MHz MGT reference clock +#create_clock -period 6.400 -name qsfp1_mgt_refclk_0 [get_ports qsfp1_mgt_refclk_0_p] + +#set_property -dict {LOC L5 } [get_ports qsfp2_tx1_p] ;# MGTYTXN0_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC L4 } [get_ports qsfp2_tx1_n] ;# MGTYTXP0_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC T2 } [get_ports qsfp2_rx1_p] ;# MGTYTXN1_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC T1 } [get_ports qsfp2_rx1_n] ;# MGTYTXP1_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC K7 } [get_ports qsfp2_tx2_p] ;# MGTYTXN2_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC K6 } [get_ports qsfp2_tx2_n] ;# MGTYTXP2_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC R4 } [get_ports qsfp2_rx2_p] ;# MGTYTXN3_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC R3 } [get_ports qsfp2_rx2_n] ;# MGTYTXP3_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC J5 } [get_ports qsfp2_tx3_p] ;# MGTYTXN0_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC J4 } [get_ports qsfp2_tx3_n] ;# MGTYTXP0_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC P2 } [get_ports qsfp2_rx3_p] ;# MGTYTXN1_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC P1 } [get_ports qsfp2_rx3_n] ;# MGTYTXP1_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC H7 } [get_ports qsfp2_tx4_p] ;# MGTYTXN2_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC H6 } [get_ports qsfp2_tx4_n] ;# MGTYTXP2_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC M2 } [get_ports qsfp2_rx4_p] ;# MGTYTXN3_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC M1 } [get_ports qsfp2_rx4_n] ;# MGTYTXP3_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC R9 } [get_ports qsfp2_mgt_refclk_0_p] ;# MGTREFCLK0P_232 from U104.13 +#set_property -dict {LOC R8 } [get_ports qsfp2_mgt_refclk_0_n] ;# MGTREFCLK0N_232 from U104.14 +#set_property -dict {LOC N9 } [get_ports qsfp2_mgt_refclk_1_p] ;# MGTREFCLK1P_232 from U57.35 +#set_property -dict {LOC N8 } [get_ports qsfp2_mgt_refclk_1_n] ;# MGTREFCLK1N_232 from U57.34 +#set_property -dict {LOC AP23 IOSTANDARD LVDS} [get_ports qsfp2_recclk_p] ;# to U57.12 +#set_property -dict {LOC AP22 IOSTANDARD LVDS} [get_ports qsfp2_recclk_n] ;# to U57.13 +#set_property -dict {LOC AN23 IOSTANDARD LVCMOS18} [get_ports qsfp2_modsell] +#set_property -dict {LOC AY22 IOSTANDARD LVCMOS18} [get_ports qsfp2_resetl] +#set_property -dict {LOC AN24 IOSTANDARD LVCMOS18} [get_ports qsfp2_modprsl] +#set_property -dict {LOC AT21 IOSTANDARD LVCMOS18} [get_ports qsfp2_intl] +#set_property -dict {LOC AT24 IOSTANDARD LVCMOS18} [get_ports qsfp2_lpmode] + +# 156.25 MHz MGT reference clock +#create_clock -period 6.400 -name qsfp2_mgt_refclk_0 [get_ports qsfp2_mgt_refclk_0_p] + +# I2C interface +#set_property -dict {LOC AM24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_scl] +#set_property -dict {LOC AL24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_sda] + diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/fpga/Makefile b/fpga/lib/eth/example/VCU118/fpga_1g/fpga/Makefile new file mode 100644 index 000000000..f5849ac8f --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/fpga/Makefile @@ -0,0 +1,98 @@ + +# FPGA settings +FPGA_PART = xcvu9p-flga2104-2l-e +FPGA_TOP = fpga +FPGA_ARCH = virtexuplus + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += rtl/mdio_master.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_fifo.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 + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl + +# IP +XCI_FILES = ip/gig_ethernet_pcs_pma_0.xci + +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 + +%_primary.mcs %_secondary.mcs %_primary.prm %_secondary.prm: %.bit + echo "write_cfgmem -force -format mcs -size 256 -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: $(FPGA_TOP)_primary.mcs $(FPGA_TOP)_secondary.mcs $(FPGA_TOP)_primary.prm $(FPGA_TOP)_secondary.prm + echo "open_hw" > 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 {mt25qu01g-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 \"$(FPGA_TOP)_primary.mcs\" \"$(FPGA_TOP)_secondary.mcs\"] [current_hw_cfgmem]" >> flash.tcl + echo "set_property PROGRAM.PRM_FILES [list \"$(FPGA_TOP)_primary.prm\" \"$(FPGA_TOP)_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 + diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/ip/gig_ethernet_pcs_pma_0.xci b/fpga/lib/eth/example/VCU118/fpga_1g/ip/gig_ethernet_pcs_pma_0.xci new file mode 100644 index 000000000..1e578e860 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/ip/gig_ethernet_pcs_pma_0.xci @@ -0,0 +1,365 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gig_ethernet_pcs_pma_0 + + + 1 + 1 + 1 + 1 + + + + 0 + + + + 0 + + + 0 + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + false + false + false + false + 0 + + + + 0 + + + + 0 + false + 100000000 + + + + 0 + + + + 0 + + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + false + false + false + false + 0 + 0 + + + + 100000000 + 0 + 0.000 + + + + 100000000 + 0 + 0.000 + false + false + false + + + + 100000000 + 0 + 0.000 + 0 + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + ACTIVE_LOW + ACTIVE_LOW + ACTIVE_LOW + ACTIVE_LOW + 1 + 0 + 0 + 0 + + 1 + 100000000 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 0.000 + AXI4LITE + READ_WRITE + 0 + 0 + 0 + 0 + 0 + + + 100000000 + 0 + 0.000 + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + true + 0 + 0 + true + false + DIFF_PAIR_0 + DIFF_PAIR_1 + false + DIFF_PAIR_2 + DIFF_PAIR_1 + virtexuplus + 0 + gig_ethernet_pcs_pma_0 + 50.0 + false + . + true + false + false + true + virtexuplus + 16 + 10 + X0Y4 + 8 + 5 + GTH + false + true + false + false + false + false + true + 1 + clk0 + 125 + TXOUTCLK + true + false + gig_ethernet_pcs_pma_0_gt + true + GTHE4 + false + 0 + true + false + false + xcvu9p + false + 1 + false + true + Sync + gig_ethernet_pcs_pma_0 + Custom + 50.0 + TEMAC + Custom + 0 + false + false + false + false + X0Y4 + GTH + false + false + 625 + Custom + false + 1G + 1 + LVDS + 125 + clk0 + TXOUTCLK + DIFF_PAIR_0 + DIFF_PAIR_1 + false + 10_100_1000 + false + SGMII + Include_Shared_Logic_in_Core + Time_of_day + false + DIFF_PAIR_2 + DIFF_PAIR_1 + 0 + false + virtexuplus + + + xcvu9p + flga2104 + VERILOG + + MIXED + -2L + + E + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/lib/eth b/fpga/lib/eth/example/VCU118/fpga_1g/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/rtl/debounce_switch.v b/fpga/lib/eth/example/VCU118/fpga_1g/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/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 + +`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/fpga/lib/eth/example/VCU118/fpga_1g/rtl/fpga.v b/fpga/lib/eth/example/VCU118/fpga_1g/rtl/fpga.v new file mode 100644 index 000000000..bf3c095c8 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/rtl/fpga.v @@ -0,0 +1,570 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 125MHz LVDS + * Reset: Push button, active low + */ + input wire clk_125mhz_p, + input wire clk_125mhz_n, + input wire reset, + + /* + * 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 SGMII + */ + input wire phy_sgmii_rx_p, + input wire phy_sgmii_rx_n, + output wire phy_sgmii_tx_p, + output wire phy_sgmii_tx_n, + input wire phy_sgmii_clk_p, + input wire phy_sgmii_clk_n, + output wire phy_reset_n, + input wire phy_int_n, + inout wire phy_mdio, + output wire phy_mdc, + + /* + * UART: 500000 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// Clock and reset + +wire clk_125mhz_ibufg; +wire clk_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire rst_125mhz_int; + +wire mmcm_rst = reset; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS #( + .DIFF_TERM("FALSE"), + .IBUF_LOW_PWR("FALSE") +) +clk_125mhz_ibufg_inst ( + .O (clk_125mhz_ibufg), + .I (clk_125mhz_p), + .IB (clk_125mhz_n) +); + +// MMCM instance +// 125 MHz in, 125 MHz out +// PFD range: 10 MHz to 500 MHz +// VCO range: 800 MHz to 1600 MHz +// M = 8, D = 1 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +MMCME3_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(8), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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(8), + .CLKFBOUT_PHASE(0), + .DIVCLK_DIVIDE(1), + .REF_JITTER1(0.010), + .CLKIN1_PERIOD(8.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_125mhz_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_125mhz_bufg_inst ( + .I(clk_125mhz_mmcm_out), + .O(clk_125mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_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_125mhz_int), + .rst(rst_125mhz_int), + .in({btnu, + btnl, + btnd, + btnr, + btnc, + sw}), + .out({btnu_int, + btnl_int, + btnd_int, + btnr_int, + btnc_int, + sw_int}) +); + +wire uart_rxd_int; +wire uart_cts_int; + +sync_signal #( + .WIDTH(2), + .N(2) +) +sync_signal_inst ( + .clk(clk_125mhz_int), + .in({uart_rxd, uart_cts}), + .out({uart_rxd_int, uart_cts_int}) +); + +// SGMII interface to PHY +wire phy_gmii_clk_int; +wire phy_gmii_rst_int; +wire phy_gmii_clk_en_int; +wire [7:0] phy_gmii_txd_int; +wire phy_gmii_tx_en_int; +wire phy_gmii_tx_er_int; +wire [7:0] phy_gmii_rxd_int; +wire phy_gmii_rx_dv_int; +wire phy_gmii_rx_er_int; + +wire [15:0] pcspma_status_vector; + +wire pcspma_status_link_status = pcspma_status_vector[0]; +wire pcspma_status_link_synchronization = pcspma_status_vector[1]; +wire pcspma_status_rudi_c = pcspma_status_vector[2]; +wire pcspma_status_rudi_i = pcspma_status_vector[3]; +wire pcspma_status_rudi_invalid = pcspma_status_vector[4]; +wire pcspma_status_rxdisperr = pcspma_status_vector[5]; +wire pcspma_status_rxnotintable = pcspma_status_vector[6]; +wire pcspma_status_phy_link_status = pcspma_status_vector[7]; +wire [1:0] pcspma_status_remote_fault_encdg = pcspma_status_vector[9:8]; +wire [1:0] pcspma_status_speed = pcspma_status_vector[11:10]; +wire pcspma_status_duplex = pcspma_status_vector[12]; +wire pcspma_status_remote_fault = pcspma_status_vector[13]; +wire [1:0] pcspma_status_pause = pcspma_status_vector[15:14]; + +wire [4:0] pcspma_config_vector; + +assign pcspma_config_vector[4] = 1'b1; // autonegotiation enable +assign pcspma_config_vector[3] = 1'b0; // isolate +assign pcspma_config_vector[2] = 1'b0; // power down +assign pcspma_config_vector[1] = 1'b0; // loopback enable +assign pcspma_config_vector[0] = 1'b0; // unidirectional enable + +wire [15:0] pcspma_an_config_vector; + +assign pcspma_an_config_vector[15] = 1'b1; // SGMII link status +assign pcspma_an_config_vector[14] = 1'b1; // SGMII Acknowledge +assign pcspma_an_config_vector[13:12] = 2'b01; // full duplex +assign pcspma_an_config_vector[11:10] = 2'b10; // SGMII speed +assign pcspma_an_config_vector[9] = 1'b0; // reserved +assign pcspma_an_config_vector[8:7] = 2'b00; // pause frames - SGMII reserved +assign pcspma_an_config_vector[6] = 1'b0; // reserved +assign pcspma_an_config_vector[5] = 1'b0; // full duplex - SGMII reserved +assign pcspma_an_config_vector[4:1] = 4'b0000; // reserved +assign pcspma_an_config_vector[0] = 1'b1; // SGMII + +gig_ethernet_pcs_pma_0 +eth_pcspma ( + // SGMII + .txp_0 (phy_sgmii_tx_p), + .txn_0 (phy_sgmii_tx_n), + .rxp_0 (phy_sgmii_rx_p), + .rxn_0 (phy_sgmii_rx_n), + + // Ref clock from PHY + .refclk625_p (phy_sgmii_clk_p), + .refclk625_n (phy_sgmii_clk_n), + + // async reset + .reset (rst_125mhz_int), + + // clock and reset outputs + .clk125_out (phy_gmii_clk_int), + .clk312_out (), + .rst_125_out (phy_gmii_rst_int), + .tx_logic_reset (), + .rx_logic_reset (), + .tx_locked (), + .rx_locked (), + .tx_pll_clk_out (), + .rx_pll_clk_out (), + + // MAC clocking + .sgmii_clk_r_0 (), + .sgmii_clk_f_0 (), + .sgmii_clk_en_0 (phy_gmii_clk_en_int), + + // Speed control + .speed_is_10_100_0 (pcspma_status_speed != 2'b10), + .speed_is_100_0 (pcspma_status_speed == 2'b01), + + // Internal GMII + .gmii_txd_0 (phy_gmii_txd_int), + .gmii_tx_en_0 (phy_gmii_tx_en_int), + .gmii_tx_er_0 (phy_gmii_tx_er_int), + .gmii_rxd_0 (phy_gmii_rxd_int), + .gmii_rx_dv_0 (phy_gmii_rx_dv_int), + .gmii_rx_er_0 (phy_gmii_rx_er_int), + .gmii_isolate_0 (), + + // Configuration + .configuration_vector_0 (pcspma_config_vector), + + .an_interrupt_0 (), + .an_adv_config_vector_0 (pcspma_an_config_vector), + .an_restart_config_0 (1'b0), + + // Status + .status_vector_0 (pcspma_status_vector), + .signal_detect_0 (1'b1), + + // Cascade + .tx_bsc_rst_out (), + .rx_bsc_rst_out (), + .tx_bs_rst_out (), + .rx_bs_rst_out (), + .tx_rst_dly_out (), + .rx_rst_dly_out (), + .tx_bsc_en_vtc_out (), + .rx_bsc_en_vtc_out (), + .tx_bs_en_vtc_out (), + .rx_bs_en_vtc_out (), + .riu_clk_out (), + .riu_addr_out (), + .riu_wr_data_out (), + .riu_wr_en_out (), + .riu_nibble_sel_out (), + .riu_rddata_1 (16'b0), + .riu_valid_1 (1'b0), + .riu_prsnt_1 (1'b0), + .riu_rddata_2 (16'b0), + .riu_valid_2 (1'b0), + .riu_prsnt_2 (1'b0), + .riu_rddata_3 (16'b0), + .riu_valid_3 (1'b0), + .riu_prsnt_3 (1'b0), + .rx_btval_1 (), + .rx_btval_2 (), + .rx_btval_3 (), + .tx_dly_rdy_1 (1'b1), + .rx_dly_rdy_1 (1'b1), + .rx_vtc_rdy_1 (1'b1), + .tx_vtc_rdy_1 (1'b1), + .tx_dly_rdy_2 (1'b1), + .rx_dly_rdy_2 (1'b1), + .rx_vtc_rdy_2 (1'b1), + .tx_vtc_rdy_2 (1'b1), + .tx_dly_rdy_3 (1'b1), + .rx_dly_rdy_3 (1'b1), + .rx_vtc_rdy_3 (1'b1), + .tx_vtc_rdy_3 (1'b1), + .tx_rdclk_out () +); + +reg [19:0] delay_reg = 20'hfffff; + +reg [4:0] mdio_cmd_phy_addr = 5'h03; +reg [4:0] mdio_cmd_reg_addr = 5'h00; +reg [15:0] mdio_cmd_data = 16'd0; +reg [1:0] mdio_cmd_opcode = 2'b01; +reg mdio_cmd_valid = 1'b0; +wire mdio_cmd_ready; + +reg [3:0] state_reg = 0; + +always @(posedge clk_125mhz_int) begin + if (rst_125mhz_int) begin + state_reg <= 0; + delay_reg <= 20'hfffff; + mdio_cmd_reg_addr <= 5'h00; + mdio_cmd_data <= 16'd0; + mdio_cmd_valid <= 1'b0; + end else begin + mdio_cmd_valid <= mdio_cmd_valid & !mdio_cmd_ready; + if (delay_reg > 0) begin + delay_reg <= delay_reg - 1; + end else if (!mdio_cmd_ready) begin + // wait for ready + state_reg <= state_reg; + end else begin + mdio_cmd_valid <= 1'b0; + case (state_reg) + // set SGMII autonegotiation timer to 11 ms + // write 0x0070 to CFG4 (0x0031) + 4'd0: begin + // write to REGCR to load address + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h001F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd1; + end + 4'd1: begin + // write address of CFG4 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h0031; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd2; + end + 4'd2: begin + // write to REGCR to load data + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h401F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd3; + end + 4'd3: begin + // write data for CFG4 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h0070; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd4; + end + // enable SGMII clock output + // write 0x4000 to SGMIICTL1 (0x00D3) + 4'd4: begin + // write to REGCR to load address + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h001F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd5; + end + 4'd5: begin + // write address of SGMIICTL1 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h00D3; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd6; + end + 4'd6: begin + // write to REGCR to load data + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h401F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd7; + end + 4'd7: begin + // write data for SGMIICTL1 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h4000; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd8; + end + // enable 10Mbps operation + // write 0x0015 to 10M_SGMII_CFG (0x016F) + 4'd8: begin + // write to REGCR to load address + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h001F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd9; + end + 4'd9: begin + // write address of 10M_SGMII_CFG to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h016F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd10; + end + 4'd10: begin + // write to REGCR to load data + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h401F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd11; + end + 4'd11: begin + // write data for 10M_SGMII_CFG to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h0015; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd12; + end + 4'd12: begin + // done + state_reg <= 4'd12; + end + endcase + end + end +end + +wire mdc; +wire mdio_i; +wire mdio_o; +wire mdio_t; + +mdio_master +mdio_master_inst ( + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + + .cmd_phy_addr(mdio_cmd_phy_addr), + .cmd_reg_addr(mdio_cmd_reg_addr), + .cmd_data(mdio_cmd_data), + .cmd_opcode(mdio_cmd_opcode), + .cmd_valid(mdio_cmd_valid), + .cmd_ready(mdio_cmd_ready), + + .data_out(), + .data_out_valid(), + .data_out_ready(1'b1), + + .mdc_o(mdc), + .mdio_i(mdio_i), + .mdio_o(mdio_o), + .mdio_t(mdio_t), + + .busy(), + + .prescale(8'd3) +); + +assign phy_mdc = mdc; +assign mdio_i = phy_mdio; +assign phy_mdio = mdio_t ? 1'bz : mdio_o; + +wire [7:0] led_int; + +// SGMII interface debug: +// SW12:4 (sw[0]) off for payload byte, on for status vector +// SW12:3 (sw[1]) off for LSB of status vector, on for MSB +assign led = sw[0] ? (sw[1] ? pcspma_status_vector[15:8] : pcspma_status_vector[7:0]) : led_int; + +fpga_core +core_inst ( + /* + * Clock: 125MHz + * Synchronous reset + */ + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + /* + * GPIO + */ + .btnu(btnu_int), + .btnl(btnl_int), + .btnd(btnd_int), + .btnr(btnr_int), + .btnc(btnc_int), + .sw(sw_int), + .led(led_int), + /* + * Ethernet: 1000BASE-T SGMII + */ + .phy_gmii_clk(phy_gmii_clk_int), + .phy_gmii_rst(phy_gmii_rst_int), + .phy_gmii_clk_en(phy_gmii_clk_en_int), + .phy_gmii_rxd(phy_gmii_rxd_int), + .phy_gmii_rx_dv(phy_gmii_rx_dv_int), + .phy_gmii_rx_er(phy_gmii_rx_er_int), + .phy_gmii_txd(phy_gmii_txd_int), + .phy_gmii_tx_en(phy_gmii_tx_en_int), + .phy_gmii_tx_er(phy_gmii_tx_er_int), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts_int) +); + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/rtl/fpga_core.v b/fpga/lib/eth/example/VCU118/fpga_1g/rtl/fpga_core.v new file mode 100644 index 000000000..f2cf9cc56 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/rtl/fpga_core.v @@ -0,0 +1,584 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core +( + /* + * Clock: 125MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + + /* + * 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 SGMII + */ + input wire phy_gmii_clk, + input wire phy_gmii_rst, + input wire phy_gmii_clk_en, + input wire [7:0] phy_gmii_rxd, + input wire phy_gmii_rx_dv, + input wire phy_gmii_rx_er, + output wire [7:0] phy_gmii_txd, + output wire phy_gmii_tx_en, + output wire phy_gmii_tx_er, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 115200 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// 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; +assign uart_rts = 0; + +eth_mac_1g_fifo #( + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_inst ( + .rx_clk(phy_gmii_clk), + .rx_rst(phy_gmii_rst), + .tx_clk(phy_gmii_clk), + .tx_rst(phy_gmii_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), + + .gmii_rxd(phy_gmii_rxd), + .gmii_rx_dv(phy_gmii_rx_dv), + .gmii_rx_er(phy_gmii_rx_er), + .gmii_txd(phy_gmii_txd), + .gmii_tx_en(phy_gmii_tx_en), + .gmii_tx_er(phy_gmii_tx_er), + + .rx_clk_enable(phy_gmii_clk_en), + .tx_clk_enable(phy_gmii_clk_en), + .rx_mii_select(1'b0), + .tx_mii_select(1'b0), + + .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(), + + .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 #( + .ADDR_WIDTH(12), + .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/fpga/lib/eth/example/VCU118/fpga_1g/rtl/mdio_master.v b/fpga/lib/eth/example/VCU118/fpga_1g/rtl/mdio_master.v new file mode 100644 index 000000000..1dc56a8cf --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/rtl/mdio_master.v @@ -0,0 +1,225 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * MDIO master + */ +module mdio_master ( + input wire clk, + input wire rst, + + /* + * Host interface + */ + input wire [4:0] cmd_phy_addr, + input wire [4:0] cmd_reg_addr, + input wire [15:0] cmd_data, + input wire [1:0] cmd_opcode, + input wire cmd_valid, + output wire cmd_ready, + + output wire [15:0] data_out, + output wire data_out_valid, + input wire data_out_ready, + + /* + * MDIO to PHY + */ + output wire mdc_o, + input wire mdio_i, + output wire mdio_o, + output wire mdio_t, + + /* + * Status + */ + output wire busy, + + /* + * Configuration + */ + input wire [7:0] prescale +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PREAMBLE = 2'd1, + STATE_TRANSFER = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [16:0] count_reg = 16'd0, count_next; +reg [6:0] bit_count_reg = 6'd0, bit_count_next; +reg cycle_reg = 1'b0, cycle_next; + +reg [31:0] data_reg = 32'd0, data_next; + +reg [1:0] op_reg = 2'b00, op_next; + +reg cmd_ready_reg = 1'b0, cmd_ready_next; + +reg [15:0] data_out_reg = 15'd0, data_out_next; +reg data_out_valid_reg = 1'b0, data_out_valid_next; + +reg mdio_i_reg = 1'b1; + +reg mdc_o_reg = 1'b0, mdc_o_next; +reg mdio_o_reg = 1'b0, mdio_o_next; +reg mdio_t_reg = 1'b1, mdio_t_next; + +reg busy_reg = 1'b0; + +assign cmd_ready = cmd_ready_reg; + +assign data_out = data_out_reg; +assign data_out_valid = data_out_valid_reg; + +assign mdc_o = mdc_o_reg; +assign mdio_o = mdio_o_reg; +assign mdio_t = mdio_t_reg; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + count_next = count_reg; + bit_count_next = bit_count_reg; + cycle_next = cycle_reg; + + data_next = data_reg; + + op_next = op_reg; + + cmd_ready_next = 1'b0; + + data_out_next = data_out_reg; + data_out_valid_next = data_out_valid_reg & ~data_out_ready; + + mdc_o_next = mdc_o_reg; + mdio_o_next = mdio_o_reg; + mdio_t_next = mdio_t_reg; + + if (count_reg > 16'd0) begin + count_next = count_reg - 16'd1; + state_next = state_reg; + end else if (cycle_reg) begin + cycle_next = 1'b0; + mdc_o_next = 1'b1; + count_next = prescale; + state_next = state_reg; + end else begin + mdc_o_next = 1'b0; + case (state_reg) + STATE_IDLE: begin + // idle - accept new command + cmd_ready_next = ~data_out_valid; + + if (cmd_ready & cmd_valid) begin + cmd_ready_next = 1'b0; + data_next = {2'b01, cmd_opcode, cmd_phy_addr, cmd_reg_addr, 2'b10, cmd_data}; + op_next = cmd_opcode; + mdio_t_next = 1'b0; + mdio_o_next = 1'b1; + bit_count_next = 6'd32; + cycle_next = 1'b1; + count_next = prescale; + state_next = STATE_PREAMBLE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + cycle_next = 1'b1; + count_next = prescale; + if (bit_count_reg > 6'd1) begin + bit_count_next = bit_count_reg - 6'd1; + state_next = STATE_PREAMBLE; + end else begin + bit_count_next = 6'd32; + {mdio_o_next, data_next} = {data_reg, mdio_i_reg}; + state_next = STATE_TRANSFER; + end + end + STATE_TRANSFER: begin + cycle_next = 1'b1; + count_next = prescale; + if ((op_reg == 2'b10 || op_reg == 2'b11) && bit_count_reg == 6'd19) begin + mdio_t_next = 1'b1; + end + if (bit_count_reg > 6'd1) begin + bit_count_next = bit_count_reg - 6'd1; + {mdio_o_next, data_next} = {data_reg, mdio_i_reg}; + state_next = STATE_TRANSFER; + end else begin + if (op_reg == 2'b10 || op_reg == 2'b11) begin + data_out_next = data_reg[15:0]; + data_out_valid_next = 1'b1; + end + mdio_t_next = 1'b1; + state_next = STATE_IDLE; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + count_reg <= 16'd0; + bit_count_reg <= 6'd0; + cycle_reg <= 1'b0; + cmd_ready_reg <= 1'b0; + data_out_valid_reg <= 1'b0; + mdc_o_reg <= 1'b0; + mdio_o_reg <= 1'b0; + mdio_t_reg <= 1'b1; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + count_reg <= count_next; + bit_count_reg <= bit_count_next; + cycle_reg <= cycle_next; + cmd_ready_reg <= cmd_ready_next; + data_out_valid_reg <= data_out_valid_next; + mdc_o_reg <= mdc_o_next; + mdio_o_reg <= mdio_o_next; + mdio_t_reg <= mdio_t_next; + busy_reg <= (state_next != STATE_IDLE || count_reg != 0 || cycle_reg || mdc_o); + end + + data_reg <= data_next; + op_reg <= op_next; + + data_out_reg <= data_out_next; + + mdio_i_reg <= mdio_i; +end + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/rtl/sync_reset.v b/fpga/lib/eth/example/VCU118/fpga_1g/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/rtl/sync_signal.v b/fpga/lib/eth/example/VCU118/fpga_1g/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/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/fpga/lib/eth/example/VCU118/fpga_1g/tb/arp_ep.py b/fpga/lib/eth/example/VCU118/fpga_1g/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/tb/axis_ep.py b/fpga/lib/eth/example/VCU118/fpga_1g/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/tb/eth_ep.py b/fpga/lib/eth/example/VCU118/fpga_1g/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/tb/gmii_ep.py b/fpga/lib/eth/example/VCU118/fpga_1g/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/tb/ip_ep.py b/fpga/lib/eth/example/VCU118/fpga_1g/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/tb/test_fpga_core.py b/fpga/lib/eth/example/VCU118/fpga_1g/tb/test_fpga_core.py new file mode 100755 index 000000000..97523fc5a --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/tb/test_fpga_core.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import gmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_1g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/udp_complete.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen.v") +srcs.append("../lib/eth/rtl/udp.v") +srcs.append("../lib/eth/rtl/udp_ip_rx.v") +srcs.append("../lib/eth/rtl/udp_ip_tx.v") +srcs.append("../lib/eth/rtl/ip_complete.v") +srcs.append("../lib/eth/rtl/ip.v") +srcs.append("../lib/eth/rtl/ip_eth_rx.v") +srcs.append("../lib/eth/rtl/ip_eth_tx.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx.v") +srcs.append("../lib/eth/rtl/arp_eth_tx.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[4:]) + phy_gmii_clk = Signal(bool(0)) + phy_gmii_rst = Signal(bool(0)) + phy_gmii_clk_en = Signal(bool(0)) + phy_gmii_rxd = Signal(intbv(0)[8:]) + phy_gmii_rx_dv = Signal(bool(0)) + phy_gmii_rx_er = Signal(bool(0)) + phy_int_n = Signal(bool(1)) + uart_rxd = Signal(bool(0)) + uart_cts = Signal(bool(0)) + + # Outputs + led = Signal(intbv(0)[8:]) + phy_gmii_txd = Signal(intbv(0)[8:]) + phy_gmii_tx_en = Signal(bool(0)) + phy_gmii_tx_er = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + + # sources and sinks + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + phy_gmii_clk, + phy_gmii_rst, + txd=phy_gmii_rxd, + tx_en=phy_gmii_rx_dv, + tx_er=phy_gmii_rx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + phy_gmii_clk, + phy_gmii_rst, + rxd=phy_gmii_txd, + rx_dv=phy_gmii_tx_en, + rx_er=phy_gmii_tx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + led=led, + + phy_gmii_clk=phy_gmii_clk, + phy_gmii_rst=phy_gmii_rst, + phy_gmii_clk_en=phy_gmii_clk_en, + phy_gmii_rxd=phy_gmii_rxd, + phy_gmii_rx_dv=phy_gmii_rx_dv, + phy_gmii_rx_er=phy_gmii_rx_er, + phy_gmii_txd=phy_gmii_txd, + phy_gmii_tx_en=phy_gmii_tx_en, + phy_gmii_tx_er=phy_gmii_tx_er, + phy_reset_n=phy_reset_n, + phy_int_n=phy_int_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd, + uart_rts=uart_rts, + uart_cts=uart_cts + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + phy_gmii_clk.next = not phy_gmii_clk + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + phy_gmii_clk_en.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + phy_gmii_clk_en.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + phy_gmii_rst.next = 1 + yield clk.posedge + rst.next = 0 + phy_gmii_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/tb/test_fpga_core.v b/fpga/lib/eth/example/VCU118/fpga_1g/tb/test_fpga_core.v new file mode 100644 index 000000000..e3de43465 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/tb/test_fpga_core.v @@ -0,0 +1,131 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [3:0] sw = 0; +reg phy_gmii_clk = 0; +reg phy_gmii_rst = 0; +reg phy_gmii_clk_en = 0; +reg [7:0] phy_gmii_rxd = 0; +reg phy_gmii_rx_dv = 0; +reg phy_gmii_rx_er = 0; +reg phy_int_n = 1; +reg uart_rxd = 0; +reg uart_cts = 0; + +// Outputs +wire [7:0] led; +wire [7:0] phy_gmii_txd; +wire phy_gmii_tx_en; +wire phy_gmii_tx_er; +wire phy_reset_n; +wire uart_txd; +wire uart_rts; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + phy_gmii_clk, + phy_gmii_rst, + phy_gmii_clk_en, + phy_gmii_rxd, + phy_gmii_rx_dv, + phy_gmii_rx_er, + phy_int_n, + uart_rxd, + uart_cts + ); + $to_myhdl( + led, + phy_gmii_txd, + phy_gmii_tx_en, + phy_gmii_tx_er, + phy_reset_n, + uart_txd, + uart_rts + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .led(led), + .phy_gmii_clk(phy_gmii_clk), + .phy_gmii_rst(phy_gmii_rst), + .phy_gmii_clk_en(phy_gmii_clk_en), + .phy_gmii_rxd(phy_gmii_rxd), + .phy_gmii_rx_dv(phy_gmii_rx_dv), + .phy_gmii_rx_er(phy_gmii_rx_er), + .phy_gmii_txd(phy_gmii_txd), + .phy_gmii_tx_en(phy_gmii_tx_en), + .phy_gmii_tx_er(phy_gmii_tx_er), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts) +); + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_1g/tb/udp_ep.py b/fpga/lib/eth/example/VCU118/fpga_1g/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_1g/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/Makefile b/fpga/lib/eth/example/VCU118/fpga_25g/Makefile new file mode 100644 index 000000000..f504bd06f --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/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/fpga/lib/eth/example/VCU118/fpga_25g/README.md b/fpga/lib/eth/example/VCU118/fpga_25g/README.md new file mode 100644 index 000000000..4d9729984 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/README.md @@ -0,0 +1,33 @@ +# Verilog Ethernet VCU118 Example Design + +## Introduction + +This example design targets the Xilinx VCU118 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. The design also enables the gigabit Ethernet interface for +testing with a QSFP loopback adapter. + +FPGA: xcvu9p-flga2104-2L-e +PHY: 25G BASE-R PHY IP core and internal GTY transceiver + +## 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 VCU118 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. + +Note that the gigabit PHY is also enabled for debugging. The gigabit port can +be inserted into the 25G data path between the 25G MAC and 25G PHY so that the +25G interface can be tested with a QSFP loopback adapter. Turn on SW12.1 to +insert the gigabit port into the 25G data path, or off to bypass the gigabit +port. Turn on SW12.2 to place the port in the TX path or off to place the +port in the RX path. + + diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/common/vivado.mk b/fpga/lib/eth/example/VCU118/fpga_25g/common/vivado.mk new file mode 100644 index 000000000..964ed04eb --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/common/vivado.mk @@ -0,0 +1,118 @@ +################################################################### +# +# 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)) + +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 + +tmpclean: + -rm -rf *.log *.jou *.cache *.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) + 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 + 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/fpga/lib/eth/example/VCU118/fpga_25g/fpga.xdc b/fpga/lib/eth/example/VCU118/fpga_25g/fpga.xdc new file mode 100644 index 000000000..efbf0cc00 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/fpga.xdc @@ -0,0 +1,151 @@ +# XDC constraints for the Xilinx VCU118 board +# part: xcvu9p-flga2104-2L-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] + +# System clocks +# 300 MHz +#set_property -dict {LOC G31 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_p] +#set_property -dict {LOC F31 IOSTANDARD DIFF_SSTL12} [get_ports clk_300mhz_n] +#create_clock -period 3.333 -name clk_300mhz [get_ports clk_300mhz_p] + +# 250 MHz +#set_property -dict {LOC E12 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_1_p] +#set_property -dict {LOC D12 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_1_n] +#create_clock -period 4 -name clk_250mhz_1 [get_ports clk_250mhz_1_p] + +#set_property -dict {LOC AW26 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_2_p] +#set_property -dict {LOC AW27 IOSTANDARD DIFF_SSTL12} [get_ports clk_250mhz_2_n] +#create_clock -period 4 -name clk_250mhz_2 [get_ports clk_250mhz_2_p] + +# 125 MHz +set_property -dict {LOC AY24 IOSTANDARD LVDS} [get_ports clk_125mhz_p] +set_property -dict {LOC AY23 IOSTANDARD LVDS} [get_ports clk_125mhz_n] +create_clock -period 8.000 -name clk_125mhz [get_ports clk_125mhz_p] + +# 90 MHz +#set_property -dict {LOC AL20 IOSTANDARD LVCMOS18} [get_ports clk_90mhz] +#create_clock -period 11.111 -name clk_90mhz [get_ports clk_90mhz] + +# LEDs +set_property -dict {LOC AT32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[0]}] +set_property -dict {LOC AV34 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[1]}] +set_property -dict {LOC AY30 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[2]}] +set_property -dict {LOC BB32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[3]}] +set_property -dict {LOC BF32 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[4]}] +set_property -dict {LOC AU37 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[5]}] +set_property -dict {LOC AV36 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[6]}] +set_property -dict {LOC BA37 IOSTANDARD LVCMOS12 SLEW SLOW DRIVE 8} [get_ports {led[7]}] + +# Reset button +set_property -dict {LOC L19 IOSTANDARD LVCMOS12} [get_ports reset] + +# Push buttons +set_property -dict {LOC BB24 IOSTANDARD LVCMOS18} [get_ports btnu] +set_property -dict {LOC BF22 IOSTANDARD LVCMOS18} [get_ports btnl] +set_property -dict {LOC BE22 IOSTANDARD LVCMOS18} [get_ports btnd] +set_property -dict {LOC BE23 IOSTANDARD LVCMOS18} [get_ports btnr] +set_property -dict {LOC BD23 IOSTANDARD LVCMOS18} [get_ports btnc] + +# DIP switches +set_property -dict {LOC B17 IOSTANDARD LVCMOS12} [get_ports {sw[0]}] +set_property -dict {LOC G16 IOSTANDARD LVCMOS12} [get_ports {sw[1]}] +set_property -dict {LOC J16 IOSTANDARD LVCMOS12} [get_ports {sw[2]}] +set_property -dict {LOC D21 IOSTANDARD LVCMOS12} [get_ports {sw[3]}] + +# UART +set_property -dict {LOC BB21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_txd] +set_property -dict {LOC AW25 IOSTANDARD LVCMOS18} [get_ports uart_rxd] +set_property -dict {LOC BB22 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports uart_rts] +set_property -dict {LOC AY25 IOSTANDARD LVCMOS18} [get_ports uart_cts] + +# Gigabit Ethernet SGMII PHY +set_property -dict {LOC AU24 IOSTANDARD LVDS} [get_ports phy_sgmii_rx_p] +set_property -dict {LOC AV24 IOSTANDARD LVDS} [get_ports phy_sgmii_rx_n] +set_property -dict {LOC AU21 IOSTANDARD LVDS} [get_ports phy_sgmii_tx_p] +set_property -dict {LOC AV21 IOSTANDARD LVDS} [get_ports phy_sgmii_tx_n] +set_property -dict {LOC AT22 IOSTANDARD LVDS} [get_ports phy_sgmii_clk_p] +set_property -dict {LOC AU22 IOSTANDARD LVDS} [get_ports phy_sgmii_clk_n] +set_property -dict {LOC BA21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_reset_n] +set_property -dict {LOC AR24 IOSTANDARD LVCMOS18} [get_ports phy_int_n] +set_property -dict {LOC AR23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_mdio] +set_property -dict {LOC AV23 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports phy_mdc] + +# 625 MHz ref clock from SGMII PHY +#create_clock -period 1.600 -name phy_sgmii_clk [get_ports phy_sgmii_clk_p] + +# QSFP28 Interfaces +set_property -dict {LOC V7 } [get_ports qsfp1_tx1_p] ;# MGTYTXN0_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC V6 } [get_ports qsfp1_tx1_n] ;# MGTYTXP0_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC Y2 } [get_ports qsfp1_rx1_p] ;# MGTYTXN1_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC Y1 } [get_ports qsfp1_rx1_n] ;# MGTYTXP1_231 GTYE3_CHANNEL_X1Y48 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC T7 } [get_ports qsfp1_tx2_p] ;# MGTYTXN2_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC T6 } [get_ports qsfp1_tx2_n] ;# MGTYTXP2_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC W4 } [get_ports qsfp1_rx2_p] ;# MGTYTXN3_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC W3 } [get_ports qsfp1_rx2_n] ;# MGTYTXP3_231 GTYE3_CHANNEL_X1Y49 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC P7 } [get_ports qsfp1_tx3_p] ;# MGTYTXN0_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC P6 } [get_ports qsfp1_tx3_n] ;# MGTYTXP0_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC V2 } [get_ports qsfp1_rx3_p] ;# MGTYTXN1_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC V1 } [get_ports qsfp1_rx3_n] ;# MGTYTXP1_231 GTYE3_CHANNEL_X1Y50 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC M7 } [get_ports qsfp1_tx4_p] ;# MGTYTXN2_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC M6 } [get_ports qsfp1_tx4_n] ;# MGTYTXP2_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC U4 } [get_ports qsfp1_rx4_p] ;# MGTYTXN3_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +#set_property -dict {LOC U3 } [get_ports qsfp1_rx4_n] ;# MGTYTXP3_231 GTYE3_CHANNEL_X1Y51 / GTYE3_COMMON_X1Y12 +set_property -dict {LOC W9 } [get_ports qsfp1_mgt_refclk_0_p] ;# MGTREFCLK0P_231 from U38.4 +#set_property -dict {LOC W8 } [get_ports qsfp1_mgt_refclk_0_n] ;# MGTREFCLK0N_231 from U38.5 +#set_property -dict {LOC U9 } [get_ports qsfp1_mgt_refclk_1_p] ;# MGTREFCLK1P_231 from U57.28 +#set_property -dict {LOC U8 } [get_ports qsfp1_mgt_refclk_1_n] ;# MGTREFCLK1N_231 from U57.29 +#set_property -dict {LOC AM23 IOSTANDARD LVDS} [get_ports qsfp1_recclk_p] ;# to U57.16 +#set_property -dict {LOC AM22 IOSTANDARD LVDS} [get_ports qsfp1_recclk_n] ;# to U57.17 +set_property -dict {LOC AM21 IOSTANDARD LVCMOS18} [get_ports qsfp1_modsell] +set_property -dict {LOC BA22 IOSTANDARD LVCMOS18} [get_ports qsfp1_resetl] +set_property -dict {LOC AL21 IOSTANDARD LVCMOS18} [get_ports qsfp1_modprsl] +set_property -dict {LOC AP21 IOSTANDARD LVCMOS18} [get_ports qsfp1_intl] +set_property -dict {LOC AN21 IOSTANDARD LVCMOS18} [get_ports qsfp1_lpmode] + +# 156.25 MHz MGT reference clock +create_clock -period 6.400 -name qsfp1_mgt_refclk_0 [get_ports qsfp1_mgt_refclk_0_p] + +set_property -dict {LOC L5 } [get_ports qsfp2_tx1_p] ;# MGTYTXN0_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC L4 } [get_ports qsfp2_tx1_n] ;# MGTYTXP0_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC T2 } [get_ports qsfp2_rx1_p] ;# MGTYTXN1_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC T1 } [get_ports qsfp2_rx1_n] ;# MGTYTXP1_232 GTYE3_CHANNEL_X1Y52 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC K7 } [get_ports qsfp2_tx2_p] ;# MGTYTXN2_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC K6 } [get_ports qsfp2_tx2_n] ;# MGTYTXP2_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC R4 } [get_ports qsfp2_rx2_p] ;# MGTYTXN3_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC R3 } [get_ports qsfp2_rx2_n] ;# MGTYTXP3_232 GTYE3_CHANNEL_X1Y53 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC J5 } [get_ports qsfp2_tx3_p] ;# MGTYTXN0_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC J4 } [get_ports qsfp2_tx3_n] ;# MGTYTXP0_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC P2 } [get_ports qsfp2_rx3_p] ;# MGTYTXN1_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC P1 } [get_ports qsfp2_rx3_n] ;# MGTYTXP1_232 GTYE3_CHANNEL_X1Y54 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC H7 } [get_ports qsfp2_tx4_p] ;# MGTYTXN2_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC H6 } [get_ports qsfp2_tx4_n] ;# MGTYTXP2_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +set_property -dict {LOC M2 } [get_ports qsfp2_rx4_p] ;# MGTYTXN3_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC M1 } [get_ports qsfp2_rx4_n] ;# MGTYTXP3_232 GTYE3_CHANNEL_X1Y55 / GTYE3_COMMON_X1Y13 +#set_property -dict {LOC R9 } [get_ports qsfp2_mgt_refclk_0_p] ;# MGTREFCLK0P_232 from U104.13 +#set_property -dict {LOC R8 } [get_ports qsfp2_mgt_refclk_0_n] ;# MGTREFCLK0N_232 from U104.14 +#set_property -dict {LOC N9 } [get_ports qsfp2_mgt_refclk_1_p] ;# MGTREFCLK1P_232 from U57.35 +#set_property -dict {LOC N8 } [get_ports qsfp2_mgt_refclk_1_n] ;# MGTREFCLK1N_232 from U57.34 +#set_property -dict {LOC AP23 IOSTANDARD LVDS} [get_ports qsfp2_recclk_p] ;# to U57.12 +#set_property -dict {LOC AP22 IOSTANDARD LVDS} [get_ports qsfp2_recclk_n] ;# to U57.13 +set_property -dict {LOC AN23 IOSTANDARD LVCMOS18} [get_ports qsfp2_modsell] +set_property -dict {LOC AY22 IOSTANDARD LVCMOS18} [get_ports qsfp2_resetl] +set_property -dict {LOC AN24 IOSTANDARD LVCMOS18} [get_ports qsfp2_modprsl] +set_property -dict {LOC AT21 IOSTANDARD LVCMOS18} [get_ports qsfp2_intl] +set_property -dict {LOC AT24 IOSTANDARD LVCMOS18} [get_ports qsfp2_lpmode] + +# 156.25 MHz MGT reference clock +#create_clock -period 6.400 -name qsfp2_mgt_refclk_0 [get_ports qsfp2_mgt_refclk_0_p] + +# I2C interface +set_property -dict {LOC AM24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_scl] +set_property -dict {LOC AL24 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_sda] + + diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/fpga/Makefile b/fpga/lib/eth/example/VCU118/fpga_25g/fpga/Makefile new file mode 100644 index 000000000..807cc1ed1 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/fpga/Makefile @@ -0,0 +1,115 @@ + +# FPGA settings +FPGA_PART = xcvu9p-flga2104-2L-e +FPGA_TOP = fpga +FPGA_ARCH = virtexuplus + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/sync_reset.v +SYN_FILES += rtl/sync_signal.v +SYN_FILES += rtl/mdio_master.v +SYN_FILES += lib/eth/rtl/eth_mac_1g_fifo.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/eth_mac_10g_fifo.v +SYN_FILES += lib/eth/rtl/eth_mac_10g.v +SYN_FILES += lib/eth/rtl/axis_xgmii_rx_64.v +SYN_FILES += lib/eth/rtl/axis_xgmii_tx_64.v +SYN_FILES += lib/eth/rtl/eth_phy_10g.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_if.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_frame_sync.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_rx_ber_mon.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx.v +SYN_FILES += lib/eth/rtl/eth_phy_10g_tx_if.v +SYN_FILES += lib/eth/rtl/xgmii_baser_dec_64.v +SYN_FILES += lib/eth/rtl/xgmii_baser_enc_64.v +SYN_FILES += lib/eth/rtl/lfsr.v +SYN_FILES += lib/eth/rtl/eth_axis_rx_64.v +SYN_FILES += lib/eth/rtl/eth_axis_tx_64.v +SYN_FILES += lib/eth/rtl/udp_complete_64.v +SYN_FILES += lib/eth/rtl/udp_checksum_gen_64.v +SYN_FILES += lib/eth/rtl/udp_64.v +SYN_FILES += lib/eth/rtl/udp_ip_rx_64.v +SYN_FILES += lib/eth/rtl/udp_ip_tx_64.v +SYN_FILES += lib/eth/rtl/ip_complete_64.v +SYN_FILES += lib/eth/rtl/ip_64.v +SYN_FILES += lib/eth/rtl/ip_eth_rx_64.v +SYN_FILES += lib/eth/rtl/ip_eth_tx_64.v +SYN_FILES += lib/eth/rtl/ip_arb_mux.v +SYN_FILES += lib/eth/rtl/arp_64.v +SYN_FILES += lib/eth/rtl/arp_cache.v +SYN_FILES += lib/eth/rtl/arp_eth_rx_64.v +SYN_FILES += lib/eth/rtl/arp_eth_tx_64.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_adapter.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_fifo.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_switch.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_register.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_async_fifo.v + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl + +# IP +XCI_FILES = ip/gig_ethernet_pcs_pma_0.xci +XCI_FILES += ip/gtwizard_ultrascale_0.xci + +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 + +%_primary.mcs %_secondary.mcs %_primary.prm %_secondary.prm: %.bit + echo "write_cfgmem -force -format mcs -size 256 -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: $(FPGA_TOP)_primary.mcs $(FPGA_TOP)_secondary.mcs $(FPGA_TOP)_primary.prm $(FPGA_TOP)_secondary.prm + echo "open_hw" > 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 {mt25qu01g-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 \"$(FPGA_TOP)_primary.mcs\" \"$(FPGA_TOP)_secondary.mcs\"] [current_hw_cfgmem]" >> flash.tcl + echo "set_property PROGRAM.PRM_FILES [list \"$(FPGA_TOP)_primary.prm\" \"$(FPGA_TOP)_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 + diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/ip/gig_ethernet_pcs_pma_0.xci b/fpga/lib/eth/example/VCU118/fpga_25g/ip/gig_ethernet_pcs_pma_0.xci new file mode 100644 index 000000000..1e578e860 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/ip/gig_ethernet_pcs_pma_0.xci @@ -0,0 +1,365 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gig_ethernet_pcs_pma_0 + + + 1 + 1 + 1 + 1 + + + + 0 + + + + 0 + + + 0 + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + false + false + false + false + 0 + + + + 0 + + + + 0 + false + 100000000 + + + + 0 + + + + 0 + + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + + + 100000000 + 0 + 0.000 + false + false + false + false + 0 + 0 + + + + 100000000 + 0 + 0.000 + + + + 100000000 + 0 + 0.000 + false + false + false + + + + 100000000 + 0 + 0.000 + 0 + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + ACTIVE_LOW + ACTIVE_LOW + ACTIVE_LOW + ACTIVE_LOW + 1 + 0 + 0 + 0 + + 1 + 100000000 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 0.000 + AXI4LITE + READ_WRITE + 0 + 0 + 0 + 0 + 0 + + + 100000000 + 0 + 0.000 + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + true + 0 + 0 + true + false + DIFF_PAIR_0 + DIFF_PAIR_1 + false + DIFF_PAIR_2 + DIFF_PAIR_1 + virtexuplus + 0 + gig_ethernet_pcs_pma_0 + 50.0 + false + . + true + false + false + true + virtexuplus + 16 + 10 + X0Y4 + 8 + 5 + GTH + false + true + false + false + false + false + true + 1 + clk0 + 125 + TXOUTCLK + true + false + gig_ethernet_pcs_pma_0_gt + true + GTHE4 + false + 0 + true + false + false + xcvu9p + false + 1 + false + true + Sync + gig_ethernet_pcs_pma_0 + Custom + 50.0 + TEMAC + Custom + 0 + false + false + false + false + X0Y4 + GTH + false + false + 625 + Custom + false + 1G + 1 + LVDS + 125 + clk0 + TXOUTCLK + DIFF_PAIR_0 + DIFF_PAIR_1 + false + 10_100_1000 + false + SGMII + Include_Shared_Logic_in_Core + Time_of_day + false + DIFF_PAIR_2 + DIFF_PAIR_1 + 0 + false + virtexuplus + + + xcvu9p + flga2104 + VERILOG + + MIXED + -2L + + E + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/ip/gtwizard_ultrascale_0.xci b/fpga/lib/eth/example/VCU118/fpga_25g/ip/gtwizard_ultrascale_0.xci new file mode 100644 index 000000000..cbac1c8b2 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/ip/gtwizard_ultrascale_0.xci @@ -0,0 +1,1415 @@ + + + xilinx.com + xci + unknown + 1.0 + + + gtwizard_ultrascale_0 + + + "000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + 2 + 2578.125 + 0 + 0 + 125 + 67 + 3 + 2 + 0 + 2 + 0 + 0 + 1 + 0 + 1 + 1 + 250 + 0 + 0 + 0 + 0 + 0 + 1 + "00000000" + "00000000" + 1 + 4 + 0 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000" + 0 + "00000000" + 1 + 0 + 5000 + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + 0 + "1010000011" + 0 + "0101111100" + 4 + 1 + 64 + 25.78125 + 144 + 1 + 390.6250000 + 4 + 0 + 0x000000000000000000000000000000000000000000000000 + 156.25 + 0 + 0 + 0 + 1 + 1 + 0 + 64 + 390.6250000 + 390.6250000 + 0 + 257.8125 + 0 + 8 + 2 + 0 + 0 + 0 + 390.625 + 0 + 0 + 1 + 4 + 1 + 64 + 25.78125 + 144 + 1 + 390.6250000 + 4 + 0 + 156.25 + 0 + 0 + 1 + 1 + 0 + 64 + 390.6250000 + 390.6250000 + 1 + X1Y55 X1Y54 X1Y53 X1Y52 X1Y51 X1Y50 X1Y49 X1Y48 + gtwizard_ultrascale_0 + 0 + 0 + + 125 + BOTH + 0 + GTY + 2 + 30 + 96 + 4 + gtye4 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + -1 + -1 + -1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + 1 + 1 + 1 + -1 + -1 + -1 + -1 + -1 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + 1 + 1 + 1 + 1 + 0 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + -1 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + -1 + -1 + -1 + 0 + 0 + 0 + -1 + -1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + -1 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + -1 + 0 + 0 + 0 + -1 + 0 + 1 + -1 + -1 + -1 + -1 + -1 + -1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + -1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 27 + 0 + 10GBASE-R + 9 + 390.6250000 + 8 + 2 + 390.6250000 + false + CORE + NONE + CORE + CORE + EXAMPLE_DESIGN + CORE + EXAMPLE_DESIGN + EXAMPLE_DESIGN + false + NAME + false + 250 + false + false + 250 + GTY-10GBASE-R + 0 + MULTI + 1 + ENABLE + DISABLE + ENABLE + 00000000 + false + false + false + false + false + false + false + false + 00000000 + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 4 + 1 + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + false + false + false + false + false + false + false + false + 00000000 + DISABLE + false + false + false + false + false + false + false + false + 1 + 00000000 + false + false + false + false + false + false + false + false + 0 + 5000 + ENABLE + 0 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 00000000 + 1 + false + 0000000000 + false + 1010000011 + NONE + false + 0101111100 + true + 0 + AC + 64B66B_ASYNC + true + AUTO + 64 + 10 + -20 + 25.78125 + X1Y48 + RXPROGDIVCLK + QPLL0 + 200 + 8388608 + + 156.25 + X1Y55 clk0+1 X1Y54 clk0+1 X1Y53 clk0+1 X1Y52 clk0+1 X1Y51 clk0+1 X1Y50 clk0+1 X1Y49 clk0+1 X1Y48 clk0+1 + OFF + 0 + PROGRAMMABLE + 800 + 64 + 15 + false + 0 + 10.3125 + 257.8125 + 0 + false + QPLL0 + 390.625 + 1 + ENABLE + 64B66B_ASYNC + CUSTOM + true + 64 + 25.78125 + X1Y48 + TXPROGDIVCLK + QPLL0 + 8388608 + 156.25 + X1Y55 clk0+1 X1Y54 clk0+1 X1Y53 clk0+1 X1Y52 clk0+1 X1Y51 clk0+1 X1Y50 clk0+1 X1Y49 clk0+1 X1Y48 clk0+1 + 64 + false + 1 + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + true + true + false + true + true + true + false + true + true + true + false + false + false + false + false + true + false + false + false + false + false + false + false + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + false + false + true + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + virtexuplus + + + xcvu9p + flga2104 + VERILOG + + MIXED + -2L + + E + TRUE + TRUE + IP_Flow + 6 + TRUE + . + + . + 2019.1 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/lib/eth b/fpga/lib/eth/example/VCU118/fpga_25g/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/rtl/debounce_switch.v b/fpga/lib/eth/example/VCU118/fpga_25g/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/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 + +`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/fpga/lib/eth/example/VCU118/fpga_25g/rtl/fpga.v b/fpga/lib/eth/example/VCU118/fpga_25g/rtl/fpga.v new file mode 100644 index 000000000..823ffbf96 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/rtl/fpga.v @@ -0,0 +1,1314 @@ +/* + +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 1ns / 1ps + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 125MHz LVDS + * Reset: Push button, active low + */ + input wire clk_125mhz_p, + input wire clk_125mhz_n, + input wire reset, + + /* + * 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, + + /* + * I2C for board management + */ + inout wire i2c_scl, + inout wire i2c_sda, + + /* + * Ethernet: QSFP28 + */ + output wire qsfp1_tx1_p, + output wire qsfp1_tx1_n, + input wire qsfp1_rx1_p, + input wire qsfp1_rx1_n, + output wire qsfp1_tx2_p, + output wire qsfp1_tx2_n, + input wire qsfp1_rx2_p, + input wire qsfp1_rx2_n, + output wire qsfp1_tx3_p, + output wire qsfp1_tx3_n, + input wire qsfp1_rx3_p, + input wire qsfp1_rx3_n, + output wire qsfp1_tx4_p, + output wire qsfp1_tx4_n, + input wire qsfp1_rx4_p, + input wire qsfp1_rx4_n, + input wire qsfp1_mgt_refclk_0_p, + input wire qsfp1_mgt_refclk_0_n, + // input wire qsfp1_mgt_refclk_1_p, + // input wire qsfp1_mgt_refclk_1_n, + // output wire qsfp1_recclk_p, + // output wire qsfp1_recclk_n, + output wire qsfp1_modsell, + output wire qsfp1_resetl, + input wire qsfp1_modprsl, + input wire qsfp1_intl, + output wire qsfp1_lpmode, + + output wire qsfp2_tx1_p, + output wire qsfp2_tx1_n, + input wire qsfp2_rx1_p, + input wire qsfp2_rx1_n, + output wire qsfp2_tx2_p, + output wire qsfp2_tx2_n, + input wire qsfp2_rx2_p, + input wire qsfp2_rx2_n, + output wire qsfp2_tx3_p, + output wire qsfp2_tx3_n, + input wire qsfp2_rx3_p, + input wire qsfp2_rx3_n, + output wire qsfp2_tx4_p, + output wire qsfp2_tx4_n, + input wire qsfp2_rx4_p, + input wire qsfp2_rx4_n, + // input wire qsfp2_mgt_refclk_0_p, + // input wire qsfp2_mgt_refclk_0_n, + // input wire qsfp2_mgt_refclk_1_p, + // input wire qsfp2_mgt_refclk_1_n, + // output wire qsfp2_recclk_p, + // output wire qsfp2_recclk_n, + output wire qsfp2_modsell, + output wire qsfp2_resetl, + input wire qsfp2_modprsl, + input wire qsfp2_intl, + output wire qsfp2_lpmode, + + /* + * Ethernet: 1000BASE-T SGMII + */ + input wire phy_sgmii_rx_p, + input wire phy_sgmii_rx_n, + output wire phy_sgmii_tx_p, + output wire phy_sgmii_tx_n, + input wire phy_sgmii_clk_p, + input wire phy_sgmii_clk_n, + output wire phy_reset_n, + input wire phy_int_n, + inout wire phy_mdio, + output wire phy_mdc, + + /* + * UART: 500000 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// Clock and reset + +wire clk_125mhz_ibufg; +wire clk_125mhz_mmcm_out; + +// Internal 125 MHz clock +wire clk_125mhz_int; +wire rst_125mhz_int; + +// Internal 156.25 MHz clock +wire clk_156mhz_int; +wire rst_156mhz_int; + +wire mmcm_rst = reset; +wire mmcm_locked; +wire mmcm_clkfb; + +IBUFGDS #( + .DIFF_TERM("FALSE"), + .IBUF_LOW_PWR("FALSE") +) +clk_125mhz_ibufg_inst ( + .O (clk_125mhz_ibufg), + .I (clk_125mhz_p), + .IB (clk_125mhz_n) +); + +// MMCM instance +// 125 MHz in, 125 MHz out +// PFD range: 10 MHz to 500 MHz +// VCO range: 800 MHz to 1600 MHz +// M = 8, D = 1 sets Fvco = 1000 MHz (in range) +// Divide by 8 to get output frequency of 125 MHz +MMCME3_BASE #( + .BANDWIDTH("OPTIMIZED"), + .CLKOUT0_DIVIDE_F(8), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + .CLKOUT2_DIVIDE(1), + .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(8), + .CLKFBOUT_PHASE(0), + .DIVCLK_DIVIDE(1), + .REF_JITTER1(0.010), + .CLKIN1_PERIOD(8.0), + .STARTUP_WAIT("FALSE"), + .CLKOUT4_CASCADE("FALSE") +) +clk_mmcm_inst ( + .CLKIN1(clk_125mhz_ibufg), + .CLKFBIN(mmcm_clkfb), + .RST(mmcm_rst), + .PWRDWN(1'b0), + .CLKOUT0(clk_125mhz_mmcm_out), + .CLKOUT0B(), + .CLKOUT1(), + .CLKOUT1B(), + .CLKOUT2(), + .CLKOUT2B(), + .CLKOUT3(), + .CLKOUT3B(), + .CLKOUT4(), + .CLKOUT5(), + .CLKOUT6(), + .CLKFBOUT(mmcm_clkfb), + .CLKFBOUTB(), + .LOCKED(mmcm_locked) +); + +BUFG +clk_125mhz_bufg_inst ( + .I(clk_125mhz_mmcm_out), + .O(clk_125mhz_int) +); + +sync_reset #( + .N(4) +) +sync_reset_125mhz_inst ( + .clk(clk_125mhz_int), + .rst(~mmcm_locked), + .sync_reset_out(rst_125mhz_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(156000) +) +debounce_switch_inst ( + .clk(clk_156mhz_int), + .rst(rst_156mhz_int), + .in({btnu, + btnl, + btnd, + btnr, + btnc, + sw}), + .out({btnu_int, + btnl_int, + btnd_int, + btnr_int, + btnc_int, + sw_int}) +); + +wire uart_rxd_int; +wire uart_cts_int; + +sync_signal #( + .WIDTH(2), + .N(2) +) +sync_signal_inst ( + .clk(clk_156mhz_int), + .in({uart_rxd, uart_cts}), + .out({uart_rxd_int, uart_cts_int}) +); + +// SI570 I2C +wire i2c_scl_i; +wire i2c_scl_o = 1'b1; +wire i2c_scl_t = 1'b1; +wire i2c_sda_i; +wire i2c_sda_o = 1'b1; +wire i2c_sda_t = 1'b1; + +assign i2c_scl_i = i2c_scl; +assign i2c_scl = i2c_scl_t ? 1'bz : i2c_scl_o; +assign i2c_sda_i = i2c_sda; +assign i2c_sda = i2c_sda_t ? 1'bz : i2c_sda_o; + +// XGMII 10G PHY +assign qsfp1_modsell = 1'b0; +assign qsfp1_resetl = 1'b1; +assign qsfp1_lpmode = 1'b0; + +wire qsfp1_tx_clk_1_int; +wire qsfp1_tx_rst_1_int; +wire [63:0] qsfp1_txd_1_int; +wire [7:0] qsfp1_txc_1_int; +wire qsfp1_rx_clk_1_int; +wire qsfp1_rx_rst_1_int; +wire [63:0] qsfp1_rxd_1_int; +wire [7:0] qsfp1_rxc_1_int; +wire qsfp1_tx_clk_2_int; +wire qsfp1_tx_rst_2_int; +wire [63:0] qsfp1_txd_2_int; +wire [7:0] qsfp1_txc_2_int; +wire qsfp1_rx_clk_2_int; +wire qsfp1_rx_rst_2_int; +wire [63:0] qsfp1_rxd_2_int; +wire [7:0] qsfp1_rxc_2_int; +wire qsfp1_tx_clk_3_int; +wire qsfp1_tx_rst_3_int; +wire [63:0] qsfp1_txd_3_int; +wire [7:0] qsfp1_txc_3_int; +wire qsfp1_rx_clk_3_int; +wire qsfp1_rx_rst_3_int; +wire [63:0] qsfp1_rxd_3_int; +wire [7:0] qsfp1_rxc_3_int; +wire qsfp1_tx_clk_4_int; +wire qsfp1_tx_rst_4_int; +wire [63:0] qsfp1_txd_4_int; +wire [7:0] qsfp1_txc_4_int; +wire qsfp1_rx_clk_4_int; +wire qsfp1_rx_rst_4_int; +wire [63:0] qsfp1_rxd_4_int; +wire [7:0] qsfp1_rxc_4_int; + +assign qsfp2_modsell = 1'b0; +assign qsfp2_resetl = 1'b1; +assign qsfp2_lpmode = 1'b0; + +wire qsfp2_tx_clk_1_int; +wire qsfp2_tx_rst_1_int; +wire [63:0] qsfp2_txd_1_int; +wire [7:0] qsfp2_txc_1_int; +wire qsfp2_rx_clk_1_int; +wire qsfp2_rx_rst_1_int; +wire [63:0] qsfp2_rxd_1_int; +wire [7:0] qsfp2_rxc_1_int; +wire qsfp2_tx_clk_2_int; +wire qsfp2_tx_rst_2_int; +wire [63:0] qsfp2_txd_2_int; +wire [7:0] qsfp2_txc_2_int; +wire qsfp2_rx_clk_2_int; +wire qsfp2_rx_rst_2_int; +wire [63:0] qsfp2_rxd_2_int; +wire [7:0] qsfp2_rxc_2_int; +wire qsfp2_tx_clk_3_int; +wire qsfp2_tx_rst_3_int; +wire [63:0] qsfp2_txd_3_int; +wire [7:0] qsfp2_txc_3_int; +wire qsfp2_rx_clk_3_int; +wire qsfp2_rx_rst_3_int; +wire [63:0] qsfp2_rxd_3_int; +wire [7:0] qsfp2_rxc_3_int; +wire qsfp2_tx_clk_4_int; +wire qsfp2_tx_rst_4_int; +wire [63:0] qsfp2_txd_4_int; +wire [7:0] qsfp2_txc_4_int; +wire qsfp2_rx_clk_4_int; +wire qsfp2_rx_rst_4_int; +wire [63:0] qsfp2_rxd_4_int; +wire [7:0] qsfp2_rxc_4_int; + +wire qsfp1_rx_block_lock_1; +wire qsfp1_rx_block_lock_2; +wire qsfp1_rx_block_lock_3; +wire qsfp1_rx_block_lock_4; + +wire qsfp2_rx_block_lock_1; +wire qsfp2_rx_block_lock_2; +wire qsfp2_rx_block_lock_3; +wire qsfp2_rx_block_lock_4; + +wire qsfp1_mgt_refclk_0; + +wire [7:0] gt_txclkout; +wire gt_txusrclk; + +wire [7:0] gt_rxclkout; +wire [7:0] gt_rxusrclk; + +wire gt_reset_tx_done; +wire gt_reset_rx_done; + +wire [7:0] gt_txprgdivresetdone; +wire [7:0] gt_txpmaresetdone; +wire [7:0] gt_rxprgdivresetdone; +wire [7:0] gt_rxpmaresetdone; + +wire gt_tx_reset = ~((>_txprgdivresetdone) & (>_txpmaresetdone)); +wire gt_rx_reset = ~>_rxpmaresetdone; + +reg gt_userclk_tx_active = 1'b0; +reg [7:0] gt_userclk_rx_active = 1'b0; + +IBUFDS_GTE4 ibufds_gte4_qsfp1_mgt_refclk_0_inst ( + .I (qsfp1_mgt_refclk_0_p), + .IB (qsfp1_mgt_refclk_0_n), + .CEB (1'b0), + .O (qsfp1_mgt_refclk_0), + .ODIV2 () +); + + +BUFG_GT bufg_gt_tx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_tx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_txclkout[0]), + .O (gt_txusrclk) +); + +assign clk_156mhz_int = gt_txusrclk; + +always @(posedge gt_txusrclk, posedge gt_tx_reset) begin + if (gt_tx_reset) begin + gt_userclk_tx_active <= 1'b0; + end else begin + gt_userclk_tx_active <= 1'b1; + end +end + +genvar n; + +generate + +for (n = 0; n < 8; n = n + 1) begin + + BUFG_GT bufg_gt_rx_usrclk_inst ( + .CE (1'b1), + .CEMASK (1'b0), + .CLR (gt_rx_reset), + .CLRMASK (1'b0), + .DIV (3'd0), + .I (gt_rxclkout[n]), + .O (gt_rxusrclk[n]) + ); + + always @(posedge gt_rxusrclk[n], posedge gt_rx_reset) begin + if (gt_rx_reset) begin + gt_userclk_rx_active[n] <= 1'b0; + end else begin + gt_userclk_rx_active[n] <= 1'b1; + end + end + +end + +endgenerate + +sync_reset #( + .N(4) +) +sync_reset_156mhz_inst ( + .clk(clk_156mhz_int), + .rst(~gt_reset_tx_done), + .sync_reset_out(rst_156mhz_int) +); + +wire [5:0] qsfp1_gt_txheader_1; +wire [127:0] qsfp1_gt_txdata_1; +wire qsfp1_gt_rxgearboxslip_1; +wire [5:0] qsfp1_gt_rxheader_1; +wire [1:0] qsfp1_gt_rxheadervalid_1; +wire [127:0] qsfp1_gt_rxdata_1; +wire [1:0] qsfp1_gt_rxdatavalid_1; + +wire [5:0] qsfp1_gt_txheader_2; +wire [127:0] qsfp1_gt_txdata_2; +wire qsfp1_gt_rxgearboxslip_2; +wire [5:0] qsfp1_gt_rxheader_2; +wire [1:0] qsfp1_gt_rxheadervalid_2; +wire [127:0] qsfp1_gt_rxdata_2; +wire [1:0] qsfp1_gt_rxdatavalid_2; + +wire [5:0] qsfp1_gt_txheader_3; +wire [127:0] qsfp1_gt_txdata_3; +wire qsfp1_gt_rxgearboxslip_3; +wire [5:0] qsfp1_gt_rxheader_3; +wire [1:0] qsfp1_gt_rxheadervalid_3; +wire [127:0] qsfp1_gt_rxdata_3; +wire [1:0] qsfp1_gt_rxdatavalid_3; + +wire [5:0] qsfp1_gt_txheader_4; +wire [127:0] qsfp1_gt_txdata_4; +wire qsfp1_gt_rxgearboxslip_4; +wire [5:0] qsfp1_gt_rxheader_4; +wire [1:0] qsfp1_gt_rxheadervalid_4; +wire [127:0] qsfp1_gt_rxdata_4; +wire [1:0] qsfp1_gt_rxdatavalid_4; + +wire [5:0] qsfp2_gt_txheader_1; +wire [127:0] qsfp2_gt_txdata_1; +wire qsfp2_gt_rxgearboxslip_1; +wire [5:0] qsfp2_gt_rxheader_1; +wire [1:0] qsfp2_gt_rxheadervalid_1; +wire [127:0] qsfp2_gt_rxdata_1; +wire [1:0] qsfp2_gt_rxdatavalid_1; + +wire [5:0] qsfp2_gt_txheader_2; +wire [127:0] qsfp2_gt_txdata_2; +wire qsfp2_gt_rxgearboxslip_2; +wire [5:0] qsfp2_gt_rxheader_2; +wire [1:0] qsfp2_gt_rxheadervalid_2; +wire [127:0] qsfp2_gt_rxdata_2; +wire [1:0] qsfp2_gt_rxdatavalid_2; + +wire [5:0] qsfp2_gt_txheader_3; +wire [127:0] qsfp2_gt_txdata_3; +wire qsfp2_gt_rxgearboxslip_3; +wire [5:0] qsfp2_gt_rxheader_3; +wire [1:0] qsfp2_gt_rxheadervalid_3; +wire [127:0] qsfp2_gt_rxdata_3; +wire [1:0] qsfp2_gt_rxdatavalid_3; + +wire [5:0] qsfp2_gt_txheader_4; +wire [127:0] qsfp2_gt_txdata_4; +wire qsfp2_gt_rxgearboxslip_4; +wire [5:0] qsfp2_gt_rxheader_4; +wire [1:0] qsfp2_gt_rxheadervalid_4; +wire [127:0] qsfp2_gt_rxdata_4; +wire [1:0] qsfp2_gt_rxdatavalid_4; + +gtwizard_ultrascale_0 +qsfp_gty_inst ( + .gtwiz_userclk_tx_active_in(>_userclk_tx_active), + .gtwiz_userclk_rx_active_in(>_userclk_rx_active), + + .gtwiz_reset_clk_freerun_in(clk_125mhz_int), + .gtwiz_reset_all_in(rst_125mhz_int), + + .gtwiz_reset_tx_pll_and_datapath_in(1'b0), + .gtwiz_reset_tx_datapath_in(1'b0), + + .gtwiz_reset_rx_pll_and_datapath_in(1'b0), + .gtwiz_reset_rx_datapath_in(1'b0), + + .gtwiz_reset_rx_cdr_stable_out(), + + .gtwiz_reset_tx_done_out(gt_reset_tx_done), + .gtwiz_reset_rx_done_out(gt_reset_rx_done), + + .gtrefclk00_in({2{qsfp1_mgt_refclk_0}}), + + .qpll0outclk_out(), + .qpll0outrefclk_out(), + + .gtyrxn_in({qsfp2_rx4_n, qsfp2_rx3_n, qsfp2_rx2_n, qsfp2_rx1_n, qsfp1_rx4_n, qsfp1_rx3_n, qsfp1_rx2_n, qsfp1_rx1_n}), + .gtyrxp_in({qsfp2_rx4_p, qsfp2_rx3_p, qsfp2_rx2_p, qsfp2_rx1_p, qsfp1_rx4_p, qsfp1_rx3_p, qsfp1_rx2_p, qsfp1_rx1_p}), + + .rxusrclk_in(gt_rxusrclk), + .rxusrclk2_in(gt_rxusrclk), + + .txdata_in({qsfp2_gt_txdata_4, qsfp2_gt_txdata_3, qsfp2_gt_txdata_2, qsfp2_gt_txdata_1, qsfp1_gt_txdata_4, qsfp1_gt_txdata_3, qsfp1_gt_txdata_2, qsfp1_gt_txdata_1}), + .txheader_in({qsfp2_gt_txheader_4, qsfp2_gt_txheader_3, qsfp2_gt_txheader_2, qsfp2_gt_txheader_1, qsfp1_gt_txheader_4, qsfp1_gt_txheader_3, qsfp1_gt_txheader_2, qsfp1_gt_txheader_1}), + .txsequence_in({8{1'b0}}), + + .txusrclk_in({8{gt_txusrclk}}), + .txusrclk2_in({8{gt_txusrclk}}), + + .gtpowergood_out(), + + .gtytxn_out({qsfp2_tx4_n, qsfp2_tx3_n, qsfp2_tx2_n, qsfp2_tx1_n, qsfp1_tx4_n, qsfp1_tx3_n, qsfp1_tx2_n, qsfp1_tx1_n}), + .gtytxp_out({qsfp2_tx4_p, qsfp2_tx3_p, qsfp2_tx2_p, qsfp2_tx1_p, qsfp1_tx4_p, qsfp1_tx3_p, qsfp1_tx2_p, qsfp1_tx1_p}), + + .rxgearboxslip_in({qsfp2_gt_rxgearboxslip_4, qsfp2_gt_rxgearboxslip_3, qsfp2_gt_rxgearboxslip_2, qsfp2_gt_rxgearboxslip_1, qsfp1_gt_rxgearboxslip_4, qsfp1_gt_rxgearboxslip_3, qsfp1_gt_rxgearboxslip_2, qsfp1_gt_rxgearboxslip_1}), + .rxdata_out({qsfp2_gt_rxdata_4, qsfp2_gt_rxdata_3, qsfp2_gt_rxdata_2, qsfp2_gt_rxdata_1, qsfp1_gt_rxdata_4, qsfp1_gt_rxdata_3, qsfp1_gt_rxdata_2, qsfp1_gt_rxdata_1}), + .rxdatavalid_out({qsfp2_gt_rxdatavalid_4, qsfp2_gt_rxdatavalid_3, qsfp2_gt_rxdatavalid_2, qsfp2_gt_rxdatavalid_1, qsfp1_gt_rxdatavalid_4, qsfp1_gt_rxdatavalid_3, qsfp1_gt_rxdatavalid_2, qsfp1_gt_rxdatavalid_1}), + .rxheader_out({qsfp2_gt_rxheader_4, qsfp2_gt_rxheader_3, qsfp2_gt_rxheader_2, qsfp2_gt_rxheader_1, qsfp1_gt_rxheader_4, qsfp1_gt_rxheader_3, qsfp1_gt_rxheader_2, qsfp1_gt_rxheader_1}), + .rxheadervalid_out({qsfp2_gt_rxheadervalid_4, qsfp2_gt_rxheadervalid_3, qsfp2_gt_rxheadervalid_2, qsfp2_gt_rxheadervalid_1, qsfp1_gt_rxheadervalid_4, qsfp1_gt_rxheadervalid_3, qsfp1_gt_rxheadervalid_2, qsfp1_gt_rxheadervalid_1}), + .rxoutclk_out(gt_rxclkout), + .rxpmaresetdone_out(gt_rxpmaresetdone), + .rxprgdivresetdone_out(gt_rxprgdivresetdone), + .rxstartofseq_out(), + + .txoutclk_out(gt_txclkout), + .txpmaresetdone_out(gt_txpmaresetdone), + .txprgdivresetdone_out(gt_txprgdivresetdone) +); + +assign qsfp1_tx_clk_1_int = clk_156mhz_int; +assign qsfp1_tx_rst_1_int = rst_156mhz_int; + +assign qsfp1_rx_clk_1_int = gt_rxusrclk[0]; + +sync_reset #( + .N(4) +) +qsfp1_rx_rst_1_reset_sync_inst ( + .clk(qsfp1_rx_clk_1_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp1_rx_rst_1_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp1_phy_1_inst ( + .tx_clk(qsfp1_tx_clk_1_int), + .tx_rst(qsfp1_tx_rst_1_int), + .rx_clk(qsfp1_rx_clk_1_int), + .rx_rst(qsfp1_rx_rst_1_int), + .xgmii_txd(qsfp1_txd_1_int), + .xgmii_txc(qsfp1_txc_1_int), + .xgmii_rxd(qsfp1_rxd_1_int), + .xgmii_rxc(qsfp1_rxc_1_int), + .serdes_tx_data(qsfp1_gt_txdata_1), + .serdes_tx_hdr(qsfp1_gt_txheader_1), + .serdes_rx_data(qsfp1_gt_rxdata_1), + .serdes_rx_hdr(qsfp1_gt_rxheader_1), + .serdes_rx_bitslip(qsfp1_gt_rxgearboxslip_1), + .rx_block_lock(qsfp1_rx_block_lock_1), + .rx_high_ber() +); + +assign qsfp1_tx_clk_2_int = clk_156mhz_int; +assign qsfp1_tx_rst_2_int = rst_156mhz_int; + +assign qsfp1_rx_clk_2_int = gt_rxusrclk[1]; + +sync_reset #( + .N(4) +) +qsfp1_rx_rst_2_reset_sync_inst ( + .clk(qsfp1_rx_clk_2_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp1_rx_rst_2_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp1_phy_2_inst ( + .tx_clk(qsfp1_tx_clk_2_int), + .tx_rst(qsfp1_tx_rst_2_int), + .rx_clk(qsfp1_rx_clk_2_int), + .rx_rst(qsfp1_rx_rst_2_int), + .xgmii_txd(qsfp1_txd_2_int), + .xgmii_txc(qsfp1_txc_2_int), + .xgmii_rxd(qsfp1_rxd_2_int), + .xgmii_rxc(qsfp1_rxc_2_int), + .serdes_tx_data(qsfp1_gt_txdata_2), + .serdes_tx_hdr(qsfp1_gt_txheader_2), + .serdes_rx_data(qsfp1_gt_rxdata_2), + .serdes_rx_hdr(qsfp1_gt_rxheader_2), + .serdes_rx_bitslip(qsfp1_gt_rxgearboxslip_2), + .rx_block_lock(qsfp1_rx_block_lock_2), + .rx_high_ber() +); + +assign qsfp1_tx_clk_3_int = clk_156mhz_int; +assign qsfp1_tx_rst_3_int = rst_156mhz_int; + +assign qsfp1_rx_clk_3_int = gt_rxusrclk[2]; + +sync_reset #( + .N(4) +) +qsfp1_rx_rst_3_reset_sync_inst ( + .clk(qsfp1_rx_clk_3_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp1_rx_rst_3_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp1_phy_3_inst ( + .tx_clk(qsfp1_tx_clk_3_int), + .tx_rst(qsfp1_tx_rst_3_int), + .rx_clk(qsfp1_rx_clk_3_int), + .rx_rst(qsfp1_rx_rst_3_int), + .xgmii_txd(qsfp1_txd_3_int), + .xgmii_txc(qsfp1_txc_3_int), + .xgmii_rxd(qsfp1_rxd_3_int), + .xgmii_rxc(qsfp1_rxc_3_int), + .serdes_tx_data(qsfp1_gt_txdata_3), + .serdes_tx_hdr(qsfp1_gt_txheader_3), + .serdes_rx_data(qsfp1_gt_rxdata_3), + .serdes_rx_hdr(qsfp1_gt_rxheader_3), + .serdes_rx_bitslip(qsfp1_gt_rxgearboxslip_3), + .rx_block_lock(qsfp1_rx_block_lock_3), + .rx_high_ber() +); + +assign qsfp1_tx_clk_4_int = clk_156mhz_int; +assign qsfp1_tx_rst_4_int = rst_156mhz_int; + +assign qsfp1_rx_clk_4_int = gt_rxusrclk[3]; + +sync_reset #( + .N(4) +) +qsfp1_rx_rst_4_reset_sync_inst ( + .clk(qsfp1_rx_clk_4_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp1_rx_rst_4_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp1_phy_4_inst ( + .tx_clk(qsfp1_tx_clk_4_int), + .tx_rst(qsfp1_tx_rst_4_int), + .rx_clk(qsfp1_rx_clk_4_int), + .rx_rst(qsfp1_rx_rst_4_int), + .xgmii_txd(qsfp1_txd_4_int), + .xgmii_txc(qsfp1_txc_4_int), + .xgmii_rxd(qsfp1_rxd_4_int), + .xgmii_rxc(qsfp1_rxc_4_int), + .serdes_tx_data(qsfp1_gt_txdata_4), + .serdes_tx_hdr(qsfp1_gt_txheader_4), + .serdes_rx_data(qsfp1_gt_rxdata_4), + .serdes_rx_hdr(qsfp1_gt_rxheader_4), + .serdes_rx_bitslip(qsfp1_gt_rxgearboxslip_4), + .rx_block_lock(qsfp1_rx_block_lock_4), + .rx_high_ber() +); + +assign qsfp2_tx_clk_1_int = clk_156mhz_int; +assign qsfp2_tx_rst_1_int = rst_156mhz_int; + +assign qsfp2_rx_clk_1_int = gt_rxusrclk[4]; + +sync_reset #( + .N(4) +) +qsfp2_rx_rst_1_reset_sync_inst ( + .clk(qsfp2_rx_clk_1_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp2_rx_rst_1_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp2_phy_1_inst ( + .tx_clk(qsfp2_tx_clk_1_int), + .tx_rst(qsfp2_tx_rst_1_int), + .rx_clk(qsfp2_rx_clk_1_int), + .rx_rst(qsfp2_rx_rst_1_int), + .xgmii_txd(qsfp2_txd_1_int), + .xgmii_txc(qsfp2_txc_1_int), + .xgmii_rxd(qsfp2_rxd_1_int), + .xgmii_rxc(qsfp2_rxc_1_int), + .serdes_tx_data(qsfp2_gt_txdata_1), + .serdes_tx_hdr(qsfp2_gt_txheader_1), + .serdes_rx_data(qsfp2_gt_rxdata_1), + .serdes_rx_hdr(qsfp2_gt_rxheader_1), + .serdes_rx_bitslip(qsfp2_gt_rxgearboxslip_1), + .rx_block_lock(qsfp2_rx_block_lock_1), + .rx_high_ber() +); + +assign qsfp2_tx_clk_2_int = clk_156mhz_int; +assign qsfp2_tx_rst_2_int = rst_156mhz_int; + +assign qsfp2_rx_clk_2_int = gt_rxusrclk[5]; + +sync_reset #( + .N(4) +) +qsfp2_rx_rst_2_reset_sync_inst ( + .clk(qsfp2_rx_clk_2_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp2_rx_rst_2_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp2_phy_2_inst ( + .tx_clk(qsfp2_tx_clk_2_int), + .tx_rst(qsfp2_tx_rst_2_int), + .rx_clk(qsfp2_rx_clk_2_int), + .rx_rst(qsfp2_rx_rst_2_int), + .xgmii_txd(qsfp2_txd_2_int), + .xgmii_txc(qsfp2_txc_2_int), + .xgmii_rxd(qsfp2_rxd_2_int), + .xgmii_rxc(qsfp2_rxc_2_int), + .serdes_tx_data(qsfp2_gt_txdata_2), + .serdes_tx_hdr(qsfp2_gt_txheader_2), + .serdes_rx_data(qsfp2_gt_rxdata_2), + .serdes_rx_hdr(qsfp2_gt_rxheader_2), + .serdes_rx_bitslip(qsfp2_gt_rxgearboxslip_2), + .rx_block_lock(qsfp2_rx_block_lock_2), + .rx_high_ber() +); + +assign qsfp2_tx_clk_3_int = clk_156mhz_int; +assign qsfp2_tx_rst_3_int = rst_156mhz_int; + +assign qsfp2_rx_clk_3_int = gt_rxusrclk[6]; + +sync_reset #( + .N(4) +) +qsfp2_rx_rst_3_reset_sync_inst ( + .clk(qsfp2_rx_clk_3_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp2_rx_rst_3_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp2_phy_3_inst ( + .tx_clk(qsfp2_tx_clk_3_int), + .tx_rst(qsfp2_tx_rst_3_int), + .rx_clk(qsfp2_rx_clk_3_int), + .rx_rst(qsfp2_rx_rst_3_int), + .xgmii_txd(qsfp2_txd_3_int), + .xgmii_txc(qsfp2_txc_3_int), + .xgmii_rxd(qsfp2_rxd_3_int), + .xgmii_rxc(qsfp2_rxc_3_int), + .serdes_tx_data(qsfp2_gt_txdata_3), + .serdes_tx_hdr(qsfp2_gt_txheader_3), + .serdes_rx_data(qsfp2_gt_rxdata_3), + .serdes_rx_hdr(qsfp2_gt_rxheader_3), + .serdes_rx_bitslip(qsfp2_gt_rxgearboxslip_3), + .rx_block_lock(qsfp2_rx_block_lock_3), + .rx_high_ber() +); + +assign qsfp2_tx_clk_4_int = clk_156mhz_int; +assign qsfp2_tx_rst_4_int = rst_156mhz_int; + +assign qsfp2_rx_clk_4_int = gt_rxusrclk[7]; + +sync_reset #( + .N(4) +) +qsfp2_rx_rst_4_reset_sync_inst ( + .clk(qsfp2_rx_clk_4_int), + .rst(~gt_reset_rx_done), + .sync_reset_out(qsfp2_rx_rst_4_int) +); + +eth_phy_10g #( + .BIT_REVERSE(1), + .TX_SERDES_PIPELINE(2), + .RX_SERDES_PIPELINE(2), + .COUNT_125US(125000/2.56) +) +qsfp2_phy_4_inst ( + .tx_clk(qsfp2_tx_clk_4_int), + .tx_rst(qsfp2_tx_rst_4_int), + .rx_clk(qsfp2_rx_clk_4_int), + .rx_rst(qsfp2_rx_rst_4_int), + .xgmii_txd(qsfp2_txd_4_int), + .xgmii_txc(qsfp2_txc_4_int), + .xgmii_rxd(qsfp2_rxd_4_int), + .xgmii_rxc(qsfp2_rxc_4_int), + .serdes_tx_data(qsfp2_gt_txdata_4), + .serdes_tx_hdr(qsfp2_gt_txheader_4), + .serdes_rx_data(qsfp2_gt_rxdata_4), + .serdes_rx_hdr(qsfp2_gt_rxheader_4), + .serdes_rx_bitslip(qsfp2_gt_rxgearboxslip_4), + .rx_block_lock(qsfp2_rx_block_lock_4), + .rx_high_ber() +); + +// SGMII interface to PHY +wire phy_gmii_clk_int; +wire phy_gmii_rst_int; +wire phy_gmii_clk_en_int; +wire [7:0] phy_gmii_txd_int; +wire phy_gmii_tx_en_int; +wire phy_gmii_tx_er_int; +wire [7:0] phy_gmii_rxd_int; +wire phy_gmii_rx_dv_int; +wire phy_gmii_rx_er_int; + +wire [15:0] gig_eth_pcspma_status_vector; + +wire gig_eth_pcspma_status_link_status = gig_eth_pcspma_status_vector[0]; +wire gig_eth_pcspma_status_link_synchronization = gig_eth_pcspma_status_vector[1]; +wire gig_eth_pcspma_status_rudi_c = gig_eth_pcspma_status_vector[2]; +wire gig_eth_pcspma_status_rudi_i = gig_eth_pcspma_status_vector[3]; +wire gig_eth_pcspma_status_rudi_invalid = gig_eth_pcspma_status_vector[4]; +wire gig_eth_pcspma_status_rxdisperr = gig_eth_pcspma_status_vector[5]; +wire gig_eth_pcspma_status_rxnotintable = gig_eth_pcspma_status_vector[6]; +wire gig_eth_pcspma_status_phy_link_status = gig_eth_pcspma_status_vector[7]; +wire [1:0] gig_eth_pcspma_status_remote_fault_encdg = gig_eth_pcspma_status_vector[9:8]; +wire [1:0] gig_eth_pcspma_status_speed = gig_eth_pcspma_status_vector[11:10]; +wire gig_eth_pcspma_status_duplex = gig_eth_pcspma_status_vector[12]; +wire gig_eth_pcspma_status_remote_fault = gig_eth_pcspma_status_vector[13]; +wire [1:0] gig_eth_pcspma_status_pause = gig_eth_pcspma_status_vector[15:14]; + +wire [4:0] gig_eth_pcspma_config_vector; + +assign gig_eth_pcspma_config_vector[4] = 1'b1; // autonegotiation enable +assign gig_eth_pcspma_config_vector[3] = 1'b0; // isolate +assign gig_eth_pcspma_config_vector[2] = 1'b0; // power down +assign gig_eth_pcspma_config_vector[1] = 1'b0; // loopback enable +assign gig_eth_pcspma_config_vector[0] = 1'b0; // unidirectional enable + +wire [15:0] gig_eth_pcspma_an_config_vector; + +assign gig_eth_pcspma_an_config_vector[15] = 1'b1; // SGMII link status +assign gig_eth_pcspma_an_config_vector[14] = 1'b1; // SGMII Acknowledge +assign gig_eth_pcspma_an_config_vector[13:12] = 2'b01; // full duplex +assign gig_eth_pcspma_an_config_vector[11:10] = 2'b10; // SGMII speed +assign gig_eth_pcspma_an_config_vector[9] = 1'b0; // reserved +assign gig_eth_pcspma_an_config_vector[8:7] = 2'b00; // pause frames - SGMII reserved +assign gig_eth_pcspma_an_config_vector[6] = 1'b0; // reserved +assign gig_eth_pcspma_an_config_vector[5] = 1'b0; // full duplex - SGMII reserved +assign gig_eth_pcspma_an_config_vector[4:1] = 4'b0000; // reserved +assign gig_eth_pcspma_an_config_vector[0] = 1'b1; // SGMII + +gig_ethernet_pcs_pma_0 +eth_pcspma ( + // SGMII + .txp_0 (phy_sgmii_tx_p), + .txn_0 (phy_sgmii_tx_n), + .rxp_0 (phy_sgmii_rx_p), + .rxn_0 (phy_sgmii_rx_n), + + // Ref clock from PHY + .refclk625_p (phy_sgmii_clk_p), + .refclk625_n (phy_sgmii_clk_n), + + // async reset + .reset (rst_125mhz_int), + + // clock and reset outputs + .clk125_out (phy_gmii_clk_int), + .clk312_out (), + .rst_125_out (phy_gmii_rst_int), + .tx_logic_reset (), + .rx_logic_reset (), + .tx_locked (), + .rx_locked (), + .tx_pll_clk_out (), + .rx_pll_clk_out (), + + // MAC clocking + .sgmii_clk_r_0 (), + .sgmii_clk_f_0 (), + .sgmii_clk_en_0 (phy_gmii_clk_en_int), + + // Speed control + .speed_is_10_100_0 (gig_eth_pcspma_status_speed != 2'b10), + .speed_is_100_0 (gig_eth_pcspma_status_speed == 2'b01), + + // Internal GMII + .gmii_txd_0 (phy_gmii_txd_int), + .gmii_tx_en_0 (phy_gmii_tx_en_int), + .gmii_tx_er_0 (phy_gmii_tx_er_int), + .gmii_rxd_0 (phy_gmii_rxd_int), + .gmii_rx_dv_0 (phy_gmii_rx_dv_int), + .gmii_rx_er_0 (phy_gmii_rx_er_int), + .gmii_isolate_0 (), + + // Configuration + .configuration_vector_0 (gig_eth_pcspma_config_vector), + + .an_interrupt_0 (), + .an_adv_config_vector_0 (gig_eth_pcspma_an_config_vector), + .an_restart_config_0 (1'b0), + + // Status + .status_vector_0 (gig_eth_pcspma_status_vector), + .signal_detect_0 (1'b1), + + // Cascade + .tx_bsc_rst_out (), + .rx_bsc_rst_out (), + .tx_bs_rst_out (), + .rx_bs_rst_out (), + .tx_rst_dly_out (), + .rx_rst_dly_out (), + .tx_bsc_en_vtc_out (), + .rx_bsc_en_vtc_out (), + .tx_bs_en_vtc_out (), + .rx_bs_en_vtc_out (), + .riu_clk_out (), + .riu_addr_out (), + .riu_wr_data_out (), + .riu_wr_en_out (), + .riu_nibble_sel_out (), + .riu_rddata_1 (16'b0), + .riu_valid_1 (1'b0), + .riu_prsnt_1 (1'b0), + .riu_rddata_2 (16'b0), + .riu_valid_2 (1'b0), + .riu_prsnt_2 (1'b0), + .riu_rddata_3 (16'b0), + .riu_valid_3 (1'b0), + .riu_prsnt_3 (1'b0), + .rx_btval_1 (), + .rx_btval_2 (), + .rx_btval_3 (), + .tx_dly_rdy_1 (1'b1), + .rx_dly_rdy_1 (1'b1), + .rx_vtc_rdy_1 (1'b1), + .tx_vtc_rdy_1 (1'b1), + .tx_dly_rdy_2 (1'b1), + .rx_dly_rdy_2 (1'b1), + .rx_vtc_rdy_2 (1'b1), + .tx_vtc_rdy_2 (1'b1), + .tx_dly_rdy_3 (1'b1), + .rx_dly_rdy_3 (1'b1), + .rx_vtc_rdy_3 (1'b1), + .tx_vtc_rdy_3 (1'b1), + .tx_rdclk_out () +); + +reg [19:0] delay_reg = 20'hfffff; + +reg [4:0] mdio_cmd_phy_addr = 5'h03; +reg [4:0] mdio_cmd_reg_addr = 5'h00; +reg [15:0] mdio_cmd_data = 16'd0; +reg [1:0] mdio_cmd_opcode = 2'b01; +reg mdio_cmd_valid = 1'b0; +wire mdio_cmd_ready; + +reg [3:0] state_reg = 0; + +always @(posedge clk_125mhz_int) begin + if (rst_125mhz_int) begin + state_reg <= 0; + delay_reg <= 20'hfffff; + mdio_cmd_reg_addr <= 5'h00; + mdio_cmd_data <= 16'd0; + mdio_cmd_valid <= 1'b0; + end else begin + mdio_cmd_valid <= mdio_cmd_valid & !mdio_cmd_ready; + if (delay_reg > 0) begin + delay_reg <= delay_reg - 1; + end else if (!mdio_cmd_ready) begin + // wait for ready + state_reg <= state_reg; + end else begin + mdio_cmd_valid <= 1'b0; + case (state_reg) + // set SGMII autonegotiation timer to 11 ms + // write 0x0070 to CFG4 (0x0031) + 4'd0: begin + // write to REGCR to load address + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h001F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd1; + end + 4'd1: begin + // write address of CFG4 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h0031; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd2; + end + 4'd2: begin + // write to REGCR to load data + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h401F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd3; + end + 4'd3: begin + // write data for CFG4 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h0070; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd4; + end + // enable SGMII clock output + // write 0x4000 to SGMIICTL1 (0x00D3) + 4'd4: begin + // write to REGCR to load address + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h001F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd5; + end + 4'd5: begin + // write address of SGMIICTL1 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h00D3; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd6; + end + 4'd6: begin + // write to REGCR to load data + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h401F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd7; + end + 4'd7: begin + // write data for SGMIICTL1 to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h4000; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd8; + end + // enable 10Mbps operation + // write 0x0015 to 10M_SGMII_CFG (0x016F) + 4'd8: begin + // write to REGCR to load address + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h001F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd9; + end + 4'd9: begin + // write address of 10M_SGMII_CFG to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h016F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd10; + end + 4'd10: begin + // write to REGCR to load data + mdio_cmd_reg_addr <= 5'h0D; + mdio_cmd_data <= 16'h401F; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd11; + end + 4'd11: begin + // write data for 10M_SGMII_CFG to ADDAR + mdio_cmd_reg_addr <= 5'h0E; + mdio_cmd_data <= 16'h0015; + mdio_cmd_valid <= 1'b1; + state_reg <= 4'd12; + end + 4'd12: begin + // done + state_reg <= 4'd12; + end + endcase + end + end +end + +wire mdc; +wire mdio_i; +wire mdio_o; +wire mdio_t; + +mdio_master +mdio_master_inst ( + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + + .cmd_phy_addr(mdio_cmd_phy_addr), + .cmd_reg_addr(mdio_cmd_reg_addr), + .cmd_data(mdio_cmd_data), + .cmd_opcode(mdio_cmd_opcode), + .cmd_valid(mdio_cmd_valid), + .cmd_ready(mdio_cmd_ready), + + .data_out(), + .data_out_valid(), + .data_out_ready(1'b1), + + .mdc_o(mdc), + .mdio_i(mdio_i), + .mdio_o(mdio_o), + .mdio_t(mdio_t), + + .busy(), + + .prescale(8'd3) +); + +assign phy_mdc = mdc; +assign mdio_i = phy_mdio; +assign phy_mdio = mdio_t ? 1'bz : mdio_o; + +wire [7:0] led_int; + +assign led = sw[0] ? {qsfp2_rx_block_lock_4, qsfp2_rx_block_lock_3, qsfp2_rx_block_lock_2, qsfp2_rx_block_lock_1, qsfp1_rx_block_lock_4, qsfp1_rx_block_lock_3, qsfp1_rx_block_lock_2, qsfp1_rx_block_lock_1} : led_int; + +fpga_core +core_inst ( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + .clk(clk_156mhz_int), + .rst(rst_156mhz_int), + /* + * GPIO + */ + .btnu(btnu_int), + .btnl(btnl_int), + .btnd(btnd_int), + .btnr(btnr_int), + .btnc(btnc_int), + .sw(sw_int), + .led(led_int), + /* + * Ethernet: QSFP28 + */ + .qsfp1_tx_clk_1(qsfp1_tx_clk_1_int), + .qsfp1_tx_rst_1(qsfp1_tx_rst_1_int), + .qsfp1_txd_1(qsfp1_txd_1_int), + .qsfp1_txc_1(qsfp1_txc_1_int), + .qsfp1_rx_clk_1(qsfp1_rx_clk_1_int), + .qsfp1_rx_rst_1(qsfp1_rx_rst_1_int), + .qsfp1_rxd_1(qsfp1_rxd_1_int), + .qsfp1_rxc_1(qsfp1_rxc_1_int), + .qsfp1_tx_clk_2(qsfp1_tx_clk_2_int), + .qsfp1_tx_rst_2(qsfp1_tx_rst_2_int), + .qsfp1_txd_2(qsfp1_txd_2_int), + .qsfp1_txc_2(qsfp1_txc_2_int), + .qsfp1_rx_clk_2(qsfp1_rx_clk_2_int), + .qsfp1_rx_rst_2(qsfp1_rx_rst_2_int), + .qsfp1_rxd_2(qsfp1_rxd_2_int), + .qsfp1_rxc_2(qsfp1_rxc_2_int), + .qsfp1_tx_clk_3(qsfp1_tx_clk_3_int), + .qsfp1_tx_rst_3(qsfp1_tx_rst_3_int), + .qsfp1_txd_3(qsfp1_txd_3_int), + .qsfp1_txc_3(qsfp1_txc_3_int), + .qsfp1_rx_clk_3(qsfp1_rx_clk_3_int), + .qsfp1_rx_rst_3(qsfp1_rx_rst_3_int), + .qsfp1_rxd_3(qsfp1_rxd_3_int), + .qsfp1_rxc_3(qsfp1_rxc_3_int), + .qsfp1_tx_clk_4(qsfp1_tx_clk_4_int), + .qsfp1_tx_rst_4(qsfp1_tx_rst_4_int), + .qsfp1_txd_4(qsfp1_txd_4_int), + .qsfp1_txc_4(qsfp1_txc_4_int), + .qsfp1_rx_clk_4(qsfp1_rx_clk_4_int), + .qsfp1_rx_rst_4(qsfp1_rx_rst_4_int), + .qsfp1_rxd_4(qsfp1_rxd_4_int), + .qsfp1_rxc_4(qsfp1_rxc_4_int), + .qsfp2_tx_clk_1(qsfp2_tx_clk_1_int), + .qsfp2_tx_rst_1(qsfp2_tx_rst_1_int), + .qsfp2_txd_1(qsfp2_txd_1_int), + .qsfp2_txc_1(qsfp2_txc_1_int), + .qsfp2_rx_clk_1(qsfp2_rx_clk_1_int), + .qsfp2_rx_rst_1(qsfp2_rx_rst_1_int), + .qsfp2_rxd_1(qsfp2_rxd_1_int), + .qsfp2_rxc_1(qsfp2_rxc_1_int), + .qsfp2_tx_clk_2(qsfp2_tx_clk_2_int), + .qsfp2_tx_rst_2(qsfp2_tx_rst_2_int), + .qsfp2_txd_2(qsfp2_txd_2_int), + .qsfp2_txc_2(qsfp2_txc_2_int), + .qsfp2_rx_clk_2(qsfp2_rx_clk_2_int), + .qsfp2_rx_rst_2(qsfp2_rx_rst_2_int), + .qsfp2_rxd_2(qsfp2_rxd_2_int), + .qsfp2_rxc_2(qsfp2_rxc_2_int), + .qsfp2_tx_clk_3(qsfp2_tx_clk_3_int), + .qsfp2_tx_rst_3(qsfp2_tx_rst_3_int), + .qsfp2_txd_3(qsfp2_txd_3_int), + .qsfp2_txc_3(qsfp2_txc_3_int), + .qsfp2_rx_clk_3(qsfp2_rx_clk_3_int), + .qsfp2_rx_rst_3(qsfp2_rx_rst_3_int), + .qsfp2_rxd_3(qsfp2_rxd_3_int), + .qsfp2_rxc_3(qsfp2_rxc_3_int), + .qsfp2_tx_clk_4(qsfp2_tx_clk_4_int), + .qsfp2_tx_rst_4(qsfp2_tx_rst_4_int), + .qsfp2_txd_4(qsfp2_txd_4_int), + .qsfp2_txc_4(qsfp2_txc_4_int), + .qsfp2_rx_clk_4(qsfp2_rx_clk_4_int), + .qsfp2_rx_rst_4(qsfp2_rx_rst_4_int), + .qsfp2_rxd_4(qsfp2_rxd_4_int), + .qsfp2_rxc_4(qsfp2_rxc_4_int), + /* + * Ethernet: 1000BASE-T SGMII + */ + .phy_gmii_clk(phy_gmii_clk_int), + .phy_gmii_rst(phy_gmii_rst_int), + .phy_gmii_clk_en(phy_gmii_clk_en_int), + .phy_gmii_rxd(phy_gmii_rxd_int), + .phy_gmii_rx_dv(phy_gmii_rx_dv_int), + .phy_gmii_rx_er(phy_gmii_rx_er_int), + .phy_gmii_txd(phy_gmii_txd_int), + .phy_gmii_tx_en(phy_gmii_tx_en_int), + .phy_gmii_tx_er(phy_gmii_tx_er_int), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + /* + * UART: 115200 bps, 8N1 + */ + .uart_rxd(uart_rxd_int), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts_int) +); + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/rtl/fpga_core.v b/fpga/lib/eth/example/VCU118/fpga_25g/rtl/fpga_core.v new file mode 100644 index 000000000..a523e3afd --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/rtl/fpga_core.v @@ -0,0 +1,908 @@ +/* + +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 1ns / 1ps + +/* + * FPGA core logic + */ +module fpga_core # +( + parameter TARGET = "XILINX" +) +( + /* + * Clock: 156.25MHz + * Synchronous reset + */ + input wire clk, + input wire rst, + + /* + * 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: QSFP28 + */ + input wire qsfp1_tx_clk_1, + input wire qsfp1_tx_rst_1, + output wire [63:0] qsfp1_txd_1, + output wire [7:0] qsfp1_txc_1, + input wire qsfp1_rx_clk_1, + input wire qsfp1_rx_rst_1, + input wire [63:0] qsfp1_rxd_1, + input wire [7:0] qsfp1_rxc_1, + input wire qsfp1_tx_clk_2, + input wire qsfp1_tx_rst_2, + output wire [63:0] qsfp1_txd_2, + output wire [7:0] qsfp1_txc_2, + input wire qsfp1_rx_clk_2, + input wire qsfp1_rx_rst_2, + input wire [63:0] qsfp1_rxd_2, + input wire [7:0] qsfp1_rxc_2, + input wire qsfp1_tx_clk_3, + input wire qsfp1_tx_rst_3, + output wire [63:0] qsfp1_txd_3, + output wire [7:0] qsfp1_txc_3, + input wire qsfp1_rx_clk_3, + input wire qsfp1_rx_rst_3, + input wire [63:0] qsfp1_rxd_3, + input wire [7:0] qsfp1_rxc_3, + input wire qsfp1_tx_clk_4, + input wire qsfp1_tx_rst_4, + output wire [63:0] qsfp1_txd_4, + output wire [7:0] qsfp1_txc_4, + input wire qsfp1_rx_clk_4, + input wire qsfp1_rx_rst_4, + input wire [63:0] qsfp1_rxd_4, + input wire [7:0] qsfp1_rxc_4, + input wire qsfp2_tx_clk_1, + input wire qsfp2_tx_rst_1, + output wire [63:0] qsfp2_txd_1, + output wire [7:0] qsfp2_txc_1, + input wire qsfp2_rx_clk_1, + input wire qsfp2_rx_rst_1, + input wire [63:0] qsfp2_rxd_1, + input wire [7:0] qsfp2_rxc_1, + input wire qsfp2_tx_clk_2, + input wire qsfp2_tx_rst_2, + output wire [63:0] qsfp2_txd_2, + output wire [7:0] qsfp2_txc_2, + input wire qsfp2_rx_clk_2, + input wire qsfp2_rx_rst_2, + input wire [63:0] qsfp2_rxd_2, + input wire [7:0] qsfp2_rxc_2, + input wire qsfp2_tx_clk_3, + input wire qsfp2_tx_rst_3, + output wire [63:0] qsfp2_txd_3, + output wire [7:0] qsfp2_txc_3, + input wire qsfp2_rx_clk_3, + input wire qsfp2_rx_rst_3, + input wire [63:0] qsfp2_rxd_3, + input wire [7:0] qsfp2_rxc_3, + input wire qsfp2_tx_clk_4, + input wire qsfp2_tx_rst_4, + output wire [63:0] qsfp2_txd_4, + output wire [7:0] qsfp2_txc_4, + input wire qsfp2_rx_clk_4, + input wire qsfp2_rx_rst_4, + input wire [63:0] qsfp2_rxd_4, + input wire [7:0] qsfp2_rxc_4, + + /* + * Ethernet: 1000BASE-T SGMII + */ + input wire phy_gmii_clk, + input wire phy_gmii_rst, + input wire phy_gmii_clk_en, + input wire [7:0] phy_gmii_rxd, + input wire phy_gmii_rx_dv, + input wire phy_gmii_rx_er, + output wire [7:0] phy_gmii_txd, + output wire phy_gmii_tx_en, + output wire phy_gmii_tx_er, + output wire phy_reset_n, + input wire phy_int_n, + + /* + * UART: 115200 bps, 8N1 + */ + input wire uart_rxd, + output wire uart_txd, + output wire uart_rts, + input wire uart_cts +); + +// AXI between MAC and Ethernet modules +wire [63:0] mac_rx_axis_tdata; +wire [7:0] mac_rx_axis_tkeep; +wire mac_rx_axis_tvalid; +wire mac_rx_axis_tready; +wire mac_rx_axis_tlast; +wire mac_rx_axis_tuser; + +wire [63:0] mac_tx_axis_tdata; +wire [7:0] mac_tx_axis_tkeep; +wire mac_tx_axis_tvalid; +wire mac_tx_axis_tready; +wire mac_tx_axis_tlast; +wire mac_tx_axis_tuser; + +wire [63:0] rx_axis_tdata; +wire [7:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tready; +wire rx_axis_tlast; +wire rx_axis_tuser; + +wire [63:0] tx_axis_tdata; +wire [7:0] tx_axis_tkeep; +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 [63:0] rx_eth_payload_axis_tdata; +wire [7:0] rx_eth_payload_axis_tkeep; +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 [63:0] tx_eth_payload_axis_tdata; +wire [7:0] tx_eth_payload_axis_tkeep; +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 [63:0] rx_ip_payload_axis_tdata; +wire [7:0] rx_ip_payload_axis_tkeep; +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 [63:0] tx_ip_payload_axis_tdata; +wire [7:0] tx_ip_payload_axis_tkeep; +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 [63:0] rx_udp_payload_axis_tdata; +wire [7:0] rx_udp_payload_axis_tkeep; +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 [63:0] tx_udp_payload_axis_tdata; +wire [7:0] tx_udp_payload_axis_tkeep; +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 [63:0] rx_fifo_udp_payload_axis_tdata; +wire [7:0] rx_fifo_udp_payload_axis_tkeep; +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 [63:0] tx_fifo_udp_payload_axis_tdata; +wire [7:0] tx_fifo_udp_payload_axis_tkeep; +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_tkeep = 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_tkeep = tx_fifo_udp_payload_axis_tkeep; +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_tkeep = rx_udp_payload_axis_tkeep; +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 + valid_last <= tx_udp_payload_axis_tvalid; + if (tx_udp_payload_axis_tvalid && !valid_last) begin + led_reg <= tx_udp_payload_axis_tdata; + end + end +end + +//assign led = sw; +assign led = led_reg; +assign phy_reset_n = !rst; + +assign qsfp1_txd_2 = 64'h0707070707070707; +assign qsfp1_txc_2 = 8'hff; +assign qsfp1_txd_3 = 64'h0707070707070707; +assign qsfp1_txc_3 = 8'hff; +assign qsfp1_txd_4 = 64'h0707070707070707; +assign qsfp1_txc_4 = 8'hff; + +assign qsfp2_txd_1 = 64'h0707070707070707; +assign qsfp2_txc_1 = 8'hff; +assign qsfp2_txd_2 = 64'h0707070707070707; +assign qsfp2_txc_2 = 8'hff; +assign qsfp2_txd_3 = 64'h0707070707070707; +assign qsfp2_txc_3 = 8'hff; +assign qsfp2_txd_4 = 64'h0707070707070707; +assign qsfp2_txc_4 = 8'hff; + +eth_mac_10g_fifo #( + .ENABLE_PADDING(1), + .ENABLE_DIC(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(9), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(9), + .RX_FRAME_FIFO(1) +) +eth_mac_10g_fifo_inst ( + .rx_clk(qsfp1_rx_clk_1), + .rx_rst(qsfp1_rx_rst_1), + .tx_clk(qsfp1_tx_clk_1), + .tx_rst(qsfp1_tx_rst_1), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(mac_tx_axis_tdata), + .tx_axis_tkeep(mac_tx_axis_tkeep), + .tx_axis_tvalid(mac_tx_axis_tvalid), + .tx_axis_tready(mac_tx_axis_tready), + .tx_axis_tlast(mac_tx_axis_tlast), + .tx_axis_tuser(mac_tx_axis_tuser), + + .rx_axis_tdata(mac_rx_axis_tdata), + .rx_axis_tkeep(mac_rx_axis_tkeep), + .rx_axis_tvalid(mac_rx_axis_tvalid), + .rx_axis_tready(mac_rx_axis_tready), + .rx_axis_tlast(mac_rx_axis_tlast), + .rx_axis_tuser(mac_rx_axis_tuser), + + .xgmii_rxd(qsfp1_rxd_1), + .xgmii_rxc(qsfp1_rxc_1), + .xgmii_txd(qsfp1_txd_1), + .xgmii_txc(qsfp1_txc_1), + + .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(), + + .ifg_delay(8'd12) +); + +// 1G interface for debugging +wire [7:0] gig_rx_axis_tdata; +wire gig_rx_axis_tvalid; +wire gig_rx_axis_tready; +wire gig_rx_axis_tlast; +wire gig_rx_axis_tuser; + +wire [7:0] gig_tx_axis_tdata; +wire gig_tx_axis_tvalid; +wire gig_tx_axis_tready; +wire gig_tx_axis_tlast; +wire gig_tx_axis_tuser; + +wire [63:0] gig_rx_axis_tdata_64; +wire [7:0] gig_rx_axis_tkeep_64; +wire gig_rx_axis_tvalid_64; +wire gig_rx_axis_tready_64; +wire gig_rx_axis_tlast_64; +wire gig_rx_axis_tuser_64; + +wire [63:0] gig_tx_axis_tdata_64; +wire [7:0] gig_tx_axis_tkeep_64; +wire gig_tx_axis_tvalid_64; +wire gig_tx_axis_tready_64; +wire gig_tx_axis_tlast_64; +wire gig_tx_axis_tuser_64; + +eth_mac_1g_fifo #( + .ENABLE_PADDING(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_ADDR_WIDTH(12), + .TX_FRAME_FIFO(1), + .RX_FIFO_ADDR_WIDTH(12), + .RX_FRAME_FIFO(1) +) +eth_mac_1g_inst ( + .rx_clk(phy_gmii_clk), + .rx_rst(phy_gmii_rst), + .tx_clk(phy_gmii_clk), + .tx_rst(phy_gmii_rst), + .logic_clk(clk), + .logic_rst(rst), + + .tx_axis_tdata(gig_tx_axis_tdata), + .tx_axis_tvalid(gig_tx_axis_tvalid), + .tx_axis_tready(gig_tx_axis_tready), + .tx_axis_tlast(gig_tx_axis_tlast), + .tx_axis_tuser(gig_tx_axis_tuser), + + .rx_axis_tdata(gig_rx_axis_tdata), + .rx_axis_tvalid(gig_rx_axis_tvalid), + .rx_axis_tready(gig_rx_axis_tready), + .rx_axis_tlast(gig_rx_axis_tlast), + .rx_axis_tuser(gig_rx_axis_tuser), + + .gmii_rxd(phy_gmii_rxd), + .gmii_rx_dv(phy_gmii_rx_dv), + .gmii_rx_er(phy_gmii_rx_er), + .gmii_txd(phy_gmii_txd), + .gmii_tx_en(phy_gmii_tx_en), + .gmii_tx_er(phy_gmii_tx_er), + + .rx_clk_enable(phy_gmii_clk_en), + .tx_clk_enable(phy_gmii_clk_en), + .rx_mii_select(1'b0), + .tx_mii_select(1'b0), + + .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(), + + .ifg_delay(12) +); + +axis_adapter #( + .S_DATA_WIDTH(8), + .M_DATA_WIDTH(64), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1) +) +gig_rx_axis_adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(gig_rx_axis_tdata), + .s_axis_tkeep(1'b1), + .s_axis_tvalid(gig_rx_axis_tvalid), + .s_axis_tready(gig_rx_axis_tready), + .s_axis_tlast(gig_rx_axis_tlast), + .s_axis_tuser(gig_rx_axis_tuser), + // AXI output + .m_axis_tdata(gig_rx_axis_tdata_64), + .m_axis_tkeep(gig_rx_axis_tkeep_64), + .m_axis_tvalid(gig_rx_axis_tvalid_64), + .m_axis_tready(gig_rx_axis_tready_64), + .m_axis_tlast(gig_rx_axis_tlast_64), + .m_axis_tuser(gig_rx_axis_tuser_64) +); + +axis_adapter #( + .S_DATA_WIDTH(64), + .M_DATA_WIDTH(8), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1) +) +gig_tx_axis_adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(gig_tx_axis_tdata_64), + .s_axis_tkeep(gig_tx_axis_tkeep_64), + .s_axis_tvalid(gig_tx_axis_tvalid_64), + .s_axis_tready(gig_tx_axis_tready_64), + .s_axis_tlast(gig_tx_axis_tlast_64), + .s_axis_tuser(gig_tx_axis_tuser_64), + // AXI output + .m_axis_tdata(gig_tx_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(gig_tx_axis_tvalid), + .m_axis_tready(gig_tx_axis_tready), + .m_axis_tlast(gig_tx_axis_tlast), + .m_axis_tuser(gig_tx_axis_tuser) +); + +// tap port mux logic +// sw[3] enable +// sw[2] select 0 rx, 1 tx + +reg [1:0] mac_rx_tdest; +reg [1:0] tx_tdest; +reg [1:0] gig_rx_tdest; + +always @* begin + if (sw[3]) begin + if (sw[2]) begin + // Tap on TX path + // MAC RX out -> stack RX in + // stack TX out -> gig TX in + // gig RX out -> MAC TX in + mac_rx_tdest = 2'd1; + tx_tdest = 2'd2; + gig_rx_tdest = 2'd0; + end else begin + // Tap on RX path + // MAC RX out -> gig TX in + // stack TX out -> MAC TX in + // gig RX out -> stack RX in + mac_rx_tdest = 2'd2; + tx_tdest = 2'd0; + gig_rx_tdest = 2'd1; + end + end else begin + // Tap disabled + // MAC RX out -> stack RX in + // stack TX out -> MAC TX in + // gig RX out -> blackhole + mac_rx_tdest = 2'd1; + tx_tdest = 2'd0; + gig_rx_tdest = 2'd3; + end +end + +axis_switch #( + .S_COUNT(3), + .M_COUNT(3), + .DATA_WIDTH(64), + .KEEP_WIDTH(8), + .ID_ENABLE(0), + .DEST_WIDTH(2), + .USER_ENABLE(1), + .USER_WIDTH(1), + .M_BASE({2'd2, 2'd1, 2'd0}), + .M_TOP({2'd2, 2'd1, 2'd0}), + .M_CONNECT({3{3'b111}}), + .S_REG_TYPE(0), + .M_REG_TYPE(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +axis_switch_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ gig_rx_axis_tdata_64, tx_axis_tdata, mac_rx_axis_tdata}), + .s_axis_tkeep({ gig_rx_axis_tkeep_64, tx_axis_tkeep, mac_rx_axis_tkeep}), + .s_axis_tvalid({gig_rx_axis_tvalid_64, tx_axis_tvalid, mac_rx_axis_tvalid}), + .s_axis_tready({gig_rx_axis_tready_64, tx_axis_tready, mac_rx_axis_tready}), + .s_axis_tlast({ gig_rx_axis_tlast_64, tx_axis_tlast, mac_rx_axis_tlast}), + .s_axis_tid(0), + .s_axis_tdest({ gig_rx_tdest, tx_tdest, mac_rx_tdest}), + .s_axis_tuser({ gig_rx_axis_tuser_64, tx_axis_tuser, mac_rx_axis_tuser}), + // AXI outputs + .m_axis_tdata({ gig_tx_axis_tdata_64, rx_axis_tdata, mac_tx_axis_tdata}), + .m_axis_tkeep({ gig_tx_axis_tkeep_64, rx_axis_tkeep, mac_tx_axis_tkeep}), + .m_axis_tvalid({gig_tx_axis_tvalid_64, rx_axis_tvalid, mac_tx_axis_tvalid}), + .m_axis_tready({gig_tx_axis_tready_64, rx_axis_tready, mac_tx_axis_tready}), + .m_axis_tlast({ gig_tx_axis_tlast_64, rx_axis_tlast, mac_tx_axis_tlast}), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser({ gig_tx_axis_tuser_64, rx_axis_tuser, mac_tx_axis_tuser}) +); + +eth_axis_rx_64 +eth_axis_rx_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(rx_axis_tdata), + .s_axis_tkeep(rx_axis_tkeep), + .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_tkeep(rx_eth_payload_axis_tkeep), + .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_64 +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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_axis_tkeep), + .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_64 +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_tkeep(rx_eth_payload_axis_tkeep), + .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_tkeep(tx_eth_payload_axis_tkeep), + .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_tkeep(tx_ip_payload_axis_tkeep), + .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_tkeep(rx_ip_payload_axis_tkeep), + .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_tkeep(tx_udp_payload_axis_tkeep), + .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_tkeep(rx_udp_payload_axis_tkeep), + .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(1'b0) +); + +axis_fifo #( + .ADDR_WIDTH(10), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .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(rx_fifo_udp_payload_axis_tkeep), + .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(tx_fifo_udp_payload_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/fpga/lib/eth/example/VCU118/fpga_25g/rtl/mdio_master.v b/fpga/lib/eth/example/VCU118/fpga_25g/rtl/mdio_master.v new file mode 100644 index 000000000..1dc56a8cf --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/rtl/mdio_master.v @@ -0,0 +1,225 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * MDIO master + */ +module mdio_master ( + input wire clk, + input wire rst, + + /* + * Host interface + */ + input wire [4:0] cmd_phy_addr, + input wire [4:0] cmd_reg_addr, + input wire [15:0] cmd_data, + input wire [1:0] cmd_opcode, + input wire cmd_valid, + output wire cmd_ready, + + output wire [15:0] data_out, + output wire data_out_valid, + input wire data_out_ready, + + /* + * MDIO to PHY + */ + output wire mdc_o, + input wire mdio_i, + output wire mdio_o, + output wire mdio_t, + + /* + * Status + */ + output wire busy, + + /* + * Configuration + */ + input wire [7:0] prescale +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PREAMBLE = 2'd1, + STATE_TRANSFER = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [16:0] count_reg = 16'd0, count_next; +reg [6:0] bit_count_reg = 6'd0, bit_count_next; +reg cycle_reg = 1'b0, cycle_next; + +reg [31:0] data_reg = 32'd0, data_next; + +reg [1:0] op_reg = 2'b00, op_next; + +reg cmd_ready_reg = 1'b0, cmd_ready_next; + +reg [15:0] data_out_reg = 15'd0, data_out_next; +reg data_out_valid_reg = 1'b0, data_out_valid_next; + +reg mdio_i_reg = 1'b1; + +reg mdc_o_reg = 1'b0, mdc_o_next; +reg mdio_o_reg = 1'b0, mdio_o_next; +reg mdio_t_reg = 1'b1, mdio_t_next; + +reg busy_reg = 1'b0; + +assign cmd_ready = cmd_ready_reg; + +assign data_out = data_out_reg; +assign data_out_valid = data_out_valid_reg; + +assign mdc_o = mdc_o_reg; +assign mdio_o = mdio_o_reg; +assign mdio_t = mdio_t_reg; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + count_next = count_reg; + bit_count_next = bit_count_reg; + cycle_next = cycle_reg; + + data_next = data_reg; + + op_next = op_reg; + + cmd_ready_next = 1'b0; + + data_out_next = data_out_reg; + data_out_valid_next = data_out_valid_reg & ~data_out_ready; + + mdc_o_next = mdc_o_reg; + mdio_o_next = mdio_o_reg; + mdio_t_next = mdio_t_reg; + + if (count_reg > 16'd0) begin + count_next = count_reg - 16'd1; + state_next = state_reg; + end else if (cycle_reg) begin + cycle_next = 1'b0; + mdc_o_next = 1'b1; + count_next = prescale; + state_next = state_reg; + end else begin + mdc_o_next = 1'b0; + case (state_reg) + STATE_IDLE: begin + // idle - accept new command + cmd_ready_next = ~data_out_valid; + + if (cmd_ready & cmd_valid) begin + cmd_ready_next = 1'b0; + data_next = {2'b01, cmd_opcode, cmd_phy_addr, cmd_reg_addr, 2'b10, cmd_data}; + op_next = cmd_opcode; + mdio_t_next = 1'b0; + mdio_o_next = 1'b1; + bit_count_next = 6'd32; + cycle_next = 1'b1; + count_next = prescale; + state_next = STATE_PREAMBLE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + cycle_next = 1'b1; + count_next = prescale; + if (bit_count_reg > 6'd1) begin + bit_count_next = bit_count_reg - 6'd1; + state_next = STATE_PREAMBLE; + end else begin + bit_count_next = 6'd32; + {mdio_o_next, data_next} = {data_reg, mdio_i_reg}; + state_next = STATE_TRANSFER; + end + end + STATE_TRANSFER: begin + cycle_next = 1'b1; + count_next = prescale; + if ((op_reg == 2'b10 || op_reg == 2'b11) && bit_count_reg == 6'd19) begin + mdio_t_next = 1'b1; + end + if (bit_count_reg > 6'd1) begin + bit_count_next = bit_count_reg - 6'd1; + {mdio_o_next, data_next} = {data_reg, mdio_i_reg}; + state_next = STATE_TRANSFER; + end else begin + if (op_reg == 2'b10 || op_reg == 2'b11) begin + data_out_next = data_reg[15:0]; + data_out_valid_next = 1'b1; + end + mdio_t_next = 1'b1; + state_next = STATE_IDLE; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + count_reg <= 16'd0; + bit_count_reg <= 6'd0; + cycle_reg <= 1'b0; + cmd_ready_reg <= 1'b0; + data_out_valid_reg <= 1'b0; + mdc_o_reg <= 1'b0; + mdio_o_reg <= 1'b0; + mdio_t_reg <= 1'b1; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + count_reg <= count_next; + bit_count_reg <= bit_count_next; + cycle_reg <= cycle_next; + cmd_ready_reg <= cmd_ready_next; + data_out_valid_reg <= data_out_valid_next; + mdc_o_reg <= mdc_o_next; + mdio_o_reg <= mdio_o_next; + mdio_t_reg <= mdio_t_next; + busy_reg <= (state_next != STATE_IDLE || count_reg != 0 || cycle_reg || mdc_o); + end + + data_reg <= data_next; + op_reg <= op_next; + + data_out_reg <= data_out_next; + + mdio_i_reg <= mdio_i; +end + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/rtl/sync_reset.v b/fpga/lib/eth/example/VCU118/fpga_25g/rtl/sync_reset.v new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/rtl/sync_reset.v @@ -0,0 +1,52 @@ +/* + +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 active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset #( + parameter N=2 // depth of synchronizer +)( + input wire clk, + input wire rst, + output wire sync_reset_out +); + +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign sync_reset_out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) + sync_reg <= {N{1'b1}}; + else + sync_reg <= {sync_reg[N-2:0], 1'b0}; +end + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/rtl/sync_signal.v b/fpga/lib/eth/example/VCU118/fpga_25g/rtl/sync_signal.v new file mode 100644 index 000000000..b2a8ce3de --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/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/fpga/lib/eth/example/VCU118/fpga_25g/tb/arp_ep.py b/fpga/lib/eth/example/VCU118/fpga_25g/tb/arp_ep.py new file mode 120000 index 000000000..7b3d3ed97 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/tb/arp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/arp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/tb/axis_ep.py b/fpga/lib/eth/example/VCU118/fpga_25g/tb/axis_ep.py new file mode 120000 index 000000000..385bb0300 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/tb/eth_ep.py b/fpga/lib/eth/example/VCU118/fpga_25g/tb/eth_ep.py new file mode 120000 index 000000000..bac19feea --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/tb/eth_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/eth_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/tb/gmii_ep.py b/fpga/lib/eth/example/VCU118/fpga_25g/tb/gmii_ep.py new file mode 120000 index 000000000..754166f2f --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/tb/gmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/gmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/tb/ip_ep.py b/fpga/lib/eth/example/VCU118/fpga_25g/tb/ip_ep.py new file mode 120000 index 000000000..6dfa928a7 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/tb/ip_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/ip_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/tb/test_fpga_core.py b/fpga/lib/eth/example/VCU118/fpga_25g/tb/test_fpga_core.py new file mode 100755 index 000000000..00ed0dd0c --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/tb/test_fpga_core.py @@ -0,0 +1,689 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import udp_ep +import gmii_ep +import xgmii_ep + +module = 'fpga_core' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/eth/rtl/eth_mac_1g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_1g.v") +srcs.append("../lib/eth/rtl/axis_gmii_rx.v") +srcs.append("../lib/eth/rtl/axis_gmii_tx.v") +srcs.append("../lib/eth/rtl/eth_mac_10g_fifo.v") +srcs.append("../lib/eth/rtl/eth_mac_10g.v") +srcs.append("../lib/eth/rtl/axis_xgmii_rx_64.v") +srcs.append("../lib/eth/rtl/axis_xgmii_tx_64.v") +srcs.append("../lib/eth/rtl/lfsr.v") +srcs.append("../lib/eth/rtl/eth_axis_rx.v") +srcs.append("../lib/eth/rtl/eth_axis_tx.v") +srcs.append("../lib/eth/rtl/eth_axis_rx_64.v") +srcs.append("../lib/eth/rtl/eth_axis_tx_64.v") +srcs.append("../lib/eth/rtl/udp_complete_64.v") +srcs.append("../lib/eth/rtl/udp_checksum_gen_64.v") +srcs.append("../lib/eth/rtl/udp_64.v") +srcs.append("../lib/eth/rtl/udp_ip_rx_64.v") +srcs.append("../lib/eth/rtl/udp_ip_tx_64.v") +srcs.append("../lib/eth/rtl/ip_complete_64.v") +srcs.append("../lib/eth/rtl/ip_64.v") +srcs.append("../lib/eth/rtl/ip_eth_rx_64.v") +srcs.append("../lib/eth/rtl/ip_eth_tx_64.v") +srcs.append("../lib/eth/rtl/ip_arb_mux.v") +srcs.append("../lib/eth/rtl/arp_64.v") +srcs.append("../lib/eth/rtl/arp_cache.v") +srcs.append("../lib/eth/rtl/arp_eth_rx_64.v") +srcs.append("../lib/eth/rtl/arp_eth_tx_64.v") +srcs.append("../lib/eth/rtl/eth_arb_mux.v") +srcs.append("../lib/eth/lib/axis/rtl/arbiter.v") +srcs.append("../lib/eth/lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_adapter.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_fifo.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_switch.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_register.v") +srcs.append("../lib/eth/lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + btnu = Signal(bool(0)) + btnl = Signal(bool(0)) + btnd = Signal(bool(0)) + btnr = Signal(bool(0)) + btnc = Signal(bool(0)) + sw = Signal(intbv(0)[4:]) + qsfp1_tx_clk_1 = Signal(bool(0)) + qsfp1_tx_rst_1 = Signal(bool(0)) + qsfp1_rx_clk_1 = Signal(bool(0)) + qsfp1_rx_rst_1 = Signal(bool(0)) + qsfp1_rxd_1 = Signal(intbv(0)[64:]) + qsfp1_rxc_1 = Signal(intbv(0)[8:]) + qsfp1_tx_clk_2 = Signal(bool(0)) + qsfp1_tx_rst_2 = Signal(bool(0)) + qsfp1_rx_clk_2 = Signal(bool(0)) + qsfp1_rx_rst_2 = Signal(bool(0)) + qsfp1_rxd_2 = Signal(intbv(0)[64:]) + qsfp1_rxc_2 = Signal(intbv(0)[8:]) + qsfp1_tx_clk_3 = Signal(bool(0)) + qsfp1_tx_rst_3 = Signal(bool(0)) + qsfp1_rx_clk_3 = Signal(bool(0)) + qsfp1_rx_rst_3 = Signal(bool(0)) + qsfp1_rxd_3 = Signal(intbv(0)[64:]) + qsfp1_rxc_3 = Signal(intbv(0)[8:]) + qsfp1_tx_clk_4 = Signal(bool(0)) + qsfp1_tx_rst_4 = Signal(bool(0)) + qsfp1_rx_clk_4 = Signal(bool(0)) + qsfp1_rx_rst_4 = Signal(bool(0)) + qsfp1_rxd_4 = Signal(intbv(0)[64:]) + qsfp1_rxc_4 = Signal(intbv(0)[8:]) + qsfp2_tx_clk_1 = Signal(bool(0)) + qsfp2_tx_rst_1 = Signal(bool(0)) + qsfp2_rx_clk_1 = Signal(bool(0)) + qsfp2_rx_rst_1 = Signal(bool(0)) + qsfp2_rxd_1 = Signal(intbv(0)[64:]) + qsfp2_rxc_1 = Signal(intbv(0)[8:]) + qsfp2_tx_clk_2 = Signal(bool(0)) + qsfp2_tx_rst_2 = Signal(bool(0)) + qsfp2_rx_clk_2 = Signal(bool(0)) + qsfp2_rx_rst_2 = Signal(bool(0)) + qsfp2_rxd_2 = Signal(intbv(0)[64:]) + qsfp2_rxc_2 = Signal(intbv(0)[8:]) + qsfp2_tx_clk_3 = Signal(bool(0)) + qsfp2_tx_rst_3 = Signal(bool(0)) + qsfp2_rx_clk_3 = Signal(bool(0)) + qsfp2_rx_rst_3 = Signal(bool(0)) + qsfp2_rxd_3 = Signal(intbv(0)[64:]) + qsfp2_rxc_3 = Signal(intbv(0)[8:]) + qsfp2_tx_clk_4 = Signal(bool(0)) + qsfp2_tx_rst_4 = Signal(bool(0)) + qsfp2_rx_clk_4 = Signal(bool(0)) + qsfp2_rx_rst_4 = Signal(bool(0)) + qsfp2_rxd_4 = Signal(intbv(0)[64:]) + qsfp2_rxc_4 = Signal(intbv(0)[8:]) + phy_gmii_clk = Signal(bool(0)) + phy_gmii_rst = Signal(bool(0)) + phy_gmii_clk_en = Signal(bool(0)) + phy_gmii_rxd = Signal(intbv(0)[8:]) + phy_gmii_rx_dv = Signal(bool(0)) + phy_gmii_rx_er = Signal(bool(0)) + phy_int_n = Signal(bool(1)) + uart_rxd = Signal(bool(0)) + uart_cts = Signal(bool(0)) + + # Outputs + led = Signal(intbv(0)[8:]) + qsfp1_txd_1 = Signal(intbv(0)[64:]) + qsfp1_txc_1 = Signal(intbv(0)[8:]) + qsfp1_txd_2 = Signal(intbv(0)[64:]) + qsfp1_txc_2 = Signal(intbv(0)[8:]) + qsfp1_txd_3 = Signal(intbv(0)[64:]) + qsfp1_txc_3 = Signal(intbv(0)[8:]) + qsfp1_txd_4 = Signal(intbv(0)[64:]) + qsfp1_txc_4 = Signal(intbv(0)[8:]) + qsfp2_txd_1 = Signal(intbv(0)[64:]) + qsfp2_txc_1 = Signal(intbv(0)[8:]) + qsfp2_txd_2 = Signal(intbv(0)[64:]) + qsfp2_txc_2 = Signal(intbv(0)[8:]) + qsfp2_txd_3 = Signal(intbv(0)[64:]) + qsfp2_txc_3 = Signal(intbv(0)[8:]) + qsfp2_txd_4 = Signal(intbv(0)[64:]) + qsfp2_txc_4 = Signal(intbv(0)[8:]) + phy_gmii_txd = Signal(intbv(0)[8:]) + phy_gmii_tx_en = Signal(bool(0)) + phy_gmii_tx_er = Signal(bool(0)) + phy_reset_n = Signal(bool(0)) + uart_txd = Signal(bool(0)) + uart_rts = Signal(bool(0)) + + # sources and sinks + qsfp1_1_source = xgmii_ep.XGMIISource() + qsfp1_1_source_logic = qsfp1_1_source.create_logic(qsfp1_rx_clk_1, qsfp1_rx_rst_1, txd=qsfp1_rxd_1, txc=qsfp1_rxc_1, name='qsfp1_1_source') + + qsfp1_1_sink = xgmii_ep.XGMIISink() + qsfp1_1_sink_logic = qsfp1_1_sink.create_logic(qsfp1_tx_clk_1, qsfp1_tx_rst_1, rxd=qsfp1_txd_1, rxc=qsfp1_txc_1, name='qsfp1_1_sink') + + qsfp1_2_source = xgmii_ep.XGMIISource() + qsfp1_2_source_logic = qsfp1_2_source.create_logic(qsfp1_rx_clk_2, qsfp1_rx_rst_2, txd=qsfp1_rxd_2, txc=qsfp1_rxc_2, name='qsfp1_2_source') + + qsfp1_2_sink = xgmii_ep.XGMIISink() + qsfp1_2_sink_logic = qsfp1_2_sink.create_logic(qsfp1_tx_clk_2, qsfp1_tx_rst_2, rxd=qsfp1_txd_2, rxc=qsfp1_txc_2, name='qsfp1_2_sink') + + qsfp1_3_source = xgmii_ep.XGMIISource() + qsfp1_3_source_logic = qsfp1_3_source.create_logic(qsfp1_rx_clk_3, qsfp1_rx_rst_3, txd=qsfp1_rxd_3, txc=qsfp1_rxc_3, name='qsfp1_3_source') + + qsfp1_3_sink = xgmii_ep.XGMIISink() + qsfp1_3_sink_logic = qsfp1_3_sink.create_logic(qsfp1_tx_clk_3, qsfp1_tx_rst_3, rxd=qsfp1_txd_3, rxc=qsfp1_txc_3, name='qsfp1_3_sink') + + qsfp1_4_source = xgmii_ep.XGMIISource() + qsfp1_4_source_logic = qsfp1_4_source.create_logic(qsfp1_rx_clk_4, qsfp1_rx_rst_4, txd=qsfp1_rxd_4, txc=qsfp1_rxc_4, name='qsfp1_4_source') + + qsfp1_4_sink = xgmii_ep.XGMIISink() + qsfp1_4_sink_logic = qsfp1_4_sink.create_logic(qsfp1_tx_clk_4, qsfp1_tx_rst_4, rxd=qsfp1_txd_4, rxc=qsfp1_txc_4, name='qsfp1_4_sink') + + qsfp2_1_source = xgmii_ep.XGMIISource() + qsfp2_1_source_logic = qsfp2_1_source.create_logic(qsfp2_rx_clk_1, qsfp2_rx_rst_1, txd=qsfp2_rxd_1, txc=qsfp2_rxc_1, name='qsfp2_1_source') + + qsfp2_1_sink = xgmii_ep.XGMIISink() + qsfp2_1_sink_logic = qsfp2_1_sink.create_logic(qsfp2_tx_clk_1, qsfp2_tx_rst_1, rxd=qsfp2_txd_1, rxc=qsfp2_txc_1, name='qsfp2_1_sink') + + qsfp2_2_source = xgmii_ep.XGMIISource() + qsfp2_2_source_logic = qsfp2_2_source.create_logic(qsfp2_rx_clk_2, qsfp2_rx_rst_2, txd=qsfp2_rxd_2, txc=qsfp2_rxc_2, name='qsfp2_2_source') + + qsfp2_2_sink = xgmii_ep.XGMIISink() + qsfp2_2_sink_logic = qsfp2_2_sink.create_logic(qsfp2_tx_clk_2, qsfp2_tx_rst_2, rxd=qsfp2_txd_2, rxc=qsfp2_txc_2, name='qsfp2_2_sink') + + qsfp2_3_source = xgmii_ep.XGMIISource() + qsfp2_3_source_logic = qsfp2_3_source.create_logic(qsfp2_rx_clk_3, qsfp2_rx_rst_3, txd=qsfp2_rxd_3, txc=qsfp2_rxc_3, name='qsfp2_3_source') + + qsfp2_3_sink = xgmii_ep.XGMIISink() + qsfp2_3_sink_logic = qsfp2_3_sink.create_logic(qsfp2_tx_clk_3, qsfp2_tx_rst_3, rxd=qsfp2_txd_3, rxc=qsfp2_txc_3, name='qsfp2_3_sink') + + qsfp2_4_source = xgmii_ep.XGMIISource() + qsfp2_4_source_logic = qsfp2_4_source.create_logic(qsfp2_rx_clk_4, qsfp2_rx_rst_4, txd=qsfp2_rxd_4, txc=qsfp2_rxc_4, name='qsfp2_4_source') + + qsfp2_4_sink = xgmii_ep.XGMIISink() + qsfp2_4_sink_logic = qsfp2_4_sink.create_logic(qsfp2_tx_clk_4, qsfp2_tx_rst_4, rxd=qsfp2_txd_4, rxc=qsfp2_txc_4, name='qsfp2_4_sink') + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + phy_gmii_clk, + phy_gmii_rst, + txd=phy_gmii_rxd, + tx_en=phy_gmii_rx_dv, + tx_er=phy_gmii_rx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + phy_gmii_clk, + phy_gmii_rst, + rxd=phy_gmii_txd, + rx_dv=phy_gmii_tx_en, + rx_er=phy_gmii_tx_er, + clk_enable=phy_gmii_clk_en, + name='gmii_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + btnu=btnu, + btnl=btnl, + btnd=btnd, + btnr=btnr, + btnc=btnc, + sw=sw, + led=led, + + qsfp1_tx_clk_1=qsfp1_tx_clk_1, + qsfp1_tx_rst_1=qsfp1_tx_rst_1, + qsfp1_txd_1=qsfp1_txd_1, + qsfp1_txc_1=qsfp1_txc_1, + qsfp1_rx_clk_1=qsfp1_rx_clk_1, + qsfp1_rx_rst_1=qsfp1_rx_rst_1, + qsfp1_rxd_1=qsfp1_rxd_1, + qsfp1_rxc_1=qsfp1_rxc_1, + qsfp1_tx_clk_2=qsfp1_tx_clk_2, + qsfp1_tx_rst_2=qsfp1_tx_rst_2, + qsfp1_txd_2=qsfp1_txd_2, + qsfp1_txc_2=qsfp1_txc_2, + qsfp1_rx_clk_2=qsfp1_rx_clk_2, + qsfp1_rx_rst_2=qsfp1_rx_rst_2, + qsfp1_rxd_2=qsfp1_rxd_2, + qsfp1_rxc_2=qsfp1_rxc_2, + qsfp1_tx_clk_3=qsfp1_tx_clk_3, + qsfp1_tx_rst_3=qsfp1_tx_rst_3, + qsfp1_txd_3=qsfp1_txd_3, + qsfp1_txc_3=qsfp1_txc_3, + qsfp1_rx_clk_3=qsfp1_rx_clk_3, + qsfp1_rx_rst_3=qsfp1_rx_rst_3, + qsfp1_rxd_3=qsfp1_rxd_3, + qsfp1_rxc_3=qsfp1_rxc_3, + qsfp1_tx_clk_4=qsfp1_tx_clk_4, + qsfp1_tx_rst_4=qsfp1_tx_rst_4, + qsfp1_txd_4=qsfp1_txd_4, + qsfp1_txc_4=qsfp1_txc_4, + qsfp1_rx_clk_4=qsfp1_rx_clk_4, + qsfp1_rx_rst_4=qsfp1_rx_rst_4, + qsfp1_rxd_4=qsfp1_rxd_4, + qsfp1_rxc_4=qsfp1_rxc_4, + qsfp2_tx_clk_1=qsfp2_tx_clk_1, + qsfp2_tx_rst_1=qsfp2_tx_rst_1, + qsfp2_txd_1=qsfp2_txd_1, + qsfp2_txc_1=qsfp2_txc_1, + qsfp2_rx_clk_1=qsfp2_rx_clk_1, + qsfp2_rx_rst_1=qsfp2_rx_rst_1, + qsfp2_rxd_1=qsfp2_rxd_1, + qsfp2_rxc_1=qsfp2_rxc_1, + qsfp2_tx_clk_2=qsfp2_tx_clk_2, + qsfp2_tx_rst_2=qsfp2_tx_rst_2, + qsfp2_txd_2=qsfp2_txd_2, + qsfp2_txc_2=qsfp2_txc_2, + qsfp2_rx_clk_2=qsfp2_rx_clk_2, + qsfp2_rx_rst_2=qsfp2_rx_rst_2, + qsfp2_rxd_2=qsfp2_rxd_2, + qsfp2_rxc_2=qsfp2_rxc_2, + qsfp2_tx_clk_3=qsfp2_tx_clk_3, + qsfp2_tx_rst_3=qsfp2_tx_rst_3, + qsfp2_txd_3=qsfp2_txd_3, + qsfp2_txc_3=qsfp2_txc_3, + qsfp2_rx_clk_3=qsfp2_rx_clk_3, + qsfp2_rx_rst_3=qsfp2_rx_rst_3, + qsfp2_rxd_3=qsfp2_rxd_3, + qsfp2_rxc_3=qsfp2_rxc_3, + qsfp2_tx_clk_4=qsfp2_tx_clk_4, + qsfp2_tx_rst_4=qsfp2_tx_rst_4, + qsfp2_txd_4=qsfp2_txd_4, + qsfp2_txc_4=qsfp2_txc_4, + qsfp2_rx_clk_4=qsfp2_rx_clk_4, + qsfp2_rx_rst_4=qsfp2_rx_rst_4, + qsfp2_rxd_4=qsfp2_rxd_4, + qsfp2_rxc_4=qsfp2_rxc_4, + + phy_gmii_clk=phy_gmii_clk, + phy_gmii_rst=phy_gmii_rst, + phy_gmii_clk_en=phy_gmii_clk_en, + phy_gmii_rxd=phy_gmii_rxd, + phy_gmii_rx_dv=phy_gmii_rx_dv, + phy_gmii_rx_er=phy_gmii_rx_er, + phy_gmii_txd=phy_gmii_txd, + phy_gmii_tx_en=phy_gmii_tx_en, + phy_gmii_tx_er=phy_gmii_tx_er, + phy_reset_n=phy_reset_n, + phy_int_n=phy_int_n, + + uart_rxd=uart_rxd, + uart_txd=uart_txd, + uart_rts=uart_rts, + uart_cts=uart_cts + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + qsfp1_tx_clk_1.next = not qsfp1_tx_clk_1 + qsfp1_rx_clk_1.next = not qsfp1_rx_clk_1 + qsfp1_tx_clk_2.next = not qsfp1_tx_clk_2 + qsfp1_rx_clk_2.next = not qsfp1_rx_clk_2 + qsfp1_tx_clk_3.next = not qsfp1_tx_clk_3 + qsfp1_rx_clk_3.next = not qsfp1_rx_clk_3 + qsfp1_tx_clk_4.next = not qsfp1_tx_clk_4 + qsfp1_rx_clk_4.next = not qsfp1_rx_clk_4 + qsfp2_tx_clk_1.next = not qsfp2_tx_clk_1 + qsfp2_rx_clk_1.next = not qsfp2_rx_clk_1 + qsfp2_tx_clk_2.next = not qsfp2_tx_clk_2 + qsfp2_rx_clk_2.next = not qsfp2_rx_clk_2 + qsfp2_tx_clk_3.next = not qsfp2_tx_clk_3 + qsfp2_rx_clk_3.next = not qsfp2_rx_clk_3 + qsfp2_tx_clk_4.next = not qsfp2_tx_clk_4 + qsfp2_rx_clk_4.next = not qsfp2_rx_clk_4 + phy_gmii_clk.next = not phy_gmii_clk + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + phy_gmii_clk_en.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + phy_gmii_clk_en.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + qsfp1_tx_rst_1.next = 1 + qsfp1_rx_rst_1.next = 1 + qsfp1_tx_rst_2.next = 1 + qsfp1_rx_rst_2.next = 1 + qsfp1_tx_rst_3.next = 1 + qsfp1_rx_rst_3.next = 1 + qsfp1_tx_rst_4.next = 1 + qsfp1_rx_rst_4.next = 1 + qsfp2_tx_rst_1.next = 1 + qsfp2_rx_rst_1.next = 1 + qsfp2_tx_rst_2.next = 1 + qsfp2_rx_rst_2.next = 1 + qsfp2_tx_rst_3.next = 1 + qsfp2_rx_rst_3.next = 1 + qsfp2_tx_rst_4.next = 1 + qsfp2_rx_rst_4.next = 1 + phy_gmii_rst.next = 1 + yield clk.posedge + rst.next = 0 + qsfp1_tx_rst_1.next = 0 + qsfp1_rx_rst_1.next = 0 + qsfp1_tx_rst_2.next = 0 + qsfp1_rx_rst_2.next = 0 + qsfp1_tx_rst_3.next = 0 + qsfp1_rx_rst_3.next = 0 + qsfp1_tx_rst_4.next = 0 + qsfp1_rx_rst_4.next = 0 + qsfp2_tx_rst_1.next = 0 + qsfp2_rx_rst_1.next = 0 + qsfp2_tx_rst_2.next = 0 + qsfp2_rx_rst_2.next = 0 + qsfp2_tx_rst_3.next = 0 + qsfp2_rx_rst_3.next = 0 + qsfp2_tx_rst_4.next = 0 + qsfp2_rx_rst_4.next = 0 + phy_gmii_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + qsfp1_1_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # wait for ARP request packet + while qsfp1_1_sink.empty(): + yield clk.posedge + + rx_frame = qsfp1_1_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x020000000000 + assert check_frame.arp_spa == 0xc0a80180 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80181 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x020000000000 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80181 + arp_frame.arp_tha = 0x020000000000 + arp_frame.arp_tpa = 0xc0a80180 + + qsfp1_1_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+arp_frame.build_eth().build_axis_fcs().data) + + while qsfp1_1_sink.empty(): + yield clk.posedge + + rx_frame = qsfp1_1_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert qsfp1_1_source.empty() + assert qsfp1_1_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test gigabit tap") + current_test.next = 2 + + sw.next = 0x8 # enable tap on RX + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # loop packet back through on XGMII interface + while qsfp1_1_sink.empty(): + yield clk.posedge + + qsfp1_1_source.send(qsfp1_1_sink.recv()) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + assert qsfp1_1_source.empty() + assert qsfp1_1_sink.empty() + + yield delay(100) + + sw.next = 0xc # enable tap on TX + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x020000000000 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80181 + test_frame.ip_dest_ip = 0xc0a80180 + test_frame.udp_source_port = 5678 + test_frame.udp_dest_port = 1234 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame.build_eth().build_axis_fcs().data) + + # loop packet back through on XGMII interface + while qsfp1_1_sink.empty(): + yield clk.posedge + + qsfp1_1_source.send(qsfp1_1_sink.recv()) + + while gmii_sink.empty(): + yield clk.posedge + + rx_frame = gmii_sink.recv() + check_eth_frame = eth_ep.EthFrame() + check_eth_frame.parse_axis_fcs(rx_frame.data[8:]) + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(check_eth_frame) + + print(check_frame) + + assert check_frame.eth_dest_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_src_mac == 0x020000000000 + assert check_frame.eth_type == 0x0800 + assert check_frame.ip_version == 4 + assert check_frame.ip_ihl == 5 + assert check_frame.ip_dscp == 0 + assert check_frame.ip_ecn == 0 + assert check_frame.ip_identification == 0 + assert check_frame.ip_flags == 2 + assert check_frame.ip_fragment_offset == 0 + assert check_frame.ip_ttl == 64 + assert check_frame.ip_protocol == 0x11 + assert check_frame.ip_source_ip == 0xc0a80180 + assert check_frame.ip_dest_ip == 0xc0a80181 + assert check_frame.udp_source_port == 1234 + assert check_frame.udp_dest_port == 5678 + assert check_frame.payload.data == bytearray(range(32)) + + assert gmii_source.empty() + assert gmii_sink.empty() + assert qsfp1_1_source.empty() + assert qsfp1_1_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/tb/test_fpga_core.v b/fpga/lib/eth/example/VCU118/fpga_25g/tb/test_fpga_core.v new file mode 100644 index 000000000..24db5dc0c --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/tb/test_fpga_core.v @@ -0,0 +1,324 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for fpga_core + */ +module test_fpga_core; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg btnu = 0; +reg btnl = 0; +reg btnd = 0; +reg btnr = 0; +reg btnc = 0; +reg [3:0] sw = 0; +reg qsfp1_tx_clk_1 = 0; +reg qsfp1_tx_rst_1 = 0; +reg qsfp1_rx_clk_1 = 0; +reg qsfp1_rx_rst_1 = 0; +reg [63:0] qsfp1_rxd_1 = 0; +reg [7:0] qsfp1_rxc_1 = 0; +reg qsfp1_tx_clk_2 = 0; +reg qsfp1_tx_rst_2 = 0; +reg qsfp1_rx_clk_2 = 0; +reg qsfp1_rx_rst_2 = 0; +reg [63:0] qsfp1_rxd_2 = 0; +reg [7:0] qsfp1_rxc_2 = 0; +reg qsfp1_tx_clk_3 = 0; +reg qsfp1_tx_rst_3 = 0; +reg qsfp1_rx_clk_3 = 0; +reg qsfp1_rx_rst_3 = 0; +reg [63:0] qsfp1_rxd_3 = 0; +reg [7:0] qsfp1_rxc_3 = 0; +reg qsfp1_tx_clk_4 = 0; +reg qsfp1_tx_rst_4 = 0; +reg qsfp1_rx_clk_4 = 0; +reg qsfp1_rx_rst_4 = 0; +reg [63:0] qsfp1_rxd_4 = 0; +reg [7:0] qsfp1_rxc_4 = 0; +reg qsfp2_tx_clk_1 = 0; +reg qsfp2_tx_rst_1 = 0; +reg qsfp2_rx_clk_1 = 0; +reg qsfp2_rx_rst_1 = 0; +reg [63:0] qsfp2_rxd_1 = 0; +reg [7:0] qsfp2_rxc_1 = 0; +reg qsfp2_tx_clk_2 = 0; +reg qsfp2_tx_rst_2 = 0; +reg qsfp2_rx_clk_2 = 0; +reg qsfp2_rx_rst_2 = 0; +reg [63:0] qsfp2_rxd_2 = 0; +reg [7:0] qsfp2_rxc_2 = 0; +reg qsfp2_tx_clk_3 = 0; +reg qsfp2_tx_rst_3 = 0; +reg qsfp2_rx_clk_3 = 0; +reg qsfp2_rx_rst_3 = 0; +reg [63:0] qsfp2_rxd_3 = 0; +reg [7:0] qsfp2_rxc_3 = 0; +reg qsfp2_tx_clk_4 = 0; +reg qsfp2_tx_rst_4 = 0; +reg qsfp2_rx_clk_4 = 0; +reg qsfp2_rx_rst_4 = 0; +reg [63:0] qsfp2_rxd_4 = 0; +reg [7:0] qsfp2_rxc_4 = 0; +reg phy_gmii_clk = 0; +reg phy_gmii_rst = 0; +reg phy_gmii_clk_en = 0; +reg [7:0] phy_gmii_rxd = 0; +reg phy_gmii_rx_dv = 0; +reg phy_gmii_rx_er = 0; +reg phy_int_n = 1; +reg uart_rxd = 0; +reg uart_cts = 0; + +// Outputs +wire [7:0] led; +wire [63:0] qsfp1_txd_1; +wire [7:0] qsfp1_txc_1; +wire [63:0] qsfp1_txd_2; +wire [7:0] qsfp1_txc_2; +wire [63:0] qsfp1_txd_3; +wire [7:0] qsfp1_txc_3; +wire [63:0] qsfp1_txd_4; +wire [7:0] qsfp1_txc_4; +wire [63:0] qsfp2_txd_1; +wire [7:0] qsfp2_txc_1; +wire [63:0] qsfp2_txd_2; +wire [7:0] qsfp2_txc_2; +wire [63:0] qsfp2_txd_3; +wire [7:0] qsfp2_txc_3; +wire [63:0] qsfp2_txd_4; +wire [7:0] qsfp2_txc_4; +wire phy_tx_clk; +wire [7:0] phy_gmii_txd; +wire phy_gmii_tx_en; +wire phy_gmii_tx_er; +wire phy_reset_n; +wire uart_txd; +wire uart_rts; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + btnu, + btnl, + btnd, + btnr, + btnc, + sw, + qsfp1_tx_clk_1, + qsfp1_tx_rst_1, + qsfp1_rx_clk_1, + qsfp1_rx_rst_1, + qsfp1_rxd_1, + qsfp1_rxc_1, + qsfp1_tx_clk_2, + qsfp1_tx_rst_2, + qsfp1_rx_clk_2, + qsfp1_rx_rst_2, + qsfp1_rxd_2, + qsfp1_rxc_2, + qsfp1_tx_clk_3, + qsfp1_tx_rst_3, + qsfp1_rx_clk_3, + qsfp1_rx_rst_3, + qsfp1_rxd_3, + qsfp1_rxc_3, + qsfp1_tx_clk_4, + qsfp1_tx_rst_4, + qsfp1_rx_clk_4, + qsfp1_rx_rst_4, + qsfp1_rxd_4, + qsfp1_rxc_4, + qsfp2_tx_clk_1, + qsfp2_tx_rst_1, + qsfp2_rx_clk_1, + qsfp2_rx_rst_1, + qsfp2_rxd_1, + qsfp2_rxc_1, + qsfp2_tx_clk_2, + qsfp2_tx_rst_2, + qsfp2_rx_clk_2, + qsfp2_rx_rst_2, + qsfp2_rxd_2, + qsfp2_rxc_2, + qsfp2_tx_clk_3, + qsfp2_tx_rst_3, + qsfp2_rx_clk_3, + qsfp2_rx_rst_3, + qsfp2_rxd_3, + qsfp2_rxc_3, + qsfp2_tx_clk_4, + qsfp2_tx_rst_4, + qsfp2_rx_clk_4, + qsfp2_rx_rst_4, + qsfp2_rxd_4, + qsfp2_rxc_4, + phy_gmii_clk, + phy_gmii_rst, + phy_gmii_clk_en, + phy_gmii_rxd, + phy_gmii_rx_dv, + phy_gmii_rx_er, + phy_int_n, + uart_rxd, + uart_cts + ); + $to_myhdl( + led, + qsfp1_txd_1, + qsfp1_txc_1, + qsfp1_txd_2, + qsfp1_txc_2, + qsfp1_txd_3, + qsfp1_txc_3, + qsfp1_txd_4, + qsfp1_txc_4, + qsfp2_txd_1, + qsfp2_txc_1, + qsfp2_txd_2, + qsfp2_txc_2, + qsfp2_txd_3, + qsfp2_txc_3, + qsfp2_txd_4, + qsfp2_txc_4, + phy_gmii_txd, + phy_gmii_tx_en, + phy_gmii_tx_er, + phy_reset_n, + uart_txd, + uart_rts + ); + + // dump file + $dumpfile("test_fpga_core.lxt"); + $dumpvars(0, test_fpga_core); +end + +fpga_core +UUT ( + .clk(clk), + .rst(rst), + .btnu(btnu), + .btnl(btnl), + .btnd(btnd), + .btnr(btnr), + .btnc(btnc), + .sw(sw), + .led(led), + .qsfp1_tx_clk_1(qsfp1_tx_clk_1), + .qsfp1_tx_rst_1(qsfp1_tx_rst_1), + .qsfp1_txd_1(qsfp1_txd_1), + .qsfp1_txc_1(qsfp1_txc_1), + .qsfp1_rx_clk_1(qsfp1_rx_clk_1), + .qsfp1_rx_rst_1(qsfp1_rx_rst_1), + .qsfp1_rxd_1(qsfp1_rxd_1), + .qsfp1_rxc_1(qsfp1_rxc_1), + .qsfp1_tx_clk_2(qsfp1_tx_clk_2), + .qsfp1_tx_rst_2(qsfp1_tx_rst_2), + .qsfp1_txd_2(qsfp1_txd_2), + .qsfp1_txc_2(qsfp1_txc_2), + .qsfp1_rx_clk_2(qsfp1_rx_clk_2), + .qsfp1_rx_rst_2(qsfp1_rx_rst_2), + .qsfp1_rxd_2(qsfp1_rxd_2), + .qsfp1_rxc_2(qsfp1_rxc_2), + .qsfp1_tx_clk_3(qsfp1_tx_clk_3), + .qsfp1_tx_rst_3(qsfp1_tx_rst_3), + .qsfp1_txd_3(qsfp1_txd_3), + .qsfp1_txc_3(qsfp1_txc_3), + .qsfp1_rx_clk_3(qsfp1_rx_clk_3), + .qsfp1_rx_rst_3(qsfp1_rx_rst_3), + .qsfp1_rxd_3(qsfp1_rxd_3), + .qsfp1_rxc_3(qsfp1_rxc_3), + .qsfp1_tx_clk_4(qsfp1_tx_clk_4), + .qsfp1_tx_rst_4(qsfp1_tx_rst_4), + .qsfp1_txd_4(qsfp1_txd_4), + .qsfp1_txc_4(qsfp1_txc_4), + .qsfp1_rx_clk_4(qsfp1_rx_clk_4), + .qsfp1_rx_rst_4(qsfp1_rx_rst_4), + .qsfp1_rxd_4(qsfp1_rxd_4), + .qsfp1_rxc_4(qsfp1_rxc_4), + .qsfp2_tx_clk_1(qsfp2_tx_clk_1), + .qsfp2_tx_rst_1(qsfp2_tx_rst_1), + .qsfp2_txd_1(qsfp2_txd_1), + .qsfp2_txc_1(qsfp2_txc_1), + .qsfp2_rx_clk_1(qsfp2_rx_clk_1), + .qsfp2_rx_rst_1(qsfp2_rx_rst_1), + .qsfp2_rxd_1(qsfp2_rxd_1), + .qsfp2_rxc_1(qsfp2_rxc_1), + .qsfp2_tx_clk_2(qsfp2_tx_clk_2), + .qsfp2_tx_rst_2(qsfp2_tx_rst_2), + .qsfp2_txd_2(qsfp2_txd_2), + .qsfp2_txc_2(qsfp2_txc_2), + .qsfp2_rx_clk_2(qsfp2_rx_clk_2), + .qsfp2_rx_rst_2(qsfp2_rx_rst_2), + .qsfp2_rxd_2(qsfp2_rxd_2), + .qsfp2_rxc_2(qsfp2_rxc_2), + .qsfp2_tx_clk_3(qsfp2_tx_clk_3), + .qsfp2_tx_rst_3(qsfp2_tx_rst_3), + .qsfp2_txd_3(qsfp2_txd_3), + .qsfp2_txc_3(qsfp2_txc_3), + .qsfp2_rx_clk_3(qsfp2_rx_clk_3), + .qsfp2_rx_rst_3(qsfp2_rx_rst_3), + .qsfp2_rxd_3(qsfp2_rxd_3), + .qsfp2_rxc_3(qsfp2_rxc_3), + .qsfp2_tx_clk_4(qsfp2_tx_clk_4), + .qsfp2_tx_rst_4(qsfp2_tx_rst_4), + .qsfp2_txd_4(qsfp2_txd_4), + .qsfp2_txc_4(qsfp2_txc_4), + .qsfp2_rx_clk_4(qsfp2_rx_clk_4), + .qsfp2_rx_rst_4(qsfp2_rx_rst_4), + .qsfp2_rxd_4(qsfp2_rxd_4), + .qsfp2_rxc_4(qsfp2_rxc_4), + .phy_gmii_clk(phy_gmii_clk), + .phy_gmii_rst(phy_gmii_rst), + .phy_gmii_clk_en(phy_gmii_clk_en), + .phy_gmii_rxd(phy_gmii_rxd), + .phy_gmii_rx_dv(phy_gmii_rx_dv), + .phy_gmii_rx_er(phy_gmii_rx_er), + .phy_gmii_txd(phy_gmii_txd), + .phy_gmii_tx_en(phy_gmii_tx_en), + .phy_gmii_tx_er(phy_gmii_tx_er), + .phy_reset_n(phy_reset_n), + .phy_int_n(phy_int_n), + .uart_rxd(uart_rxd), + .uart_txd(uart_txd), + .uart_rts(uart_rts), + .uart_cts(uart_cts) +); + +endmodule diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/tb/udp_ep.py b/fpga/lib/eth/example/VCU118/fpga_25g/tb/udp_ep.py new file mode 120000 index 000000000..073c5d3c6 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/tb/udp_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/udp_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/example/VCU118/fpga_25g/tb/xgmii_ep.py b/fpga/lib/eth/example/VCU118/fpga_25g/tb/xgmii_ep.py new file mode 120000 index 000000000..63b6d3567 --- /dev/null +++ b/fpga/lib/eth/example/VCU118/fpga_25g/tb/xgmii_ep.py @@ -0,0 +1 @@ +../lib/eth/tb/xgmii_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/lib/axis/.gitignore b/fpga/lib/eth/lib/axis/.gitignore new file mode 100644 index 000000000..964df4d4a --- /dev/null +++ b/fpga/lib/eth/lib/axis/.gitignore @@ -0,0 +1,6 @@ +*~ +*.lxt +*.pyc +*.vvp +*.kate-swp + diff --git a/fpga/lib/eth/lib/axis/.travis.yml b/fpga/lib/eth/lib/axis/.travis.yml new file mode 100644 index 000000000..a3a8fd49c --- /dev/null +++ b/fpga/lib/eth/lib/axis/.travis.yml @@ -0,0 +1,15 @@ +language: python +python: + - "3.6" +before_install: + - export d=`pwd` + - export PYTHON_EXE=`which python` + - sudo apt-get update -qq + - sudo apt-get install -y iverilog + - git clone https://github.com/jandecaluwe/myhdl.git + - cd $d/myhdl && sudo $PYTHON_EXE setup.py install + - cd $d/myhdl/cosimulation/icarus && make && sudo install -m 0755 -D ./myhdl.vpi /usr/lib/x86_64-linux-gnu/ivl/myhdl.vpi + - cd $d +script: + - cd tb && IVERILOG_DUMPER=none py.test + diff --git a/fpga/lib/eth/lib/axis/AUTHORS b/fpga/lib/eth/lib/axis/AUTHORS new file mode 100644 index 000000000..7dab2b3a5 --- /dev/null +++ b/fpga/lib/eth/lib/axis/AUTHORS @@ -0,0 +1 @@ +Alex Forencich diff --git a/fpga/lib/eth/lib/axis/COPYING b/fpga/lib/eth/lib/axis/COPYING new file mode 100644 index 000000000..dc298464e --- /dev/null +++ b/fpga/lib/eth/lib/axis/COPYING @@ -0,0 +1,19 @@ +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. diff --git a/fpga/lib/eth/lib/axis/README b/fpga/lib/eth/lib/axis/README new file mode 120000 index 000000000..42061c01a --- /dev/null +++ b/fpga/lib/eth/lib/axis/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/fpga/lib/eth/lib/axis/README.md b/fpga/lib/eth/lib/axis/README.md new file mode 100644 index 000000000..6ac5e504f --- /dev/null +++ b/fpga/lib/eth/lib/axis/README.md @@ -0,0 +1,281 @@ +# Verilog AXI Stream Components Readme + +For more information and updates: http://alexforencich.com/wiki/en/verilog/axis/start + +GitHub repository: https://github.com/alexforencich/verilog-axis + +## Introduction + +Collection of AXI Stream bus components. Most components are fully +parametrizable in interface widths. Includes full MyHDL testbench with +intelligent bus cosimulation endpoints. + +## Documentation + +### arbiter module + +General-purpose parametrizable arbiter. Supports priority and round-robin +arbitration. Supports blocking until request release or acknowledge. + +### axis_adapter module + +The axis_adapter module bridges AXI stream busses of differing widths. The +module is parametrizable, but there are certain restrictions. First, the bus +word widths must be identical (e.g. one 8-bit lane and eight 8-bit lanes, but +not one 16-bit lane and one 32-bit lane). Second, the bus widths must be +related by an integer multiple (e.g. 2 words and 6 words, but not 4 words +and 6 words). Wait states will be inserted on the wider bus side when +necessary. + +### axis_arb_mux module + +Frame-aware AXI stream arbitrated muliplexer with parametrizable data width +and port count. Supports priority and round-robin arbitration. + +Wrappers can generated with axis_arb_mux_wrap.py. + +### axis_async_fifo module + +Configurable word-based or frame-based asynchronous FIFO with parametrizable +data width, depth, type, and bad frame detection. Supports power of two +depths only. + +### axis_broadcast module + +AXI stream broadcaster. Duplicates one input stream across multiple output +streams. + +### axis_cobs_decode + +Consistent Overhead Byte Stuffing (COBS) decoder. Fixed 8 bit width. + +### axis_cobs_encode + +Consistent Overhead Byte Stuffing (COBS) encoder. Fixed 8 bit width. +Configurable zero insertion. + +### axis_crosspoint module + +Basic crosspoint switch. tready signal not supported. Parametrizable data +width. + +Wrappers can generated with axis_crosspoint_wrap.py. + +### axis_demux module + +Frame-aware AXI stream demuliplexer with parametrizable data width and port +count. + +### axis_fifo module + +Configurable word-based or frame-based synchronous FIFO with parametrizable +data width, depth, type, and bad frame detection. Supports power of two +depths only. + +### axis_frame_join module + +Frame joiner with optional tag and parametrizable port count. 8 bit data path +only. + +Wrappers can generated with axis_frame_join_wrap.py. + +### axis_frame_length_adjust module + +Frame length adjuster module. Truncates or pads frames as necessary to meet +the specified minimum and maximum length. Reports the original and current +lengths as well as whether the packet was truncated or padded. Length limits +are configurable at run time. + +### axis_frame_length_adjust_fifo module + +Frame length adjuster module with FIFO. Truncates or pads frames as necessary +to meet the specified minimum and maximum length. Reports the original and +current lengths as well as whether the packet was truncated or padded. FIFOs +are used so that the status information can be read before the packet itself. +Length limits are configurable at run time. + +### axis_ll_bridge module + +AXI stream to LocalLink bridge. + +### axis_mux module + +Frame-aware AXI stream muliplexer with parametrizable data width and port +count. + +Wrappers can generated with axis_mux_wrap.py. + +### axis_pipeline_register module + +Parametrizable register pipeline. LENGTH parameter determines number of +register stages. + +### axis_rate_limit module + +Fractional rate limiter, supports word and frame modes. Inserts wait states +to limit data rate to specified ratio. Frame mode inserts wait states at end +of frames, word mode ignores frames and inserts wait states at any point. +Parametrizable data width. Rate and mode are configurable at run time. + +### axis_register module + +Datapath register with parameter to select between skid buffer, simple buffer, +and bypass. Use to improve timing for long routes. + +### axis_srl_fifo module + +SRL-based FIFO. Good for small FIFOs. SRLs on Xilinx FPGAs have a very fast +input setup time, so this module can be used to aid in timing closure. + +### axis_srl_register module + +SRL-based register. SRLs on Xilinx FPGAs have a very fast input setup time, +so this module can be used to aid in timing closure. + +### axis_stat_counter module + +Statistics counter module. Counts bytes and frames passing through monitored +AXI stream interface. Trigger signal used to reset and dump counts out of AXI +interface, along with tag value. Use with axis_frame_join_N to form a single +monolithic frame from multiple monitored points with the same trigger. + +### axis_switch module + +Frame-aware AXI stream switch with parametrizable data width and port count. + +Wrappers can generated with axis_switch_wrap.py. + +### axis_tap module + +AXI stream tap module. Used to make a copy of an AXI stream bus without +affecting the bus. Back-pressure on the output results in truncated frames +with tuser set. + +### ll_axis_bridge module + +LocalLink to AXI stream bridge. + +### priority_encoder module + +Parametrizable priority encoder. + +### Common signals + + tdata : Data (width generally DATA_WIDTH) + tkeep : Data word valid (width generally KEEP_WIDTH) + tvalid : Data valid + tready : Sink ready + tlast : End-of-frame + tid : Identifier tag (width generally ID_WIDTH) + tdest : Destination tag (width generally DEST_WIDTH) + tuser : User sideband signals (width generally USER_WIDTH) + +### Common parameters + + DATA_WIDTH : width of tdata signal + KEEP_ENABLE : enable tkeep signal (default DATA_WIDTH>8) + KEEP_WIDTH : width of tkeep signal (default DATA_WIDTH/8) + LAST_ENABLE : enable tlast signal + ID_ENABLE : enable tid signal + ID_WIDTH : width of tid signal + DEST_ENABLE : enable tdest signal + DEST_WIDTH : width of tdest signal + USER_ENABLE : enable tuser signal + USER_WIDTH : width of tuser signal + USER_BAD_FRAME_VALUE : value of tuser indicating bad frame + USER_BAD_FRAME_MASK : bitmask for tuser bad frame indication + +### Source Files + + arbiter.v : General-purpose parametrizable arbiter + axis_adapter.v : Parametrizable bus width adapter + axis_arb_mux.v : Parametrizable arbitrated multiplexer + axis_async_fifo.v : Parametrizable asynchronous FIFO + axis_broadcast.v : AXI stream broadcaster + axis_cobs_decode.v : COBS decoder + axis_cobs_encode.v : COBS encoder + axis_crosspoint.v : Parametrizable crosspoint switch + axis_demux.v : Parametrizable demultiplexer + axis_fifo.v : Parametrizable synchronous FIFO + axis_frame_join.v : Parametrizable frame joiner + axis_frame_length_adjust.v : Frame length adjuster + axis_frame_length_adjust_fifo.v : Frame length adjuster with FIFO + axis_ll_bridge.v : AXI stream to LocalLink bridge + axis_mux.v : Multiplexer generator + axis_rate_limit.v : Fractional rate limiter + axis_register.v : AXI Stream register + axis_srl_fifo.v : SRL-based FIFO + axis_srl_register.v : SRL-based register + axis_switch.v : Parametrizable AXI stream switch + axis_stat_counter.v : Statistics counter + axis_tap.v : AXI stream tap + ll_axis_bridge.v : LocalLink to AXI stream bridge + priority_encoder.v : Parametrizable priority encoder + +### AXI Stream Interface Example + +two byte transfer with sink pause after each byte + + __ __ __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _________________ + tdata XXXXXXXXX_D0__X_D1______________XXXXXXXXXXXXXXXXXXXXXXXX + _____ _________________ + tkeep XXXXXXXXX_K0__X_K1______________XXXXXXXXXXXXXXXXXXXXXXXX + _______________________ + tvalid ________/ \_______________________ + ______________ _____ ___________ + tready \___________/ \___________/ + _________________ + tlast ______________/ \_______________________ + + tuser ________________________________________________________ + + +two back-to-back packets, no pauses + + __ __ __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _____ _____ _____ _____ _____ + tdata XXXXXXXXX_A0__X_A1__X_A2__X_B0__X_B1__X_B2__XXXXXXXXXXXX + _____ _____ _____ _____ _____ _____ + tkeep XXXXXXXXX_K0__X_K1__X_K2__X_K0__X_K1__X_K2__XXXXXXXXXXXX + ___________________________________ + tvalid ________/ \___________ + ________________________________________________________ + tready + _____ _____ + tlast ____________________/ \___________/ \___________ + + tuser ________________________________________________________ + + +bad frame + + __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _____ _____ + tdata XXXXXXXXX_A0__X_A1__X_A2__XXXXXXXXXXXX + _____ _____ _____ + tkeep XXXXXXXXX_K0__X_K1__X_K2__XXXXXXXXXXXX + _________________ + tvalid ________/ \___________ + ______________________________________ + tready + _____ + tlast ____________________/ \___________ + _____ + tuser ____________________/ \___________ + + +## Testing + +Running the included testbenches requires MyHDL and Icarus Verilog. Make sure +that myhdl.vpi is installed properly for cosimulation to work correctly. The +testbenches can be run with a Python test runner like nose or py.test, or the +individual test scripts can be run with python directly. + +### Testbench Files + + tb/axis_ep.py : MyHDL AXI Stream endpoints + tb/ll_ep.py : MyHDL LocalLink endpoints diff --git a/fpga/lib/eth/lib/axis/rtl/arbiter.v b/fpga/lib/eth/lib/axis/rtl/arbiter.v new file mode 100644 index 000000000..8b0443fdb --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/arbiter.v @@ -0,0 +1,153 @@ +/* + +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 1ns / 1ps + +/* + * Arbiter module + */ +module arbiter # +( + parameter PORTS = 4, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter TYPE = "PRIORITY", + // block type: "NONE", "REQUEST", "ACKNOWLEDGE" + parameter BLOCK = "NONE", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "LOW" +) +( + input wire clk, + input wire rst, + + input wire [PORTS-1:0] request, + input wire [PORTS-1:0] acknowledge, + + output wire [PORTS-1:0] grant, + output wire grant_valid, + output wire [$clog2(PORTS)-1:0] grant_encoded +); + +reg [PORTS-1:0] grant_reg = 0, grant_next; +reg grant_valid_reg = 0, grant_valid_next; +reg [$clog2(PORTS)-1:0] grant_encoded_reg = 0, grant_encoded_next; + +assign grant_valid = grant_valid_reg; +assign grant = grant_reg; +assign grant_encoded = grant_encoded_reg; + +wire request_valid; +wire [$clog2(PORTS)-1:0] request_index; +wire [PORTS-1:0] request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY(LSB_PRIORITY) +) +priority_encoder_inst ( + .input_unencoded(request), + .output_valid(request_valid), + .output_encoded(request_index), + .output_unencoded(request_mask) +); + +reg [PORTS-1:0] mask_reg = 0, mask_next; + +wire masked_request_valid; +wire [$clog2(PORTS)-1:0] masked_request_index; +wire [PORTS-1:0] masked_request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY(LSB_PRIORITY) +) +priority_encoder_masked ( + .input_unencoded(request & mask_reg), + .output_valid(masked_request_valid), + .output_encoded(masked_request_index), + .output_unencoded(masked_request_mask) +); + +always @* begin + grant_next = 0; + grant_valid_next = 0; + grant_encoded_next = 0; + mask_next = mask_reg; + + if (BLOCK == "REQUEST" && grant_reg & request) begin + // granted request still asserted; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (BLOCK == "ACKNOWLEDGE" && grant_valid && !(grant_reg & acknowledge)) begin + // granted request not yet acknowledged; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (request_valid) begin + if (TYPE == "PRIORITY") begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + end else if (TYPE == "ROUND_ROBIN") begin + if (masked_request_valid) begin + grant_valid_next = 1; + grant_next = masked_request_mask; + grant_encoded_next = masked_request_index; + if (LSB_PRIORITY == "LOW") begin + mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index); + end else begin + mask_next = {PORTS{1'b1}} << (masked_request_index + 1); + end + end else begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + if (LSB_PRIORITY == "LOW") begin + mask_next = {PORTS{1'b1}} >> (PORTS - request_index); + end else begin + mask_next = {PORTS{1'b1}} << (request_index + 1); + end + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + grant_reg <= 0; + grant_valid_reg <= 0; + grant_encoded_reg <= 0; + mask_reg <= 0; + end else begin + grant_reg <= grant_next; + grant_valid_reg <= grant_valid_next; + grant_encoded_reg <= grant_encoded_next; + mask_reg <= mask_next; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_adapter.v b/fpga/lib/eth/lib/axis/rtl/axis_adapter.v new file mode 100644 index 000000000..da31f4b32 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_adapter.v @@ -0,0 +1,540 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream bus width adapter + */ +module axis_adapter # +( + parameter S_DATA_WIDTH = 8, + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + parameter M_DATA_WIDTH = 8, + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [S_DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [M_DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +// 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; + +// 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; +// output bus is wider +parameter 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; +// required number of segments in wider bus +parameter SEGMENT_COUNT = EXPAND_BUS ? (M_KEEP_WIDTH_INT / S_KEEP_WIDTH_INT) : (S_KEEP_WIDTH_INT / M_KEEP_WIDTH_INT); +parameter SEGMENT_COUNT_WIDTH = SEGMENT_COUNT == 1 ? 1 : $clog2(SEGMENT_COUNT); +// data width and keep width per segment +parameter SEGMENT_DATA_WIDTH = DATA_WIDTH / SEGMENT_COUNT; +parameter SEGMENT_KEEP_WIDTH = KEEP_WIDTH / SEGMENT_COUNT; + +// bus width assertions +initial begin + if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisble"); + $finish; + end + + if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisble"); + $finish; + end + + if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin + $error("Error: word size mismatch"); + $finish; + end +end + +// state register +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_TRANSFER_IN = 3'd1, + STATE_TRANSFER_OUT = 3'd2; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg [SEGMENT_COUNT_WIDTH-1:0] segment_count_reg = 0, segment_count_next; + +reg last_segment; + +reg [DATA_WIDTH-1:0] temp_tdata_reg = {DATA_WIDTH{1'b0}}, temp_tdata_next; +reg [KEEP_WIDTH-1:0] temp_tkeep_reg = {KEEP_WIDTH{1'b0}}, temp_tkeep_next; +reg temp_tlast_reg = 1'b0, temp_tlast_next; +reg [ID_WIDTH-1:0] temp_tid_reg = {ID_WIDTH{1'b0}}, temp_tid_next; +reg [DEST_WIDTH-1:0] temp_tdest_reg = {DEST_WIDTH{1'b0}}, temp_tdest_next; +reg [USER_WIDTH-1:0] temp_tuser_reg = {USER_WIDTH{1'b0}}, temp_tuser_next; + +// internal datapath +reg [M_DATA_WIDTH-1:0] m_axis_tdata_int; +reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +assign s_axis_tready = s_axis_tready_reg; + +always @* begin + state_next = STATE_IDLE; + + segment_count_next = segment_count_reg; + + last_segment = 0; + + temp_tdata_next = temp_tdata_reg; + temp_tkeep_next = temp_tkeep_reg; + temp_tlast_next = temp_tlast_reg; + temp_tid_next = temp_tid_reg; + temp_tdest_next = temp_tdest_reg; + temp_tuser_next = temp_tuser_reg; + + if (EXPAND_BUS) begin + m_axis_tdata_int = temp_tdata_reg; + m_axis_tkeep_int = temp_tkeep_reg; + m_axis_tlast_int = temp_tlast_reg; + end else begin + m_axis_tdata_int = {M_DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {M_KEEP_WIDTH{1'b0}}; + m_axis_tlast_int = 1'b0; + end + m_axis_tvalid_int = 1'b0; + m_axis_tid_int = temp_tid_reg; + m_axis_tdest_int = temp_tdest_reg; + m_axis_tuser_int = temp_tuser_reg; + + s_axis_tready_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - no data in registers + if (SEGMENT_COUNT == 1) begin + // output and input same width - just act like a register + + // accept data next cycle if output register ready next cycle + s_axis_tready_next = m_axis_tready_int_early; + + // transfer through + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + state_next = STATE_IDLE; + end else if (EXPAND_BUS) begin + // output bus is wider + + // accept new data + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store it in data register + + // pass complete input word, zero-extended to temp register + temp_tdata_next = s_axis_tdata; + temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + // first input segment complete + segment_count_next = 1; + + if (s_axis_tlast) begin + // got last signal on first segment, so output it + s_axis_tready_next = 1'b0; + state_next = STATE_TRANSFER_OUT; + end else begin + // otherwise, transfer in the rest of the words + s_axis_tready_next = 1'b1; + state_next = STATE_TRANSFER_IN; + end + end else begin + state_next = STATE_IDLE; + end + end else begin + // output bus is narrower + + // accept new data + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store it in data register + segment_count_next = 0; + + // is this the last segment? + if (SEGMENT_COUNT == 1) begin + // last segment by counter value + last_segment = 1'b1; + end else if (S_KEEP_ENABLE && s_axis_tkeep[SEGMENT_KEEP_WIDTH-1:0] != {SEGMENT_KEEP_WIDTH{1'b1}}) begin + // last segment by tkeep fall in current segment + last_segment = 1'b1; + end else if (S_KEEP_ENABLE && s_axis_tkeep[(SEGMENT_KEEP_WIDTH*2)-1:SEGMENT_KEEP_WIDTH] == {SEGMENT_KEEP_WIDTH{1'b0}}) begin + // last segment by tkeep fall at end of current segment + last_segment = 1'b1; + end else begin + last_segment = 1'b0; + end + + // pass complete input word, zero-extended to temp register + temp_tdata_next = s_axis_tdata; + temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + // short-circuit and get first word out the door + m_axis_tdata_int = s_axis_tdata[SEGMENT_DATA_WIDTH-1:0]; + m_axis_tkeep_int = s_axis_tkeep[SEGMENT_KEEP_WIDTH-1:0]; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = s_axis_tlast & last_segment; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + if (m_axis_tready_int_reg) begin + // if output register is ready for first word, then move on to the next one + segment_count_next = 1; + end + + if (!last_segment || !m_axis_tready_int_reg) begin + // continue outputting words + s_axis_tready_next = 1'b0; + state_next = STATE_TRANSFER_OUT; + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_IDLE; + end + end + end + STATE_TRANSFER_IN: begin + // transfer word to temp registers + // only used when output is wider + + // accept new data + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store in data register + + temp_tdata_next[segment_count_reg*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH] = s_axis_tdata; + temp_tkeep_next[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + segment_count_next = segment_count_reg + 1; + + if ((segment_count_reg == SEGMENT_COUNT-1) || s_axis_tlast) begin + // terminated by counter or tlast signal, output complete word + // read input word next cycle if output will be ready + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_TRANSFER_OUT; + end else begin + // more words to read + s_axis_tready_next = 1'b1; + state_next = STATE_TRANSFER_IN; + end + end else begin + state_next = STATE_TRANSFER_IN; + end + end + STATE_TRANSFER_OUT: begin + // transfer word to output registers + + if (EXPAND_BUS) begin + // output bus is wider + + // do not accept new data + s_axis_tready_next = 1'b0; + + // single-cycle output of entire stored word (output wider) + m_axis_tdata_int = temp_tdata_reg; + m_axis_tkeep_int = temp_tkeep_reg; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = temp_tlast_reg; + m_axis_tid_int = temp_tid_reg; + m_axis_tdest_int = temp_tdest_reg; + m_axis_tuser_int = temp_tuser_reg; + + if (m_axis_tready_int_reg) begin + // word transfer out + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in + + // pass complete input word, zero-extended to temp register + temp_tdata_next = s_axis_tdata; + temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + // first input segment complete + segment_count_next = 1; + + if (s_axis_tlast) begin + // got last signal on first segment, so output it + s_axis_tready_next = 1'b0; + state_next = STATE_TRANSFER_OUT; + end else begin + // otherwise, transfer in the rest of the words + s_axis_tready_next = 1'b1; + state_next = STATE_TRANSFER_IN; + end + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_TRANSFER_OUT; + end + end else begin + // output bus is narrower + + // do not accept new data + s_axis_tready_next = 1'b0; + + // is this the last segment? + if (segment_count_reg == SEGMENT_COUNT-1) begin + // last segment by counter value + last_segment = 1'b1; + end else if (temp_tkeep_reg[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] != {SEGMENT_KEEP_WIDTH{1'b1}}) begin + // last segment by tkeep fall in current segment + last_segment = 1'b1; + end else if (temp_tkeep_reg[(segment_count_reg+1)*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] == {SEGMENT_KEEP_WIDTH{1'b0}}) begin + // last segment by tkeep fall at end of current segment + last_segment = 1'b1; + end else begin + last_segment = 1'b0; + end + + // output current part of stored word (output narrower) + m_axis_tdata_int = temp_tdata_reg[segment_count_reg*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH]; + m_axis_tkeep_int = temp_tkeep_reg[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH]; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = temp_tlast_reg && last_segment; + m_axis_tid_int = temp_tid_reg; + m_axis_tdest_int = temp_tdest_reg; + m_axis_tuser_int = temp_tuser_reg; + + if (m_axis_tready_int_reg) begin + // word transfer out + + segment_count_next = segment_count_reg + 1; + + if (last_segment) begin + // terminated by counter or tlast signal + + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + // more words to write + state_next = STATE_TRANSFER_OUT; + end + end else begin + state_next = STATE_TRANSFER_OUT; + end + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axis_tready_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_axis_tready_reg <= s_axis_tready_next; + end + + segment_count_reg <= segment_count_next; + + temp_tdata_reg <= temp_tdata_next; + temp_tkeep_reg <= temp_tkeep_next; + temp_tlast_reg <= temp_tlast_next; + temp_tid_reg <= temp_tid_next; + temp_tdest_reg <= temp_tdest_next; + temp_tuser_reg <= temp_tuser_next; +end + +// output datapath logic +reg [M_DATA_WIDTH-1:0] m_axis_tdata_reg = {M_DATA_WIDTH{1'b0}}; +reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_reg = {M_KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [M_DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {M_DATA_WIDTH{1'b0}}; +reg [M_KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {M_KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = M_KEEP_ENABLE ? m_axis_tkeep_reg : {M_KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_arb_mux.v b/fpga/lib/eth/lib/axis/rtl/axis_arb_mux.v new file mode 100644 index 000000000..a23c6a5ed --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_arb_mux.v @@ -0,0 +1,239 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream arbitrated multiplexer + */ +module axis_arb_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +wire [S_COUNT-1:0] request; +wire [S_COUNT-1:0] acknowledge; +wire [S_COUNT-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT-1:0] grant_encoded; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = (m_axis_tready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_axis_tvalid[grant_encoded]; +wire current_s_tready = s_axis_tready[grant_encoded]; +wire current_s_tlast = s_axis_tlast[grant_encoded]; +wire [ID_WIDTH-1:0] current_s_tid = s_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + +// arbiter instance +arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_tvalid & ~grant; +assign acknowledge = grant & s_axis_tvalid & s_axis_tready & s_axis_tlast; + +always @* begin + // pass through selected packet data + m_axis_tdata_int = current_s_tdata; + m_axis_tkeep_int = current_s_tkeep; + m_axis_tvalid_int = current_s_tvalid && m_axis_tready_int_reg && grant_valid; + m_axis_tlast_int = current_s_tlast; + m_axis_tid_int = current_s_tid; + m_axis_tdest_int = current_s_tdest; + m_axis_tuser_int = current_s_tuser; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_arb_mux_wrap.py b/fpga/lib/eth/lib/axis/rtl/axis_arb_mux_wrap.py new file mode 100755 index 000000000..292dcc326 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_arb_mux_wrap.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream arbitrated mux wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + n = ports + + if name is None: + name = "axis_arb_mux_wrap_{0}".format(n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0} port AXI stream arbitrated mux wrapper {1}...".format(n, name)) + + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 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 1ns / 1ps + +/* + * AXI4-Stream {{n}} port arbitrated mux (wrapper) + */ +module {{name}} # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ +{%- for p in range(n) %} + input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata, + input wire [KEEP_WIDTH-1:0] s{{'%02d'%p}}_axis_tkeep, + input wire s{{'%02d'%p}}_axis_tvalid, + output wire s{{'%02d'%p}}_axis_tready, + input wire s{{'%02d'%p}}_axis_tlast, + input wire [ID_WIDTH-1:0] s{{'%02d'%p}}_axis_tid, + input wire [DEST_WIDTH-1:0] s{{'%02d'%p}}_axis_tdest, + input wire [USER_WIDTH-1:0] s{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +axis_arb_mux #( + .S_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +axis_arb_mux_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tkeep({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tvalid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tready({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tlast({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tdest({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tuser({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule + +""") + + output_file.write(t.render( + n=n, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/fpga/lib/eth/lib/axis/rtl/axis_async_fifo.v b/fpga/lib/eth/lib/axis/rtl/axis_async_fifo.v new file mode 100644 index 000000000..d1b9fb4a4 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_async_fifo.v @@ -0,0 +1,512 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream asynchronous FIFO + */ +module axis_async_fifo # +( + parameter ADDR_WIDTH = 12, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LAST_ENABLE = 1, + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + parameter FRAME_FIFO = 0, + parameter USER_BAD_FRAME_VALUE = 1'b1, + parameter USER_BAD_FRAME_MASK = 1'b1, + parameter DROP_BAD_FRAME = 0, + parameter DROP_WHEN_FULL = 0 +) +( + /* + * Common asynchronous reset + */ + input wire async_rst, + + /* + * AXI input + */ + input wire s_clk, + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + input wire m_clk, + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire s_status_overflow, + output wire s_status_bad_frame, + output wire s_status_good_frame, + output wire m_status_overflow, + output wire m_status_bad_frame, + output wire m_status_good_frame +); + +// check configuration +initial begin + if (FRAME_FIFO && !LAST_ENABLE) begin + $error("Error: FRAME_FIFO set requires LAST_ENABLE set"); + $finish; + end + + if (DROP_BAD_FRAME && !FRAME_FIFO) begin + $error("Error: DROP_BAD_FRAME set requires FRAME_FIFO set"); + $finish; + end + + if (DROP_WHEN_FULL && !FRAME_FIFO) begin + $error("Error: DROP_WHEN_FULL set requires FRAME_FIFO set"); + $finish; + end + + if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin + $error("Error: Invalid USER_BAD_FRAME_MASK value"); + $finish; + end +end + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next; +reg [ADDR_WIDTH:0] wr_ptr_gray_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_gray_next; +reg [ADDR_WIDTH:0] wr_ptr_sync_gray_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_sync_gray_next; +reg [ADDR_WIDTH:0] wr_ptr_cur_gray_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_gray_next; +reg [ADDR_WIDTH:0] wr_addr_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next; +reg [ADDR_WIDTH:0] rd_ptr_gray_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_gray_next; +reg [ADDR_WIDTH:0] rd_addr_reg = {ADDR_WIDTH+1{1'b0}}; + +reg [ADDR_WIDTH:0] wr_ptr_gray_sync1_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_gray_sync2_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_sync1_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_sync2_reg = {ADDR_WIDTH+1{1'b0}}; + +reg wr_ptr_update_valid_reg = 1'b0, wr_ptr_update_valid_next; +reg wr_ptr_update_reg = 1'b0, wr_ptr_update_next; +reg wr_ptr_update_sync1_reg = 1'b0; +reg wr_ptr_update_sync2_reg = 1'b0; +reg wr_ptr_update_sync3_reg = 1'b0; +reg wr_ptr_update_ack_sync1_reg = 1'b0; +reg wr_ptr_update_ack_sync2_reg = 1'b0; + +reg s_rst_sync1_reg = 1'b1; +reg s_rst_sync2_reg = 1'b1; +reg s_rst_sync3_reg = 1'b1; +reg m_rst_sync1_reg = 1'b1; +reg m_rst_sync2_reg = 1'b1; +reg m_rst_sync3_reg = 1'b1; + +reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0]; +reg [WIDTH-1:0] mem_read_data_reg; +reg mem_read_data_valid_reg = 1'b0, mem_read_data_valid_next; + +wire [WIDTH-1:0] s_axis; + +reg [WIDTH-1:0] m_axis_reg; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; + +// full when first TWO MSBs do NOT match, but rest matches +// (gray code equivalent of first MSB different but rest same) +wire full = ((wr_ptr_gray_reg[ADDR_WIDTH] != rd_ptr_gray_sync2_reg[ADDR_WIDTH]) && + (wr_ptr_gray_reg[ADDR_WIDTH-1] != rd_ptr_gray_sync2_reg[ADDR_WIDTH-1]) && + (wr_ptr_gray_reg[ADDR_WIDTH-2:0] == rd_ptr_gray_sync2_reg[ADDR_WIDTH-2:0])); +wire full_cur = ((wr_ptr_cur_gray_reg[ADDR_WIDTH] != rd_ptr_gray_sync2_reg[ADDR_WIDTH]) && + (wr_ptr_cur_gray_reg[ADDR_WIDTH-1] != rd_ptr_gray_sync2_reg[ADDR_WIDTH-1]) && + (wr_ptr_cur_gray_reg[ADDR_WIDTH-2:0] == rd_ptr_gray_sync2_reg[ADDR_WIDTH-2:0])); +// empty when pointers match exactly +wire empty = rd_ptr_gray_reg == (FRAME_FIFO ? wr_ptr_gray_sync1_reg : wr_ptr_gray_sync2_reg); +// overflow within packet +wire full_wr = ((wr_ptr_reg[ADDR_WIDTH] != wr_ptr_cur_reg[ADDR_WIDTH]) && + (wr_ptr_reg[ADDR_WIDTH-1:0] == wr_ptr_cur_reg[ADDR_WIDTH-1:0])); + +// control signals +reg write; +reg read; +reg store_output; + +reg drop_frame_reg = 1'b0, drop_frame_next; +reg overflow_reg = 1'b0, overflow_next; +reg bad_frame_reg = 1'b0, bad_frame_next; +reg good_frame_reg = 1'b0, good_frame_next; + +reg overflow_sync1_reg = 1'b0; +reg overflow_sync2_reg = 1'b0; +reg overflow_sync3_reg = 1'b0; +reg overflow_sync4_reg = 1'b0; +reg bad_frame_sync1_reg = 1'b0; +reg bad_frame_sync2_reg = 1'b0; +reg bad_frame_sync3_reg = 1'b0; +reg bad_frame_sync4_reg = 1'b0; +reg good_frame_sync1_reg = 1'b0; +reg good_frame_sync2_reg = 1'b0; +reg good_frame_sync3_reg = 1'b0; +reg good_frame_sync4_reg = 1'b0; + +assign s_axis_tready = (FRAME_FIFO ? (!full_cur || full_wr || DROP_WHEN_FULL) : !full) && !s_rst_sync3_reg; + +generate + assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; +endgenerate + +assign m_axis_tvalid = m_axis_tvalid_reg; + +assign m_axis_tdata = m_axis_reg[DATA_WIDTH-1:0]; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_reg[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; +assign m_axis_tlast = LAST_ENABLE ? m_axis_reg[LAST_OFFSET] : 1'b1; +assign m_axis_tid = ID_ENABLE ? m_axis_reg[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_reg[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_reg[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}}; + +assign s_status_overflow = overflow_reg; +assign s_status_bad_frame = bad_frame_reg; +assign s_status_good_frame = good_frame_reg; + +assign m_status_overflow = overflow_sync3_reg ^ overflow_sync4_reg; +assign m_status_bad_frame = bad_frame_sync3_reg ^ bad_frame_sync4_reg; +assign m_status_good_frame = good_frame_sync3_reg ^ good_frame_sync4_reg; + +// reset synchronization +always @(posedge s_clk or posedge async_rst) begin + if (async_rst) begin + s_rst_sync1_reg <= 1'b1; + s_rst_sync2_reg <= 1'b1; + s_rst_sync3_reg <= 1'b1; + end else begin + s_rst_sync1_reg <= 1'b0; + s_rst_sync2_reg <= s_rst_sync1_reg || m_rst_sync1_reg; + s_rst_sync3_reg <= s_rst_sync2_reg; + end +end + +always @(posedge m_clk or posedge async_rst) begin + if (async_rst) begin + m_rst_sync1_reg <= 1'b1; + m_rst_sync2_reg <= 1'b1; + m_rst_sync3_reg <= 1'b1; + end else begin + m_rst_sync1_reg <= 1'b0; + m_rst_sync2_reg <= s_rst_sync1_reg || m_rst_sync1_reg; + m_rst_sync3_reg <= m_rst_sync2_reg; + end +end + +// Write logic +always @* begin + write = 1'b0; + + drop_frame_next = 1'b0; + overflow_next = 1'b0; + bad_frame_next = 1'b0; + good_frame_next = 1'b0; + + wr_ptr_next = wr_ptr_reg; + wr_ptr_cur_next = wr_ptr_cur_reg; + wr_ptr_gray_next = wr_ptr_gray_reg; + wr_ptr_sync_gray_next = wr_ptr_sync_gray_reg; + wr_ptr_cur_gray_next = wr_ptr_cur_gray_reg; + + wr_ptr_update_valid_next = wr_ptr_update_valid_reg; + wr_ptr_update_next = wr_ptr_update_reg; + + if (FRAME_FIFO && wr_ptr_update_valid_reg) begin + // have updated pointer to sync + if (wr_ptr_update_next == wr_ptr_update_ack_sync2_reg) begin + // no sync in progress; sync update + wr_ptr_update_valid_next = 1'b0; + wr_ptr_sync_gray_next = wr_ptr_gray_reg; + wr_ptr_update_next = !wr_ptr_update_ack_sync2_reg; + end + end + + if (s_axis_tready && s_axis_tvalid) begin + // transfer in + if (!FRAME_FIFO) begin + // normal FIFO mode + write = 1'b1; + wr_ptr_next = wr_ptr_reg + 1; + wr_ptr_gray_next = wr_ptr_next ^ (wr_ptr_next >> 1); + end else if (full_cur || full_wr || drop_frame_reg) begin + // full, packet overflow, or currently dropping frame + // drop frame + drop_frame_next = 1'b1; + if (s_axis_tlast) begin + // end of frame, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1); + drop_frame_next = 1'b0; + overflow_next = 1'b1; + end + end else begin + write = 1'b1; + wr_ptr_cur_next = wr_ptr_cur_reg + 1; + wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1); + if (s_axis_tlast) begin + // end of frame + if (DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(s_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin + // bad packet, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1); + bad_frame_next = 1'b1; + end else begin + // good packet, update write pointer + wr_ptr_next = wr_ptr_cur_reg + 1; + wr_ptr_gray_next = wr_ptr_next ^ (wr_ptr_next >> 1); + + if (wr_ptr_update_next == wr_ptr_update_ack_sync2_reg) begin + // no sync in progress; sync update + wr_ptr_update_valid_next = 1'b0; + wr_ptr_sync_gray_next = wr_ptr_gray_next; + wr_ptr_update_next = !wr_ptr_update_ack_sync2_reg; + end else begin + // sync in progress; flag it for later + wr_ptr_update_valid_next = 1'b1; + end + + good_frame_next = 1'b1; + end + end + end + end +end + +always @(posedge s_clk) begin + if (s_rst_sync3_reg) begin + wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_sync_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_cur_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + + wr_ptr_update_valid_reg <= 1'b0; + wr_ptr_update_reg <= 1'b0; + + drop_frame_reg <= 1'b0; + overflow_reg <= 1'b0; + bad_frame_reg <= 1'b0; + good_frame_reg <= 1'b0; + end else begin + wr_ptr_reg <= wr_ptr_next; + wr_ptr_cur_reg <= wr_ptr_cur_next; + wr_ptr_gray_reg <= wr_ptr_gray_next; + wr_ptr_sync_gray_reg <= wr_ptr_sync_gray_next; + wr_ptr_cur_gray_reg <= wr_ptr_cur_gray_next; + + wr_ptr_update_valid_reg <= wr_ptr_update_valid_next; + wr_ptr_update_reg <= wr_ptr_update_next; + + drop_frame_reg <= drop_frame_next; + overflow_reg <= overflow_next; + bad_frame_reg <= bad_frame_next; + good_frame_reg <= good_frame_next; + end + + if (FRAME_FIFO) begin + wr_addr_reg <= wr_ptr_cur_next; + end else begin + wr_addr_reg <= wr_ptr_next; + end + + if (write) begin + mem[wr_addr_reg[ADDR_WIDTH-1:0]] <= s_axis; + end +end + +// pointer synchronization +always @(posedge s_clk) begin + if (s_rst_sync3_reg) begin + rd_ptr_gray_sync1_reg <= {ADDR_WIDTH+1{1'b0}}; + rd_ptr_gray_sync2_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_update_ack_sync1_reg <= 1'b0; + wr_ptr_update_ack_sync2_reg <= 1'b0; + end else begin + rd_ptr_gray_sync1_reg <= rd_ptr_gray_reg; + rd_ptr_gray_sync2_reg <= rd_ptr_gray_sync1_reg; + wr_ptr_update_ack_sync1_reg <= wr_ptr_update_sync3_reg; + wr_ptr_update_ack_sync2_reg <= wr_ptr_update_ack_sync1_reg; + end +end + +always @(posedge m_clk) begin + if (m_rst_sync3_reg) begin + wr_ptr_gray_sync1_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_gray_sync2_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_update_sync1_reg <= 1'b0; + wr_ptr_update_sync2_reg <= 1'b0; + wr_ptr_update_sync3_reg <= 1'b0; + end else begin + if (!FRAME_FIFO) begin + wr_ptr_gray_sync1_reg <= wr_ptr_gray_reg; + end else if (wr_ptr_update_sync2_reg ^ wr_ptr_update_sync3_reg) begin + wr_ptr_gray_sync1_reg <= wr_ptr_sync_gray_reg; + end + wr_ptr_gray_sync2_reg <= wr_ptr_gray_sync1_reg; + wr_ptr_update_sync1_reg <= wr_ptr_update_reg; + wr_ptr_update_sync2_reg <= wr_ptr_update_sync1_reg; + wr_ptr_update_sync3_reg <= wr_ptr_update_sync2_reg; + end +end + +// status synchronization +always @(posedge s_clk) begin + if (s_rst_sync3_reg) begin + overflow_sync1_reg <= 1'b0; + bad_frame_sync1_reg <= 1'b0; + good_frame_sync1_reg <= 1'b0; + end else begin + overflow_sync1_reg <= overflow_sync1_reg ^ overflow_reg; + bad_frame_sync1_reg <= bad_frame_sync1_reg ^ bad_frame_reg; + good_frame_sync1_reg <= good_frame_sync1_reg ^ good_frame_reg; + end +end + +always @(posedge m_clk) begin + if (m_rst_sync3_reg) begin + overflow_sync2_reg <= 1'b0; + overflow_sync3_reg <= 1'b0; + overflow_sync4_reg <= 1'b0; + bad_frame_sync2_reg <= 1'b0; + bad_frame_sync3_reg <= 1'b0; + bad_frame_sync4_reg <= 1'b0; + good_frame_sync2_reg <= 1'b0; + good_frame_sync3_reg <= 1'b0; + good_frame_sync4_reg <= 1'b0; + end else begin + overflow_sync2_reg <= overflow_sync1_reg; + overflow_sync3_reg <= overflow_sync2_reg; + overflow_sync4_reg <= overflow_sync3_reg; + bad_frame_sync2_reg <= bad_frame_sync1_reg; + bad_frame_sync3_reg <= bad_frame_sync2_reg; + bad_frame_sync4_reg <= bad_frame_sync3_reg; + good_frame_sync2_reg <= good_frame_sync1_reg; + good_frame_sync3_reg <= good_frame_sync2_reg; + good_frame_sync4_reg <= good_frame_sync3_reg; + end +end + +// Read logic +always @* begin + read = 1'b0; + + rd_ptr_next = rd_ptr_reg; + rd_ptr_gray_next = rd_ptr_gray_reg; + + mem_read_data_valid_next = mem_read_data_valid_reg; + + if (store_output || !mem_read_data_valid_reg) begin + // output data not valid OR currently being transferred + if (!empty) begin + // not empty, perform read + read = 1'b1; + mem_read_data_valid_next = 1'b1; + rd_ptr_next = rd_ptr_reg + 1; + rd_ptr_gray_next = rd_ptr_next ^ (rd_ptr_next >> 1); + end else begin + // empty, invalidate + mem_read_data_valid_next = 1'b0; + end + end +end + +always @(posedge m_clk) begin + if (m_rst_sync3_reg) begin + rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + rd_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + mem_read_data_valid_reg <= 1'b0; + end else begin + rd_ptr_reg <= rd_ptr_next; + rd_ptr_gray_reg <= rd_ptr_gray_next; + mem_read_data_valid_reg <= mem_read_data_valid_next; + end + + rd_addr_reg <= rd_ptr_next; + + if (read) begin + mem_read_data_reg <= mem[rd_addr_reg[ADDR_WIDTH-1:0]]; + end +end + +// Output register +always @* begin + store_output = 1'b0; + + m_axis_tvalid_next = m_axis_tvalid_reg; + + if (m_axis_tready || !m_axis_tvalid) begin + store_output = 1'b1; + m_axis_tvalid_next = mem_read_data_valid_reg; + end +end + +always @(posedge m_clk) begin + if (m_rst_sync3_reg) begin + m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + end + + if (store_output) begin + m_axis_reg <= mem_read_data_reg; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_async_fifo_adapter.v b/fpga/lib/eth/lib/axis/rtl/axis_async_fifo_adapter.v new file mode 100644 index 000000000..d8e626096 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_async_fifo_adapter.v @@ -0,0 +1,321 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * AXI4-Stream asynchronous FIFO with width converter + */ +module axis_async_fifo_adapter # +( + parameter ADDR_WIDTH = 12, + parameter S_DATA_WIDTH = 8, + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + parameter M_DATA_WIDTH = 8, + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + parameter FRAME_FIFO = 0, + parameter USER_BAD_FRAME_VALUE = 1'b1, + parameter USER_BAD_FRAME_MASK = 1'b1, + parameter DROP_BAD_FRAME = 0, + parameter DROP_WHEN_FULL = 0 +) +( + /* + * AXI input + */ + input wire s_clk, + input wire s_rst, + input wire [S_DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + input wire m_clk, + input wire m_rst, + output wire [M_DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire s_status_overflow, + output wire s_status_bad_frame, + output wire s_status_good_frame, + output wire m_status_overflow, + output wire m_status_bad_frame, + output wire m_status_good_frame +); + +// 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; + +// 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; +// output bus is wider +parameter 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; + +// bus width assertions +initial begin + if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisble"); + $finish; + end + + if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisble"); + $finish; + end + + if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin + $error("Error: word size mismatch"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] pre_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] pre_fifo_axis_tkeep; +wire pre_fifo_axis_tvalid; +wire pre_fifo_axis_tready; +wire pre_fifo_axis_tlast; +wire [ID_WIDTH-1:0] pre_fifo_axis_tid; +wire [DEST_WIDTH-1:0] pre_fifo_axis_tdest; +wire [USER_WIDTH-1:0] pre_fifo_axis_tuser; + +wire [DATA_WIDTH-1:0] post_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] post_fifo_axis_tkeep; +wire post_fifo_axis_tvalid; +wire post_fifo_axis_tready; +wire post_fifo_axis_tlast; +wire [ID_WIDTH-1:0] post_fifo_axis_tid; +wire [DEST_WIDTH-1:0] post_fifo_axis_tdest; +wire [USER_WIDTH-1:0] post_fifo_axis_tuser; + +generate + +if (M_KEEP_WIDTH == S_KEEP_WIDTH) begin + + // same width, no adapter needed + + assign pre_fifo_axis_tdata = s_axis_tdata; + assign pre_fifo_axis_tkeep = s_axis_tkeep; + assign pre_fifo_axis_tvalid = s_axis_tvalid; + assign s_axis_tready = pre_fifo_axis_tready; + assign pre_fifo_axis_tlast = s_axis_tlast; + assign pre_fifo_axis_tid = s_axis_tid; + assign pre_fifo_axis_tdest = s_axis_tdest; + assign pre_fifo_axis_tuser = s_axis_tuser; + + assign m_axis_tdata = post_fifo_axis_tdata; + assign m_axis_tkeep = post_fifo_axis_tkeep; + assign m_axis_tvalid = post_fifo_axis_tvalid; + assign post_fifo_axis_tready = m_axis_tready; + assign m_axis_tlast = post_fifo_axis_tlast; + assign m_axis_tid = post_fifo_axis_tid; + assign m_axis_tdest = post_fifo_axis_tdest; + assign m_axis_tuser = post_fifo_axis_tuser; + + +end else if (EXPAND_BUS) begin + + // output wider, adapt width before FIFO + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(s_clk), + .rst(s_rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(pre_fifo_axis_tdata), + .m_axis_tkeep(pre_fifo_axis_tkeep), + .m_axis_tvalid(pre_fifo_axis_tvalid), + .m_axis_tready(pre_fifo_axis_tready), + .m_axis_tlast(pre_fifo_axis_tlast), + .m_axis_tid(pre_fifo_axis_tid), + .m_axis_tdest(pre_fifo_axis_tdest), + .m_axis_tuser(pre_fifo_axis_tuser) + ); + + assign m_axis_tdata = post_fifo_axis_tdata; + assign m_axis_tkeep = post_fifo_axis_tkeep; + assign m_axis_tvalid = post_fifo_axis_tvalid; + assign post_fifo_axis_tready = m_axis_tready; + assign m_axis_tlast = post_fifo_axis_tlast; + assign m_axis_tid = post_fifo_axis_tid; + assign m_axis_tdest = post_fifo_axis_tdest; + assign m_axis_tuser = post_fifo_axis_tuser; + +end else begin + + // input wider, adapt width after FIFO + + assign pre_fifo_axis_tdata = s_axis_tdata; + assign pre_fifo_axis_tkeep = s_axis_tkeep; + assign pre_fifo_axis_tvalid = s_axis_tvalid; + assign s_axis_tready = pre_fifo_axis_tready; + assign pre_fifo_axis_tlast = s_axis_tlast; + assign pre_fifo_axis_tid = s_axis_tid; + assign pre_fifo_axis_tdest = s_axis_tdest; + assign pre_fifo_axis_tuser = s_axis_tuser; + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(m_clk), + .rst(m_rst), + // AXI input + .s_axis_tdata(post_fifo_axis_tdata), + .s_axis_tkeep(post_fifo_axis_tkeep), + .s_axis_tvalid(post_fifo_axis_tvalid), + .s_axis_tready(post_fifo_axis_tready), + .s_axis_tlast(post_fifo_axis_tlast), + .s_axis_tid(post_fifo_axis_tid), + .s_axis_tdest(post_fifo_axis_tdest), + .s_axis_tuser(post_fifo_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) + ); + +end + +endgenerate + +axis_async_fifo #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(EXPAND_BUS ? M_KEEP_ENABLE : S_KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +fifo_inst ( + // Common reset + .async_rst(s_rst | m_rst), + // AXI input + .s_clk(s_clk), + .s_axis_tdata(pre_fifo_axis_tdata), + .s_axis_tkeep(pre_fifo_axis_tkeep), + .s_axis_tvalid(pre_fifo_axis_tvalid), + .s_axis_tready(pre_fifo_axis_tready), + .s_axis_tlast(pre_fifo_axis_tlast), + .s_axis_tid(pre_fifo_axis_tid), + .s_axis_tdest(pre_fifo_axis_tdest), + .s_axis_tuser(pre_fifo_axis_tuser), + // AXI output + .m_clk(m_clk), + .m_axis_tdata(post_fifo_axis_tdata), + .m_axis_tkeep(post_fifo_axis_tkeep), + .m_axis_tvalid(post_fifo_axis_tvalid), + .m_axis_tready(post_fifo_axis_tready), + .m_axis_tlast(post_fifo_axis_tlast), + .m_axis_tid(post_fifo_axis_tid), + .m_axis_tdest(post_fifo_axis_tdest), + .m_axis_tuser(post_fifo_axis_tuser), + // Status + .s_status_overflow(s_status_overflow), + .s_status_bad_frame(s_status_bad_frame), + .s_status_good_frame(s_status_good_frame), + .m_status_overflow(m_status_overflow), + .m_status_bad_frame(m_status_bad_frame), + .m_status_good_frame(m_status_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_broadcast.v b/fpga/lib/eth/lib/axis/rtl/axis_broadcast.v new file mode 100644 index 000000000..c285ff3e3 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_broadcast.v @@ -0,0 +1,180 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * AXI4-Stream broadcaster + */ +module axis_broadcast # +( + parameter M_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LAST_ENABLE = 1, + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI outputs + */ + output wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep, + output wire [M_COUNT-1:0] m_axis_tvalid, + input wire [M_COUNT-1:0] m_axis_tready, + output wire [M_COUNT-1:0] m_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +// datapath registers +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_axis_tvalid_reg = {M_COUNT{1'b0}}, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// // datapath control +reg store_axis_input_to_output; +reg store_axis_input_to_temp; +reg store_axis_temp_to_output; + +assign s_axis_tready = s_axis_tready_reg; + +assign m_axis_tdata = {M_COUNT{m_axis_tdata_reg}}; +assign m_axis_tkeep = KEEP_ENABLE ? {M_COUNT{m_axis_tkeep_reg}} : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = LAST_ENABLE ? {M_COUNT{m_axis_tlast_reg}} : {M_COUNT{1'b1}}; +assign m_axis_tid = ID_ENABLE ? {M_COUNT{m_axis_tid_reg}} : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? {M_COUNT{m_axis_tdest_reg}} : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? {M_COUNT{m_axis_tuser_reg}} : {M_COUNT*USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axis_tready_early = ((m_axis_tready & m_axis_tvalid) == m_axis_tvalid) || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid || !s_axis_tvalid)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg & ~m_axis_tready; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_input_to_output = 1'b0; + store_axis_input_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (s_axis_tready_reg) begin + // input is ready + if (((m_axis_tready & m_axis_tvalid) == m_axis_tvalid) || !m_axis_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = {M_COUNT{s_axis_tvalid}}; + store_axis_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = s_axis_tvalid; + store_axis_input_to_temp = 1'b1; + end + end else if ((m_axis_tready & m_axis_tvalid) == m_axis_tvalid) begin + // input is not ready, but output is ready + m_axis_tvalid_next = {M_COUNT{temp_m_axis_tvalid_reg}}; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axis_tready_reg <= 1'b0; + m_axis_tvalid_reg <= {M_COUNT{1'b0}}; + temp_m_axis_tvalid_reg <= {M_COUNT{1'b0}}; + end else begin + s_axis_tready_reg <= s_axis_tready_early; + m_axis_tvalid_reg <= m_axis_tvalid_next; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_input_to_output) begin + m_axis_tdata_reg <= s_axis_tdata; + m_axis_tkeep_reg <= s_axis_tkeep; + m_axis_tlast_reg <= s_axis_tlast; + m_axis_tid_reg <= s_axis_tid; + m_axis_tdest_reg <= s_axis_tdest; + m_axis_tuser_reg <= s_axis_tuser; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_input_to_temp) begin + temp_m_axis_tdata_reg <= s_axis_tdata; + temp_m_axis_tkeep_reg <= s_axis_tkeep; + temp_m_axis_tlast_reg <= s_axis_tlast; + temp_m_axis_tid_reg <= s_axis_tid; + temp_m_axis_tdest_reg <= s_axis_tdest; + temp_m_axis_tuser_reg <= s_axis_tuser; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_cobs_decode.v b/fpga/lib/eth/lib/axis/rtl/axis_cobs_decode.v new file mode 100644 index 000000000..aed0b1ef7 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_cobs_decode.v @@ -0,0 +1,328 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * AXI4-Stream consistent overhead byte stuffing (COBS) decoder + */ +module axis_cobs_decode +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser +); + +// state register +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_SEGMENT = 2'd1, + STATE_NEXT_SEGMENT = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [7:0] count_reg = 8'd0, count_next; +reg suppress_zero_reg = 1'b0, suppress_zero_next; + +reg [7:0] temp_tdata_reg = 8'd0, temp_tdata_next; +reg temp_tvalid_reg = 1'b0, temp_tvalid_next; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +assign s_axis_tready = s_axis_tready_reg; + +always @* begin + state_next = STATE_IDLE; + + count_next = count_reg; + suppress_zero_next = suppress_zero_reg; + + temp_tdata_next = temp_tdata_reg; + temp_tvalid_next = temp_tvalid_reg; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + s_axis_tready_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state + s_axis_tready_next = m_axis_tready_int_early || !temp_tvalid_reg; + + // output final word + m_axis_tdata_int = temp_tdata_reg; + m_axis_tvalid_int = temp_tvalid_reg; + m_axis_tlast_int = temp_tvalid_reg; + temp_tvalid_next = temp_tvalid_reg && !m_axis_tready_int_reg; + + if (s_axis_tready && s_axis_tvalid) begin + // valid input data + // skip any leading zeros + if (s_axis_tdata != 8'd0) begin + // store count value and zero suppress + count_next = s_axis_tdata-1; + suppress_zero_next = (s_axis_tdata == 8'd255); + s_axis_tready_next = m_axis_tready_int_early; + if (s_axis_tdata == 8'd1) begin + // next byte will be count value + state_next = STATE_NEXT_SEGMENT; + end else begin + // next byte will be data + state_next = STATE_SEGMENT; + end + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_SEGMENT: begin + // receive segment + s_axis_tready_next = m_axis_tready_int_early; + + if (s_axis_tready && s_axis_tvalid) begin + // valid input data + // store in temp register + temp_tdata_next = s_axis_tdata; + temp_tvalid_next = 1'b1; + // move temp to output + m_axis_tdata_int = temp_tdata_reg; + m_axis_tvalid_int = temp_tvalid_reg; + // decrement count + count_next = count_reg - 1; + if (s_axis_tdata == 8'd0) begin + // got a zero byte in a frame - mark it as an error and re-sync + temp_tvalid_next = 1'b0; + m_axis_tvalid_int = 1'b1; + m_axis_tuser_int = 1'b1; + m_axis_tlast_int = 1'b1; + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end else if (s_axis_tlast) begin + // end of frame + if (count_reg == 8'd1 && !s_axis_tuser) begin + // end of frame indication at correct time, go to idle to output final byte + state_next = STATE_IDLE; + end else begin + // end of frame indication at invalid time or tuser assert, so mark as an error and re-sync + temp_tvalid_next = 1'b0; + m_axis_tvalid_int = 1'b1; + m_axis_tuser_int = 1'b1; + m_axis_tlast_int = 1'b1; + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else if (count_reg == 8'd1) begin + // next byte will be count value + state_next = STATE_NEXT_SEGMENT; + end else begin + // next byte will be data + state_next = STATE_SEGMENT; + end + end else begin + state_next = STATE_SEGMENT; + end + end + STATE_NEXT_SEGMENT: begin + // next segment + s_axis_tready_next = m_axis_tready_int_early; + + if (s_axis_tready && s_axis_tvalid) begin + // valid input data + // store zero in temp if not suppressed + temp_tdata_next = 8'd0; + temp_tvalid_next = !suppress_zero_reg; + // move temp to output + m_axis_tdata_int = temp_tdata_reg; + m_axis_tvalid_int = temp_tvalid_reg; + if (s_axis_tdata == 8'd0) begin + // got a zero byte delineating the end of the frame, so mark as such and re-sync + temp_tvalid_next = 1'b0; + m_axis_tuser_int = s_axis_tuser; + m_axis_tlast_int = 1'b1; + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end else if (s_axis_tlast) begin + if (s_axis_tdata == 8'd1 && !s_axis_tuser) begin + // end of frame indication at correct time, go to idle to output final byte + state_next = STATE_IDLE; + end else begin + // end of frame indication at invalid time or tuser assert, so mark as an error and re-sync + temp_tvalid_next = 1'b0; + m_axis_tvalid_int = 1'b1; + m_axis_tuser_int = 1'b1; + m_axis_tlast_int = 1'b1; + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + // otherwise, store count value and zero suppress + count_next = s_axis_tdata-1; + suppress_zero_next = (s_axis_tdata == 8'd255); + s_axis_tready_next = m_axis_tready_int_early; + if (s_axis_tdata == 8'd1) begin + // next byte will be count value + state_next = STATE_NEXT_SEGMENT; + end else begin + // next byte will be data + state_next = STATE_SEGMENT; + end + end + end else begin + state_next = STATE_NEXT_SEGMENT; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + temp_tvalid_reg <= 1'b0; + s_axis_tready_reg <= 1'b0; + end else begin + state_reg <= state_next; + temp_tvalid_reg <= temp_tvalid_next; + s_axis_tready_reg <= s_axis_tready_next; + end + + temp_tdata_reg <= temp_tdata_next; + + count_reg <= count_next; + suppress_zero_reg <= suppress_zero_next; +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_cobs_encode.v b/fpga/lib/eth/lib/axis/rtl/axis_cobs_encode.v new file mode 100644 index 000000000..00a8e4ccb --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_cobs_encode.v @@ -0,0 +1,506 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * AXI4-Stream consistent overhead byte stuffing (COBS) encoder + */ +module axis_cobs_encode # +( + // append zero for in band framing + parameter APPEND_ZERO = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser +); + +// state register +localparam [1:0] + INPUT_STATE_IDLE = 2'd0, + INPUT_STATE_SEGMENT = 2'd1, + INPUT_STATE_FINAL_ZERO = 2'd2, + INPUT_STATE_APPEND_ZERO = 2'd3; + +reg [1:0] input_state_reg = INPUT_STATE_IDLE, input_state_next; + +localparam [0:0] + OUTPUT_STATE_IDLE = 1'd0, + OUTPUT_STATE_SEGMENT = 1'd1; + +reg [0:0] output_state_reg = OUTPUT_STATE_IDLE, output_state_next; + +reg [7:0] input_count_reg = 8'd0, input_count_next; +reg [7:0] output_count_reg = 8'd0, output_count_next; +reg fail_frame_reg = 1'b0, fail_frame_next; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg s_axis_tready_mask; + +assign s_axis_tready = code_fifo_in_tready && data_fifo_in_tready && s_axis_tready_mask; + +reg [7:0] code_fifo_in_tdata; +reg code_fifo_in_tvalid; +reg code_fifo_in_tlast; +reg code_fifo_in_tuser; +wire code_fifo_in_tready; + +wire [7:0] code_fifo_out_tdata; +wire code_fifo_out_tvalid; +wire code_fifo_out_tlast; +wire code_fifo_out_tuser; +reg code_fifo_out_tready; + +axis_fifo #( + .ADDR_WIDTH(8), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(0) +) +code_fifo_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(code_fifo_in_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(code_fifo_in_tvalid), + .s_axis_tready(code_fifo_in_tready), + .s_axis_tlast(code_fifo_in_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(code_fifo_in_tuser), + // AXI output + .m_axis_tdata(code_fifo_out_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(code_fifo_out_tvalid), + .m_axis_tready(code_fifo_out_tready), + .m_axis_tlast(code_fifo_out_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(code_fifo_out_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +reg [7:0] data_fifo_in_tdata; +reg data_fifo_in_tvalid; +reg data_fifo_in_tlast; +wire data_fifo_in_tready; + +wire [7:0] data_fifo_out_tdata; +wire data_fifo_out_tvalid; +wire data_fifo_out_tlast; +reg data_fifo_out_tready; + +axis_fifo #( + .ADDR_WIDTH(8), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +data_fifo_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(data_fifo_in_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(data_fifo_in_tvalid), + .s_axis_tready(data_fifo_in_tready), + .s_axis_tlast(data_fifo_in_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + // AXI output + .m_axis_tdata(data_fifo_out_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(data_fifo_out_tvalid), + .m_axis_tready(data_fifo_out_tready), + .m_axis_tlast(data_fifo_out_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +always @* begin + input_state_next = INPUT_STATE_IDLE; + + input_count_next = input_count_reg; + + fail_frame_next = fail_frame_reg; + + s_axis_tready_mask = 1'b0; + + code_fifo_in_tdata = 8'd0; + code_fifo_in_tvalid = 1'b0; + code_fifo_in_tlast = 1'b0; + code_fifo_in_tuser = 1'b0; + + data_fifo_in_tdata = s_axis_tdata; + data_fifo_in_tvalid = 1'b0; + data_fifo_in_tlast = 1'b0; + + case (input_state_reg) + INPUT_STATE_IDLE: begin + // idle state + s_axis_tready_mask = 1'b1; + fail_frame_next = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + // valid input data + + if (s_axis_tdata == 8'd0 || (s_axis_tlast && s_axis_tuser)) begin + // got a zero or propagated error, so store a zero code + code_fifo_in_tdata = 8'd1; + code_fifo_in_tvalid = 1'b1; + if (s_axis_tlast) begin + // last byte, so close out the frame + fail_frame_next = s_axis_tuser; + input_state_next = INPUT_STATE_FINAL_ZERO; + end else begin + // return to idle to await next segment + input_state_next = INPUT_STATE_IDLE; + end + end else begin + // got something other than a zero, so store it and init the segment counter + input_count_next = 8'd2; + data_fifo_in_tdata = s_axis_tdata; + data_fifo_in_tvalid = 1'b1; + if (s_axis_tlast) begin + // last byte, so store the code and close out the frame + code_fifo_in_tdata = 8'd2; + code_fifo_in_tvalid = 1'b1; + if (APPEND_ZERO) begin + // zero frame mode, need to add a zero code to end the frame + input_state_next = INPUT_STATE_APPEND_ZERO; + end else begin + // normal frame mode, close out the frame + data_fifo_in_tlast = 1'b1; + input_state_next = INPUT_STATE_IDLE; + end + end else begin + // await more segment data + input_state_next = INPUT_STATE_SEGMENT; + end + end + end else begin + input_state_next = INPUT_STATE_IDLE; + end + end + INPUT_STATE_SEGMENT: begin + // encode segment + s_axis_tready_mask = 1'b1; + fail_frame_next = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + // valid input data + + if (s_axis_tdata == 8'd0 || (s_axis_tlast && s_axis_tuser)) begin + // got a zero or propagated error, so store the code + code_fifo_in_tdata = input_count_reg; + code_fifo_in_tvalid = 1'b1; + if (s_axis_tlast) begin + // last byte, so close out the frame + fail_frame_next = s_axis_tuser; + input_state_next = INPUT_STATE_FINAL_ZERO; + end else begin + // return to idle to await next segment + input_state_next = INPUT_STATE_IDLE; + end + end else begin + // got something other than a zero, so store it and increment the segment counter + input_count_next = input_count_reg+1; + data_fifo_in_tdata = s_axis_tdata; + data_fifo_in_tvalid = 1'b1; + if (input_count_reg == 8'd254) begin + // 254 bytes in frame, so dump and reset counter + code_fifo_in_tdata = input_count_reg+1; + code_fifo_in_tvalid = 1'b1; + input_count_next = 8'd1; + end + if (s_axis_tlast) begin + // last byte, so store the code and close out the frame + code_fifo_in_tdata = input_count_reg+1; + code_fifo_in_tvalid = 1'b1; + if (APPEND_ZERO) begin + // zero frame mode, need to add a zero code to end the frame + input_state_next = INPUT_STATE_APPEND_ZERO; + end else begin + // normal frame mode, close out the frame + data_fifo_in_tlast = 1'b1; + input_state_next = INPUT_STATE_IDLE; + end + end else begin + // await more segment data + input_state_next = INPUT_STATE_SEGMENT; + end + end + end else begin + input_state_next = INPUT_STATE_SEGMENT; + end + end + INPUT_STATE_FINAL_ZERO: begin + // final zero code required + s_axis_tready_mask = 1'b0; + + if (code_fifo_in_tready) begin + // push a zero code and close out frame + if (fail_frame_reg) begin + code_fifo_in_tdata = 8'd2; + code_fifo_in_tuser = 1'b1; + end else begin + code_fifo_in_tdata = 8'd1; + end + code_fifo_in_tvalid = 1'b1; + if (APPEND_ZERO) begin + // zero frame mode, need to add a zero code to end the frame + input_state_next = INPUT_STATE_APPEND_ZERO; + end else begin + // normal frame mode, close out the frame + code_fifo_in_tlast = 1'b1; + fail_frame_next = 1'b0; + input_state_next = INPUT_STATE_IDLE; + end + end else begin + input_state_next = INPUT_STATE_FINAL_ZERO; + end + end + INPUT_STATE_APPEND_ZERO: begin + // append zero for zero framing + s_axis_tready_mask = 1'b0; + + if (code_fifo_in_tready) begin + // push frame termination code and close out frame + code_fifo_in_tdata = 8'd0; + code_fifo_in_tlast = 1'b1; + code_fifo_in_tuser = fail_frame_reg; + code_fifo_in_tvalid = 1'b1; + fail_frame_next = 1'b0; + input_state_next = INPUT_STATE_IDLE; + end else begin + input_state_next = INPUT_STATE_APPEND_ZERO; + end + end + endcase +end + +always @* begin + output_state_next = OUTPUT_STATE_IDLE; + + output_count_next = output_count_reg; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + code_fifo_out_tready = 1'b0; + + data_fifo_out_tready = 1'b0; + + case (output_state_reg) + OUTPUT_STATE_IDLE: begin + // idle state + + if (m_axis_tready_int_reg && code_fifo_out_tvalid) begin + // transfer out code byte and load counter + m_axis_tdata_int = code_fifo_out_tdata; + m_axis_tlast_int = code_fifo_out_tlast; + m_axis_tuser_int = code_fifo_out_tuser && code_fifo_out_tlast; + output_count_next = code_fifo_out_tdata-1; + m_axis_tvalid_int = 1'b1; + code_fifo_out_tready = 1'b1; + if (code_fifo_out_tdata == 8'd0 || code_fifo_out_tdata == 8'd1 || code_fifo_out_tuser) begin + // frame termination and zero codes will be followed by codes + output_state_next = OUTPUT_STATE_IDLE; + end else begin + // transfer out data + output_state_next = OUTPUT_STATE_SEGMENT; + end + end else begin + output_state_next = OUTPUT_STATE_IDLE; + end + end + OUTPUT_STATE_SEGMENT: begin + // segment output + + if (m_axis_tready_int_reg && data_fifo_out_tvalid) begin + // transfer out data byte and decrement counter + m_axis_tdata_int = data_fifo_out_tdata; + m_axis_tlast_int = data_fifo_out_tlast; + output_count_next = output_count_reg - 1; + m_axis_tvalid_int = 1'b1; + data_fifo_out_tready = 1'b1; + if (output_count_reg == 1'b1) begin + // done with segment, get a code byte next + output_state_next = OUTPUT_STATE_IDLE; + end else begin + // more data to transfer + output_state_next = OUTPUT_STATE_SEGMENT; + end + end else begin + output_state_next = OUTPUT_STATE_SEGMENT; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + input_state_reg <= INPUT_STATE_IDLE; + output_state_reg <= OUTPUT_STATE_IDLE; + end else begin + input_state_reg <= input_state_next; + output_state_reg <= output_state_next; + end + + input_count_reg <= input_count_next; + output_count_reg <= output_count_next; + fail_frame_reg <= fail_frame_next; +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_crosspoint.v b/fpga/lib/eth/lib/axis/rtl/axis_crosspoint.v new file mode 100644 index 000000000..86abdb08a --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_crosspoint.v @@ -0,0 +1,139 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream crosspoint + */ +module axis_crosspoint # +( + parameter S_COUNT = 4, + parameter M_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LAST_ENABLE = 1, + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream outputs + */ + output wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep, + output wire [M_COUNT-1:0] m_axis_tvalid, + output wire [M_COUNT-1:0] m_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser, + + /* + * Control + */ + input wire [M_COUNT*$clog2(S_COUNT)-1:0] select +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata_reg = {S_COUNT*DATA_WIDTH{1'b0}}; +reg [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep_reg = {S_COUNT*KEEP_WIDTH{1'b0}}; +reg [S_COUNT-1:0] s_axis_tvalid_reg = {S_COUNT{1'b0}}; +reg [S_COUNT-1:0] s_axis_tlast_reg = {S_COUNT{1'b0}}; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid_reg = {S_COUNT*ID_WIDTH{1'b0}}; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest_reg = {S_COUNT*DEST_WIDTH{1'b0}}; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser_reg = {S_COUNT*USER_WIDTH{1'b0}}; + +reg [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata_reg = {M_COUNT*DATA_WIDTH{1'b0}}; +reg [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep_reg = {M_COUNT*KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_axis_tvalid_reg = {M_COUNT{1'b0}}; +reg [M_COUNT-1:0] m_axis_tlast_reg = {M_COUNT{1'b0}}; +reg [M_COUNT*ID_WIDTH-1:0] m_axis_tid_reg = {M_COUNT*ID_WIDTH{1'b0}}; +reg [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest_reg = {M_COUNT*DEST_WIDTH{1'b0}}; +reg [M_COUNT*USER_WIDTH-1:0] m_axis_tuser_reg = {M_COUNT*USER_WIDTH{1'b0}}; + +reg [M_COUNT*CL_S_COUNT-1:0] select_reg = {M_COUNT*CL_S_COUNT{1'b0}}; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : {M_COUNT{1'b1}}; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {M_COUNT*USER_WIDTH{1'b0}}; + +integer i; + +always @(posedge clk) begin + if (rst) begin + s_axis_tvalid_reg <= {S_COUNT{1'b0}}; + m_axis_tvalid_reg <= {S_COUNT{1'b0}}; + select_reg <= {M_COUNT*CL_S_COUNT{1'b0}}; + end else begin + s_axis_tvalid_reg <= s_axis_tvalid; + for (i = 0; i < M_COUNT; i = i + 1) begin + m_axis_tvalid_reg[i] = s_axis_tvalid_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]]; + end + select_reg <= select; + end + + s_axis_tdata_reg <= s_axis_tdata; + s_axis_tkeep_reg <= s_axis_tkeep; + s_axis_tlast_reg <= s_axis_tlast; + s_axis_tid_reg <= s_axis_tid; + s_axis_tdest_reg <= s_axis_tdest; + s_axis_tuser_reg <= s_axis_tuser; + + for (i = 0; i < M_COUNT; i = i + 1) begin + m_axis_tdata_reg[i*DATA_WIDTH +: DATA_WIDTH] <= s_axis_tdata_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]*DATA_WIDTH +: DATA_WIDTH]; + m_axis_tkeep_reg[i*KEEP_WIDTH +: KEEP_WIDTH] <= s_axis_tkeep_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]*KEEP_WIDTH +: KEEP_WIDTH]; + m_axis_tlast_reg[i] <= s_axis_tlast_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]]; + m_axis_tid_reg[i*ID_WIDTH +: ID_WIDTH] <= s_axis_tid_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]*ID_WIDTH +: ID_WIDTH]; + m_axis_tdest_reg[i*DEST_WIDTH +: DEST_WIDTH] <= s_axis_tdest_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]*DEST_WIDTH +: DEST_WIDTH]; + m_axis_tuser_reg[i*USER_WIDTH +: USER_WIDTH] <= s_axis_tuser_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]*USER_WIDTH +: USER_WIDTH]; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_crosspoint_wrap.py b/fpga/lib/eth/lib/axis/rtl/axis_crosspoint_wrap.py new file mode 100755 index 000000000..7f2dcba99 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_crosspoint_wrap.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream crosspoint wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=[4], nargs='+', help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + if type(ports) is int: + m = n = ports + elif len(ports) == 1: + m = n = ports[0] + else: + m, n = ports + + if name is None: + name = "axis_crosspoint_wrap_{0}x{1}".format(m, n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0}x{1} port AXI stream crosspoint wrapper {2}...".format(m, n, name)) + + cm = int(math.ceil(math.log(m, 2))) + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 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 1ns / 1ps + +/* + * AXI4-Stream {{m}}x{{n}} crosspoint (wrapper) + */ +module {{name}} # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LAST_ENABLE = 1, + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_WIDTH = {{cm}}, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ +{%- for p in range(m) %} + input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata, + input wire [KEEP_WIDTH-1:0] s{{'%02d'%p}}_axis_tkeep, + input wire s{{'%02d'%p}}_axis_tvalid, + input wire s{{'%02d'%p}}_axis_tlast, + input wire [ID_WIDTH-1:0] s{{'%02d'%p}}_axis_tid, + input wire [DEST_WIDTH-1:0] s{{'%02d'%p}}_axis_tdest, + input wire [USER_WIDTH-1:0] s{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * AXI Stream outputs + */ +{%- for p in range(n) %} + output wire [DATA_WIDTH-1:0] m{{'%02d'%p}}_axis_tdata, + output wire [KEEP_WIDTH-1:0] m{{'%02d'%p}}_axis_tkeep, + output wire m{{'%02d'%p}}_axis_tvalid, + output wire m{{'%02d'%p}}_axis_tlast, + output wire [ID_WIDTH-1:0] m{{'%02d'%p}}_axis_tid, + output wire [DEST_WIDTH-1:0] m{{'%02d'%p}}_axis_tdest, + output wire [USER_WIDTH-1:0] m{{'%02d'%p}}_axis_tuser{% if not loop.last %},{% endif %} +{% endfor -%} +); + +axis_crosspoint #( + .S_COUNT({{m}}), + .M_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +axis_crosspoint_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tkeep({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tvalid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tlast({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tdest({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tuser({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // AXI output + .m_axis_tdata({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tkeep({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tlast({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tdest({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tuser({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }) +); + +endmodule + +""") + + output_file.write(t.render( + m=m, + n=n, + cm=cm, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/fpga/lib/eth/lib/axis/rtl/axis_demux.v b/fpga/lib/eth/lib/axis/rtl/axis_demux.v new file mode 100644 index 000000000..546734ad1 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_demux.v @@ -0,0 +1,256 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * AXI4-Stream demultiplexer + */ +module axis_demux # +( + parameter M_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI outputs + */ + output wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep, + output wire [M_COUNT-1:0] m_axis_tvalid, + input wire [M_COUNT-1:0] m_axis_tready, + output wire [M_COUNT-1:0] m_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [$clog2(M_COUNT)-1:0] select +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +reg [CL_M_COUNT-1:0] select_reg = {CL_M_COUNT{1'b0}}, select_ctl, select_next; +reg drop_reg = 1'b0, drop_ctl, drop_next; +reg frame_reg = 1'b0, frame_ctl, frame_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg [M_COUNT-1:0] m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg && enable; + +always @* begin + select_next = select_reg; + select_ctl = select_reg; + drop_next = drop_reg; + drop_ctl = drop_reg; + frame_next = frame_reg; + frame_ctl = frame_reg; + + s_axis_tready_next = 1'b0; + + if (s_axis_tvalid && s_axis_tready) begin + // end of frame detection + if (s_axis_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + + if (!frame_reg && s_axis_tvalid && s_axis_tready) begin + // start of frame, grab select value + select_ctl = select; + drop_ctl = drop; + frame_ctl = 1'b1; + if (!(s_axis_tready && s_axis_tvalid && s_axis_tlast)) begin + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = 1'b1; + end + end + + s_axis_tready_next = (m_axis_tready_int_early || drop_ctl); + + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = (s_axis_tvalid && s_axis_tready && !drop_ctl) << select_ctl; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 2'd0; + drop_reg <= 1'b0; + frame_reg <= 1'b0; + s_axis_tready_reg <= 1'b0; + end else begin + select_reg <= select_next; + drop_reg <= drop_next; + frame_reg <= frame_next; + s_axis_tready_reg <= s_axis_tready_next; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_axis_tvalid_reg = {M_COUNT{1'b0}}, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] temp_m_axis_tvalid_reg = {M_COUNT{1'b0}}, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = {M_COUNT{m_axis_tdata_reg}}; +assign m_axis_tkeep = KEEP_ENABLE ? {M_COUNT{m_axis_tkeep_reg}} : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = {M_COUNT{m_axis_tlast_reg}}; +assign m_axis_tid = ID_ENABLE ? {M_COUNT{m_axis_tid_reg}} : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? {M_COUNT{m_axis_tdest_reg}} : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? {M_COUNT{m_axis_tuser_reg}} : {M_COUNT*USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = (m_axis_tready & m_axis_tvalid) || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if ((m_axis_tready & m_axis_tvalid) || !m_axis_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready & m_axis_tvalid) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = {M_COUNT{1'b0}}; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= {M_COUNT{1'b0}}; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= {M_COUNT{1'b0}}; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_demux_wrap.py b/fpga/lib/eth/lib/axis/rtl/axis_demux_wrap.py new file mode 100755 index 000000000..c0554afb5 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_demux_wrap.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream demux wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + n = ports + + if name is None: + name = "axis_demux_wrap_{0}".format(n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0} port AXI stream demux wrapper {1}...".format(n, name)) + + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 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 1ns / 1ps + +/* + * AXI4-Stream {{n}} port demux (wrapper) + */ +module {{name}} # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream outputs + */ +{%- for p in range(n) %} + output wire [DATA_WIDTH-1:0] m{{'%02d'%p}}_axis_tdata, + output wire [KEEP_WIDTH-1:0] m{{'%02d'%p}}_axis_tkeep, + output wire m{{'%02d'%p}}_axis_tvalid, + input wire m{{'%02d'%p}}_axis_tready, + output wire m{{'%02d'%p}}_axis_tlast, + output wire [ID_WIDTH-1:0] m{{'%02d'%p}}_axis_tid, + output wire [DEST_WIDTH-1:0] m{{'%02d'%p}}_axis_tdest, + output wire [USER_WIDTH-1:0] m{{'%02d'%p}}_axis_tuser, +{% endfor -%} + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [{{cn-1}}:0] select +); + +axis_demux #( + .M_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +axis_demux_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tkeep({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tready({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tlast({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tdest({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tuser({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // Control + .enable(enable), + .drop(drop), + .select(select) +); + +endmodule + +""") + + output_file.write(t.render( + n=n, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/fpga/lib/eth/lib/axis/rtl/axis_fifo.v b/fpga/lib/eth/lib/axis/rtl/axis_fifo.v new file mode 100644 index 000000000..d6329b1d9 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_fifo.v @@ -0,0 +1,316 @@ +/* + +Copyright (c) 2013-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 1ns / 1ps + +/* + * AXI4-Stream FIFO + */ +module axis_fifo # +( + parameter ADDR_WIDTH = 12, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LAST_ENABLE = 1, + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + parameter FRAME_FIFO = 0, + parameter USER_BAD_FRAME_VALUE = 1'b1, + parameter USER_BAD_FRAME_MASK = 1'b1, + parameter DROP_BAD_FRAME = 0, + parameter DROP_WHEN_FULL = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire status_overflow, + output wire status_bad_frame, + output wire status_good_frame +); + +// check configuration +initial begin + if (FRAME_FIFO && !LAST_ENABLE) begin + $error("Error: FRAME_FIFO set requires LAST_ENABLE set"); + $finish; + end + + if (DROP_BAD_FRAME && !FRAME_FIFO) begin + $error("Error: DROP_BAD_FRAME set requires FRAME_FIFO set"); + $finish; + end + + if (DROP_WHEN_FULL && !FRAME_FIFO) begin + $error("Error: DROP_WHEN_FULL set requires FRAME_FIFO set"); + $finish; + end + + if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin + $error("Error: Invalid USER_BAD_FRAME_MASK value"); + $finish; + end +end + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next; +reg [ADDR_WIDTH:0] wr_addr_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next; +reg [ADDR_WIDTH:0] rd_addr_reg = {ADDR_WIDTH+1{1'b0}}; + +reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0]; +reg [WIDTH-1:0] mem_read_data_reg; +reg mem_read_data_valid_reg = 1'b0, mem_read_data_valid_next; + +wire [WIDTH-1:0] s_axis; + +reg [WIDTH-1:0] m_axis_reg; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; + +// full when first MSB different but rest same +wire full = ((wr_ptr_reg[ADDR_WIDTH] != rd_ptr_reg[ADDR_WIDTH]) && + (wr_ptr_reg[ADDR_WIDTH-1:0] == rd_ptr_reg[ADDR_WIDTH-1:0])); +wire full_cur = ((wr_ptr_cur_reg[ADDR_WIDTH] != rd_ptr_reg[ADDR_WIDTH]) && + (wr_ptr_cur_reg[ADDR_WIDTH-1:0] == rd_ptr_reg[ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire empty = wr_ptr_reg == rd_ptr_reg; +// overflow within packet +wire full_wr = ((wr_ptr_reg[ADDR_WIDTH] != wr_ptr_cur_reg[ADDR_WIDTH]) && + (wr_ptr_reg[ADDR_WIDTH-1:0] == wr_ptr_cur_reg[ADDR_WIDTH-1:0])); + +// control signals +reg write; +reg read; +reg store_output; + +reg drop_frame_reg = 1'b0, drop_frame_next; +reg overflow_reg = 1'b0, overflow_next; +reg bad_frame_reg = 1'b0, bad_frame_next; +reg good_frame_reg = 1'b0, good_frame_next; + +assign s_axis_tready = FRAME_FIFO ? (!full_cur || full_wr || DROP_WHEN_FULL) : !full; + +generate + assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; +endgenerate + +assign m_axis_tvalid = m_axis_tvalid_reg; + +assign m_axis_tdata = m_axis_reg[DATA_WIDTH-1:0]; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_reg[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; +assign m_axis_tlast = LAST_ENABLE ? m_axis_reg[LAST_OFFSET] : 1'b1; +assign m_axis_tid = ID_ENABLE ? m_axis_reg[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_reg[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_reg[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}}; + +assign status_overflow = overflow_reg; +assign status_bad_frame = bad_frame_reg; +assign status_good_frame = good_frame_reg; + +// Write logic +always @* begin + write = 1'b0; + + drop_frame_next = 1'b0; + overflow_next = 1'b0; + bad_frame_next = 1'b0; + good_frame_next = 1'b0; + + wr_ptr_next = wr_ptr_reg; + wr_ptr_cur_next = wr_ptr_cur_reg; + + if (s_axis_tready && s_axis_tvalid) begin + // transfer in + if (!FRAME_FIFO) begin + // normal FIFO mode + write = 1'b1; + wr_ptr_next = wr_ptr_reg + 1; + end else if (full_cur || full_wr || drop_frame_reg) begin + // full, packet overflow, or currently dropping frame + // drop frame + drop_frame_next = 1'b1; + if (s_axis_tlast) begin + // end of frame, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + drop_frame_next = 1'b0; + overflow_next = 1'b1; + end + end else begin + write = 1'b1; + wr_ptr_cur_next = wr_ptr_cur_reg + 1; + if (s_axis_tlast) begin + // end of frame + if (DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(s_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin + // bad packet, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + bad_frame_next = 1'b1; + end else begin + // good packet, update write pointer + wr_ptr_next = wr_ptr_cur_reg + 1; + good_frame_next = 1'b1; + end + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}}; + + drop_frame_reg <= 1'b0; + overflow_reg <= 1'b0; + bad_frame_reg <= 1'b0; + good_frame_reg <= 1'b0; + end else begin + wr_ptr_reg <= wr_ptr_next; + wr_ptr_cur_reg <= wr_ptr_cur_next; + + drop_frame_reg <= drop_frame_next; + overflow_reg <= overflow_next; + bad_frame_reg <= bad_frame_next; + good_frame_reg <= good_frame_next; + end + + if (FRAME_FIFO) begin + wr_addr_reg <= wr_ptr_cur_next; + end else begin + wr_addr_reg <= wr_ptr_next; + end + + if (write) begin + mem[wr_addr_reg[ADDR_WIDTH-1:0]] <= s_axis; + end +end + +// Read logic +always @* begin + read = 1'b0; + + rd_ptr_next = rd_ptr_reg; + + mem_read_data_valid_next = mem_read_data_valid_reg; + + if (store_output || !mem_read_data_valid_reg) begin + // output data not valid OR currently being transferred + if (!empty) begin + // not empty, perform read + read = 1'b1; + mem_read_data_valid_next = 1'b1; + rd_ptr_next = rd_ptr_reg + 1; + end else begin + // empty, invalidate + mem_read_data_valid_next = 1'b0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + mem_read_data_valid_reg <= 1'b0; + end else begin + rd_ptr_reg <= rd_ptr_next; + mem_read_data_valid_reg <= mem_read_data_valid_next; + end + + rd_addr_reg <= rd_ptr_next; + + if (read) begin + mem_read_data_reg <= mem[rd_addr_reg[ADDR_WIDTH-1:0]]; + end +end + +// Output register +always @* begin + store_output = 1'b0; + + m_axis_tvalid_next = m_axis_tvalid_reg; + + if (m_axis_tready || !m_axis_tvalid) begin + store_output = 1'b1; + m_axis_tvalid_next = mem_read_data_valid_reg; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + end + + if (store_output) begin + m_axis_reg <= mem_read_data_reg; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_fifo_adapter.v b/fpga/lib/eth/lib/axis/rtl/axis_fifo_adapter.v new file mode 100644 index 000000000..fac142c7d --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_fifo_adapter.v @@ -0,0 +1,311 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * AXI4-Stream FIFO with width converter + */ +module axis_fifo_adapter # +( + parameter ADDR_WIDTH = 12, + parameter S_DATA_WIDTH = 8, + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + parameter M_DATA_WIDTH = 8, + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + parameter FRAME_FIFO = 0, + parameter USER_BAD_FRAME_VALUE = 1'b1, + parameter USER_BAD_FRAME_MASK = 1'b1, + parameter DROP_BAD_FRAME = 0, + parameter DROP_WHEN_FULL = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [S_DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [M_DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire status_overflow, + output wire status_bad_frame, + output wire status_good_frame +); + +// 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; + +// 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; +// output bus is wider +parameter 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; + +// bus width assertions +initial begin + if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisble"); + $finish; + end + + if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisble"); + $finish; + end + + if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin + $error("Error: word size mismatch"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] pre_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] pre_fifo_axis_tkeep; +wire pre_fifo_axis_tvalid; +wire pre_fifo_axis_tready; +wire pre_fifo_axis_tlast; +wire [ID_WIDTH-1:0] pre_fifo_axis_tid; +wire [DEST_WIDTH-1:0] pre_fifo_axis_tdest; +wire [USER_WIDTH-1:0] pre_fifo_axis_tuser; + +wire [DATA_WIDTH-1:0] post_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] post_fifo_axis_tkeep; +wire post_fifo_axis_tvalid; +wire post_fifo_axis_tready; +wire post_fifo_axis_tlast; +wire [ID_WIDTH-1:0] post_fifo_axis_tid; +wire [DEST_WIDTH-1:0] post_fifo_axis_tdest; +wire [USER_WIDTH-1:0] post_fifo_axis_tuser; + +generate + +if (M_KEEP_WIDTH_INT == S_KEEP_WIDTH_INT) begin + + // same width, no adapter needed + + assign pre_fifo_axis_tdata = s_axis_tdata; + assign pre_fifo_axis_tkeep = s_axis_tkeep; + assign pre_fifo_axis_tvalid = s_axis_tvalid; + assign s_axis_tready = pre_fifo_axis_tready; + assign pre_fifo_axis_tlast = s_axis_tlast; + assign pre_fifo_axis_tid = s_axis_tid; + assign pre_fifo_axis_tdest = s_axis_tdest; + assign pre_fifo_axis_tuser = s_axis_tuser; + + assign m_axis_tdata = post_fifo_axis_tdata; + assign m_axis_tkeep = post_fifo_axis_tkeep; + assign m_axis_tvalid = post_fifo_axis_tvalid; + assign post_fifo_axis_tready = m_axis_tready; + assign m_axis_tlast = post_fifo_axis_tlast; + assign m_axis_tid = post_fifo_axis_tid; + assign m_axis_tdest = post_fifo_axis_tdest; + assign m_axis_tuser = post_fifo_axis_tuser; + +end else if (EXPAND_BUS) begin + + // output wider, adapt width before FIFO + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(pre_fifo_axis_tdata), + .m_axis_tkeep(pre_fifo_axis_tkeep), + .m_axis_tvalid(pre_fifo_axis_tvalid), + .m_axis_tready(pre_fifo_axis_tready), + .m_axis_tlast(pre_fifo_axis_tlast), + .m_axis_tid(pre_fifo_axis_tid), + .m_axis_tdest(pre_fifo_axis_tdest), + .m_axis_tuser(pre_fifo_axis_tuser) + ); + + assign m_axis_tdata = post_fifo_axis_tdata; + assign m_axis_tkeep = post_fifo_axis_tkeep; + assign m_axis_tvalid = post_fifo_axis_tvalid; + assign post_fifo_axis_tready = m_axis_tready; + assign m_axis_tlast = post_fifo_axis_tlast; + assign m_axis_tid = post_fifo_axis_tid; + assign m_axis_tdest = post_fifo_axis_tdest; + assign m_axis_tuser = post_fifo_axis_tuser; + +end else begin + + // input wider, adapt width after FIFO + + assign pre_fifo_axis_tdata = s_axis_tdata; + assign pre_fifo_axis_tkeep = s_axis_tkeep; + assign pre_fifo_axis_tvalid = s_axis_tvalid; + assign s_axis_tready = pre_fifo_axis_tready; + assign pre_fifo_axis_tlast = s_axis_tlast; + assign pre_fifo_axis_tid = s_axis_tid; + assign pre_fifo_axis_tdest = s_axis_tdest; + assign pre_fifo_axis_tuser = s_axis_tuser; + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(post_fifo_axis_tdata), + .s_axis_tkeep(post_fifo_axis_tkeep), + .s_axis_tvalid(post_fifo_axis_tvalid), + .s_axis_tready(post_fifo_axis_tready), + .s_axis_tlast(post_fifo_axis_tlast), + .s_axis_tid(post_fifo_axis_tid), + .s_axis_tdest(post_fifo_axis_tdest), + .s_axis_tuser(post_fifo_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) + ); + +end + +endgenerate + +axis_fifo #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(EXPAND_BUS ? M_KEEP_ENABLE : S_KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +fifo_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(pre_fifo_axis_tdata), + .s_axis_tkeep(pre_fifo_axis_tkeep), + .s_axis_tvalid(pre_fifo_axis_tvalid), + .s_axis_tready(pre_fifo_axis_tready), + .s_axis_tlast(pre_fifo_axis_tlast), + .s_axis_tid(pre_fifo_axis_tid), + .s_axis_tdest(pre_fifo_axis_tdest), + .s_axis_tuser(pre_fifo_axis_tuser), + // AXI output + .m_axis_tdata(post_fifo_axis_tdata), + .m_axis_tkeep(post_fifo_axis_tkeep), + .m_axis_tvalid(post_fifo_axis_tvalid), + .m_axis_tready(post_fifo_axis_tready), + .m_axis_tlast(post_fifo_axis_tlast), + .m_axis_tid(post_fifo_axis_tid), + .m_axis_tdest(post_fifo_axis_tdest), + .m_axis_tuser(post_fifo_axis_tuser), + // Status + .status_overflow(status_overflow), + .status_bad_frame(status_bad_frame), + .status_good_frame(status_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_frame_join.v b/fpga/lib/eth/lib/axis/rtl/axis_frame_join.v new file mode 100644 index 000000000..a79ded93c --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_frame_join.v @@ -0,0 +1,323 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream frame joiner + */ +module axis_frame_join # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter TAG_ENABLE = 1, + parameter TAG_WIDTH = 16 +) +( + input wire clk, + input wire rst, + + /* + * AXI inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Configuration + */ + input wire [TAG_WIDTH-1:0] tag, + + /* + * Status signals + */ + output wire busy +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +parameter TAG_WORD_WIDTH = (TAG_WIDTH + DATA_WIDTH - 1) / DATA_WIDTH; +parameter CL_TAG_WORD_WIDTH = $clog2(TAG_WORD_WIDTH); + +// state register +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_WRITE_TAG = 2'd1, + STATE_TRANSFER = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [CL_TAG_WORD_WIDTH-1:0] frame_ptr_reg = {CL_TAG_WORD_WIDTH{1'b0}}, frame_ptr_next; +reg [CL_S_COUNT-1:0] port_sel_reg = {CL_S_COUNT{1'b0}}, port_sel_next; + +reg busy_reg = 1'b0, busy_next; + +reg output_tuser_reg = 1'b0, output_tuser_next; + +reg [S_COUNT-1:0] s_axis_tready_reg = {S_COUNT{1'b0}}, s_axis_tready_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign busy = busy_reg; + +wire [DATA_WIDTH-1:0] input_tdata = s_axis_tdata[port_sel_reg*DATA_WIDTH +: DATA_WIDTH]; +wire input_tvalid = s_axis_tvalid[port_sel_reg]; +wire input_tlast = s_axis_tlast[port_sel_reg]; +wire input_tuser = s_axis_tuser[port_sel_reg]; + +always @* begin + state_next = STATE_IDLE; + + frame_ptr_next = frame_ptr_reg; + port_sel_next = port_sel_reg; + + s_axis_tready_next = {S_COUNT{1'b0}}; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + output_tuser_next = output_tuser_reg; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = {CL_TAG_WORD_WIDTH{1'b0}}; + port_sel_next = {CL_S_COUNT{1'b0}}; + output_tuser_next = 1'b0; + + if (TAG_ENABLE) begin + // next cycle if started will send tag, so do not enable input + s_axis_tready_next = 1'b0; + end else begin + // next cycle if started will send data, so enable input + s_axis_tready_next = m_axis_tready_int_early; + end + + if (s_axis_tvalid) begin + // input 0 valid; start transferring data + if (TAG_ENABLE) begin + // tag enabled, so transmit it + if (m_axis_tready_int_reg) begin + // output is ready, so short-circuit first tag word + frame_ptr_next = 1; + m_axis_tdata_int = tag; + m_axis_tvalid_int = 1'b1; + end + state_next = STATE_WRITE_TAG; + end else begin + // tag disabled, so transmit data + if (m_axis_tready_int_reg) begin + // output is ready, so short-circuit first data word + m_axis_tdata_int = s_axis_tdata; + m_axis_tvalid_int = 1'b1; + end + state_next = STATE_TRANSFER; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_TAG: begin + // write tag data + if (m_axis_tready_int_reg) begin + // output ready, so send tag word + state_next = STATE_WRITE_TAG; + frame_ptr_next = frame_ptr_reg + 1; + m_axis_tvalid_int = 1'b1; + + m_axis_tdata_int = tag >> frame_ptr_reg*DATA_WIDTH; + if (frame_ptr_reg == TAG_WORD_WIDTH-1) begin + s_axis_tready_next = m_axis_tready_int_early << 0; + state_next = STATE_TRANSFER; + end + end else begin + state_next = STATE_WRITE_TAG; + end + end + STATE_TRANSFER: begin + // transfer input data + + // set ready for current input + s_axis_tready_next = m_axis_tready_int_early << port_sel_reg; + + if (input_tvalid && m_axis_tready_int_reg) begin + // output ready, transfer byte + state_next = STATE_TRANSFER; + m_axis_tdata_int = input_tdata; + m_axis_tvalid_int = input_tvalid; + + if (input_tlast) begin + // last flag received, switch to next port + port_sel_next = port_sel_reg + 1; + // save tuser - assert tuser out if ANY tuser asserts received + output_tuser_next = output_tuser_next | input_tuser; + // disable input + s_axis_tready_next = {S_COUNT{1'b0}}; + + if (S_COUNT == 1 || port_sel_reg == S_COUNT-1) begin + // last port - send tlast and tuser and revert to idle + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = output_tuser_next; + state_next = STATE_IDLE; + end else begin + // otherwise, disable enable next port + s_axis_tready_next = m_axis_tready_int_early << port_sel_next; + end + end + end else begin + state_next = STATE_TRANSFER; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= {CL_TAG_WORD_WIDTH{1'b0}}; + port_sel_reg <= {CL_S_COUNT{1'b0}}; + s_axis_tready_reg <= {S_COUNT{1'b0}}; + output_tuser_reg <= 1'b0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + port_sel_reg <= port_sel_next; + + s_axis_tready_reg <= s_axis_tready_next; + + output_tuser_reg <= output_tuser_next; + + busy_reg <= state_next != STATE_IDLE; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_frame_join_wrap.py b/fpga/lib/eth/lib/axis/rtl/axis_frame_join_wrap.py new file mode 100755 index 000000000..5a9b9b66f --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_frame_join_wrap.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream frame joiner wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + n = ports + + if name is None: + name = "axis_frame_join_wrap_{0}".format(n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0} port AXI stream frame joiner wrapper {1}...".format(n, name)) + + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 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 1ns / 1ps + +/* + * AXI4-Stream {{n}} port frame joiner (wrapper) + */ +module {{name}} # +( + parameter DATA_WIDTH = 8, + parameter TAG_ENABLE = 1, + parameter TAG_WIDTH = 16 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ +{%- for p in range(n) %} + input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata, + input wire s{{'%02d'%p}}_axis_tvalid, + output wire s{{'%02d'%p}}_axis_tready, + input wire s{{'%02d'%p}}_axis_tlast, + input wire s{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Configuration + */ + input wire [TAG_WIDTH-1:0] tag, + + /* + * Status signals + */ + output wire busy +); + +axis_frame_join #( + .S_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .TAG_ENABLE(TAG_ENABLE), + .TAG_WIDTH(TAG_WIDTH) +) +axis_frame_join_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tvalid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tready({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tlast({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tuser({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + // Configuration + .tag(tag), + // Status + .busy(busy) +); + +endmodule + +""") + + output_file.write(t.render( + n=n, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/fpga/lib/eth/lib/axis/rtl/axis_frame_len.v b/fpga/lib/eth/lib/axis/rtl/axis_frame_len.v new file mode 100644 index 000000000..e4dfe6596 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_frame_len.v @@ -0,0 +1,110 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * AXI4-Stream frame length measurement + */ +module axis_frame_len # +( + parameter DATA_WIDTH = 64, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LEN_WIDTH = 16 +) +( + input wire clk, + input wire rst, + + /* + * AXI monitor + */ + input wire [KEEP_WIDTH-1:0] monitor_axis_tkeep, + input wire monitor_axis_tvalid, + input wire monitor_axis_tready, + input wire monitor_axis_tlast, + + /* + * Status + */ + output wire [LEN_WIDTH-1:0] frame_len, + output wire frame_len_valid +); + +reg [LEN_WIDTH-1:0] frame_len_reg = 0, frame_len_next; +reg frame_len_valid_reg = 1'b0, frame_len_valid_next; +reg frame_reg = 1'b0, frame_next; + +assign frame_len = frame_len_reg; +assign frame_len_valid = frame_len_valid_reg; + +integer offset, i, bit_cnt; + +always @* begin + frame_len_next = frame_len_reg; + frame_len_valid_next = 1'b0; + frame_next = frame_reg; + + if (monitor_axis_tready && monitor_axis_tvalid) begin + // valid transfer cycle + + if (monitor_axis_tlast) begin + // end of frame + frame_len_valid_next = 1'b1; + frame_next = 1'b0; + end else if (!frame_reg) begin + // first word after end of frame + frame_len_next = 0; + frame_next = 1'b1; + end + + // increment frame length by number of words transferred + if (KEEP_ENABLE) begin + bit_cnt = 0; + for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin + if (monitor_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) bit_cnt = i; + end + frame_len_next = frame_len_next + bit_cnt; + end else begin + frame_len_next = frame_len_next + 1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + frame_len_reg <= 0; + frame_len_valid_reg <= 0; + frame_reg <= 1'b0; + end else begin + frame_len_reg <= frame_len_next; + frame_len_valid_reg <= frame_len_valid_next; + frame_reg <= frame_next; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_frame_length_adjust.v b/fpga/lib/eth/lib/axis/rtl/axis_frame_length_adjust.v new file mode 100644 index 000000000..f1182a049 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_frame_length_adjust.v @@ -0,0 +1,593 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream frame length adjuster + */ +module axis_frame_length_adjust # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire status_valid, + input wire status_ready, + output wire status_frame_pad, + output wire status_frame_truncate, + output wire [15:0] status_frame_length, + output wire [15:0] status_frame_original_length, + + /* + * Configuration + */ + input wire [15:0] length_min, + input wire [15:0] length_max +); + +// bus word width +localparam DATA_WORD_WIDTH = DATA_WIDTH / KEEP_WIDTH; + +// bus width assertions +initial begin + if (DATA_WORD_WIDTH * KEEP_WIDTH != DATA_WIDTH) begin + $error("Error: data width not evenly divisble"); + $finish; + end +end + +// state register +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_TRANSFER = 3'd1, + STATE_PAD = 3'd2, + STATE_TRUNCATE = 3'd3; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_last_word; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +// frame length counters +reg [15:0] short_counter_reg = 16'd0, short_counter_next = 16'd0; +reg [15:0] long_counter_reg = 16'd0, long_counter_next = 16'd0; + +reg [DATA_WIDTH-1:0] last_word_data_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] last_word_keep_reg = {KEEP_WIDTH{1'b0}}; +reg [ID_WIDTH-1:0] last_word_id_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] last_word_dest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] last_word_user_reg = {USER_WIDTH{1'b0}}; + +reg status_valid_reg = 1'b0, status_valid_next; +reg status_frame_pad_reg = 1'b0, status_frame_pad_next; +reg status_frame_truncate_reg = 1'b0, status_frame_truncate_next; +reg [15:0] status_frame_length_reg = 16'd0, status_frame_length_next; +reg [15:0] status_frame_original_length_reg = 16'd0, status_frame_original_length_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; +assign s_axis_tready = s_axis_tready_reg; + +assign status_valid = status_valid_reg; +assign status_frame_pad = status_frame_pad_reg; +assign status_frame_truncate = status_frame_truncate_reg; +assign status_frame_length = status_frame_length_reg; +assign status_frame_original_length = status_frame_original_length_reg; + +integer i, word_cnt; + +always @* begin + state_next = STATE_IDLE; + + store_last_word = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + short_counter_next = short_counter_reg; + long_counter_next = long_counter_reg; + + m_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {KEEP_WIDTH{1'b0}}; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tid_int = {ID_WIDTH{1'b0}}; + m_axis_tdest_int = {DEST_WIDTH{1'b0}}; + m_axis_tuser_int = {USER_WIDTH{1'b0}}; + + s_axis_tready_next = 1'b0; + + status_valid_next = status_valid_reg && !status_ready; + status_frame_pad_next = status_frame_pad_reg; + status_frame_truncate_next = status_frame_truncate_reg; + status_frame_length_next = status_frame_length_reg; + status_frame_original_length_next = status_frame_original_length_reg; + + case (state_reg) + STATE_IDLE: begin + // idle state + // accept data next cycle if output register ready next cycle + s_axis_tready_next = m_axis_tready_int_early && (!status_valid_reg || status_ready); + + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + short_counter_next = length_min; + long_counter_next = length_max; + + if (s_axis_tready && s_axis_tvalid) begin + // transfer through + word_cnt = 0; + for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin + //bit_cnt = bit_cnt + monitor_axis_tkeep[i]; + if (s_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) word_cnt = i; + end + frame_ptr_next = frame_ptr_reg+KEEP_WIDTH; + + if (short_counter_reg > KEEP_WIDTH) begin + short_counter_next = short_counter_reg - KEEP_WIDTH; + end else begin + short_counter_next = 16'd0; + end + + if (long_counter_reg > KEEP_WIDTH) begin + long_counter_next = long_counter_reg - KEEP_WIDTH; + end else begin + long_counter_next = 16'd0; + end + + if (long_counter_reg <= word_cnt) begin + m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-long_counter_reg); + if (s_axis_tlast) begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b0; + status_frame_truncate_next = word_cnt > long_counter_reg; + status_frame_length_next = length_max; + status_frame_original_length_next = frame_ptr_reg+word_cnt; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end else begin + m_axis_tvalid_int = 1'b0; + store_last_word = 1'b1; + state_next = STATE_TRUNCATE; + end + end else begin + if (s_axis_tlast) begin + status_frame_original_length_next = frame_ptr_reg+word_cnt; + if (short_counter_reg > word_cnt) begin + if (short_counter_reg > KEEP_WIDTH) begin + frame_ptr_next = frame_ptr_reg + KEEP_WIDTH; + s_axis_tready_next = 1'b0; + m_axis_tkeep_int = {KEEP_WIDTH{1'b1}}; + m_axis_tlast_int = 1'b0; + store_last_word = 1'b1; + state_next = STATE_PAD; + end else begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b1; + status_frame_truncate_next = 1'b0; + status_frame_length_next = length_min; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-(length_min - frame_ptr_reg)); + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end + end else begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b0; + status_frame_truncate_next = 1'b0; + status_frame_length_next = frame_ptr_reg+word_cnt; + status_frame_original_length_next = frame_ptr_reg+word_cnt; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_TRANSFER; + end + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_TRANSFER: begin + // transfer data + // accept data next cycle if output register ready next cycle + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + if (s_axis_tready && s_axis_tvalid) begin + // transfer through + word_cnt = 1; + for (i = 1; i <= KEEP_WIDTH; i = i + 1) begin + //bit_cnt = bit_cnt + monitor_axis_tkeep[i]; + if (s_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) word_cnt = i; + end + frame_ptr_next = frame_ptr_reg+KEEP_WIDTH; + + if (short_counter_reg > KEEP_WIDTH) begin + short_counter_next = short_counter_reg - KEEP_WIDTH; + end else begin + short_counter_next = 16'd0; + end + + if (long_counter_reg > KEEP_WIDTH) begin + long_counter_next = long_counter_reg - KEEP_WIDTH; + end else begin + long_counter_next = 16'd0; + end + + if (long_counter_reg <= word_cnt) begin + m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-long_counter_reg); + if (s_axis_tlast) begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b0; + status_frame_truncate_next = word_cnt > long_counter_reg; + status_frame_length_next = length_max; + status_frame_original_length_next = frame_ptr_reg+word_cnt; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end else begin + m_axis_tvalid_int = 1'b0; + store_last_word = 1'b1; + state_next = STATE_TRUNCATE; + end + end else begin + if (s_axis_tlast) begin + status_frame_original_length_next = frame_ptr_reg+word_cnt; + if (short_counter_reg > word_cnt) begin + if (short_counter_reg > KEEP_WIDTH) begin + frame_ptr_next = frame_ptr_reg + KEEP_WIDTH; + s_axis_tready_next = 1'b0; + m_axis_tkeep_int = {KEEP_WIDTH{1'b1}}; + m_axis_tlast_int = 1'b0; + store_last_word = 1'b1; + state_next = STATE_PAD; + end else begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b1; + status_frame_truncate_next = 1'b0; + status_frame_length_next = length_min; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-short_counter_reg); + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end + end else begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b0; + status_frame_truncate_next = 1'b0; + status_frame_length_next = frame_ptr_reg+word_cnt; + status_frame_original_length_next = frame_ptr_reg+word_cnt; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_TRANSFER; + end + end + end else begin + state_next = STATE_TRANSFER; + end + end + STATE_PAD: begin + // pad to minimum length + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {KEEP_WIDTH{1'b1}}; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b0; + m_axis_tid_int = last_word_id_reg; + m_axis_tdest_int = last_word_dest_reg; + m_axis_tuser_int = last_word_user_reg; + + if (m_axis_tready_int_reg) begin + frame_ptr_next = frame_ptr_reg + KEEP_WIDTH; + + if (short_counter_reg > KEEP_WIDTH) begin + short_counter_next = short_counter_reg - KEEP_WIDTH; + end else begin + short_counter_next = 16'd0; + end + + if (long_counter_reg > KEEP_WIDTH) begin + long_counter_next = long_counter_reg - KEEP_WIDTH; + end else begin + long_counter_next = 16'd0; + end + + if (short_counter_reg <= KEEP_WIDTH) begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b1; + status_frame_truncate_next = 1'b0; + status_frame_length_next = length_min; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-short_counter_reg); + m_axis_tlast_int = 1'b1; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAD; + end + end else begin + state_next = STATE_PAD; + end + end + STATE_TRUNCATE: begin + // drop after maximum length + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = last_word_data_reg; + m_axis_tkeep_int = last_word_keep_reg; + m_axis_tvalid_int = s_axis_tvalid && s_axis_tlast; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = last_word_id_reg; + m_axis_tdest_int = last_word_dest_reg; + m_axis_tuser_int = s_axis_tuser; + + if (s_axis_tready && s_axis_tvalid) begin + word_cnt = 0; + for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin + //bit_cnt = bit_cnt + monitor_axis_tkeep[i]; + if (s_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) word_cnt = i; + end + frame_ptr_next = frame_ptr_reg+KEEP_WIDTH; + + if (s_axis_tlast) begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b0; + status_frame_truncate_next = 1'b1; + status_frame_length_next = length_max; + status_frame_original_length_next = frame_ptr_reg+word_cnt; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end else begin + state_next = STATE_TRUNCATE; + end + end else begin + state_next = STATE_TRUNCATE; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 16'd0; + short_counter_reg <= 16'd0; + long_counter_reg <= 16'd0; + s_axis_tready_reg <= 1'b0; + status_valid_reg <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + short_counter_reg <= short_counter_next; + long_counter_reg <= long_counter_next; + + s_axis_tready_reg <= s_axis_tready_next; + + status_valid_reg <= status_valid_next; + end + + status_frame_pad_reg <= status_frame_pad_next; + status_frame_truncate_reg <= status_frame_truncate_next; + status_frame_length_reg <= status_frame_length_next; + status_frame_original_length_reg <= status_frame_original_length_next; + + if (store_last_word) begin + last_word_data_reg <= m_axis_tdata_int; + last_word_keep_reg <= m_axis_tkeep_int; + last_word_id_reg <= m_axis_tid_int; + last_word_dest_reg <= m_axis_tdest_int; + last_word_user_reg <= m_axis_tuser_int; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_frame_length_adjust_fifo.v b/fpga/lib/eth/lib/axis/rtl/axis_frame_length_adjust_fifo.v new file mode 100644 index 000000000..0bcb9f3d1 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_frame_length_adjust_fifo.v @@ -0,0 +1,225 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream frame length adjuster with FIFO + */ +module axis_frame_length_adjust_fifo # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + parameter FRAME_FIFO_ADDR_WIDTH = 12, + parameter HEADER_FIFO_ADDR_WIDTH = 3 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire m_axis_hdr_valid, + input wire m_axis_hdr_ready, + output wire m_axis_hdr_pad, + output wire m_axis_hdr_truncate, + output wire [15:0] m_axis_hdr_length, + output wire [15:0] m_axis_hdr_original_length, + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Configuration + */ + input wire [15:0] length_min, + input wire [15:0] length_max +); + +wire [DATA_WIDTH-1:0] fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] fifo_axis_tkeep; +wire fifo_axis_tvalid; +wire fifo_axis_tready; +wire fifo_axis_tlast; +wire [ID_WIDTH-1:0] fifo_axis_tid; +wire [DEST_WIDTH-1:0] fifo_axis_tdest; +wire [USER_WIDTH-1:0] fifo_axis_tuser; + +wire status_valid; +wire status_ready; +wire status_frame_pad; +wire status_frame_truncate; +wire [15:0] status_frame_length; +wire [15:0] status_frame_original_length; + +axis_frame_length_adjust #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +axis_frame_length_adjust_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(fifo_axis_tdata), + .m_axis_tkeep(fifo_axis_tkeep), + .m_axis_tvalid(fifo_axis_tvalid), + .m_axis_tready(fifo_axis_tready), + .m_axis_tlast(fifo_axis_tlast), + .m_axis_tid(fifo_axis_tid), + .m_axis_tdest(fifo_axis_tdest), + .m_axis_tuser(fifo_axis_tuser), + // Status + .status_valid(status_valid), + .status_ready(status_ready), + .status_frame_pad(status_frame_pad), + .status_frame_truncate(status_frame_truncate), + .status_frame_length(status_frame_length), + .status_frame_original_length(status_frame_original_length), + // Configuration + .length_min(length_min), + .length_max(length_max) +); + +axis_fifo #( + .ADDR_WIDTH(FRAME_FIFO_ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(0) +) +frame_fifo_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(fifo_axis_tdata), + .s_axis_tkeep(fifo_axis_tkeep), + .s_axis_tvalid(fifo_axis_tvalid), + .s_axis_tready(fifo_axis_tready), + .s_axis_tlast(fifo_axis_tlast), + .s_axis_tid(fifo_axis_tid), + .s_axis_tdest(fifo_axis_tdest), + .s_axis_tuser(fifo_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +axis_fifo #( + .ADDR_WIDTH(HEADER_FIFO_ADDR_WIDTH), + .DATA_WIDTH(1+1+16+16), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +header_fifo_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata({status_frame_pad, status_frame_truncate, status_frame_length, status_frame_original_length}), + .s_axis_tkeep(0), + .s_axis_tvalid(status_valid), + .s_axis_tready(status_ready), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + // AXI output + .m_axis_tdata({m_axis_hdr_pad, m_axis_hdr_truncate, m_axis_hdr_length, m_axis_hdr_original_length}), + .m_axis_tkeep(), + .m_axis_tvalid(m_axis_hdr_valid), + .m_axis_tready(m_axis_hdr_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_ll_bridge.v b/fpga/lib/eth/lib/axis/rtl/axis_ll_bridge.v new file mode 100644 index 000000000..ad0e00216 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_ll_bridge.v @@ -0,0 +1,79 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream to LocalLink bridge + */ +module axis_ll_bridge # +( + parameter DATA_WIDTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + + /* + * LocalLink output + */ + output wire [DATA_WIDTH-1:0] ll_data_out, + output wire ll_sof_out_n, + output wire ll_eof_out_n, + output wire ll_src_rdy_out_n, + input wire ll_dst_rdy_in_n +); + +reg last_tlast = 1'b1; + +always @(posedge clk) begin + if (rst) begin + last_tlast = 1'b1; + end else begin + if (s_axis_tvalid && s_axis_tready) last_tlast = s_axis_tlast; + end +end + +// high for packet length 1 -> cannot set SOF and EOF in same cycle +// invalid packets are discarded +wire invalid = s_axis_tvalid && s_axis_tlast && last_tlast; + +assign s_axis_tready = !ll_dst_rdy_in_n; + +assign ll_data_out = s_axis_tdata; +assign ll_sof_out_n = !(last_tlast && s_axis_tvalid && !invalid); +assign ll_eof_out_n = !(s_axis_tlast && !invalid); +assign ll_src_rdy_out_n = !(s_axis_tvalid && !invalid); + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_mux.v b/fpga/lib/eth/lib/axis/rtl/axis_mux.v new file mode 100644 index 000000000..9d1e45f6e --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_mux.v @@ -0,0 +1,253 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream multiplexer + */ +module axis_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire [$clog2(S_COUNT)-1:0] select +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg [CL_S_COUNT-1:0] select_reg = 2'd0, select_next; +reg frame_reg = 1'b0, frame_next; + +reg [S_COUNT-1:0] s_axis_tready_reg = 0, s_axis_tready_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_axis_tdata[select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_axis_tkeep[select_reg*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_axis_tvalid[select_reg]; +wire current_s_tready = s_axis_tready[select_reg]; +wire current_s_tlast = s_axis_tlast[select_reg]; +wire [ID_WIDTH-1:0] current_s_tid = s_axis_tid[select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_axis_tdest[select_reg*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_axis_tuser[select_reg*USER_WIDTH +: USER_WIDTH]; + +always @* begin + select_next = select_reg; + frame_next = frame_reg; + + s_axis_tready_next = 0; + + if (current_s_tvalid & current_s_tready) begin + // end of frame detection + if (current_s_tlast) begin + frame_next = 1'b0; + end + end + + if (!frame_reg && enable && (s_axis_tvalid & (1 << select))) begin + // start of frame, grab select value + frame_next = 1'b1; + select_next = select; + end + + // generate ready signal on selected port + s_axis_tready_next = (m_axis_tready_int_early && frame_next) << select_next; + + // pass through selected packet data + m_axis_tdata_int = current_s_tdata; + m_axis_tkeep_int = current_s_tkeep; + m_axis_tvalid_int = current_s_tvalid && current_s_tready && frame_reg; + m_axis_tlast_int = current_s_tlast; + m_axis_tid_int = current_s_tid; + m_axis_tdest_int = current_s_tdest; + m_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 0; + frame_reg <= 1'b0; + s_axis_tready_reg <= 0; + end else begin + select_reg <= select_next; + frame_reg <= frame_next; + s_axis_tready_reg <= s_axis_tready_next; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_mux_wrap.py b/fpga/lib/eth/lib/axis/rtl/axis_mux_wrap.py new file mode 100755 index 000000000..2274de981 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_mux_wrap.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream mux wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + n = ports + + if name is None: + name = "axis_mux_wrap_{0}".format(n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0} port AXI stream mux wrapper {1}...".format(n, name)) + + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 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 1ns / 1ps + +/* + * AXI4-Stream {{n}} port mux (wrapper) + */ +module {{name}} # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ +{%- for p in range(n) %} + input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata, + input wire [KEEP_WIDTH-1:0] s{{'%02d'%p}}_axis_tkeep, + input wire s{{'%02d'%p}}_axis_tvalid, + output wire s{{'%02d'%p}}_axis_tready, + input wire s{{'%02d'%p}}_axis_tlast, + input wire [ID_WIDTH-1:0] s{{'%02d'%p}}_axis_tid, + input wire [DEST_WIDTH-1:0] s{{'%02d'%p}}_axis_tdest, + input wire [USER_WIDTH-1:0] s{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire [{{cn-1}}:0] select +); + +axis_mux #( + .S_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +axis_mux_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tkeep({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tvalid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tready({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tlast({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tdest({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tuser({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Control + .enable(enable), + .select(select) +); + +endmodule + +""") + + output_file.write(t.render( + n=n, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/fpga/lib/eth/lib/axis/rtl/axis_pipeline_register.v b/fpga/lib/eth/lib/axis/rtl/axis_pipeline_register.v new file mode 100644 index 000000000..c7a8623c3 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_pipeline_register.v @@ -0,0 +1,145 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * AXI4-Stream pipeline register + */ +module axis_pipeline_register # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LAST_ENABLE = 1, + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + parameter REG_TYPE = 2, + parameter LENGTH = 2 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +wire [DATA_WIDTH-1:0] axis_tdata[0:LENGTH]; +wire [KEEP_WIDTH-1:0] axis_tkeep[0:LENGTH]; +wire axis_tvalid[0:LENGTH]; +wire axis_tready[0:LENGTH]; +wire axis_tlast[0:LENGTH]; +wire [ID_WIDTH-1:0] axis_tid[0:LENGTH]; +wire [DEST_WIDTH-1:0] axis_tdest[0:LENGTH]; +wire [USER_WIDTH-1:0] axis_tuser[0:LENGTH]; + +assign axis_tdata[0] = s_axis_tdata; +assign axis_tkeep[0] = s_axis_tkeep; +assign axis_tvalid[0] = s_axis_tvalid; +assign s_axis_tready = axis_tready[0]; +assign axis_tlast[0] = s_axis_tlast; +assign axis_tid[0] = s_axis_tid; +assign axis_tdest[0] = s_axis_tdest; +assign axis_tuser[0] = s_axis_tuser; + +assign m_axis_tdata = axis_tdata[LENGTH]; +assign m_axis_tkeep = axis_tkeep[LENGTH]; +assign m_axis_tvalid = axis_tvalid[LENGTH]; +assign axis_tready[LENGTH] = m_axis_tready; +assign m_axis_tlast = axis_tlast[LENGTH]; +assign m_axis_tid = axis_tid[LENGTH]; +assign m_axis_tdest = axis_tdest[LENGTH]; +assign m_axis_tuser = axis_tuser[LENGTH]; + +generate + genvar i; + + for (i = 0; i < LENGTH; i = i + 1) begin : pipe_reg + axis_register #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .REG_TYPE(REG_TYPE) + ) + reg_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(axis_tdata[i]), + .s_axis_tkeep(axis_tkeep[i]), + .s_axis_tvalid(axis_tvalid[i]), + .s_axis_tready(axis_tready[i]), + .s_axis_tlast(axis_tlast[i]), + .s_axis_tid(axis_tid[i]), + .s_axis_tdest(axis_tdest[i]), + .s_axis_tuser(axis_tuser[i]), + // AXI output + .m_axis_tdata(axis_tdata[i+1]), + .m_axis_tkeep(axis_tkeep[i+1]), + .m_axis_tvalid(axis_tvalid[i+1]), + .m_axis_tready(axis_tready[i+1]), + .m_axis_tlast(axis_tlast[i+1]), + .m_axis_tid(axis_tid[i+1]), + .m_axis_tdest(axis_tdest[i+1]), + .m_axis_tuser(axis_tuser[i+1]) + ); + end +endgenerate + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_rate_limit.v b/fpga/lib/eth/lib/axis/rtl/axis_rate_limit.v new file mode 100644 index 000000000..a497f54a0 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_rate_limit.v @@ -0,0 +1,245 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream rate limiter + */ +module axis_rate_limit # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LAST_ENABLE = 1, + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Configuration + */ + input wire [7:0] rate_num, + input wire [7:0] rate_denom, + input wire rate_by_frame +); + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg [23:0] acc_reg = 24'd0, acc_next; +reg pause; +reg frame_reg = 1'b0, frame_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +assign s_axis_tready = s_axis_tready_reg; + +always @* begin + acc_next = acc_reg; + pause = 1'b0; + frame_next = frame_reg; + + if (acc_reg >= rate_num) begin + acc_next = acc_reg - rate_num; + end + + if (s_axis_tready && s_axis_tvalid) begin + // read input + frame_next = !s_axis_tlast; + acc_next = acc_reg + (rate_denom - rate_num); + end + + if (acc_next >= rate_num) begin + if (LAST_ENABLE && rate_by_frame) begin + pause = !frame_next; + end else begin + pause = 1'b1; + end + end + + s_axis_tready_next = m_axis_tready_int_early && !pause; + + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid && s_axis_tready; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; +end + +always @(posedge clk) begin + if (rst) begin + acc_reg <= 24'd0; + frame_reg <= 1'b0; + s_axis_tready_reg <= 1'b0; + end else begin + acc_reg <= acc_next; + frame_reg <= frame_next; + s_axis_tready_reg <= s_axis_tready_next; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_register.v b/fpga/lib/eth/lib/axis/rtl/axis_register.v new file mode 100644 index 000000000..251bcde20 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_register.v @@ -0,0 +1,264 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream register + */ +module axis_register # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LAST_ENABLE = 1, + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + parameter REG_TYPE = 2 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +generate + +if (REG_TYPE > 1) begin + // skid buffer, no bubble cycles + + // datapath registers + reg s_axis_tready_reg = 1'b0; + + reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; + reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; + reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; + reg m_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; + reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; + reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; + reg temp_m_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + // datapath control + reg store_axis_input_to_output; + reg store_axis_input_to_temp; + reg store_axis_temp_to_output; + + assign s_axis_tready = s_axis_tready_reg; + + assign m_axis_tdata = m_axis_tdata_reg; + assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; + assign m_axis_tvalid = m_axis_tvalid_reg; + assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1; + assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; + assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; + assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + + // enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) + wire s_axis_tready_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !s_axis_tvalid)); + + always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_input_to_output = 1'b0; + store_axis_input_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (s_axis_tready_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = s_axis_tvalid; + store_axis_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = s_axis_tvalid; + store_axis_input_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + s_axis_tready_reg <= 1'b0; + m_axis_tvalid_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + s_axis_tready_reg <= s_axis_tready_early; + m_axis_tvalid_reg <= m_axis_tvalid_next; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_input_to_output) begin + m_axis_tdata_reg <= s_axis_tdata; + m_axis_tkeep_reg <= s_axis_tkeep; + m_axis_tlast_reg <= s_axis_tlast; + m_axis_tid_reg <= s_axis_tid; + m_axis_tdest_reg <= s_axis_tdest; + m_axis_tuser_reg <= s_axis_tuser; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_input_to_temp) begin + temp_m_axis_tdata_reg <= s_axis_tdata; + temp_m_axis_tkeep_reg <= s_axis_tkeep; + temp_m_axis_tlast_reg <= s_axis_tlast; + temp_m_axis_tid_reg <= s_axis_tid; + temp_m_axis_tdest_reg <= s_axis_tdest; + temp_m_axis_tuser_reg <= s_axis_tuser; + end + end + +end else if (REG_TYPE == 1) begin + // simple register, inserts bubble cycles + + // datapath registers + reg s_axis_tready_reg = 1'b0; + + reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; + reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; + reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; + reg m_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + // datapath control + reg store_axis_input_to_output; + + assign s_axis_tready = s_axis_tready_reg; + + assign m_axis_tdata = m_axis_tdata_reg; + assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; + assign m_axis_tvalid = m_axis_tvalid_reg; + assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1; + assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; + assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; + assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + + // enable ready input next cycle if output buffer will be empty + wire s_axis_tready_early = !m_axis_tvalid_next; + + always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + + store_axis_input_to_output = 1'b0; + + if (s_axis_tready_reg) begin + m_axis_tvalid_next = s_axis_tvalid; + store_axis_input_to_output = 1'b1; + end else if (m_axis_tready) begin + m_axis_tvalid_next = 1'b0; + end + end + + always @(posedge clk) begin + if (rst) begin + s_axis_tready_reg <= 1'b0; + m_axis_tvalid_reg <= 1'b0; + end else begin + s_axis_tready_reg <= s_axis_tready_early; + m_axis_tvalid_reg <= m_axis_tvalid_next; + end + + // datapath + if (store_axis_input_to_output) begin + m_axis_tdata_reg <= s_axis_tdata; + m_axis_tkeep_reg <= s_axis_tkeep; + m_axis_tlast_reg <= s_axis_tlast; + m_axis_tid_reg <= s_axis_tid; + m_axis_tdest_reg <= s_axis_tdest; + m_axis_tuser_reg <= s_axis_tuser; + end + end + +end else begin + // bypass + + assign m_axis_tdata = s_axis_tdata; + assign m_axis_tkeep = KEEP_ENABLE ? s_axis_tkeep : {KEEP_WIDTH{1'b1}}; + assign m_axis_tvalid = s_axis_tvalid; + assign m_axis_tlast = LAST_ENABLE ? s_axis_tlast : 1'b1; + assign m_axis_tid = ID_ENABLE ? s_axis_tid : {ID_WIDTH{1'b0}}; + assign m_axis_tdest = DEST_ENABLE ? s_axis_tdest : {DEST_WIDTH{1'b0}}; + assign m_axis_tuser = USER_ENABLE ? s_axis_tuser : {USER_WIDTH{1'b0}}; + + assign s_axis_tready = m_axis_tready; + +end + +endgenerate + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_srl_fifo.v b/fpga/lib/eth/lib/axis/rtl/axis_srl_fifo.v new file mode 100644 index 000000000..3a57c2043 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_srl_fifo.v @@ -0,0 +1,184 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream SRL-based FIFO + */ +module axis_srl_fifo # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LAST_ENABLE = 1, + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + parameter DEPTH = 16 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire [$clog2(DEPTH+1)-1:0] count +); + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +reg [WIDTH-1:0] data_reg[DEPTH-1:0]; +reg [$clog2(DEPTH+1)-1:0] ptr_reg = 0; +reg full_reg = 1'b0, full_next; +reg empty_reg = 1'b1, empty_next; + +wire [WIDTH-1:0] s_axis; + +wire [WIDTH-1:0] m_axis = data_reg[ptr_reg-1]; + +assign s_axis_tready = !full_reg; + +generate + assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; +endgenerate + +assign m_axis_tvalid = !empty_reg; + +assign m_axis_tdata = m_axis[DATA_WIDTH-1:0]; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; +assign m_axis_tlast = LAST_ENABLE ? m_axis[LAST_OFFSET] : 1'b1; +assign m_axis_tid = ID_ENABLE ? m_axis[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}}; + +assign count = ptr_reg; + +wire ptr_empty = ptr_reg == 0; +wire ptr_empty1 = ptr_reg == 1; +wire ptr_full = ptr_reg == DEPTH; +wire ptr_full1 = ptr_reg == DEPTH-1; + +reg shift; +reg inc; +reg dec; + +integer i; + +initial begin + for (i = 0; i < DEPTH; i = i + 1) begin + data_reg[i] <= 0; + end +end + +always @* begin + shift = 1'b0; + inc = 1'b0; + dec = 1'b0; + full_next = full_reg; + empty_next = empty_reg; + + if (m_axis_tready && s_axis_tvalid && s_axis_tready) begin + shift = 1'b1; + inc = ptr_empty; + empty_next = 1'b0; + end else if (m_axis_tready && m_axis_tvalid) begin + dec = 1'b1; + full_next = 1'b0; + empty_next = ptr_empty1; + end else if (s_axis_tvalid && s_axis_tready) begin + shift = 1'b1; + inc = 1'b1; + full_next = ptr_full1; + empty_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + ptr_reg <= 0; + full_reg <= 1'b0; + empty_reg <= 1'b1; + end else begin + if (inc) begin + ptr_reg <= ptr_reg + 1; + end else if (dec) begin + ptr_reg <= ptr_reg - 1; + end else begin + ptr_reg <= ptr_reg; + end + + full_reg <= full_next; + empty_reg <= empty_next; + end + + if (shift) begin + data_reg[0] <= s_axis; + for (i = 0; i < DEPTH-1; i = i + 1) begin + data_reg[i+1] <= data_reg[i]; + end + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_srl_register.v b/fpga/lib/eth/lib/axis/rtl/axis_srl_register.v new file mode 100644 index 000000000..e9735a811 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_srl_register.v @@ -0,0 +1,144 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream SRL-based FIFO register + */ +module axis_srl_register # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter LAST_ENABLE = 1, + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +reg [WIDTH-1:0] data_reg[1:0]; +reg valid_reg[1:0]; +reg ptr_reg = 0; +reg full_reg = 0; + +wire [WIDTH-1:0] s_axis; + +wire [WIDTH-1:0] m_axis = data_reg[ptr_reg]; + +assign s_axis_tready = !full_reg; + +generate + assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; +endgenerate + +assign m_axis_tvalid = valid_reg[ptr_reg]; + +assign m_axis_tdata = m_axis[DATA_WIDTH-1:0]; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; +assign m_axis_tlast = LAST_ENABLE ? m_axis[LAST_OFFSET] : 1'b1; +assign m_axis_tid = ID_ENABLE ? m_axis[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}}; + +integer i; + +initial begin + for (i = 0; i < 2; i = i + 1) begin + data_reg[i] <= 0; + valid_reg[i] <= 0; + end +end + +always @(posedge clk) begin + if (rst) begin + ptr_reg <= 0; + full_reg <= 0; + end else begin + // transfer empty to full + full_reg <= !m_axis_tready && m_axis_tvalid; + + // transfer in if not full + if (s_axis_tready) begin + data_reg[0] <= s_axis; + valid_reg[0] <= s_axis_tvalid; + for (i = 0; i < 1; i = i + 1) begin + data_reg[i+1] <= data_reg[i]; + valid_reg[i+1] <= valid_reg[i]; + end + ptr_reg <= valid_reg[0]; + end + + if (m_axis_tready) begin + ptr_reg <= 0; + end + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_stat_counter.v b/fpga/lib/eth/lib/axis/rtl/axis_stat_counter.v new file mode 100644 index 000000000..f20a9bf7f --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_stat_counter.v @@ -0,0 +1,354 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream statistics counter + */ +module axis_stat_counter # +( + parameter DATA_WIDTH = 64, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter TAG_ENABLE = 1, + parameter TAG_WIDTH = 16, + parameter TICK_COUNT_ENABLE = 1, + parameter TICK_COUNT_WIDTH = 32, + parameter BYTE_COUNT_ENABLE = 1, + parameter BYTE_COUNT_WIDTH = 32, + parameter FRAME_COUNT_ENABLE = 1, + parameter FRAME_COUNT_WIDTH = 32 +) +( + input wire clk, + input wire rst, + + /* + * AXI monitor + */ + input wire [KEEP_WIDTH-1:0] monitor_axis_tkeep, + input wire monitor_axis_tvalid, + input wire monitor_axis_tready, + input wire monitor_axis_tlast, + + /* + * AXI status data output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Configuration + */ + input wire [TAG_WIDTH-1:0] tag, + input wire trigger, + + /* + * Status + */ + output wire busy +); + +localparam TAG_BYTE_WIDTH = (TAG_WIDTH + 7) / 8; +localparam TICK_COUNT_BYTE_WIDTH = (TICK_COUNT_WIDTH + 7) / 8; +localparam BYTE_COUNT_BYTE_WIDTH = (BYTE_COUNT_WIDTH + 7) / 8; +localparam FRAME_COUNT_BYTE_WIDTH = (FRAME_COUNT_WIDTH + 7) / 8; +localparam TOTAL_LENGTH = TAG_BYTE_WIDTH + TICK_COUNT_BYTE_WIDTH + BYTE_COUNT_BYTE_WIDTH + FRAME_COUNT_BYTE_WIDTH; + +// state register +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_OUTPUT_DATA = 2'd1; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [TICK_COUNT_WIDTH-1:0] tick_count_reg = 0, tick_count_next; +reg [BYTE_COUNT_WIDTH-1:0] byte_count_reg = 0, byte_count_next; +reg [FRAME_COUNT_WIDTH-1:0] frame_count_reg = 0, frame_count_next; +reg frame_reg = 1'b0, frame_next; + +reg store_output; +reg [$clog2(TOTAL_LENGTH)-1:0] frame_ptr_reg = 0, frame_ptr_next; + +reg [TICK_COUNT_WIDTH-1:0] tick_count_output_reg = 0; +reg [BYTE_COUNT_WIDTH-1:0] byte_count_output_reg = 0; +reg [FRAME_COUNT_WIDTH-1:0] frame_count_output_reg = 0; + +reg busy_reg = 1'b0; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign busy = busy_reg; + +integer offset, i, bit_cnt; + +always @* begin + state_next = STATE_IDLE; + + tick_count_next = tick_count_reg; + byte_count_next = byte_count_reg; + frame_count_next = frame_count_reg; + frame_next = frame_reg; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + store_output = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + // data readout + + case (state_reg) + STATE_IDLE: begin + if (trigger) begin + store_output = 1'b1; + tick_count_next = 0; + byte_count_next = 0; + frame_count_next = 0; + frame_ptr_next = 0; + + if (m_axis_tready_int_reg) begin + frame_ptr_next = 1; + if (TAG_ENABLE) begin + m_axis_tdata_int = tag[(TAG_BYTE_WIDTH-1)*8 +: 8]; + end else if (TICK_COUNT_ENABLE) begin + m_axis_tdata_int = tick_count_reg[(TICK_COUNT_BYTE_WIDTH-1)*8 +: 8]; + end else if (BYTE_COUNT_ENABLE) begin + m_axis_tdata_int = byte_count_reg[(BYTE_COUNT_BYTE_WIDTH-1)*8 +: 8]; + end else if (FRAME_COUNT_ENABLE) begin + m_axis_tdata_int = frame_count_reg[(FRAME_COUNT_BYTE_WIDTH-1)*8 +: 8]; + end + m_axis_tvalid_int = 1'b1; + end + + state_next = STATE_OUTPUT_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_OUTPUT_DATA: begin + if (m_axis_tready_int_reg) begin + state_next = STATE_OUTPUT_DATA; + frame_ptr_next = frame_ptr_reg + 1; + m_axis_tvalid_int = 1'b1; + + offset = 0; + if (TAG_ENABLE) begin + for (i = TAG_BYTE_WIDTH-1; i >= 0; i = i - 1) begin + if (frame_ptr_reg == offset) begin + m_axis_tdata_int = tag[i*8 +: 8]; + end + offset = offset + 1; + end + end + if (TICK_COUNT_ENABLE) begin + for (i = TICK_COUNT_BYTE_WIDTH-1; i >= 0; i = i - 1) begin + if (frame_ptr_reg == offset) begin + m_axis_tdata_int = tick_count_output_reg[i*8 +: 8]; + end + offset = offset + 1; + end + end + if (BYTE_COUNT_ENABLE) begin + for (i = BYTE_COUNT_BYTE_WIDTH-1; i >= 0; i = i - 1) begin + if (frame_ptr_reg == offset) begin + m_axis_tdata_int = byte_count_output_reg[i*8 +: 8]; + end + offset = offset + 1; + end + end + if (FRAME_COUNT_ENABLE) begin + for (i = FRAME_COUNT_BYTE_WIDTH-1; i >= 0; i = i - 1) begin + if (frame_ptr_reg == offset) begin + m_axis_tdata_int = frame_count_output_reg[i*8 +: 8]; + end + offset = offset + 1; + end + end + if (frame_ptr_reg == offset-1) begin + m_axis_tlast_int = 1'b1; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_OUTPUT_DATA; + end + end + endcase + + // stats collection + + // increment tick count by number of words that can be transferred per cycle + tick_count_next = tick_count_next + (KEEP_ENABLE ? KEEP_WIDTH : 1); + + if (monitor_axis_tready && monitor_axis_tvalid) begin + // valid transfer cycle + + // increment byte count by number of words transferred + if (KEEP_ENABLE) begin + bit_cnt = 0; + for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin + //bit_cnt = bit_cnt + monitor_axis_tkeep[i]; + if (monitor_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) bit_cnt = i; + end + byte_count_next = byte_count_next + bit_cnt; + end else begin + byte_count_next = byte_count_next + 1; + end + + // count frames + if (monitor_axis_tlast) begin + // end of frame + frame_next = 1'b0; + end else if (!frame_reg) begin + // first word after end of frame + frame_count_next = frame_count_next + 1; + frame_next = 1'b1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + tick_count_reg <= 0; + byte_count_reg <= 0; + frame_count_reg <= 0; + frame_reg <= 1'b0; + frame_ptr_reg <= 0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + tick_count_reg <= tick_count_next; + byte_count_reg <= byte_count_next; + frame_count_reg <= frame_count_next; + frame_reg <= frame_next; + frame_ptr_reg <= frame_ptr_next; + + busy_reg <= state_next != STATE_IDLE; + end + + if (store_output) begin + tick_count_output_reg <= tick_count_reg; + byte_count_output_reg <= byte_count_reg; + frame_count_output_reg <= frame_count_reg; + end +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_switch.v b/fpga/lib/eth/lib/axis/rtl/axis_switch.v new file mode 100644 index 000000000..f8433b5e7 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_switch.v @@ -0,0 +1,300 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * AXI4-Stream switch + */ +module axis_switch # +( + parameter S_COUNT = 4, + parameter M_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_WIDTH = $clog2(S_COUNT), + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + parameter M_BASE = {2'd3, 2'd2, 2'd1, 2'd0}, + parameter M_TOP = {2'd3, 2'd2, 2'd1, 2'd0}, + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + parameter S_REG_TYPE = 0, + parameter M_REG_TYPE = 2, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "ROUND_ROBIN", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream outputs + */ + output wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep, + output wire [M_COUNT-1:0] m_axis_tvalid, + input wire [M_COUNT-1:0] m_axis_tready, + output wire [M_COUNT-1:0] m_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +integer i, j; + +// check configuration +initial begin + if (2**DEST_WIDTH < CL_M_COUNT) begin + $error("Error: DEST_WIDTH too small for port count"); + $finish; + end + + for (i = 0; i < M_COUNT; i = i + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] < 0 || M_BASE[i*DEST_WIDTH +: DEST_WIDTH] > 2**DEST_WIDTH-1 || M_TOP[i*DEST_WIDTH +: DEST_WIDTH] < 0 || M_TOP[i*DEST_WIDTH +: DEST_WIDTH] > 2**DEST_WIDTH-1) begin + $error("Error: value out of range"); + $finish; + end + end + + for (i = 0; i < M_COUNT; i = i + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] > M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin + $error("Error: invalid range"); + $finish; + end + end + + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = i+1; j < M_COUNT; j = j + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[j*DEST_WIDTH +: DEST_WIDTH] && M_BASE[j*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin + $display("%d: %08x-%08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_TOP[i*DEST_WIDTH +: DEST_WIDTH]); + $display("%d: %08x-%08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH], M_TOP[j*DEST_WIDTH +: DEST_WIDTH]); + $error("Error: ranges overlap"); + $finish; + end + end + end +end + +wire [S_COUNT*DATA_WIDTH-1:0] int_s_axis_tdata; +wire [S_COUNT*KEEP_WIDTH-1:0] int_s_axis_tkeep; +wire [S_COUNT-1:0] int_s_axis_tvalid; +wire [S_COUNT-1:0] int_s_axis_tready; +wire [S_COUNT-1:0] int_s_axis_tlast; +wire [S_COUNT*ID_WIDTH-1:0] int_s_axis_tid; +wire [S_COUNT*DEST_WIDTH-1:0] int_s_axis_tdest; +wire [S_COUNT*USER_WIDTH-1:0] int_s_axis_tuser; + +wire [S_COUNT*M_COUNT-1:0] int_axis_tvalid; +wire [M_COUNT*S_COUNT-1:0] int_axis_tready; + +generate + + genvar m, n; + + for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces + + // decoding + reg [CL_S_COUNT-1:0] select_reg = 0, select_next; + reg drop_reg = 1'b0, drop_next; + reg select_valid_reg = 1'b0, select_valid_next; + + integer k; + + always @* begin + select_next = select_reg; + drop_next = drop_reg && !(int_s_axis_tvalid[m] && int_s_axis_tready[m] && int_s_axis_tlast[m]); + select_valid_next = select_valid_reg && !(int_s_axis_tvalid[m] && int_s_axis_tready[m] && int_s_axis_tlast[m]); + + if (int_s_axis_tvalid[m] && !select_valid_reg) begin + select_next = 1'b0; + select_valid_next = 1'b0; + drop_next = 1'b1; + for (k = 0; k < M_COUNT; k = k + 1) begin + if (int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH] >= M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end + end + end + end + + always @(posedge clk) begin + if (rst) begin + select_valid_reg <= 1'b0; + end else begin + select_valid_reg <= select_valid_next; + end + + select_reg <= select_next; + drop_reg <= drop_next; + end + + // forwarding + assign int_axis_tvalid[m*M_COUNT +: M_COUNT] = (int_s_axis_tvalid[m] && select_valid_reg && !drop_reg) << select_reg; + assign int_s_axis_tready[m] = int_axis_tready[select_reg*M_COUNT+m] || drop_reg; + + // S side register + axis_register #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(1), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .REG_TYPE(S_REG_TYPE) + ) + reg_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata[m*DATA_WIDTH +: DATA_WIDTH]), + .s_axis_tkeep(s_axis_tkeep[m*KEEP_WIDTH +: KEEP_WIDTH]), + .s_axis_tvalid(s_axis_tvalid[m]), + .s_axis_tready(s_axis_tready[m]), + .s_axis_tlast(s_axis_tlast[m]), + .s_axis_tid(s_axis_tid[m*ID_WIDTH +: ID_WIDTH]), + .s_axis_tdest(s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH]), + .s_axis_tuser(s_axis_tuser[m*USER_WIDTH +: USER_WIDTH]), + // AXI output + .m_axis_tdata(int_s_axis_tdata[m*DATA_WIDTH +: DATA_WIDTH]), + .m_axis_tkeep(int_s_axis_tkeep[m*KEEP_WIDTH +: KEEP_WIDTH]), + .m_axis_tvalid(int_s_axis_tvalid[m]), + .m_axis_tready(int_s_axis_tready[m]), + .m_axis_tlast(int_s_axis_tlast[m]), + .m_axis_tid(int_s_axis_tid[m*ID_WIDTH +: ID_WIDTH]), + .m_axis_tdest(int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH]), + .m_axis_tuser(int_s_axis_tuser[m*USER_WIDTH +: USER_WIDTH]) + ); + end // s_ifaces + + for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces + + // arbitration + wire [S_COUNT-1:0] request; + wire [S_COUNT-1:0] acknowledge; + wire [S_COUNT-1:0] grant; + wire grant_valid; + wire [CL_S_COUNT-1:0] grant_encoded; + + arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) + ) + arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) + ); + + // mux + wire [DATA_WIDTH-1:0] s_axis_tdata_mux = int_s_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; + wire [KEEP_WIDTH-1:0] s_axis_tkeep_mux = int_s_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; + wire s_axis_tvalid_mux = int_axis_tvalid[grant_encoded*S_COUNT+n] && grant_valid; + wire s_axis_tready_mux; + wire s_axis_tlast_mux = int_s_axis_tlast[grant_encoded]; + wire [ID_WIDTH-1:0] s_axis_tid_mux = int_s_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; + wire [DEST_WIDTH-1:0] s_axis_tdest_mux = int_s_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; + wire [USER_WIDTH-1:0] s_axis_tuser_mux = int_s_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + + assign int_axis_tready[n*S_COUNT +: S_COUNT] = (grant_valid && s_axis_tready_mux) << grant_encoded; + + for (m = 0; m < S_COUNT; m = m + 1) begin + assign request[m] = int_axis_tvalid[m*M_COUNT+n] && !grant[m]; + assign acknowledge[m] = grant[m] && int_axis_tvalid[m*M_COUNT+n] && s_axis_tlast_mux && s_axis_tready_mux; + end + + // M side register + axis_register #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(1), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .REG_TYPE(M_REG_TYPE) + ) + reg_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata_mux), + .s_axis_tkeep(s_axis_tkeep_mux), + .s_axis_tvalid(s_axis_tvalid_mux), + .s_axis_tready(s_axis_tready_mux), + .s_axis_tlast(s_axis_tlast_mux), + .s_axis_tid(s_axis_tid_mux), + .s_axis_tdest(s_axis_tdest_mux), + .s_axis_tuser(s_axis_tuser_mux), + // AXI output + .m_axis_tdata(m_axis_tdata[n*DATA_WIDTH +: DATA_WIDTH]), + .m_axis_tkeep(m_axis_tkeep[n*KEEP_WIDTH +: KEEP_WIDTH]), + .m_axis_tvalid(m_axis_tvalid[n]), + .m_axis_tready(m_axis_tready[n]), + .m_axis_tlast(m_axis_tlast[n]), + .m_axis_tid(m_axis_tid[n*ID_WIDTH +: ID_WIDTH]), + .m_axis_tdest(m_axis_tdest[n*DEST_WIDTH +: DEST_WIDTH]), + .m_axis_tuser(m_axis_tuser[n*USER_WIDTH +: USER_WIDTH]) + ); + end // m_ifaces + +endgenerate + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/axis_tap.v b/fpga/lib/eth/lib/axis/rtl/axis_tap.v new file mode 100644 index 000000000..bc9ceb088 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/axis_tap.v @@ -0,0 +1,317 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream tap + */ +module axis_tap # +( + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + parameter USER_BAD_FRAME_VALUE = 1'b1, + parameter USER_BAD_FRAME_MASK = 1'b1 +) +( + input wire clk, + input wire rst, + + /* + * AXI tap + */ + input wire [DATA_WIDTH-1:0] tap_axis_tdata, + input wire [KEEP_WIDTH-1:0] tap_axis_tkeep, + input wire tap_axis_tvalid, + input wire tap_axis_tready, + input wire tap_axis_tlast, + input wire [ID_WIDTH-1:0] tap_axis_tid, + input wire [DEST_WIDTH-1:0] tap_axis_tdest, + input wire [USER_WIDTH-1:0] tap_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +// datapath control signals +reg store_last_word; + +reg [ID_WIDTH-1:0] last_word_id_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] last_word_dest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] last_word_user_reg = {USER_WIDTH{1'b0}}; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_TRANSFER = 2'd1, + STATE_TRUNCATE = 2'd2, + STATE_WAIT = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg frame_reg = 1'b0, frame_next; + +always @* begin + state_next = STATE_IDLE; + + store_last_word = 1'b0; + + frame_next = frame_reg; + + m_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {KEEP_WIDTH{1'b0}}; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tid_int = {ID_WIDTH{1'b0}}; + m_axis_tdest_int = {DEST_WIDTH{1'b0}}; + m_axis_tuser_int = {USER_WIDTH{1'b0}}; + + if (tap_axis_tready && tap_axis_tvalid) begin + frame_next = !tap_axis_tlast; + end + + case (state_reg) + STATE_IDLE: begin + if (tap_axis_tready && tap_axis_tvalid) begin + // start of frame + if (m_axis_tready_int_reg) begin + m_axis_tdata_int = tap_axis_tdata; + m_axis_tkeep_int = tap_axis_tkeep; + m_axis_tvalid_int = tap_axis_tvalid && tap_axis_tready; + m_axis_tlast_int = tap_axis_tlast; + m_axis_tid_int = tap_axis_tid; + m_axis_tdest_int = tap_axis_tdest; + m_axis_tuser_int = tap_axis_tuser; + if (tap_axis_tlast) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_TRANSFER; + end + end else begin + state_next = STATE_WAIT; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_TRANSFER: begin + if (tap_axis_tready && tap_axis_tvalid) begin + // transfer data + if (m_axis_tready_int_reg) begin + m_axis_tdata_int = tap_axis_tdata; + m_axis_tkeep_int = tap_axis_tkeep; + m_axis_tvalid_int = tap_axis_tvalid && tap_axis_tready; + m_axis_tlast_int = tap_axis_tlast; + m_axis_tid_int = tap_axis_tid; + m_axis_tdest_int = tap_axis_tdest; + m_axis_tuser_int = tap_axis_tuser; + if (tap_axis_tlast) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_TRANSFER; + end + end else begin + store_last_word = 1'b1; + state_next = STATE_TRUNCATE; + end + end else begin + state_next = STATE_TRANSFER; + end + end + STATE_TRUNCATE: begin + if (m_axis_tready_int_reg) begin + m_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {{KEEP_WIDTH-1{1'b0}}, 1'b1}; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tid_int = last_word_id_reg; + m_axis_tdest_int = last_word_dest_reg; + m_axis_tuser_int = (last_word_user_reg & ~USER_BAD_FRAME_MASK) | (USER_BAD_FRAME_VALUE & USER_BAD_FRAME_MASK); + if (frame_next) begin + state_next = STATE_WAIT; + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_TRUNCATE; + end + end + STATE_WAIT: begin + if (tap_axis_tready && tap_axis_tvalid) begin + if (tap_axis_tlast) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT; + end + end else begin + state_next = STATE_WAIT; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_reg <= 1'b0; + end else begin + state_reg <= state_next; + frame_reg <= frame_next; + end + + if (store_last_word) begin + last_word_id_reg <= tap_axis_tid; + last_word_dest_reg <= tap_axis_tdest; + last_word_user_reg <= tap_axis_tuser; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/ll_axis_bridge.v b/fpga/lib/eth/lib/axis/rtl/ll_axis_bridge.v new file mode 100644 index 000000000..ab2aeab01 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/ll_axis_bridge.v @@ -0,0 +1,64 @@ +/* + +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 1ns / 1ps + +/* + * LocalLink to AXI4-Stream bridge + */ +module ll_axis_bridge # +( + parameter DATA_WIDTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * LocalLink input + */ + input wire [DATA_WIDTH-1:0] ll_data_in, + input wire ll_sof_in_n, + input wire ll_eof_in_n, + input wire ll_src_rdy_in_n, + output wire ll_dst_rdy_out_n, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast +); + +assign m_axis_tdata = ll_data_in; +assign m_axis_tvalid = !ll_src_rdy_in_n; +assign m_axis_tlast = !ll_eof_in_n; + +assign ll_dst_rdy_out_n = !m_axis_tready; + +endmodule diff --git a/fpga/lib/eth/lib/axis/rtl/priority_encoder.v b/fpga/lib/eth/lib/axis/rtl/priority_encoder.v new file mode 100644 index 000000000..730306302 --- /dev/null +++ b/fpga/lib/eth/lib/axis/rtl/priority_encoder.v @@ -0,0 +1,98 @@ +/* + +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 1ns / 1ps + +/* + * Priority encoder module + */ +module priority_encoder # +( + parameter WIDTH = 4, + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "LOW" +) +( + input wire [WIDTH-1:0] input_unencoded, + output wire output_valid, + output wire [$clog2(WIDTH)-1:0] output_encoded, + output wire [WIDTH-1:0] output_unencoded +); + +// power-of-two width +parameter W1 = 2**$clog2(WIDTH); +parameter W2 = W1/2; + +generate + if (WIDTH == 1) begin + // one input + assign output_valid = input_unencoded; + assign output_encoded = 0; + end else if (WIDTH == 2) begin + // two inputs - just an OR gate + assign output_valid = |input_unencoded; + if (LSB_PRIORITY == "LOW") begin + assign output_encoded = input_unencoded[1]; + end else begin + assign output_encoded = ~input_unencoded[0]; + end + end else begin + // more than two inputs - split into two parts and recurse + // also pad input to correct power-of-two width + wire [$clog2(W2)-1:0] out1, out2; + wire valid1, valid2; + priority_encoder #( + .WIDTH(W2), + .LSB_PRIORITY(LSB_PRIORITY) + ) + priority_encoder_inst1 ( + .input_unencoded(input_unencoded[W2-1:0]), + .output_valid(valid1), + .output_encoded(out1) + ); + priority_encoder #( + .WIDTH(W2), + .LSB_PRIORITY(LSB_PRIORITY) + ) + priority_encoder_inst2 ( + .input_unencoded({{W1-WIDTH{1'b0}}, input_unencoded[WIDTH-1:W2]}), + .output_valid(valid2), + .output_encoded(out2) + ); + // multiplexer to select part + assign output_valid = valid1 | valid2; + if (LSB_PRIORITY == "LOW") begin + assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1}; + end else begin + assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2}; + end + end +endgenerate + +// unencoded output +assign output_unencoded = 1 << output_encoded; + +endmodule diff --git a/fpga/lib/eth/lib/axis/syn/axis_async_fifo.tcl b/fpga/lib/eth/lib/axis/syn/axis_async_fifo.tcl new file mode 100644 index 000000000..bc7bd5e4c --- /dev/null +++ b/fpga/lib/eth/lib/axis/syn/axis_async_fifo.tcl @@ -0,0 +1,88 @@ +# Copyright (c) 2019 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. + +# AXI stream asynchronous FIFO timing constraints + +foreach fifo_inst [get_cells -hier -filter {(ORIG_REF_NAME == axis_async_fifo || REF_NAME == axis_async_fifo)}] { + puts "Inserting timing constraints for axis_async_fifo instance $fifo_inst" + + # get clock periods + set read_clk [get_clocks -of_objects [get_pins $fifo_inst/rd_ptr_reg_reg[0]/C]] + set write_clk [get_clocks -of_objects [get_pins $fifo_inst/wr_ptr_reg_reg[0]/C]] + + set read_clk_period [get_property -min PERIOD $read_clk] + set write_clk_period [get_property -min PERIOD $write_clk] + + set min_clk_period [expr $read_clk_period < $write_clk_period ? $read_clk_period : $write_clk_period] + + # reset synchronization + set reset_ffs [get_cells -quiet -hier -regexp ".*/(s|m)_rst_sync\[123\]_reg_reg" -filter "PARENT == $fifo_inst"] + + if {[llength $reset_ffs]} { + set_property ASYNC_REG TRUE $reset_ffs + set_false_path -to [get_pins -of_objects $reset_ffs -filter {IS_PRESET || IS_RESET}] + } + + if {[llength [get_cells -quiet $fifo_inst/s_rst_sync2_reg_reg]]} { + set_false_path -to [get_pins $fifo_inst/s_rst_sync2_reg_reg/D] + set_max_delay -from [get_cells $fifo_inst/s_rst_sync2_reg_reg] -to [get_cells $fifo_inst/s_rst_sync3_reg_reg] $min_clk_period + } + + if {[llength [get_cells -quiet $fifo_inst/m_rst_sync2_reg_reg]]} { + set_false_path -to [get_pins $fifo_inst/m_rst_sync2_reg_reg/D] + set_max_delay -from [get_cells $fifo_inst/m_rst_sync2_reg_reg] -to [get_cells $fifo_inst/m_rst_sync3_reg_reg] $min_clk_period + } + + # pointer synchronization + set_property ASYNC_REG TRUE [get_cells -hier -regexp ".*/(wr|rd)_ptr_gray_sync\[12\]_reg_reg\\\[\\d+\\\]" -filter "PARENT == $fifo_inst"] + + set_max_delay -from [get_cells "$fifo_inst/rd_ptr_reg_reg[*] $fifo_inst/rd_ptr_gray_reg_reg[*]"] -to [get_cells $fifo_inst/rd_ptr_gray_sync1_reg_reg[*]] -datapath_only $read_clk_period + set_bus_skew -from [get_cells "$fifo_inst/rd_ptr_reg_reg[*] $fifo_inst/rd_ptr_gray_reg_reg[*]"] -to [get_cells $fifo_inst/rd_ptr_gray_sync1_reg_reg[*]] $write_clk_period + set_max_delay -from [get_cells -quiet "$fifo_inst/wr_ptr_reg_reg[*] $fifo_inst/wr_ptr_gray_reg_reg[*] $fifo_inst/wr_ptr_sync_gray_reg_reg[*]"] -to [get_cells $fifo_inst/wr_ptr_gray_sync1_reg_reg[*]] -datapath_only $write_clk_period + set_bus_skew -from [get_cells -quiet "$fifo_inst/wr_ptr_reg_reg[*] $fifo_inst/wr_ptr_gray_reg_reg[*] $fifo_inst/wr_ptr_sync_gray_reg_reg[*]"] -to [get_cells $fifo_inst/wr_ptr_gray_sync1_reg_reg[*]] $read_clk_period + + # output register (needed for distributed RAM sync write/async read) + set output_reg_ffs [get_cells -quiet "$fifo_inst/mem_read_data_reg_reg[*]"] + + if {[llength $output_reg_ffs]} { + set_false_path -from $write_clk -to $output_reg_ffs + } + + # frame FIFO pointer update synchronization + set update_ffs [get_cells -quiet -hier -regexp ".*/wr_ptr_update(_ack)?_sync\[123\]_reg_reg" -filter "PARENT == $fifo_inst"] + + if {[llength $update_ffs]} { + set_property ASYNC_REG TRUE $update_ffs + + set_max_delay -from [get_cells $fifo_inst/wr_ptr_update_reg_reg] -to [get_cells $fifo_inst/wr_ptr_update_sync1_reg_reg] -datapath_only $write_clk_period + set_max_delay -from [get_cells $fifo_inst/wr_ptr_update_sync3_reg_reg] -to [get_cells $fifo_inst/wr_ptr_update_ack_sync1_reg_reg] -datapath_only $read_clk_period + } + + # status synchronization + foreach i {overflow bad_frame good_frame} { + set status_sync_regs [get_cells -quiet -hier -regexp ".*/${i}_sync\[123\]_reg_reg" -filter "PARENT == $fifo_inst"] + + if {[llength $status_sync_regs]} { + set_property ASYNC_REG TRUE $status_sync_regs + + set_max_delay -from [get_cells $fifo_inst/${i}_sync1_reg_reg] -to [get_cells $fifo_inst/${i}_sync2_reg_reg] -datapath_only $read_clk_period + } + } +} diff --git a/fpga/lib/eth/lib/axis/tb/axis_ep.py b/fpga/lib/eth/lib/axis/tb/axis_ep.py new file mode 100644 index 000000000..d794cc4a9 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/axis_ep.py @@ -0,0 +1,535 @@ +""" + +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. + +""" + +from myhdl import * + +skip_asserts = False + +class AXIStreamFrame(object): + def __init__(self, data=b'', keep=None, id=None, dest=None, user=None, last_cycle_user=None): + self.B = 0 + self.N = 8 + self.M = 1 + self.WL = 8 + self.data = b'' + self.keep = None + self.id = 0 + self.dest = 0 + self.user = None + self.last_cycle_user = None + + if type(data) in (bytes, bytearray): + self.data = bytearray(data) + self.keep = keep + self.id = id + self.dest = dest + self.user = user + self.last_cycle_user = last_cycle_user + elif type(data) is AXIStreamFrame: + self.N = data.N + self.WL = data.WL + if type(data.data) is bytearray: + self.data = bytearray(data.data) + else: + self.data = list(data.data) + if data.keep is not None: + self.keep = list(data.keep) + if data.id is not None: + if type(data.id) in (int, bool): + self.id = data.id + else: + self.id = list(data.id) + if data.dest is not None: + if type(data.dest) in (int, bool): + self.dest = data.dest + else: + self.dest = list(data.dest) + if data.user is not None: + if type(data.user) in (int, bool): + self.user = data.user + else: + self.user = list(data.user) + self.last_cycle_user = data.last_cycle_user + else: + self.data = list(data) + self.keep = keep + self.id = id + self.dest = dest + self.user = user + self.last_cycle_user = last_cycle_user + + def build(self): + if self.data is None: + return + + f = list(self.data) + tdata = [] + tkeep = [] + tid = [] + tdest = [] + tuser = [] + i = 0 + + while len(f) > 0: + if self.B == 0: + data = 0 + keep = 0 + for j in range(self.M): + data = data | (f.pop(0) << (j*self.WL)) + keep = keep | (1 << j) + if len(f) == 0: break + tdata.append(data) + + if self.keep is None: + tkeep.append(keep) + else: + tkeep.append(self.keep[i]) + else: + # multiple tdata signals + data = 0 + tdata.append(f.pop(0)) + tkeep.append(0) + + if self.id is None: + tid.append(0) + elif type(self.id) is int: + tid.append(self.id) + else: + tid.append(self.id[i]) + + if self.dest is None: + tdest.append(0) + elif type(self.dest) is int: + tdest.append(self.dest) + else: + tdest.append(self.dest[i]) + + if self.user is None: + tuser.append(0) + elif type(self.user) is int: + tuser.append(self.user) + else: + tuser.append(self.user[i]) + i += 1 + + if self.last_cycle_user: + tuser[-1] = self.last_cycle_user + + return tdata, tkeep, tid, tdest, tuser + + def parse(self, tdata, tkeep, tid, tdest, tuser): + if tdata is None or tkeep is None or tuser is None: + return + if len(tdata) != len(tkeep) or len(tdata) != len(tid) or len(tdata) != len(tdest) or len(tdata) != len(tuser): + raise Exception("Invalid data") + + self.data = [] + self.keep = [] + self.id = [] + self.dest = [] + self.user = [] + + if self.B == 0: + mask = 2**self.WL-1 + + for i in range(len(tdata)): + for j in range(self.M): + if tkeep[i] & (1 << j): + self.data.append((tdata[i] >> (j*self.WL)) & mask) + self.keep.append(tkeep[i]) + self.id.append(tid[i]) + self.dest.append(tdest[i]) + self.user.append(tuser[i]) + else: + for i in range(len(tdata)): + self.data.append(tdata[i]) + self.keep.append(tkeep[i]) + self.id.append(tid[i]) + self.dest.append(tdest[i]) + self.user.append(tuser[i]) + + if self.WL == 8: + self.data = bytearray(self.data) + + self.last_cycle_user = self.user[-1] + + def __eq__(self, other): + if not isinstance(other, AXIStreamFrame): + return False + if self.data != other.data: + return False + if self.keep is not None and other.keep is not None: + if self.keep != other.keep: + return False + if self.id is not None and other.id is not None: + if type(self.id) in (int, bool) and type(other.id) is list: + for k in other.id: + if self.id != k: + return False + elif type(other.id) in (int, bool) and type(self.id) is list: + for k in self.id: + if other.id != k: + return False + elif self.id != other.id: + return False + if self.dest is not None and other.dest is not None: + if type(self.dest) in (int, bool) and type(other.dest) is list: + for k in other.dest: + if self.dest != k: + return False + elif type(other.dest) in (int, bool) and type(self.dest) is list: + for k in self.dest: + if other.dest != k: + return False + elif self.dest != other.dest: + return False + if self.last_cycle_user is not None and other.last_cycle_user is not None: + if self.last_cycle_user != other.last_cycle_user: + return False + if self.user is not None and other.user is not None: + if type(self.user) in (int, bool) and type(other.user) is list: + for k in other.user[:-1]: + if self.user != k: + return False + elif type(other.user) in (int, bool) and type(self.user) is list: + for k in self.user[:-1]: + if other.user != k: + return False + elif self.user != other.user: + return False + else: + if self.user is not None and other.user is not None: + if type(self.user) in (int, bool) and type(other.user) is list: + for k in other.user: + if self.user != k: + return False + elif type(other.user) in (int, bool) and type(self.user) is list: + for k in self.user: + if other.user != k: + return False + elif self.user != other.user: + return False + return True + + def __repr__(self): + return ( + ('AXIStreamFrame(data=%s, ' % repr(self.data)) + + ('keep=%s, ' % repr(self.keep)) + + ('id=%s, ' % repr(self.id)) + + ('dest=%s, ' % repr(self.dest)) + + ('user=%s, ' % repr(self.user)) + + ('last_cycle_user=%s)' % repr(self.last_cycle_user)) + ) + + def __iter__(self): + return self.data.__iter__() + + +class AXIStreamSource(object): + def __init__(self): + self.active = False + self.has_logic = False + self.queue = [] + + def send(self, frame): + self.queue.append(AXIStreamFrame(frame)) + + def write(self, data): + self.send(data) + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def idle(self): + return not self.queue and not self.active + + def wait(self): + while not self.idle(): + yield self.clk.posedge + + def create_logic(self, + clk, + rst, + tdata=None, + tkeep=Signal(bool(True)), + tvalid=Signal(bool(False)), + tready=Signal(bool(True)), + tlast=Signal(bool(False)), + tid=Signal(intbv(0)), + tdest=Signal(intbv(0)), + tuser=Signal(intbv(0)), + pause=0, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + @instance + def logic(): + data = [] + keep = [] + id = [] + dest = [] + user = [] + self.active = False + B = 0 + N = len(tdata) + M = len(tkeep) + WL = int((len(tdata)+M-1)/M) + + if type(tdata) is list or type(tdata) is tuple: + # multiple tdata signals + B = len(tdata) + N = [len(b) for b in tdata] + M = 1 + WL = [1]*B + + while True: + yield clk.posedge, rst.posedge + + if rst: + data = [] + keep = [] + id = [] + dest = [] + user = [] + self.active = False + if B > 0: + for s in tdata: + s.next = 0 + else: + tdata.next = 0 + tkeep.next = 0 + tid.next = 0 + tdest.next = 0 + tuser.next = False + tvalid.next = False + tlast.next = False + else: + tvalid.next = self.active and (tvalid or not pause) + if tready and tvalid: + if len(data) > 0: + if B > 0: + l = data.pop(0) + for i in range(B): + tdata[i].next = l[i] + else: + tdata.next = data.pop(0) + tkeep.next = keep.pop(0) + tid.next = id.pop(0) + tdest.next = dest.pop(0) + tuser.next = user.pop(0) + tvalid.next = not pause + tlast.next = len(data) == 0 + else: + tvalid.next = False + tlast.next = False + self.active = False + if not self.active and self.queue: + frame = self.queue.pop(0) + frame.B = B + frame.N = N + frame.M = M + frame.WL = WL + data, keep, id, dest, user = frame.build() + if name is not None: + print("[%s] Sending frame %s" % (name, repr(frame))) + if B > 0: + l = data.pop(0) + for i in range(B): + tdata[i].next = l[i] + else: + tdata.next = data.pop(0) + tkeep.next = keep.pop(0) + tid.next = id.pop(0) + tdest.next = dest.pop(0) + tuser.next = user.pop(0) + tvalid.next = not pause + tlast.next = len(data) == 0 + self.active = True + + return instances() + + +class AXIStreamSink(object): + def __init__(self): + self.active = False + self.has_logic = False + self.queue = [] + self.read_queue = [] + self.sync = Signal(intbv(0)) + + def recv(self): + if self.queue: + return self.queue.pop(0) + return None + + def read(self, count=-1): + while self.queue: + self.read_queue.extend(self.queue.pop(0).data) + if count < 0: + count = len(self.read_queue) + data = self.read_queue[:count] + del self.read_queue[:count] + return data + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def idle(self): + return not self.active + + def wait(self, timeout=0): + if self.queue: + return + if timeout: + yield self.sync, delay(timeout) + else: + yield self.sync + + def create_logic(self, + clk, + rst, + tdata=None, + tkeep=Signal(bool(True)), + tvalid=Signal(bool(False)), + tready=Signal(bool(True)), + tlast=Signal(bool(True)), + tid=Signal(intbv(0)), + tdest=Signal(intbv(0)), + tuser=Signal(intbv(0)), + pause=0, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + tready_int = Signal(bool(False)) + tvalid_int = Signal(bool(False)) + + @always_comb + def pause_logic(): + tready.next = tready_int and not pause + tvalid_int.next = tvalid and not pause + + @instance + def logic(): + data = [] + keep = [] + id = [] + dest = [] + user = [] + B = 0 + N = len(tdata) + M = len(tkeep) + WL = int((len(tdata)+M-1)/M) + first = True + + if type(tdata) is list or type(tdata) is tuple: + # multiple tdata signals + B = len(tdata) + N = [len(b) for b in tdata] + M = 1 + WL = [1]*B + + while True: + yield clk.posedge, rst.posedge + + if rst: + tready_int.next = False + data = [] + keep = [] + id = [] + dest = [] + user = [] + first = True + self.active = False + else: + tready_int.next = True + + if tvalid_int: + + if not skip_asserts: + # zero tkeep not allowed + assert int(tkeep) != 0 + # tkeep must be contiguous + # i.e. 0b00011110 allowed, but 0b00011010 not allowed + b = int(tkeep) + while b & 1 == 0: + b = b >> 1 + while b & 1 == 1: + b = b >> 1 + assert b == 0 + # tkeep must not have gaps across cycles + if not first: + # not first cycle; lowest bit must be set + assert int(tkeep) & 1 + if not tlast: + # not last cycle; highest bit must be set + assert int(tkeep) & (1 << len(tkeep)-1) + + if B > 0: + l = [] + for i in range(B): + l.append(int(tdata[i])) + data.append(l) + else: + data.append(int(tdata)) + keep.append(int(tkeep)) + id.append(int(tid)) + dest.append(int(tdest)) + user.append(int(tuser)) + first = False + self.active = True + if tlast: + frame = AXIStreamFrame() + frame.B = B + frame.N = N + frame.M = M + frame.WL = WL + frame.parse(data, keep, id, dest, user) + self.queue.append(frame) + self.sync.next = not self.sync + self.active = False + if name is not None: + print("[%s] Got frame %s" % (name, repr(frame))) + data = [] + keep = [] + id = [] + dest = [] + user = [] + first = True + + return instances() + diff --git a/fpga/lib/eth/lib/axis/tb/ll_ep.py b/fpga/lib/eth/lib/axis/tb/ll_ep.py new file mode 100644 index 000000000..e009516d8 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/ll_ep.py @@ -0,0 +1,172 @@ +""" + +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. + +""" + +from myhdl import * + +class LocalLinkSource(object): + def __init__(self): + self.has_logic = False + self.queue = [] + + def send(self, frame): + self.queue.append(bytearray(frame)) + + def count(self): + return len(self.queue) + + def empty(self): + return self.count() == 0 + + def create_logic(self, + clk, + rst, + data_out, + sof_out_n, + eof_out_n, + src_rdy_out_n, + dst_rdy_in_n, + pause=0, + name=None): + + assert not self.has_logic + + self.has_logic = True + + src_rdy_out_n_int = Signal(bool(True)) + dst_rdy_in_n_int = Signal(bool(True)) + + @always_comb + def pause_logic(): + dst_rdy_in_n_int.next = dst_rdy_in_n or pause + src_rdy_out_n.next = src_rdy_out_n_int or pause + + @instance + def logic(): + frame = [] + + while True: + yield clk.posedge, rst.posedge + + if rst: + data_out.next = 0 + src_rdy_out_n_int.next = True + sof_out_n.next = True + eof_out_n.next = True + else: + if not dst_rdy_in_n_int and not src_rdy_out_n: + if len(frame) > 0: + data_out.next = frame.pop(0) + src_rdy_out_n_int.next = False + sof_out_n.next = True + eof_out_n.next = len(frame) != 0 + else: + src_rdy_out_n_int.next = True + eof_out_n.next = True + if (not eof_out_n and not dst_rdy_in_n_int and not src_rdy_out_n) or src_rdy_out_n_int: + if self.queue: + frame = self.queue.pop(0) + if name is not None: + print("[%s] Sending frame %s" % (name, repr(frame))) + data_out.next = frame.pop(0) + src_rdy_out_n_int.next = False + sof_out_n.next = False + eof_out_n.next = len(frame) != 0 + + return instances() + + +class LocalLinkSink(object): + def __init__(self): + self.has_logic = False + self.queue = [] + self.sync = Signal(intbv(0)) + + def recv(self): + if self.queue: + return self.queue.pop(0) + return None + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def wait(self, timeout=0): + if self.queue: + return + if timeout: + yield self.sync, delay(timeout) + else: + yield self.sync + + def create_logic(self, + clk, + rst, + data_in, + sof_in_n, + eof_in_n, + src_rdy_in_n, + dst_rdy_out_n, + pause=0, + name=None): + + assert not self.has_logic + + self.has_logic = True + + src_rdy_in_n_int = Signal(bool(True)) + dst_rdy_out_n_int = Signal(bool(True)) + + @always_comb + def pause_logic(): + dst_rdy_out_n.next = dst_rdy_out_n_int or pause + src_rdy_in_n_int.next = src_rdy_in_n or pause + + @instance + def logic(): + frame = [] + + while True: + yield clk.posedge, rst.posedge + + if rst: + dst_rdy_out_n_int.next = True + frame = [] + else: + dst_rdy_out_n_int.next = False + + if not src_rdy_in_n_int: + if not sof_in_n: + frame = [] + frame.append(int(data_in)) + if not eof_in_n: + self.queue.append(frame) + self.sync.next = not self.sync + if name is not None: + print("[%s] Got frame %s" % (name, repr(frame))) + frame = [] + + return instances() + diff --git a/fpga/lib/eth/lib/axis/tb/test_arbiter.py b/fpga/lib/eth/lib/axis/tb/test_arbiter.py new file mode 100755 index 000000000..6c2db5ac8 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_arbiter.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +module = 'arbiter' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + PORTS = 32 + TYPE = "PRIORITY" + BLOCK = "REQUEST" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + request = Signal(intbv(0)[PORTS:]) + acknowledge = Signal(intbv(0)[PORTS:]) + + # Outputs + grant = Signal(intbv(0)[PORTS:]) + grant_valid = Signal(bool(0)) + grant_encoded = Signal(intbv(0)[5:]) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + request=request, + acknowledge=acknowledge, + + grant=grant, + grant_valid=grant_valid, + grant_encoded=grant_encoded + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + print("test 1: one bit") + current_test.next = 1 + + yield clk.posedge + + for i in range(32): + l = [i] + k = 0 + for y in l: + k = k | 1 << y + request.next = k + yield clk.posedge + request.next = 0 + yield clk.posedge + + assert grant == 1 << i + assert grant_encoded == i + + yield clk.posedge + + yield delay(100) + + yield clk.posedge + + print("test 2: two bits") + current_test.next = 2 + + for i in range(32): + for j in range(32): + l = [i, j] + k = 0 + for y in l: + k = k | 1 << y + request.next = k + yield clk.posedge + request.next = 0 + yield clk.posedge + + assert grant == 1 << max(l) + assert grant_encoded == max(l) + + request.next = 0 + + yield clk.posedge + + print("test 3: five bits") + current_test.next = 3 + + for i in range(32): + l = [(i*x) % 32 for x in [1,2,3,4,5]] + k = 0 + for y in l: + k = k | 1 << y + request.next = k + yield clk.posedge + request.next = 0 + yield clk.posedge + + assert grant == 1 << max(l) + assert grant_encoded == max(l) + + prev = int(grant_encoded) + + yield clk.posedge + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_arbiter.v b/fpga/lib/eth/lib/axis/tb/test_arbiter.v new file mode 100644 index 000000000..e11ee9675 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_arbiter.v @@ -0,0 +1,87 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for arbiter + */ +module test_arbiter; + +// Parameters +localparam PORTS = 32; +localparam TYPE = "PRIORITY"; +localparam BLOCK = "REQUEST"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [PORTS-1:0] request = 0; +reg [PORTS-1:0] acknowledge = 0; + +// Outputs +wire [PORTS-1:0] grant; +wire grant_valid; +wire [$clog2(PORTS)-1:0] grant_encoded; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + request, + acknowledge + ); + $to_myhdl( + grant, + grant_valid, + grant_encoded + ); + + // dump file + $dumpfile("test_arbiter.lxt"); + $dumpvars(0, test_arbiter); +end + +arbiter #( + .PORTS(PORTS), + .TYPE(TYPE), + .BLOCK(BLOCK) +) +UUT ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_arbiter_rr.py b/fpga/lib/eth/lib/axis/tb/test_arbiter_rr.py new file mode 100755 index 000000000..5624e41f8 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_arbiter_rr.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +module = 'arbiter' +testbench = 'test_%s_rr' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + PORTS = 32 + TYPE = "ROUND_ROBIN" + BLOCK = "REQUEST" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + request = Signal(intbv(0)[PORTS:]) + acknowledge = Signal(intbv(0)[PORTS:]) + + # Outputs + grant = Signal(intbv(0)[PORTS:]) + grant_valid = Signal(bool(0)) + grant_encoded = Signal(intbv(0)[5:]) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + request=request, + acknowledge=acknowledge, + + grant=grant, + grant_valid=grant_valid, + grant_encoded=grant_encoded + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + prev = 0 + + print("test 1: one bit") + current_test.next = 1 + + yield clk.posedge + + for i in range(32): + l = [i] + k = 0 + for y in l: + k = k | 1 << y + request.next = k + yield clk.posedge + request.next = 0 + yield clk.posedge + + # emulate round robin + l2 = [x for x in l if x < prev] + if len(l2) == 0: + l2 = l + g = max(l2) + + assert grant == 1 << g + assert grant_encoded == g + + prev = int(grant_encoded) + + yield clk.posedge + + yield delay(100) + + yield clk.posedge + + print("test 2: cycle") + current_test.next = 2 + + for i in range(32): + l = [0, 5, 10, 15, 20, 25, 30] + k = 0 + for y in l: + k = k | 1 << y + request.next = k + yield clk.posedge + request.next = 0 + yield clk.posedge + + # emulate round robin + l2 = [x for x in l if x < prev] + if len(l2) == 0: + l2 = l + g = max(l2) + + assert grant == 1 << g + assert grant_encoded == g + + prev = int(grant_encoded) + + yield clk.posedge + + yield delay(100) + + yield clk.posedge + + print("test 3: two bits") + current_test.next = 3 + + for i in range(32): + for j in range(32): + l = [i, j] + k = 0 + for y in l: + k = k | 1 << y + request.next = k + yield clk.posedge + request.next = 0 + yield clk.posedge + + # emulate round robin + l2 = [x for x in l if x < prev] + if len(l2) == 0: + l2 = l + g = max(l2) + + assert grant == 1 << g + assert grant_encoded == g + + prev = int(grant_encoded) + + yield clk.posedge + + yield delay(100) + + yield clk.posedge + + print("test 4: five bits") + current_test.next = 4 + + for i in range(32): + l = [(i*x) % 32 for x in [1,2,3,4,5]] + k = 0 + for y in l: + k = k | 1 << y + request.next = k + yield clk.posedge + request.next = 0 + yield clk.posedge + + # emulate round robin + l2 = [x for x in l if x < prev] + if len(l2) == 0: + l2 = l + g = max(l2) + + assert grant == 1 << g + assert grant_encoded == g + + prev = int(grant_encoded) + + yield clk.posedge + + yield delay(100) + + yield clk.posedge + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_arbiter_rr.v b/fpga/lib/eth/lib/axis/tb/test_arbiter_rr.v new file mode 100644 index 000000000..5ddd2da02 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_arbiter_rr.v @@ -0,0 +1,87 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for arbiter + */ +module test_arbiter_rr; + +// Parameters +localparam PORTS = 32; +localparam TYPE = "ROUND_ROBIN"; +localparam BLOCK = "REQUEST"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [PORTS-1:0] request = 0; +reg [PORTS-1:0] acknowledge = 0; + +// Outputs +wire [PORTS-1:0] grant; +wire grant_valid; +wire [$clog2(PORTS)-1:0] grant_encoded; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + request, + acknowledge + ); + $to_myhdl( + grant, + grant_valid, + grant_encoded + ); + + // dump file + $dumpfile("test_arbiter_rr.lxt"); + $dumpvars(0, test_arbiter_rr); +end + +arbiter #( + .PORTS(PORTS), + .TYPE(TYPE), + .BLOCK(BLOCK) +) +UUT ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_adapter_64_8.py b/fpga/lib/eth/lib/axis/tb/test_axis_adapter_64_8.py new file mode 100755 index 000000000..7c1b7b48d --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_adapter_64_8.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_adapter' +testbench = 'test_%s_64_8' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_DATA_WIDTH = 64 + S_KEEP_ENABLE = (S_DATA_WIDTH>8) + S_KEEP_WIDTH = (S_DATA_WIDTH/8) + M_DATA_WIDTH = 8 + M_KEEP_ENABLE = (M_DATA_WIDTH>8) + M_KEEP_WIDTH = (M_DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[S_DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[S_KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[M_DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[M_KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=1, + dest=1, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=1, + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=1, + last_cycle_user=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_adapter_64_8.v b/fpga/lib/eth/lib/axis/tb/test_axis_adapter_64_8.v new file mode 100644 index 000000000..3afe0ef07 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_adapter_64_8.v @@ -0,0 +1,140 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_adapter + */ +module test_axis_adapter_64_8; + +// Parameters +parameter S_DATA_WIDTH = 64; +parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8); +parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8); +parameter M_DATA_WIDTH = 8; +parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8); +parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [M_DATA_WIDTH-1:0] m_axis_tdata; +wire [M_KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_adapter_64_8.lxt"); + $dumpvars(0, test_axis_adapter_64_8); +end + +axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_adapter_8_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_adapter_8_64.py new file mode 100755 index 000000000..43c2a46e0 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_adapter_8_64.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_adapter' +testbench = 'test_%s_8_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_DATA_WIDTH = 8 + S_KEEP_ENABLE = (S_DATA_WIDTH>8) + S_KEEP_WIDTH = (S_DATA_WIDTH/8) + M_DATA_WIDTH = 64 + M_KEEP_ENABLE = (M_DATA_WIDTH>8) + M_KEEP_WIDTH = (M_DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[S_DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[S_KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[M_DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[M_KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=1, + dest=1, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=1, + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=1, + last_cycle_user=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_adapter_8_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_adapter_8_64.v new file mode 100644 index 000000000..dc01a0ae0 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_adapter_8_64.v @@ -0,0 +1,140 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_adapter + */ +module test_axis_adapter_8_64; + +// Parameters +parameter S_DATA_WIDTH = 8; +parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8); +parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8); +parameter M_DATA_WIDTH = 64; +parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8); +parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [M_DATA_WIDTH-1:0] m_axis_tdata; +wire [M_KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_adapter_8_64.lxt"); + $dumpvars(0, test_axis_adapter_8_64); +end + +axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4.py b/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4.py new file mode 100755 index 000000000..d5d055d67 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4.py @@ -0,0 +1,476 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_arb_mux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/arbiter.v") +srcs.append("../rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + ARB_TYPE = "PRIORITY" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(0)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_axis_tready_list = [s_axis_tready(i) for i in range(S_COUNT)] + + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tready=s_axis_tready_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: port 0") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x00\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: port 1") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x00\x52\x53\x54\x55' + + b'\x80\x01' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=2, + dest=1 + ) + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x00\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x00\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=2 + ) + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x01\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x02\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x01\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x02\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x01\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x02\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: back-to-back packets, different ports, arbitration test") + current_test.next = 7 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x01\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x02\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + yield clk.posedge + + yield delay(800) + yield clk.posedge + source_list[1].send(test_frame1) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4.v b/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4.v new file mode 100644 index 000000000..ae929053d --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4.v @@ -0,0 +1,142 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_arb_mux + */ +module test_axis_arb_mux_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter ARB_TYPE = "PRIORITY"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; + +reg m_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_axis_tready; + +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_arb_mux_4.lxt"); + $dumpvars(0, test_axis_arb_mux_4); +end + +axis_arb_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4_64.py new file mode 100755 index 000000000..4d4889f0c --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4_64.py @@ -0,0 +1,476 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_arb_mux' +testbench = 'test_%s_4_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/arbiter.v") +srcs.append("../rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + ARB_TYPE = "PRIORITY" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(0)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_axis_tready_list = [s_axis_tready(i) for i in range(S_COUNT)] + + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tready=s_axis_tready_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: port 0") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x00\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: port 1") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x00\x52\x53\x54\x55' + + b'\x80\x01' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=2, + dest=1 + ) + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x00\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x00\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=2 + ) + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x01\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x02\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x01\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x02\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x01\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x02\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: back-to-back packets, different ports, arbitration test") + current_test.next = 7 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x01\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x02\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + yield clk.posedge + + yield delay(100) + yield clk.posedge + source_list[1].send(test_frame1) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4_64.v new file mode 100644 index 000000000..107a4de12 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_arb_mux_4_64.v @@ -0,0 +1,142 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_arb_mux + */ +module test_axis_arb_mux_4_64; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter ARB_TYPE = "PRIORITY"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; + +reg m_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_axis_tready; + +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_arb_mux_4_64.lxt"); + $dumpvars(0, test_axis_arb_mux_4_64); +end + +axis_arb_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo.py b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo.py new file mode 100755 index 000000000..67989257e --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo.py @@ -0,0 +1,536 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_async_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 2 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 0 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 0 + DROP_WHEN_FULL = 0 + + # Inputs + async_rst = Signal(bool(0)) + s_clk = Signal(bool(0)) + m_clk = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + s_clk, + async_rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + m_clk, + async_rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + async_rst=async_rst, + s_clk=s_clk, + m_clk=m_clk, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def s_clkgen(): + s_clk.next = not s_clk + + @always(delay(5)) + def m_clkgen(): + m_clk.next = not m_clk + + @instance + def check(): + yield delay(100) + yield s_clk.posedge + async_rst.next = 1 + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + async_rst.next = 0 + yield s_clk.posedge + yield delay(100) + yield s_clk.posedge + + yield s_clk.posedge + + yield s_clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield s_clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + + source.send(test_frame) + yield s_clk.posedge + + yield delay(64) + yield s_clk.posedge + source_pause.next = True + yield delay(32) + yield s_clk.posedge + source_pause.next = False + + yield delay(64) + yield m_clk.posedge + sink_pause.next = True + yield delay(32) + yield m_clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + test_frame1.id = 4 + test_frame1.dest = 1 + test_frame2.id = 4 + test_frame2.dest = 2 + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield s_clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield s_clk.posedge + yield s_clk.posedge + source_pause.next = False + yield s_clk.posedge + source_pause.next = True + yield s_clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield s_clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield m_clk.posedge + yield m_clk.posedge + yield m_clk.posedge + sink_pause.next = False + yield m_clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield s_clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + yield s_clk.posedge + print("test 8: initial sink pause") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + b'\x01\x02\x03', + id=8, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 9: initial sink pause, assert reset") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + b'\x01\x02\x03', + id=9, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + + async_rst.next = 1 + yield s_clk.posedge + async_rst.next = 0 + + sink_pause.next = 0 + + yield delay(100) + + yield m_clk.posedge + yield m_clk.posedge + yield m_clk.posedge + + assert sink.empty() + + yield delay(100) + + yield s_clk.posedge + print("test 10: backpressure test") + current_test.next = 10 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=10, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + source.send(test_frame) + yield delay(5000) + yield s_clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 11: many small packets") + current_test.next = 11 + + test_frame = axis_ep.AXIStreamFrame( + b'\xAA', + id=11, + dest=1 + ) + + for k in range(64): + source.send(test_frame) + + for k in range(64): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo.v b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo.v new file mode 100644 index 000000000..9851813e7 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo.v @@ -0,0 +1,159 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_async_fifo + */ +module test_axis_async_fifo; + +// Parameters +parameter ADDR_WIDTH = 2; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 0; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 0; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg async_rst = 0; +reg s_clk = 0; +reg m_clk = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + async_rst, + s_clk, + m_clk, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_async_fifo.lxt"); + $dumpvars(0, test_axis_async_fifo); +end + +axis_async_fifo #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + // Common reset + .async_rst(async_rst), + // AXI input + .s_clk(s_clk), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_clk(m_clk), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_64.py new file mode 100755 index 000000000..44925adf4 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_64.py @@ -0,0 +1,536 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_async_fifo' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 2 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 0 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 0 + DROP_WHEN_FULL = 0 + + # Inputs + async_rst = Signal(bool(0)) + s_clk = Signal(bool(0)) + m_clk = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + s_clk, + async_rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + m_clk, + async_rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + async_rst=async_rst, + s_clk=s_clk, + m_clk=m_clk, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def s_clkgen(): + s_clk.next = not s_clk + + @always(delay(5)) + def m_clkgen(): + m_clk.next = not m_clk + + @instance + def check(): + yield delay(100) + yield s_clk.posedge + async_rst.next = 1 + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + async_rst.next = 0 + yield s_clk.posedge + yield delay(100) + yield s_clk.posedge + + yield s_clk.posedge + + yield s_clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield s_clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=3, + dest=1 + ) + + source.send(test_frame) + yield s_clk.posedge + + yield delay(64) + yield s_clk.posedge + source_pause.next = True + yield delay(32) + yield s_clk.posedge + source_pause.next = False + + yield delay(64) + yield m_clk.posedge + sink_pause.next = True + yield delay(32) + yield m_clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame1.id = 4 + test_frame1.dest = 1 + test_frame2.id = 4 + test_frame2.dest = 2 + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield s_clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield s_clk.posedge + yield s_clk.posedge + source_pause.next = False + yield s_clk.posedge + source_pause.next = True + yield s_clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield s_clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield m_clk.posedge + yield m_clk.posedge + yield m_clk.posedge + sink_pause.next = False + yield m_clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield s_clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + yield s_clk.posedge + print("test 8: initial sink pause") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(24)), + id=8, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 9: initial sink pause, assert reset") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(24)), + id=9, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + + async_rst.next = 1 + yield s_clk.posedge + async_rst.next = 0 + + sink_pause.next = 0 + + yield delay(100) + + yield m_clk.posedge + yield m_clk.posedge + yield m_clk.posedge + + assert sink.empty() + + yield delay(100) + + yield s_clk.posedge + print("test 10: backpressure test") + current_test.next = 10 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=10, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + source.send(test_frame) + yield delay(1000) + yield s_clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 11: many small packets") + current_test.next = 11 + + test_frame = axis_ep.AXIStreamFrame( + b'\xAA', + id=11, + dest=1 + ) + + for k in range(64): + source.send(test_frame) + + for k in range(64): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_64.v new file mode 100644 index 000000000..5605f7ab8 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_64.v @@ -0,0 +1,152 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_async_fifo + */ +module test_axis_async_fifo_64; + +// Parameters +parameter ADDR_WIDTH = 2; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 0; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 0; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg async_rst = 0; +reg s_clk = 0; +reg m_clk = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + async_rst, + s_clk, + m_clk, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_async_fifo_64.lxt"); + $dumpvars(0, test_axis_async_fifo_64); +end + +axis_async_fifo #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + // Common reset + .async_rst(async_rst), + // AXI input + .s_clk(s_clk), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_clk(m_clk), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_64_8.py b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_64_8.py new file mode 100755 index 000000000..9f1cca6a7 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_64_8.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_async_fifo_adapter' +testbench = 'test_%s_64_8' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_adapter.v") +srcs.append("../rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 2 + S_DATA_WIDTH = 64 + S_KEEP_ENABLE = (S_DATA_WIDTH>8) + S_KEEP_WIDTH = (S_DATA_WIDTH/8) + M_DATA_WIDTH = 8 + M_KEEP_ENABLE = (M_DATA_WIDTH>8) + M_KEEP_WIDTH = (M_DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 0 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 0 + DROP_WHEN_FULL = 0 + + # Inputs + s_clk = Signal(bool(0)) + s_rst = Signal(bool(0)) + m_clk = Signal(bool(0)) + m_rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[S_DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[S_KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[M_DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[M_KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + s_clk, + s_rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + m_clk, + m_rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + s_clk=s_clk, + s_rst=s_rst, + m_clk=m_clk, + m_rst=m_rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def s_clkgen(): + s_clk.next = not s_clk + + @always(delay(5)) + def m_clkgen(): + m_clk.next = not m_clk + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield s_clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_axis_tvalid: + yield s_clk.posedge + yield s_clk.posedge + source_pause.next = False + yield s_clk.posedge + source_pause.next = True + yield s_clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + sink_pause.next = False + yield s_clk.posedge + + @instance + def check(): + yield delay(100) + yield s_clk.posedge + s_rst.next = 1 + m_rst.next = 1 + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + s_rst.next = 0 + m_rst.next = 0 + yield s_clk.posedge + yield delay(100) + yield s_clk.posedge + + for payload_len in range(1,18): + yield s_clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=1, + dest=1, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield s_clk.posedge + yield s_clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield s_clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=1, + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + yield s_clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield s_clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=1, + last_cycle_user=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + yield s_clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_64_8.v b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_64_8.v new file mode 100644 index 000000000..22e206694 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_64_8.v @@ -0,0 +1,165 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for axis_async_fifo_adapter + */ +module test_axis_async_fifo_adapter_64_8; + +// Parameters +parameter ADDR_WIDTH = 2; +parameter S_DATA_WIDTH = 64; +parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8); +parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8); +parameter M_DATA_WIDTH = 8; +parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8); +parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 0; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 0; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg s_clk = 0; +reg s_rst = 0; +reg m_clk = 0; +reg m_rst = 0; +reg [7:0] current_test = 0; + +reg [S_DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [M_DATA_WIDTH-1:0] m_axis_tdata; +wire [M_KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + s_clk, + s_rst, + m_clk, + m_rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_async_fifo_adapter_64_8.lxt"); + $dumpvars(0, test_axis_async_fifo_adapter_64_8); +end + +axis_async_fifo_adapter #( + .ADDR_WIDTH(ADDR_WIDTH), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + // AXI input + .s_clk(s_clk), + .s_rst(s_rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_clk(m_clk), + .m_rst(m_rst), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_8_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_8_64.py new file mode 100755 index 000000000..d04ab1312 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_8_64.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_async_fifo_adapter' +testbench = 'test_%s_8_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_adapter.v") +srcs.append("../rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 2 + S_DATA_WIDTH = 8 + S_KEEP_ENABLE = (S_DATA_WIDTH>8) + S_KEEP_WIDTH = (S_DATA_WIDTH/8) + M_DATA_WIDTH = 64 + M_KEEP_ENABLE = (M_DATA_WIDTH>8) + M_KEEP_WIDTH = (M_DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 0 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 0 + DROP_WHEN_FULL = 0 + + # Inputs + s_clk = Signal(bool(0)) + s_rst = Signal(bool(0)) + m_clk = Signal(bool(0)) + m_rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[S_DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[S_KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[M_DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[M_KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + s_clk, + s_rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + m_clk, + m_rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + s_clk=s_clk, + s_rst=s_rst, + m_clk=m_clk, + m_rst=m_rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def s_clkgen(): + s_clk.next = not s_clk + + @always(delay(5)) + def m_clkgen(): + m_clk.next = not m_clk + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield s_clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_axis_tvalid: + yield s_clk.posedge + yield s_clk.posedge + source_pause.next = False + yield s_clk.posedge + source_pause.next = True + yield s_clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + sink_pause.next = False + yield s_clk.posedge + + @instance + def check(): + yield delay(100) + yield s_clk.posedge + s_rst.next = 1 + m_rst.next = 1 + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + s_rst.next = 0 + m_rst.next = 0 + yield s_clk.posedge + yield delay(100) + yield s_clk.posedge + + for payload_len in range(1,18): + yield s_clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=1, + dest=1, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield s_clk.posedge + yield s_clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield s_clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=1, + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + yield s_clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield s_clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=1, + last_cycle_user=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + yield s_clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_8_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_8_64.v new file mode 100644 index 000000000..ddb4a1f35 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_fifo_adapter_8_64.v @@ -0,0 +1,165 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for axis_async_fifo_adapter + */ +module test_axis_async_fifo_adapter_8_64; + +// Parameters +parameter ADDR_WIDTH = 2; +parameter S_DATA_WIDTH = 8; +parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8); +parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8); +parameter M_DATA_WIDTH = 64; +parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8); +parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 0; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 0; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg s_clk = 0; +reg s_rst = 0; +reg m_clk = 0; +reg m_rst = 0; +reg [7:0] current_test = 0; + +reg [S_DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [M_DATA_WIDTH-1:0] m_axis_tdata; +wire [M_KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + s_clk, + s_rst, + m_clk, + m_rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_async_fifo_adapter_8_64.lxt"); + $dumpvars(0, test_axis_async_fifo_adapter_8_64); +end + +axis_async_fifo_adapter #( + .ADDR_WIDTH(ADDR_WIDTH), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + // AXI input + .s_clk(s_clk), + .s_rst(s_rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_clk(m_clk), + .m_rst(m_rst), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo.py b/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo.py new file mode 100755 index 000000000..367efd19d --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo.py @@ -0,0 +1,704 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_async_fifo' +testbench = 'test_axis_async_frame_fifo' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 9 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 1 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 1 + DROP_WHEN_FULL = 0 + + # Inputs + async_rst = Signal(bool(0)) + s_clk = Signal(bool(0)) + m_clk = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + s_status_overflow = Signal(bool(0)) + s_status_bad_frame = Signal(bool(0)) + s_status_good_frame = Signal(bool(0)) + m_status_overflow = Signal(bool(0)) + m_status_bad_frame = Signal(bool(0)) + m_status_good_frame = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + s_clk, + async_rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + m_clk, + async_rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + async_rst=async_rst, + s_clk=s_clk, + m_clk=m_clk, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + s_status_overflow=s_status_overflow, + s_status_bad_frame=s_status_bad_frame, + s_status_good_frame=s_status_good_frame, + m_status_overflow=m_status_overflow, + m_status_bad_frame=m_status_bad_frame, + m_status_good_frame=m_status_good_frame + ) + + @always(delay(4)) + def s_clkgen(): + s_clk.next = not s_clk + + @always(delay(5)) + def m_clkgen(): + m_clk.next = not m_clk + + s_status_overflow_asserted = Signal(bool(0)) + s_status_bad_frame_asserted = Signal(bool(0)) + s_status_good_frame_asserted = Signal(bool(0)) + m_status_overflow_asserted = Signal(bool(0)) + m_status_bad_frame_asserted = Signal(bool(0)) + m_status_good_frame_asserted = Signal(bool(0)) + + @always(s_clk.posedge) + def monitor_1(): + if (s_status_overflow): + s_status_overflow_asserted.next = 1 + if (s_status_bad_frame): + s_status_bad_frame_asserted.next = 1 + if (s_status_good_frame): + s_status_good_frame_asserted.next = 1 + + @always(m_clk.posedge) + def monitor_2(): + if (m_status_overflow): + m_status_overflow_asserted.next = 1 + if (m_status_bad_frame): + m_status_bad_frame_asserted.next = 1 + if (m_status_good_frame): + m_status_good_frame_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield s_clk.posedge + async_rst.next = 1 + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + async_rst.next = 0 + yield s_clk.posedge + yield delay(100) + yield s_clk.posedge + + yield s_clk.posedge + + yield s_clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield s_clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield s_clk.posedge + + yield delay(64) + yield s_clk.posedge + source_pause.next = True + yield delay(32) + yield s_clk.posedge + source_pause.next = False + + yield delay(64) + yield m_clk.posedge + sink_pause.next = True + yield delay(32) + yield m_clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield s_clk.posedge + yield s_clk.posedge + source_pause.next = False + yield s_clk.posedge + source_pause.next = True + yield s_clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=6 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield m_clk.posedge + yield m_clk.posedge + yield m_clk.posedge + sink_pause.next = False + yield m_clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield s_clk.posedge + + yield delay(1000) + + assert sink.empty() + + assert not s_status_overflow_asserted + assert s_status_bad_frame_asserted + assert not s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert m_status_bad_frame_asserted + assert not m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 8: single packet overflow") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256))*2, + id=8, + dest=1 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield s_clk.posedge + + yield delay(10000) + + assert sink.empty() + + assert s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert not s_status_good_frame_asserted + assert m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert not m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 9: initial sink pause") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + b'\x01\x02\x03', + id=9, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 10: initial sink pause, assert reset") + current_test.next = 10 + + test_frame = axis_ep.AXIStreamFrame( + b'\x01\x02\x03', + id=10, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + + async_rst.next = 1 + yield s_clk.posedge + async_rst.next = 0 + + sink_pause.next = 0 + + yield delay(100) + + yield m_clk.posedge + yield m_clk.posedge + yield m_clk.posedge + + assert sink.empty() + + yield delay(100) + + yield s_clk.posedge + print("test 11: backpressure test") + current_test.next = 11 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=11, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + source.send(test_frame) + yield delay(5000) + yield s_clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 12: many small packets") + current_test.next = 12 + + test_frame = axis_ep.AXIStreamFrame( + b'\xAA', + id=12, + dest=1 + ) + + for k in range(64): + source.send(test_frame) + + for k in range(64): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo.v b/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo.v new file mode 100644 index 000000000..4f8e7829e --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo.v @@ -0,0 +1,171 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_async_fifo + */ +module test_axis_async_frame_fifo; + +// Parameters +parameter ADDR_WIDTH = 9; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 1; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 1; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg async_rst = 0; +reg s_clk = 0; +reg m_clk = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire s_status_overflow; +wire s_status_bad_frame; +wire s_status_good_frame; +wire m_status_overflow; +wire m_status_bad_frame; +wire m_status_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + async_rst, + s_clk, + m_clk, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + s_status_overflow, + s_status_bad_frame, + s_status_good_frame, + m_status_overflow, + m_status_bad_frame, + m_status_good_frame + ); + + // dump file + $dumpfile("test_axis_async_frame_fifo.lxt"); + $dumpvars(0, test_axis_async_frame_fifo); +end + +axis_async_fifo #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + // Common reset + .async_rst(async_rst), + // AXI input + .s_clk(s_clk), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_clk(m_clk), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .s_status_overflow(s_status_overflow), + .s_status_bad_frame(s_status_bad_frame), + .s_status_good_frame(s_status_good_frame), + .m_status_overflow(m_status_overflow), + .m_status_bad_frame(m_status_bad_frame), + .m_status_good_frame(m_status_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo_64.py new file mode 100755 index 000000000..fbd5a76d2 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo_64.py @@ -0,0 +1,704 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_async_fifo' +testbench = 'test_axis_async_frame_fifo_64' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 6 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 1 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 1 + DROP_WHEN_FULL = 0 + + # Inputs + async_rst = Signal(bool(0)) + s_clk = Signal(bool(0)) + m_clk = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + s_status_overflow = Signal(bool(0)) + s_status_bad_frame = Signal(bool(0)) + s_status_good_frame = Signal(bool(0)) + m_status_overflow = Signal(bool(0)) + m_status_bad_frame = Signal(bool(0)) + m_status_good_frame = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + s_clk, + async_rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + m_clk, + async_rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + async_rst=async_rst, + s_clk=s_clk, + m_clk=m_clk, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + s_status_overflow=s_status_overflow, + s_status_bad_frame=s_status_bad_frame, + s_status_good_frame=s_status_good_frame, + m_status_overflow=m_status_overflow, + m_status_bad_frame=m_status_bad_frame, + m_status_good_frame=m_status_good_frame + ) + + @always(delay(4)) + def s_clkgen(): + s_clk.next = not s_clk + + @always(delay(5)) + def m_clkgen(): + m_clk.next = not m_clk + + s_status_overflow_asserted = Signal(bool(0)) + s_status_bad_frame_asserted = Signal(bool(0)) + s_status_good_frame_asserted = Signal(bool(0)) + m_status_overflow_asserted = Signal(bool(0)) + m_status_bad_frame_asserted = Signal(bool(0)) + m_status_good_frame_asserted = Signal(bool(0)) + + @always(s_clk.posedge) + def monitor_1(): + if (s_status_overflow): + s_status_overflow_asserted.next = 1 + if (s_status_bad_frame): + s_status_bad_frame_asserted.next = 1 + if (s_status_good_frame): + s_status_good_frame_asserted.next = 1 + + @always(m_clk.posedge) + def monitor_2(): + if (m_status_overflow): + m_status_overflow_asserted.next = 1 + if (m_status_bad_frame): + m_status_bad_frame_asserted.next = 1 + if (m_status_good_frame): + m_status_good_frame_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield s_clk.posedge + async_rst.next = 1 + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + async_rst.next = 0 + yield s_clk.posedge + yield delay(100) + yield s_clk.posedge + + yield s_clk.posedge + + yield s_clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield s_clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=3, + dest=1 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield s_clk.posedge + + yield delay(64) + yield s_clk.posedge + source_pause.next = True + yield delay(32) + yield s_clk.posedge + source_pause.next = False + + yield delay(64) + yield m_clk.posedge + sink_pause.next = True + yield delay(32) + yield m_clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield s_clk.posedge + yield s_clk.posedge + source_pause.next = False + yield s_clk.posedge + source_pause.next = True + yield s_clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=6 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + yield s_clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield m_clk.posedge + yield m_clk.posedge + yield m_clk.posedge + sink_pause.next = False + yield m_clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield s_clk.posedge + + yield delay(1000) + + assert sink.empty() + + assert not s_status_overflow_asserted + assert s_status_bad_frame_asserted + assert not s_status_good_frame_asserted + assert not m_status_overflow_asserted + assert m_status_bad_frame_asserted + assert not m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 8: single packet overflow") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256))*2, + id=8, + dest=1 + ) + + s_status_overflow_asserted.next = 0 + s_status_bad_frame_asserted.next = 0 + s_status_good_frame_asserted.next = 0 + m_status_overflow_asserted.next = 0 + m_status_bad_frame_asserted.next = 0 + m_status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield s_clk.posedge + + yield delay(10000) + + assert sink.empty() + + assert s_status_overflow_asserted + assert not s_status_bad_frame_asserted + assert not s_status_good_frame_asserted + assert m_status_overflow_asserted + assert not m_status_bad_frame_asserted + assert not m_status_good_frame_asserted + + yield delay(100) + + yield s_clk.posedge + print("test 9: initial sink pause") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(24)), + id=9, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 10: initial sink pause, assert reset") + current_test.next = 10 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(24)), + id=10, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + yield s_clk.posedge + + async_rst.next = 1 + yield s_clk.posedge + async_rst.next = 0 + + sink_pause.next = 0 + + yield delay(100) + + yield m_clk.posedge + yield m_clk.posedge + yield m_clk.posedge + + assert sink.empty() + + yield delay(100) + + yield s_clk.posedge + print("test 11: backpressure test") + current_test.next = 11 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=11, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + source.send(test_frame) + yield delay(1000) + yield s_clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield s_clk.posedge + print("test 12: many small packets") + current_test.next = 12 + + test_frame = axis_ep.AXIStreamFrame( + b'\xAA', + id=12, + dest=1 + ) + + for k in range(64): + source.send(test_frame) + + for k in range(64): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo_64.v new file mode 100644 index 000000000..ada5bc5cb --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_async_frame_fifo_64.v @@ -0,0 +1,171 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_async_fifo + */ +module test_axis_async_frame_fifo_64; + +// Parameters +parameter ADDR_WIDTH = 6; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 1; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 1; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg async_rst = 0; +reg s_clk = 0; +reg m_clk = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire s_status_overflow; +wire s_status_bad_frame; +wire s_status_good_frame; +wire m_status_overflow; +wire m_status_bad_frame; +wire m_status_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + async_rst, + s_clk, + m_clk, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + s_status_overflow, + s_status_bad_frame, + s_status_good_frame, + m_status_overflow, + m_status_bad_frame, + m_status_good_frame + ); + + // dump file + $dumpfile("test_axis_async_frame_fifo_64.lxt"); + $dumpvars(0, test_axis_async_frame_fifo_64); +end + +axis_async_fifo #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + // Common reset + .async_rst(async_rst), + // AXI input + .s_clk(s_clk), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_clk(m_clk), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .s_status_overflow(s_status_overflow), + .s_status_bad_frame(s_status_bad_frame), + .s_status_good_frame(s_status_good_frame), + .m_status_overflow(m_status_overflow), + .m_status_bad_frame(m_status_bad_frame), + .m_status_good_frame(m_status_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_broadcast_4.py b/fpga/lib/eth/lib/axis/tb/test_axis_broadcast_4.py new file mode 100755 index 000000000..1a29c1d05 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_broadcast_4.py @@ -0,0 +1,540 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep +import math + +module = 'axis_broadcast' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + M_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + m_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_axis_tready = ConcatSignal(*reversed(m_axis_tready_list)) + + # Outputs + s_axis_tready = Signal(bool(0)) + + m_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_axis_tdata_list = [m_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_axis_tkeep_list = [m_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_axis_tvalid_list = [m_axis_tvalid(i) for i in range(M_COUNT)] + m_axis_tlast_list = [m_axis_tlast(i) for i in range(M_COUNT)] + m_axis_tid_list = [m_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_axis_tdest_list = [m_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_axis_tuser_list = [m_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + for k in range(M_COUNT): + s = axis_ep.AXIStreamSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + tdata=m_axis_tdata_list[k], + tkeep=m_axis_tkeep_list[k], + tvalid=m_axis_tvalid_list[k], + tready=m_axis_tready_list[k], + tlast=m_axis_tlast_list[k], + tid=m_axis_tid_list[k], + tdest=m_axis_tdest_list[k], + tuser=m_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1 + ) + + source.send(test_frame) + + for sink in sink_list: + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=1 + ) + + source.send(test_frame) + + for sink in sink_list: + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield delay(32) + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + + for sink in sink_list: + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + for sink in sink_list: + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + for sink in sink_list: + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + + for sink in sink_list: + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: alternate pause individual sinks") + current_test.next = 7 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + for pause in sink_pause_list: + pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + pause.next = False + yield clk.posedge + + for sink in sink_list: + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 8: alternate un-pause individual sinks") + current_test.next = 8 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=8, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=8, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + for pause in sink_pause_list: + pause.next = True + + while s_axis_tvalid or m_axis_tvalid: + for pause in sink_pause_list: + yield clk.posedge + yield clk.posedge + yield clk.posedge + pause.next = False + yield clk.posedge + pause.next = True + + for pause in sink_pause_list: + pause.next = False + + for sink in sink_list: + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 9: tuser assert") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=9, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + for sink in sink_list: + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_broadcast_4.v b/fpga/lib/eth/lib/axis/tb/test_axis_broadcast_4.v new file mode 100644 index 000000000..e4f707697 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_broadcast_4.v @@ -0,0 +1,140 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for axis_broadcast + */ +module test_axis_broadcast_4; + +// Parameters +parameter M_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; + +reg [M_COUNT-1:0] m_axis_tready = 0; + +// Outputs +wire s_axis_tready; + +wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep; +wire [M_COUNT-1:0] m_axis_tvalid; +wire [M_COUNT-1:0] m_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_broadcast_4.lxt"); + $dumpvars(0, test_axis_broadcast_4); +end + +axis_broadcast #( + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI outputs + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_cobs_decode.py b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_decode.py new file mode 100755 index 000000000..889dba45d --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_decode.py @@ -0,0 +1,452 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_cobs_decode' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def cobs_encode(block): + block = bytearray(block) + enc = bytearray() + + seg = bytearray() + code = 1 + + new_data = True + + for b in block: + if b == 0: + enc.append(code) + enc.extend(seg) + code = 1 + seg = bytearray() + new_data = True + else: + code += 1 + seg.append(b) + new_data = True + if code == 255: + enc.append(code) + enc.extend(seg) + code = 1 + seg = bytearray() + new_data = False + + if new_data: + enc.append(code) + enc.extend(seg) + + return bytes(enc) + +def cobs_decode(block): + block = bytearray(block) + dec = bytearray() + + code = 0 + + i = 0 + + if 0 in block: + return None + + while i < len(block): + code = block[i] + i += 1 + if i+code-1 > len(block): + return None + dec.extend(block[i:i+code-1]) + i += code-1 + if code < 255 and i < len(block): + dec.append(0) + + return bytes(dec) + +def prbs31(state = 0x7fffffff): + while True: + for i in range(8): + if bool(state & 0x08000000) ^ bool(state & 0x40000000): + state = ((state & 0x3fffffff) << 1) | 1 + else: + state = (state & 0x3fffffff) << 1 + yield state & 0xff + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[8:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(bool(0)) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[8:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + i = 4 + while i > 0: + i = max(0, i-1) + if s_axis_tvalid or m_axis_tvalid or not source.empty(): + i = 4 + yield clk.posedge + + def wait_pause_source(): + i = 2 + while i > 0: + i = max(0, i-1) + if s_axis_tvalid or m_axis_tvalid or not source.empty(): + i = 2 + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + i = 2 + while i > 0: + i = max(0, i-1) + if s_axis_tvalid or m_axis_tvalid or not source.empty(): + i = 2 + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(1,33))+list(range(253,259))+[512]: + gen = prbs31() + for block in [bytearray([0]*payload_len), + bytearray([k%255+1 for k in range(payload_len)]), + b'\x00'+bytearray([k%255+1 for k in range(payload_len)])+b'\x00', + bytearray([next(gen) for i in range(payload_len)])]: + + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + enc = cobs_encode(block) + + test_frame = axis_ep.AXIStreamFrame(enc) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test packet, length %d, zero frame" % payload_len) + current_test.next = 2 + + enc = cobs_encode(block) + + test_frame = axis_ep.AXIStreamFrame(enc+b'\x00') + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, length %d" % payload_len) + current_test.next = 3 + + test_frame2 = axis_ep.AXIStreamFrame(enc) + test_frame1 = axis_ep.AXIStreamFrame(enc) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == block + assert not rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, length %d, zero frame" % payload_len) + current_test.next = 4 + + test_frame = axis_ep.AXIStreamFrame(enc+b'\x00'+enc+b'\x00') + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == block + assert not rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: tuser assert and bad frame, length %d" % payload_len) + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame(enc) + test_frame2 = axis_ep.AXIStreamFrame(enc+b'\x02') + test_frame3 = axis_ep.AXIStreamFrame(enc) + + test_frame1.last_cycle_user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + source.send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 6: tuser assert and bad frame, length %d, zero frame" % payload_len) + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame(enc+b'\x00') + test_frame2 = axis_ep.AXIStreamFrame(enc+b'\x02\x00') + test_frame3 = axis_ep.AXIStreamFrame(enc+b'\x00') + + test_frame1.last_cycle_user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + source.send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_cobs_decode.v b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_decode.v new file mode 100644 index 000000000..e4fe6aba4 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_decode.v @@ -0,0 +1,95 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for axis_cobs_decode + */ +module test_axis_cobs_decode; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] s_axis_tdata = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [7:0] m_axis_tdata; +wire m_axis_tvalid; +wire m_axis_tlast; +wire m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_cobs_decode.lxt"); + $dumpvars(0, test_axis_cobs_decode); +end + +axis_cobs_decode +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode.py b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode.py new file mode 100755 index 000000000..05b6e0ac8 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_cobs_encode' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def cobs_encode(block): + block = bytearray(block) + enc = bytearray() + + seg = bytearray() + code = 1 + + new_data = True + + for b in block: + if b == 0: + enc.append(code) + enc.extend(seg) + code = 1 + seg = bytearray() + new_data = True + else: + code += 1 + seg.append(b) + new_data = True + if code == 255: + enc.append(code) + enc.extend(seg) + code = 1 + seg = bytearray() + new_data = False + + if new_data: + enc.append(code) + enc.extend(seg) + + return bytes(enc) + +def cobs_decode(block): + block = bytearray(block) + dec = bytearray() + + code = 0 + + i = 0 + + if 0 in block: + return None + + while i < len(block): + code = block[i] + i += 1 + if i+code-1 > len(block): + return None + dec.extend(block[i:i+code-1]) + i += code-1 + if code < 255 and i < len(block): + dec.append(0) + + return bytes(dec) + +def prbs31(state = 0x7fffffff): + while True: + for i in range(8): + if bool(state & 0x08000000) ^ bool(state & 0x40000000): + state = ((state & 0x3fffffff) << 1) | 1 + else: + state = (state & 0x3fffffff) << 1 + yield state & 0xff + +def bench(): + + # Parameters + APPEND_ZERO = 0 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[8:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(bool(0)) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[8:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + i = 4 + while i > 0: + i = max(0, i-1) + if s_axis_tvalid or m_axis_tvalid or not source.empty(): + i = 4 + yield clk.posedge + + def wait_pause_source(): + i = 2 + while i > 0: + i = max(0, i-1) + if s_axis_tvalid or m_axis_tvalid or not source.empty(): + i = 2 + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + i = 2 + while i > 0: + i = max(0, i-1) + if s_axis_tvalid or m_axis_tvalid or not source.empty(): + i = 2 + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(1,33))+list(range(253,259))+[512]: + gen = prbs31() + for block in [bytearray([0]*payload_len), + bytearray([k%255+1 for k in range(payload_len)]), + b'\x00'+bytearray([k%255+1 for k in range(payload_len)])+b'\x00', + bytearray([next(gen) for i in range(payload_len)])]: + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + enc = cobs_encode(block) + + test_frame = axis_ep.AXIStreamFrame(block) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == enc + assert cobs_decode(rx_frame.data) == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame(block) + test_frame2 = axis_ep.AXIStreamFrame(block) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == enc + assert cobs_decode(rx_frame.data) == block + assert not rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == enc + assert cobs_decode(rx_frame.data) == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame(block) + test_frame2 = axis_ep.AXIStreamFrame(block) + + test_frame1.last_cycle_user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(rx_frame.data) == None + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == enc + assert cobs_decode(rx_frame.data) == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode.v b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode.v new file mode 100644 index 000000000..5da466ede --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode.v @@ -0,0 +1,98 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for axis_cobs_encode + */ +module test_axis_cobs_encode; + +// Parameters +parameter APPEND_ZERO = 0; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] s_axis_tdata = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [7:0] m_axis_tdata; +wire m_axis_tvalid; +wire m_axis_tlast; +wire m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_cobs_encode.lxt"); + $dumpvars(0, test_axis_cobs_encode); +end + +axis_cobs_encode #( + .APPEND_ZERO(APPEND_ZERO) +) +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode_zero_frame.py b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode_zero_frame.py new file mode 100755 index 000000000..5fc3b55cd --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode_zero_frame.py @@ -0,0 +1,354 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_cobs_encode' +testbench = 'test_%s_zero_frame' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def cobs_encode(block): + block = bytearray(block) + enc = bytearray() + + seg = bytearray() + code = 1 + + new_data = True + + for b in block: + if b == 0: + enc.append(code) + enc.extend(seg) + code = 1 + seg = bytearray() + new_data = True + else: + code += 1 + seg.append(b) + new_data = True + if code == 255: + enc.append(code) + enc.extend(seg) + code = 1 + seg = bytearray() + new_data = False + + if new_data: + enc.append(code) + enc.extend(seg) + + return bytes(enc) + +def cobs_decode(block): + block = bytearray(block) + dec = bytearray() + + code = 0 + + i = 0 + + if 0 in block: + return None + + while i < len(block): + code = block[i] + i += 1 + if i+code-1 > len(block): + return None + for k in range(code-1): + dec.append(block[i]) + i += 1 + if code < 255 and i < len(block): + dec.append(0) + + return bytes(dec) + +def prbs31(state = 0x7fffffff): + while True: + for i in range(8): + if bool(state & 0x08000000) ^ bool(state & 0x40000000): + state = ((state & 0x3fffffff) << 1) | 1 + else: + state = (state & 0x3fffffff) << 1 + yield state & 0xff + +def bench(): + + # Parameters + APPEND_ZERO = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[8:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(bool(0)) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[8:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + i = 4 + while i > 0: + i = max(0, i-1) + if s_axis_tvalid or m_axis_tvalid or not source.empty(): + i = 4 + yield clk.posedge + + def wait_pause_source(): + i = 2 + while i > 0: + i = max(0, i-1) + if s_axis_tvalid or m_axis_tvalid or not source.empty(): + i = 2 + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + i = 2 + while i > 0: + i = max(0, i-1) + if s_axis_tvalid or m_axis_tvalid or not source.empty(): + i = 2 + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(1,33))+list(range(253,259))+[512]: + gen = prbs31() + for block in [bytearray([0]*payload_len), + bytearray([k%255+1 for k in range(payload_len)]), + b'\x00'+bytearray([k%255+1 for k in range(payload_len)])+b'\x00', + bytearray([next(gen) for i in range(payload_len)])]: + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + enc = cobs_encode(block) + + test_frame = axis_ep.AXIStreamFrame(block) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == enc+b'\x00' + assert cobs_decode(rx_frame.data[:-1]) == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame(block) + test_frame2 = axis_ep.AXIStreamFrame(block) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == enc+b'\x00' + assert cobs_decode(rx_frame.data[:-1]) == block + assert not rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == enc+b'\x00' + assert cobs_decode(rx_frame.data[:-1]) == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame(block) + test_frame2 = axis_ep.AXIStreamFrame(block) + + test_frame1.last_cycle_user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(rx_frame.data[:-1]) == None + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert cobs_decode(enc) == block + assert rx_frame.data == enc+b'\x00' + assert cobs_decode(rx_frame.data[:-1]) == block + assert not rx_frame.last_cycle_user + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode_zero_frame.v b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode_zero_frame.v new file mode 100644 index 000000000..931e0fef0 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_cobs_encode_zero_frame.v @@ -0,0 +1,98 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for axis_cobs_encode + */ +module test_axis_cobs_encode_zero_frame; + +// Parameters +parameter APPEND_ZERO = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] s_axis_tdata = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [7:0] m_axis_tdata; +wire m_axis_tvalid; +wire m_axis_tlast; +wire m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_cobs_encode_zero_frame.lxt"); + $dumpvars(0, test_axis_cobs_encode_zero_frame); +end + +axis_cobs_encode #( + .APPEND_ZERO(APPEND_ZERO) +) +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4.py b/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4.py new file mode 100755 index 000000000..b832355ee --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import math + +module = 'axis_crosspoint' +testbench = 'test_%s_4x4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + M_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + select_list = [Signal(intbv(0)[math.ceil(math.log(S_COUNT, 2)):]) for i in range(M_COUNT)] + + select = ConcatSignal(*reversed(select_list)) + + # Outputs + m_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_axis_tdata_list = [m_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_axis_tkeep_list = [m_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_axis_tvalid_list = [m_axis_tvalid(i) for i in range(M_COUNT)] + m_axis_tlast_list = [m_axis_tlast(i) for i in range(M_COUNT)] + m_axis_tid_list = [m_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_axis_tdest_list = [m_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_axis_tuser_list = [m_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + for k in range(M_COUNT): + s = axis_ep.AXIStreamSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + tdata=m_axis_tdata_list[k], + tkeep=m_axis_tkeep_list[k], + tvalid=m_axis_tvalid_list[k], + tlast=m_axis_tlast_list[k], + tid=m_axis_tid_list[k], + tdest=m_axis_tdest_list[k], + tuser=m_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: 0123 -> 0123") + current_test.next = 1 + + select_list[0].next = 0 + select_list[1].next = 1 + select_list[2].next = 2 + select_list[3].next = 3 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x01\x00\x00\xFF\x01\x02\x03\x04', id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x01\x01\x01\xFF\x01\x02\x03\x04', id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x01\x02\x02\xFF\x01\x02\x03\x04', id=2, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x01\x03\x03\xFF\x01\x02\x03\x04', id=3, dest=3) + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + yield delay(100) + + yield clk.posedge + print("test 2: 0123 -> 3210") + current_test.next = 2 + + select_list[0].next = 3 + select_list[1].next = 2 + select_list[2].next = 1 + select_list[3].next = 0 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x03\xFF\x01\x02\x03\x04', id=0, dest=3) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x01\x02\xFF\x01\x02\x03\x04', id=1, dest=2) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x02\x01\xFF\x01\x02\x03\x04', id=2, dest=1) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x03\x00\xFF\x01\x02\x03\x04', id=3, dest=0) + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame3 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame2 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame1 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame0 + + yield delay(100) + + yield clk.posedge + print("test 3: 0000 -> 0123") + current_test.next = 3 + + select_list[0].next = 0 + select_list[1].next = 0 + select_list[2].next = 0 + select_list[3].next = 0 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x03\x00\xFF\xFF\x01\x02\x03\x04', id=0, dest=0) + source_list[0].send(test_frame0) + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame0 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame0 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4.v b/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4.v new file mode 100644 index 000000000..33889afc6 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4.v @@ -0,0 +1,139 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_crosspoint + */ +module test_axis_crosspoint_4x4; + +// Parameters +parameter S_COUNT = 4; +parameter M_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; + +reg [M_COUNT*$clog2(S_COUNT)-1:0] select = 0; + +// Outputs +wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep; +wire [M_COUNT-1:0] m_axis_tvalid; +wire [M_COUNT-1:0] m_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + select + ); + $to_myhdl( + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_crosspoint_4x4.lxt"); + $dumpvars(0, test_axis_crosspoint_4x4); +end + +axis_crosspoint #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Control + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4_64.py new file mode 100755 index 000000000..ee48ba586 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4_64.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import math + +module = 'axis_crosspoint' +testbench = 'test_%s_4x4_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + M_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + select_list = [Signal(intbv(0)[math.ceil(math.log(S_COUNT, 2)):]) for i in range(M_COUNT)] + + select = ConcatSignal(*reversed(select_list)) + + # Outputs + m_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_axis_tdata_list = [m_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_axis_tkeep_list = [m_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_axis_tvalid_list = [m_axis_tvalid(i) for i in range(M_COUNT)] + m_axis_tlast_list = [m_axis_tlast(i) for i in range(M_COUNT)] + m_axis_tid_list = [m_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_axis_tdest_list = [m_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_axis_tuser_list = [m_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + for k in range(M_COUNT): + s = axis_ep.AXIStreamSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + tdata=m_axis_tdata_list[k], + tkeep=m_axis_tkeep_list[k], + tvalid=m_axis_tvalid_list[k], + tlast=m_axis_tlast_list[k], + tid=m_axis_tid_list[k], + tdest=m_axis_tdest_list[k], + tuser=m_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: 0123 -> 0123") + current_test.next = 1 + + select_list[0].next = 0 + select_list[1].next = 1 + select_list[2].next = 2 + select_list[3].next = 3 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x01\x00\x00\xFF\x01\x02\x03\x04', id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x01\x01\x01\xFF\x01\x02\x03\x04', id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x01\x02\x02\xFF\x01\x02\x03\x04', id=2, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x01\x03\x03\xFF\x01\x02\x03\x04', id=3, dest=3) + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + yield delay(100) + + yield clk.posedge + print("test 2: 0123 -> 3210") + current_test.next = 2 + + select_list[0].next = 3 + select_list[1].next = 2 + select_list[2].next = 1 + select_list[3].next = 0 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x03\xFF\x01\x02\x03\x04', id=0, dest=3) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x01\x02\xFF\x01\x02\x03\x04', id=1, dest=2) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x02\x01\xFF\x01\x02\x03\x04', id=2, dest=1) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x03\x00\xFF\x01\x02\x03\x04', id=3, dest=0) + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame3 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame2 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame1 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame0 + + yield delay(100) + + yield clk.posedge + print("test 3: 0000 -> 0123") + current_test.next = 3 + + select_list[0].next = 0 + select_list[1].next = 0 + select_list[2].next = 0 + select_list[3].next = 0 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x03\x00\xFF\xFF\x01\x02\x03\x04', id=0, dest=0) + source_list[0].send(test_frame0) + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame0 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame0 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4_64.v new file mode 100644 index 000000000..238390ac6 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_crosspoint_4x4_64.v @@ -0,0 +1,139 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_crosspoint + */ +module test_axis_crosspoint_4x4_64; + +// Parameters +parameter S_COUNT = 4; +parameter M_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; + +reg [M_COUNT*$clog2(S_COUNT)-1:0] select = 0; + +// Outputs +wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep; +wire [M_COUNT-1:0] m_axis_tvalid; +wire [M_COUNT-1:0] m_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + select + ); + $to_myhdl( + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_crosspoint_4x4_64.lxt"); + $dumpvars(0, test_axis_crosspoint_4x4_64); +end + +axis_crosspoint #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Control + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_demux_4.py b/fpga/lib/eth/lib/axis/tb/test_axis_demux_4.py new file mode 100755 index 000000000..5e02ffce7 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_demux_4.py @@ -0,0 +1,493 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import math + +module = 'axis_demux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + M_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + m_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_axis_tready = ConcatSignal(*reversed(m_axis_tready_list)) + + enable = Signal(bool(0)) + drop = Signal(bool(0)) + select = Signal(intbv(0)[math.ceil(math.log(M_COUNT, 2)):]) + + # Outputs + s_axis_tready = Signal(bool(0)) + + m_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_axis_tdata_list = [m_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_axis_tkeep_list = [m_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_axis_tvalid_list = [m_axis_tvalid(i) for i in range(M_COUNT)] + m_axis_tlast_list = [m_axis_tlast(i) for i in range(M_COUNT)] + m_axis_tid_list = [m_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_axis_tdest_list = [m_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_axis_tuser_list = [m_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + for k in range(M_COUNT): + s = axis_ep.AXIStreamSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + tdata=m_axis_tdata_list[k], + tkeep=m_axis_tkeep_list[k], + tvalid=m_axis_tvalid_list[k], + tready=m_axis_tready_list[k], + tlast=m_axis_tlast_list[k], + tid=m_axis_tid_list[k], + tdest=m_axis_tdest_list[k], + tuser=m_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + enable=enable, + drop=drop, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + drop.next = False + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame1 + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + yield clk.posedge + select.next = 2 + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: enable") + current_test.next = 7 + + enable.next = False + select.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1 + ) + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + enable.next = True + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 8: drop") + current_test.next = 8 + + drop.next = True + select.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=8, + dest=1 + ) + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + drop.next = False + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_demux_4.v b/fpga/lib/eth/lib/axis/tb/test_axis_demux_4.v new file mode 100644 index 000000000..b58507609 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_demux_4.v @@ -0,0 +1,149 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_demux + */ +module test_axis_demux_4; + +// Parameters +parameter M_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; + +reg [M_COUNT-1:0] m_axis_tready = 0; + +reg enable = 0; +reg drop = 0; +reg [$clog2(M_COUNT)-1:0] select = 0; + +// Outputs +wire s_axis_tready; + +wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep; +wire [M_COUNT-1:0] m_axis_tvalid; +wire [M_COUNT-1:0] m_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready, + enable, + drop, + select + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_demux_4.lxt"); + $dumpvars(0, test_axis_demux_4); +end + +axis_demux #( + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI outputs + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Control + .enable(enable), + .drop(drop), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_demux_4_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_demux_4_64.py new file mode 100755 index 000000000..5fd01de50 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_demux_4_64.py @@ -0,0 +1,493 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import math + +module = 'axis_demux' +testbench = 'test_%s_4_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + M_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + m_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_axis_tready = ConcatSignal(*reversed(m_axis_tready_list)) + + enable = Signal(bool(0)) + drop = Signal(bool(0)) + select = Signal(intbv(0)[math.ceil(math.log(M_COUNT, 2)):]) + + # Outputs + s_axis_tready = Signal(bool(0)) + + m_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_axis_tdata_list = [m_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_axis_tkeep_list = [m_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_axis_tvalid_list = [m_axis_tvalid(i) for i in range(M_COUNT)] + m_axis_tlast_list = [m_axis_tlast(i) for i in range(M_COUNT)] + m_axis_tid_list = [m_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_axis_tdest_list = [m_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_axis_tuser_list = [m_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + for k in range(M_COUNT): + s = axis_ep.AXIStreamSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + tdata=m_axis_tdata_list[k], + tkeep=m_axis_tkeep_list[k], + tvalid=m_axis_tvalid_list[k], + tready=m_axis_tready_list[k], + tlast=m_axis_tlast_list[k], + tid=m_axis_tid_list[k], + tdest=m_axis_tdest_list[k], + tuser=m_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + enable=enable, + drop=drop, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + drop.next = False + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame1 + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + yield clk.posedge + select.next = 2 + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: enable") + current_test.next = 7 + + enable.next = False + select.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1 + ) + + source.send(test_frame) + + yield delay(100) + + assert sink_list[0].empty() + + enable.next = True + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 8: drop") + current_test.next = 8 + + drop.next = True + select.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=8, + dest=1 + ) + + source.send(test_frame) + + yield delay(100) + + assert sink_list[0].empty() + + drop.next = False + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_demux_4_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_demux_4_64.v new file mode 100644 index 000000000..2fb7d8894 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_demux_4_64.v @@ -0,0 +1,149 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_demux + */ +module test_axis_demux_4_64; + +// Parameters +parameter M_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; + +reg [M_COUNT-1:0] m_axis_tready = 0; + +reg enable = 0; +reg drop = 0; +reg [$clog2(M_COUNT)-1:0] select = 0; + +// Outputs +wire s_axis_tready; + +wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep; +wire [M_COUNT-1:0] m_axis_tvalid; +wire [M_COUNT-1:0] m_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready, + enable, + drop, + select + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_demux_4_64.lxt"); + $dumpvars(0, test_axis_demux_4_64); +end + +axis_demux #( + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI outputs + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Control + .enable(enable), + .drop(drop), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_fifo.py b/fpga/lib/eth/lib/axis/tb/test_axis_fifo.py new file mode 100755 index 000000000..c28c90189 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_fifo.py @@ -0,0 +1,525 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 2 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 0 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 0 + DROP_WHEN_FULL = 0 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + yield clk.posedge + print("test 8: initial sink pause") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + b'\x01\x02\x03', + id=8, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 9: initial sink pause, reset") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + b'\x01\x02\x03', + id=9, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rst.next = 1 + yield clk.posedge + rst.next = 0 + + sink_pause.next = 0 + + yield delay(100) + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 10: backpressure test") + current_test.next = 10 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=10, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + source.send(test_frame) + yield delay(5000) + yield clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 11: many small packets") + current_test.next = 11 + + test_frame = axis_ep.AXIStreamFrame( + b'\xAA', + id=11, + dest=1 + ) + + for k in range(64): + source.send(test_frame) + + for k in range(64): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_fifo.v b/fpga/lib/eth/lib/axis/tb/test_axis_fifo.v new file mode 100644 index 000000000..6a8d4ee77 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_fifo.v @@ -0,0 +1,152 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_fifo + */ +module test_axis_fifo; + +// Parameters +parameter ADDR_WIDTH = 2; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 0; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 0; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_fifo.lxt"); + $dumpvars(0, test_axis_fifo); +end + +axis_fifo #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_fifo_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_64.py new file mode 100755 index 000000000..eeb28ba4a --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_64.py @@ -0,0 +1,525 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_fifo' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 2 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 0 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 0 + DROP_WHEN_FULL = 0 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + yield clk.posedge + print("test 8: initial sink pause") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(24)), + id=8, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 9: initial sink pause, reset") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(24)), + id=9, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rst.next = 1 + yield clk.posedge + rst.next = 0 + + sink_pause.next = 0 + + yield delay(100) + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 10: backpressure test") + current_test.next = 10 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=10, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + source.send(test_frame) + yield delay(1000) + yield clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 11: many small packets") + current_test.next = 11 + + test_frame = axis_ep.AXIStreamFrame( + b'\xAA', + id=11, + dest=1 + ) + + for k in range(64): + source.send(test_frame) + + for k in range(64): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_fifo_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_64.v new file mode 100644 index 000000000..e1ea50f6a --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_64.v @@ -0,0 +1,152 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_fifo + */ +module test_axis_fifo_64; + +// Parameters +parameter ADDR_WIDTH = 2; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 0; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 0; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_fifo_64.lxt"); + $dumpvars(0, test_axis_fifo_64); +end + +axis_fifo #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_64_8.py b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_64_8.py new file mode 100755 index 000000000..2129221e1 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_64_8.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_fifo_adapter' +testbench = 'test_%s_64_8' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_adapter.v") +srcs.append("../rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 2 + S_DATA_WIDTH = 64 + S_KEEP_ENABLE = (S_DATA_WIDTH>8) + S_KEEP_WIDTH = (S_DATA_WIDTH/8) + M_DATA_WIDTH = 8 + M_KEEP_ENABLE = (M_DATA_WIDTH>8) + M_KEEP_WIDTH = (M_DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 0 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 0 + DROP_WHEN_FULL = 0 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[S_DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[S_KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[M_DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[M_KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=1, + dest=1, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=1, + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=1, + last_cycle_user=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_64_8.v b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_64_8.v new file mode 100644 index 000000000..5afd8d8d2 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_64_8.v @@ -0,0 +1,156 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for axis_fifo_adapter + */ +module test_axis_fifo_adapter_64_8; + +// Parameters +parameter ADDR_WIDTH = 2; +parameter S_DATA_WIDTH = 64; +parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8); +parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8); +parameter M_DATA_WIDTH = 8; +parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8); +parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 0; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 0; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [M_DATA_WIDTH-1:0] m_axis_tdata; +wire [M_KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_fifo_adapter_64_8.lxt"); + $dumpvars(0, test_axis_fifo_adapter_64_8); +end + +axis_fifo_adapter #( + .ADDR_WIDTH(ADDR_WIDTH), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_8_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_8_64.py new file mode 100755 index 000000000..f59dd451a --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_8_64.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_fifo_adapter' +testbench = 'test_%s_8_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_adapter.v") +srcs.append("../rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 2 + S_DATA_WIDTH = 8 + S_KEEP_ENABLE = (S_DATA_WIDTH>8) + S_KEEP_WIDTH = (S_DATA_WIDTH/8) + M_DATA_WIDTH = 64 + M_KEEP_ENABLE = (M_DATA_WIDTH>8) + M_KEEP_WIDTH = (M_DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 0 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 0 + DROP_WHEN_FULL = 0 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[S_DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[S_KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[M_DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[M_KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=1, + dest=1, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=1, + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=2, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=1, + last_cycle_user=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + bytearray(range(payload_len)), + id=3, + dest=2, + ) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_8_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_8_64.v new file mode 100644 index 000000000..c97a3a422 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_fifo_adapter_8_64.v @@ -0,0 +1,156 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for axis_fifo_adapter + */ +module test_axis_fifo_adapter_8_64; + +// Parameters +parameter ADDR_WIDTH = 2; +parameter S_DATA_WIDTH = 8; +parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8); +parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8); +parameter M_DATA_WIDTH = 64; +parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8); +parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 0; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 0; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [M_DATA_WIDTH-1:0] m_axis_tdata; +wire [M_KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_fifo_adapter_8_64.lxt"); + $dumpvars(0, test_axis_fifo_adapter_8_64); +end + +axis_fifo_adapter #( + .ADDR_WIDTH(ADDR_WIDTH), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo.py b/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo.py new file mode 100755 index 000000000..b5ebab517 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo.py @@ -0,0 +1,630 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_fifo' +testbench = 'test_axis_frame_fifo' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 9 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 1 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 1 + DROP_WHEN_FULL = 0 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + status_overflow = Signal(bool(0)) + status_bad_frame = Signal(bool(0)) + status_good_frame = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + status_overflow=status_overflow, + status_bad_frame=status_bad_frame, + status_good_frame=status_good_frame + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + status_overflow_asserted = Signal(bool(0)) + status_bad_frame_asserted = Signal(bool(0)) + status_good_frame_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (status_overflow): + status_overflow_asserted.next = 1 + if (status_bad_frame): + status_bad_frame_asserted.next = 1 + if (status_good_frame): + status_good_frame_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield clk.posedge + + yield delay(1000) + + assert sink.empty() + + assert not status_overflow_asserted + assert status_bad_frame_asserted + assert not status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 8: single packet overflow") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256))*2, + id=8, + dest=1 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield clk.posedge + + yield delay(10000) + + assert sink.empty() + + assert status_overflow_asserted + assert not status_bad_frame_asserted + assert not status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 9: initial sink pause") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + b'\x01\x02\x03', + id=9, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 10: initial sink pause, reset") + current_test.next = 10 + + test_frame = axis_ep.AXIStreamFrame( + b'\x01\x02\x03', + id=10, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rst.next = 1 + yield clk.posedge + rst.next = 0 + + sink_pause.next = 0 + + yield delay(100) + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 11: backpressure test") + current_test.next = 11 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=11, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + source.send(test_frame) + yield delay(5000) + yield clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 12: many small packets") + current_test.next = 12 + + test_frame = axis_ep.AXIStreamFrame( + b'\xAA', + id=12, + dest=1 + ) + + for k in range(64): + source.send(test_frame) + + for k in range(64): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo.v b/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo.v new file mode 100644 index 000000000..8ba096c22 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo.v @@ -0,0 +1,158 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_fifo + */ +module test_axis_frame_fifo; + +// Parameters +parameter ADDR_WIDTH = 9; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 1; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 1; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire status_overflow; +wire status_bad_frame; +wire status_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + status_overflow, + status_bad_frame, + status_good_frame + ); + + // dump file + $dumpfile("test_axis_frame_fifo.lxt"); + $dumpvars(0, test_axis_frame_fifo); +end + +axis_fifo #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(status_overflow), + .status_bad_frame(status_bad_frame), + .status_good_frame(status_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo_64.py new file mode 100755 index 000000000..e0200d27a --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo_64.py @@ -0,0 +1,630 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_fifo' +testbench = 'test_axis_frame_fifo_64' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 6 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO = 1 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 1 + DROP_WHEN_FULL = 0 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + status_overflow = Signal(bool(0)) + status_bad_frame = Signal(bool(0)) + status_good_frame = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + status_overflow=status_overflow, + status_bad_frame=status_bad_frame, + status_good_frame=status_good_frame + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + status_overflow_asserted = Signal(bool(0)) + status_bad_frame_asserted = Signal(bool(0)) + status_good_frame_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (status_overflow): + status_overflow_asserted.next = 1 + if (status_bad_frame): + status_bad_frame_asserted.next = 1 + if (status_good_frame): + status_good_frame_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=3, + dest=1 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert not status_overflow_asserted + assert not status_bad_frame_asserted + assert status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield clk.posedge + + yield delay(1000) + + assert sink.empty() + + assert not status_overflow_asserted + assert status_bad_frame_asserted + assert not status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 8: single packet overflow") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256))*2, + id=8, + dest=1 + ) + + status_overflow_asserted.next = 0 + status_bad_frame_asserted.next = 0 + status_good_frame_asserted.next = 0 + + source.send(test_frame) + yield clk.posedge + + yield delay(10000) + + assert sink.empty() + + assert status_overflow_asserted + assert not status_bad_frame_asserted + assert not status_good_frame_asserted + + yield delay(100) + + yield clk.posedge + print("test 9: initial sink pause") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(24)), + id=9, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 10: initial sink pause, reset") + current_test.next = 10 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(24)), + id=10, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rst.next = 1 + yield clk.posedge + rst.next = 0 + + sink_pause.next = 0 + + yield delay(100) + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 11: backpressure test") + current_test.next = 11 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=11, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + source.send(test_frame) + yield delay(1000) + yield clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 12: many small packets") + current_test.next = 12 + + test_frame = axis_ep.AXIStreamFrame( + b'\xAA', + id=12, + dest=1 + ) + + for k in range(64): + source.send(test_frame) + + for k in range(64): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo_64.v new file mode 100644 index 000000000..94e858918 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_fifo_64.v @@ -0,0 +1,158 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_fifo + */ +module test_axis_frame_fifo_64; + +// Parameters +parameter ADDR_WIDTH = 6; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO = 1; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 1; +parameter DROP_WHEN_FULL = 0; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire status_overflow; +wire status_bad_frame; +wire status_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + status_overflow, + status_bad_frame, + status_good_frame + ); + + // dump file + $dumpfile("test_axis_frame_fifo_64.lxt"); + $dumpvars(0, test_axis_frame_fifo_64); +end + +axis_fifo #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(status_overflow), + .status_bad_frame(status_bad_frame), + .status_good_frame(status_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_join_4.py b/fpga/lib/eth/lib/axis/tb/test_axis_frame_join_4.py new file mode 100755 index 000000000..c89b9d9f6 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_join_4.py @@ -0,0 +1,395 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os +import struct + +import axis_ep + +module = 'axis_frame_join' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 8 + TAG_ENABLE = 1 + TAG_WIDTH = 16 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(bool(0)) for i in range(S_COUNT)] + + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + m_axis_tready = Signal(bool(0)) + tag = Signal(intbv(0)[TAG_WIDTH:]) + + # Outputs + s_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_axis_tready_list = [s_axis_tready(i) for i in range(S_COUNT)] + + m_axis_tdata = Signal(intbv(0)[8:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tvalid=s_axis_tvalid_list[k], + tready=s_axis_tready_list[k], + tlast=s_axis_tlast_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser, + + tag=tag, + busy=busy + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + tag.next = 1 + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame_0 = axis_ep.AXIStreamFrame(b'\x00\xAA\xBB\xCC\xDD\x00') + test_frame_1 = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_2 = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_3 = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + source_list[0].send(test_frame_0) + source_list[1].send(test_frame_1) + source_list[2].send(test_frame_2) + source_list[3].send(test_frame_3) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == struct.pack('8) + KEEP_WIDTH = (DATA_WIDTH/8) + LEN_WIDTH = 16 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + monitor_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + monitor_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + monitor_axis_tvalid = Signal(bool(0)) + monitor_axis_tready = Signal(bool(0)) + monitor_axis_tlast = Signal(bool(0)) + + # Outputs + frame_len = Signal(intbv(0)[LEN_WIDTH:]) + frame_len_valid = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=monitor_axis_tdata, + tkeep=monitor_axis_tkeep, + tvalid=monitor_axis_tvalid, + tready=monitor_axis_tready, + tlast=monitor_axis_tlast, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=monitor_axis_tdata, + tkeep=monitor_axis_tkeep, + tvalid=monitor_axis_tvalid, + tready=monitor_axis_tready, + tlast=monitor_axis_tlast, + pause=sink_pause, + name='sink' + ) + + frame_len_sink = axis_ep.AXIStreamSink() + + frame_len_sink_logic = frame_len_sink.create_logic( + clk, + rst, + tdata=frame_len, + tvalid=frame_len_valid, + name='frame_len_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + monitor_axis_tkeep=monitor_axis_tkeep, + monitor_axis_tvalid=monitor_axis_tvalid, + monitor_axis_tready=monitor_axis_tready, + monitor_axis_tlast=monitor_axis_tlast, + + frame_len=frame_len, + frame_len_valid=frame_len_valid + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 4: longer packet") + current_test.next = 4 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)) + ) + + source.send(test_frame) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 5: test packet with pauses") + current_test.next = 5 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)) + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + while monitor_axis_tvalid: + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 6: back-to-back packets") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 7: alternate pause source") + current_test.next = 7 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while monitor_axis_tvalid: + source_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 8: alternate pause sink") + current_test.next = 8 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while monitor_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 9: various length packets") + current_test.next = 9 + + lens = [32, 48, 96, 128, 256] + test_frame = [] + + for i in lens: + test_frame.append(axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(i))) + ) + + for f in test_frame: + source.send(f) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + for i in lens: + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_len_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_frame_len_64.v new file mode 100644 index 000000000..ec4dadf01 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_len_64.v @@ -0,0 +1,94 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for axis_frame_len + */ +module test_axis_frame_len_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LEN_WIDTH = 16; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [KEEP_WIDTH-1:0] monitor_axis_tkeep = 0; +reg monitor_axis_tvalid = 0; +reg monitor_axis_tready = 0; +reg monitor_axis_tlast = 0; + +// Outputs +wire [LEN_WIDTH-1:0] frame_len; +wire frame_len_valid; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + monitor_axis_tkeep, + monitor_axis_tvalid, + monitor_axis_tready, + monitor_axis_tlast + ); + $to_myhdl( + frame_len, + frame_len_valid + ); + + // dump file + $dumpfile("test_axis_frame_len_64.lxt"); + $dumpvars(0, test_axis_frame_len_64); +end + +axis_frame_len #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LEN_WIDTH(LEN_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI monitor + .monitor_axis_tkeep(monitor_axis_tkeep), + .monitor_axis_tvalid(monitor_axis_tvalid), + .monitor_axis_tready(monitor_axis_tready), + .monitor_axis_tlast(monitor_axis_tlast), + // Status + .frame_len(frame_len), + .frame_len_valid(frame_len_valid) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_len_8.py b/fpga/lib/eth/lib/axis/tb/test_axis_frame_len_8.py new file mode 100755 index 000000000..9bca5f310 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_len_8.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_frame_len' +testbench = 'test_%s_8' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LEN_WIDTH = 16 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + monitor_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + monitor_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + monitor_axis_tvalid = Signal(bool(0)) + monitor_axis_tready = Signal(bool(0)) + monitor_axis_tlast = Signal(bool(0)) + + # Outputs + frame_len = Signal(intbv(0)[LEN_WIDTH:]) + frame_len_valid = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=monitor_axis_tdata, + tkeep=monitor_axis_tkeep, + tvalid=monitor_axis_tvalid, + tready=monitor_axis_tready, + tlast=monitor_axis_tlast, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=monitor_axis_tdata, + tkeep=monitor_axis_tkeep, + tvalid=monitor_axis_tvalid, + tready=monitor_axis_tready, + tlast=monitor_axis_tlast, + pause=sink_pause, + name='sink' + ) + + frame_len_sink = axis_ep.AXIStreamSink() + + frame_len_sink_logic = frame_len_sink.create_logic( + clk, + rst, + tdata=frame_len, + tvalid=frame_len_valid, + name='frame_len_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + monitor_axis_tkeep=monitor_axis_tkeep, + monitor_axis_tvalid=monitor_axis_tvalid, + monitor_axis_tready=monitor_axis_tready, + monitor_axis_tlast=monitor_axis_tlast, + + frame_len=frame_len, + frame_len_valid=frame_len_valid + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 4: longer packet") + current_test.next = 4 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)) + ) + + source.send(test_frame) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 5: test packet with pauses") + current_test.next = 5 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)) + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + while monitor_axis_tvalid: + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 6: back-to-back packets") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 7: alternate pause source") + current_test.next = 7 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 8: alternate pause sink") + current_test.next = 8 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while monitor_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + yield clk.posedge + print("test 9: various length packets") + current_test.next = 9 + + lens = [32, 48, 96, 128, 256] + test_frame = [] + + for i in lens: + test_frame.append(axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(i))) + ) + + for f in test_frame: + source.send(f) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + for i in lens: + yield sink.wait() + f = sink.recv() + + yield frame_len_sink.wait() + l = frame_len_sink.recv() + + assert len(f.data) == l.data[0] + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_len_8.v b/fpga/lib/eth/lib/axis/tb/test_axis_frame_len_8.v new file mode 100644 index 000000000..0f40aa8e2 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_len_8.v @@ -0,0 +1,94 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for axis_frame_len + */ +module test_axis_frame_len_8; + +// Parameters +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LEN_WIDTH = 16; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [KEEP_WIDTH-1:0] monitor_axis_tkeep = 0; +reg monitor_axis_tvalid = 0; +reg monitor_axis_tready = 0; +reg monitor_axis_tlast = 0; + +// Outputs +wire [LEN_WIDTH-1:0] frame_len; +wire frame_len_valid; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + monitor_axis_tkeep, + monitor_axis_tvalid, + monitor_axis_tready, + monitor_axis_tlast + ); + $to_myhdl( + frame_len, + frame_len_valid + ); + + // dump file + $dumpfile("test_axis_frame_len_8.lxt"); + $dumpvars(0, test_axis_frame_len_8); +end + +axis_frame_len #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LEN_WIDTH(LEN_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI monitor + .monitor_axis_tkeep(monitor_axis_tkeep), + .monitor_axis_tvalid(monitor_axis_tvalid), + .monitor_axis_tready(monitor_axis_tready), + .monitor_axis_tlast(monitor_axis_tlast), + // Status + .frame_len(frame_len), + .frame_len_valid(frame_len_valid) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_64.py new file mode 100755 index 000000000..a92a926bf --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_64.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_frame_length_adjust' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + status_ready = Signal(bool(0)) + length_min = Signal(intbv(0)[16:]) + length_max = Signal(intbv(0)[16:]) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + status_valid = Signal(bool(0)) + status_frame_pad = Signal(bool(0)) + status_frame_truncate = Signal(bool(0)) + status_frame_length = Signal(intbv(0)[16:]) + status_frame_original_length = Signal(intbv(0)[16:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + status_sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + status_sink = axis_ep.AXIStreamSink() + + status_sink_logic = status_sink.create_logic( + clk, + rst, + tdata=(status_frame_pad, status_frame_truncate, status_frame_length, status_frame_original_length), + tvalid=status_valid, + tready=status_ready, + pause=status_sink_pause, + name='status_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + status_valid=status_valid, + status_ready=status_ready, + status_frame_pad=status_frame_pad, + status_frame_truncate=status_frame_truncate, + status_frame_length=status_frame_length, + status_frame_original_length=status_frame_original_length, + + length_min=length_min, + length_max=length_max + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + length_min.next = 1 + length_max.next = 20 + + for lmax in range(1,18): + for lmin in range(0,lmax+1): + length_min.next = lmin + length_max.next = lmax + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=1, dest=1) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame.data[:lm] + + yield status_sink.wait() + status = status_sink.recv() + assert status.data[0][0] == (lt < lmin) + assert status.data[0][1] == (lt > lmax) + assert status.data[0][2] == lrx + assert status.data[0][3] == lt + + assert sink.empty() + assert status_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=2, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=2, dest=2) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame1.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame1.data[:lm] + + yield status_sink.wait() + status = status_sink.recv() + assert status.data[0][0] == (lt < lmin) + assert status.data[0][1] == (lt > lmax) + assert status.data[0][2] == lrx + assert status.data[0][3] == lt + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame2.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame2.data[:lm] + + yield status_sink.wait() + status = status_sink.recv() + assert status.data[0][0] == (lt < lmin) + assert status.data[0][1] == (lt > lmax) + assert status.data[0][2] == lrx + assert status.data[0][3] == lt + + assert sink.empty() + assert status_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=3, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=3, dest=2) + + test_frame1.last_cycle_user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame1.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame1.data[:lm] + + assert rx_frame.last_cycle_user + + yield status_sink.wait() + status = status_sink.recv() + assert status.data[0][0] == (lt < lmin) + assert status.data[0][1] == (lt > lmax) + assert status.data[0][2] == lrx + assert status.data[0][3] == lt + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame2.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame2.data[:lm] + + yield status_sink.wait() + status = status_sink.recv() + assert status.data[0][0] == (lt < lmin) + assert status.data[0][1] == (lt > lmax) + assert status.data[0][2] == lrx + assert status.data[0][3] == lt + + assert sink.empty() + assert status_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_64.v new file mode 100644 index 000000000..0f6a31014 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_64.v @@ -0,0 +1,160 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for axis_frame_length_adjust + */ +module test_axis_frame_length_adjust_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; +reg status_ready = 0; +reg [15:0] length_min = 0; +reg [15:0] length_max = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire status_valid; +wire status_frame_pad; +wire status_frame_truncate; +wire [15:0] status_frame_length; +wire [15:0] status_frame_original_length; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready, + status_ready, + length_min, + length_max + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + status_valid, + status_frame_pad, + status_frame_truncate, + status_frame_length, + status_frame_original_length + ); + + // dump file + $dumpfile("test_axis_frame_length_adjust_64.lxt"); + $dumpvars(0, test_axis_frame_length_adjust_64); +end + +axis_frame_length_adjust #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_valid(status_valid), + .status_ready(status_ready), + .status_frame_pad(status_frame_pad), + .status_frame_truncate(status_frame_truncate), + .status_frame_length(status_frame_length), + .status_frame_original_length(status_frame_original_length), + // Configuration + .length_min(length_min), + .length_max(length_max) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_8.py b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_8.py new file mode 100755 index 000000000..a180c5349 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_8.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_frame_length_adjust' +testbench = 'test_%s_8' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + status_ready = Signal(bool(0)) + length_min = Signal(intbv(0)[16:]) + length_max = Signal(intbv(0)[16:]) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + status_valid = Signal(bool(0)) + status_frame_pad = Signal(bool(0)) + status_frame_truncate = Signal(bool(0)) + status_frame_length = Signal(intbv(0)[16:]) + status_frame_original_length = Signal(intbv(0)[16:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + status_sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + status_sink = axis_ep.AXIStreamSink() + + status_sink_logic = status_sink.create_logic( + clk, + rst, + tdata=(status_frame_pad, status_frame_truncate, status_frame_length, status_frame_original_length), + tvalid=status_valid, + tready=status_ready, + pause=status_sink_pause, + name='status_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + status_valid=status_valid, + status_ready=status_ready, + status_frame_pad=status_frame_pad, + status_frame_truncate=status_frame_truncate, + status_frame_length=status_frame_length, + status_frame_original_length=status_frame_original_length, + + length_min=length_min, + length_max=length_max + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + length_min.next = 1 + length_max.next = 20 + + for lmax in range(1,6): + for lmin in range(0,lmax+1): + length_min.next = lmin + length_max.next = lmax + + for payload_len in range(1,6): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=1, dest=1) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame.data[:lm] + + yield status_sink.wait() + status = status_sink.recv() + assert status.data[0][0] == (lt < lmin) + assert status.data[0][1] == (lt > lmax) + assert status.data[0][2] == lrx + assert status.data[0][3] == lt + + assert sink.empty() + assert status_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=2, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=2, dest=2) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame1.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame1.data[:lm] + + yield status_sink.wait() + status = status_sink.recv() + assert status.data[0][0] == (lt < lmin) + assert status.data[0][1] == (lt > lmax) + assert status.data[0][2] == lrx + assert status.data[0][3] == lt + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame2.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame2.data[:lm] + + yield status_sink.wait() + status = status_sink.recv() + assert status.data[0][0] == (lt < lmin) + assert status.data[0][1] == (lt > lmax) + assert status.data[0][2] == lrx + assert status.data[0][3] == lt + + assert sink.empty() + assert status_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=3, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=3, dest=2) + + test_frame1.last_cycle_user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame1.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame1.data[:lm] + + assert rx_frame.last_cycle_user + + yield status_sink.wait() + status = status_sink.recv() + assert status.data[0][0] == (lt < lmin) + assert status.data[0][1] == (lt > lmax) + assert status.data[0][2] == lrx + assert status.data[0][3] == lt + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame2.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame2.data[:lm] + + yield status_sink.wait() + status = status_sink.recv() + assert status.data[0][0] == (lt < lmin) + assert status.data[0][1] == (lt > lmax) + assert status.data[0][2] == lrx + assert status.data[0][3] == lt + + assert sink.empty() + assert status_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_8.v b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_8.v new file mode 100644 index 000000000..ea65c815d --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_8.v @@ -0,0 +1,160 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for axis_frame_length_adjust + */ +module test_axis_frame_length_adjust_8; + +// Parameters +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; +reg status_ready = 0; +reg [15:0] length_min = 0; +reg [15:0] length_max = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire status_valid; +wire status_frame_pad; +wire status_frame_truncate; +wire [15:0] status_frame_length; +wire [15:0] status_frame_original_length; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready, + status_ready, + length_min, + length_max + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + status_valid, + status_frame_pad, + status_frame_truncate, + status_frame_length, + status_frame_original_length + ); + + // dump file + $dumpfile("test_axis_frame_length_adjust_8.lxt"); + $dumpvars(0, test_axis_frame_length_adjust_8); +end + +axis_frame_length_adjust #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_valid(status_valid), + .status_ready(status_ready), + .status_frame_pad(status_frame_pad), + .status_frame_truncate(status_frame_truncate), + .status_frame_length(status_frame_length), + .status_frame_original_length(status_frame_original_length), + // Configuration + .length_min(length_min), + .length_max(length_max) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo.py b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo.py new file mode 100755 index 000000000..7cf95db9c --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo.py @@ -0,0 +1,369 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_frame_length_adjust_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_frame_length_adjust.v") +srcs.append("../rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO_ADDR_WIDTH = 12 + HEADER_FIFO_ADDR_WIDTH = 3 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_hdr_ready = Signal(bool(0)) + m_axis_tready = Signal(bool(0)) + length_min = Signal(intbv(0)[16:]) + length_max = Signal(intbv(0)[16:]) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_hdr_valid = Signal(bool(0)) + m_axis_hdr_pad = Signal(bool(0)) + m_axis_hdr_truncate = Signal(bool(0)) + m_axis_hdr_length = Signal(intbv(0)[16:]) + m_axis_hdr_original_length = Signal(intbv(0)[16:]) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + hdr_sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + hdr_sink = axis_ep.AXIStreamSink() + + hdr_sink_logic = hdr_sink.create_logic( + clk, + rst, + tdata=(m_axis_hdr_pad, m_axis_hdr_truncate, m_axis_hdr_length, m_axis_hdr_original_length), + tvalid=m_axis_hdr_valid, + tready=m_axis_hdr_ready, + pause=hdr_sink_pause, + name='hdr_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_hdr_valid=m_axis_hdr_valid, + m_axis_hdr_ready=m_axis_hdr_ready, + m_axis_hdr_pad=m_axis_hdr_pad, + m_axis_hdr_truncate=m_axis_hdr_truncate, + m_axis_hdr_length=m_axis_hdr_length, + m_axis_hdr_original_length=m_axis_hdr_original_length, + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + length_min=length_min, + length_max=length_max + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + length_min.next = 1 + length_max.next = 20 + + for lmax in range(1,6): + for lmin in range(0,lmax+1): + length_min.next = lmin + length_max.next = lmax + + for payload_len in range(1,6): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=1, dest=1) + + for wait in wait_normal,: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame.data[:lm] + + yield hdr_sink.wait() + hdr = hdr_sink.recv() + assert hdr.data[0][0] == (lt < lmin) + assert hdr.data[0][1] == (lt > lmax) + assert hdr.data[0][2] == lrx + assert hdr.data[0][3] == lt + + assert sink.empty() + assert hdr_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=2, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=2, dest=2) + + for wait in wait_normal,: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame1.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame1.data[:lm] + + yield hdr_sink.wait() + hdr = hdr_sink.recv() + assert hdr.data[0][0] == (lt < lmin) + assert hdr.data[0][1] == (lt > lmax) + assert hdr.data[0][2] == lrx + assert hdr.data[0][3] == lt + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame2.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame2.data[:lm] + + yield hdr_sink.wait() + hdr = hdr_sink.recv() + assert hdr.data[0][0] == (lt < lmin) + assert hdr.data[0][1] == (lt > lmax) + assert hdr.data[0][2] == lrx + assert hdr.data[0][3] == lt + + assert sink.empty() + assert hdr_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=3, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=3, dest=2) + + test_frame1.last_cycle_user = 1 + + for wait in wait_normal,: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame1.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame1.data[:lm] + + yield hdr_sink.wait() + hdr = hdr_sink.recv() + assert hdr.data[0][0] == (lt < lmin) + assert hdr.data[0][1] == (lt > lmax) + assert hdr.data[0][2] == lrx + assert hdr.data[0][3] == lt + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame2.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame2.data[:lm] + + yield hdr_sink.wait() + hdr = hdr_sink.recv() + assert hdr.data[0][0] == (lt < lmin) + assert hdr.data[0][1] == (lt > lmax) + assert hdr.data[0][2] == lrx + assert hdr.data[0][3] == lt + + assert sink.empty() + assert hdr_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo.v b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo.v new file mode 100644 index 000000000..c8ae07b8f --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo.v @@ -0,0 +1,163 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for axis_frame_length_adjust_fifo + */ +module test_axis_frame_length_adjust_fifo; + +// Parameters +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO_ADDR_WIDTH = 12; +parameter HEADER_FIFO_ADDR_WIDTH = 3; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_hdr_ready = 0; +reg m_axis_tready = 0; +reg [15:0] length_min = 0; +reg [15:0] length_max = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire m_axis_hdr_valid; +wire m_axis_hdr_pad; +wire m_axis_hdr_truncate; +wire [15:0] m_axis_hdr_length; +wire [15:0] m_axis_hdr_original_length; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_hdr_ready, + m_axis_tready, + length_min, + length_max + ); + $to_myhdl( + s_axis_tready, + m_axis_hdr_valid, + m_axis_hdr_pad, + m_axis_hdr_truncate, + m_axis_hdr_length, + m_axis_hdr_original_length, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_frame_length_adjust_fifo.lxt"); + $dumpvars(0, test_axis_frame_length_adjust_fifo); +end + +axis_frame_length_adjust_fifo #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO_ADDR_WIDTH(FRAME_FIFO_ADDR_WIDTH), + .HEADER_FIFO_ADDR_WIDTH(HEADER_FIFO_ADDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_hdr_valid(m_axis_hdr_valid), + .m_axis_hdr_ready(m_axis_hdr_ready), + .m_axis_hdr_pad(m_axis_hdr_pad), + .m_axis_hdr_truncate(m_axis_hdr_truncate), + .m_axis_hdr_length(m_axis_hdr_length), + .m_axis_hdr_original_length(m_axis_hdr_original_length), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Configuration + .length_min(length_min), + .length_max(length_max) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo_64.py new file mode 100755 index 000000000..cb7d0163c --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo_64.py @@ -0,0 +1,369 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_frame_length_adjust_fifo' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_frame_length_adjust.v") +srcs.append("../rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + FRAME_FIFO_ADDR_WIDTH = 9 + HEADER_FIFO_ADDR_WIDTH = 3 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_hdr_ready = Signal(bool(0)) + m_axis_tready = Signal(bool(0)) + length_min = Signal(intbv(0)[16:]) + length_max = Signal(intbv(0)[16:]) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_hdr_valid = Signal(bool(0)) + m_axis_hdr_pad = Signal(bool(0)) + m_axis_hdr_truncate = Signal(bool(0)) + m_axis_hdr_length = Signal(intbv(0)[16:]) + m_axis_hdr_original_length = Signal(intbv(0)[16:]) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + hdr_sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + hdr_sink = axis_ep.AXIStreamSink() + + hdr_sink_logic = hdr_sink.create_logic( + clk, + rst, + tdata=(m_axis_hdr_pad, m_axis_hdr_truncate, m_axis_hdr_length, m_axis_hdr_original_length), + tvalid=m_axis_hdr_valid, + tready=m_axis_hdr_ready, + pause=hdr_sink_pause, + name='hdr_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_hdr_valid=m_axis_hdr_valid, + m_axis_hdr_ready=m_axis_hdr_ready, + m_axis_hdr_pad=m_axis_hdr_pad, + m_axis_hdr_truncate=m_axis_hdr_truncate, + m_axis_hdr_length=m_axis_hdr_length, + m_axis_hdr_original_length=m_axis_hdr_original_length, + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + length_min=length_min, + length_max=length_max + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + length_min.next = 1 + length_max.next = 20 + + for lmax in range(1,18): + for lmin in range(0,lmax+1): + length_min.next = lmin + length_max.next = lmax + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=1, dest=1) + + for wait in wait_normal,: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame.data[:lm] + + yield hdr_sink.wait() + hdr = hdr_sink.recv() + assert hdr.data[0][0] == (lt < lmin) + assert hdr.data[0][1] == (lt > lmax) + assert hdr.data[0][2] == lrx + assert hdr.data[0][3] == lt + + assert sink.empty() + assert hdr_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=2, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=2, dest=2) + + for wait in wait_normal,: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame1.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame1.data[:lm] + + yield hdr_sink.wait() + hdr = hdr_sink.recv() + assert hdr.data[0][0] == (lt < lmin) + assert hdr.data[0][1] == (lt > lmax) + assert hdr.data[0][2] == lrx + assert hdr.data[0][3] == lt + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame2.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame2.data[:lm] + + yield hdr_sink.wait() + hdr = hdr_sink.recv() + assert hdr.data[0][0] == (lt < lmin) + assert hdr.data[0][1] == (lt > lmax) + assert hdr.data[0][2] == lrx + assert hdr.data[0][3] == lt + + assert sink.empty() + assert hdr_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=3, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(bytearray(range(payload_len)), id=3, dest=2) + + test_frame1.last_cycle_user = 1 + + for wait in wait_normal,: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame1.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame1.data[:lm] + + yield hdr_sink.wait() + hdr = hdr_sink.recv() + assert hdr.data[0][0] == (lt < lmin) + assert hdr.data[0][1] == (lt > lmax) + assert hdr.data[0][2] == lrx + assert hdr.data[0][3] == lt + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + lrx = len(rx_frame.data) + lt = len(test_frame2.data) + lm = min(lrx, lt) + assert lrx >= lmin + assert lrx <= lmax + assert rx_frame.data[:lm] == test_frame2.data[:lm] + + yield hdr_sink.wait() + hdr = hdr_sink.recv() + assert hdr.data[0][0] == (lt < lmin) + assert hdr.data[0][1] == (lt > lmax) + assert hdr.data[0][2] == lrx + assert hdr.data[0][3] == lt + + assert sink.empty() + assert hdr_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo_64.v new file mode 100644 index 000000000..f5fdc6d61 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_frame_length_adjust_fifo_64.v @@ -0,0 +1,163 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for axis_frame_length_adjust_fifo + */ +module test_axis_frame_length_adjust_fifo_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter FRAME_FIFO_ADDR_WIDTH = 9; +parameter HEADER_FIFO_ADDR_WIDTH = 3; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_hdr_ready = 0; +reg m_axis_tready = 0; +reg [15:0] length_min = 0; +reg [15:0] length_max = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire m_axis_hdr_valid; +wire m_axis_hdr_pad; +wire m_axis_hdr_truncate; +wire [15:0] m_axis_hdr_length; +wire [15:0] m_axis_hdr_original_length; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_hdr_ready, + m_axis_tready, + length_min, + length_max + ); + $to_myhdl( + s_axis_tready, + m_axis_hdr_valid, + m_axis_hdr_pad, + m_axis_hdr_truncate, + m_axis_hdr_length, + m_axis_hdr_original_length, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_frame_length_adjust_fifo_64.lxt"); + $dumpvars(0, test_axis_frame_length_adjust_fifo_64); +end + +axis_frame_length_adjust_fifo #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO_ADDR_WIDTH(FRAME_FIFO_ADDR_WIDTH), + .HEADER_FIFO_ADDR_WIDTH(HEADER_FIFO_ADDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_hdr_valid(m_axis_hdr_valid), + .m_axis_hdr_ready(m_axis_hdr_ready), + .m_axis_hdr_pad(m_axis_hdr_pad), + .m_axis_hdr_truncate(m_axis_hdr_truncate), + .m_axis_hdr_length(m_axis_hdr_length), + .m_axis_hdr_original_length(m_axis_hdr_original_length), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Configuration + .length_min(length_min), + .length_max(length_max) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_ll_bridge.py b/fpga/lib/eth/lib/axis/tb/test_axis_ll_bridge.py new file mode 100755 index 000000000..1343eefef --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_ll_bridge.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import ll_ep + +module = 'axis_ll_bridge' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + ll_dst_rdy_in_n = Signal(bool(1)) + + # Outputs + ll_data_out = Signal(intbv(0)[DATA_WIDTH:]) + ll_sof_out_n = Signal(bool(1)) + ll_eof_out_n = Signal(bool(1)) + ll_src_rdy_out_n = Signal(bool(1)) + s_axis_tready = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + pause=source_pause, + name='source' + ) + + sink = ll_ep.LocalLinkSink() + + sink_logic = sink.create_logic( + clk, + rst, + data_in=ll_data_out, + sof_in_n=ll_sof_out_n, + eof_in_n=ll_eof_out_n, + src_rdy_in_n=ll_src_rdy_out_n, + dst_rdy_out_n=ll_dst_rdy_in_n, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + + ll_data_out=ll_data_out, + ll_sof_out_n=ll_sof_out_n, + ll_eof_out_n=ll_eof_out_n, + ll_src_rdy_out_n=ll_src_rdy_out_n, + ll_dst_rdy_in_n=ll_dst_rdy_in_n + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = bytearray(b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert bytearray(rx_frame) == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test packet with pauses") + current_test.next = 2 + + test_frame = bytearray(b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert bytearray(rx_frame) == test_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_ll_bridge.v b/fpga/lib/eth/lib/axis/tb/test_axis_ll_bridge.v new file mode 100644 index 000000000..b0ce6f29c --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_ll_bridge.v @@ -0,0 +1,97 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_ll_bridge + */ +module test_axis_ll_bridge; + +// Parameters +parameter DATA_WIDTH = 8; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg ll_dst_rdy_in_n = 1; + +// Outputs +wire [DATA_WIDTH-1:0] ll_data_out; +wire ll_sof_out_n; +wire ll_eof_out_n; +wire ll_src_rdy_out_n; +wire s_axis_tready; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tvalid, + s_axis_tlast, + ll_dst_rdy_in_n + ); + $to_myhdl( + ll_data_out, + ll_sof_out_n, + ll_eof_out_n, + ll_src_rdy_out_n, + s_axis_tready + ); + + // dump file + $dumpfile("test_axis_ll_bridge.lxt"); + $dumpvars(0, test_axis_ll_bridge); +end + +axis_ll_bridge #( + .DATA_WIDTH(DATA_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // axi input + .s_axis_tdata(s_axis_tdata), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + // locallink output + .ll_data_out(ll_data_out), + .ll_sof_out_n(ll_sof_out_n), + .ll_eof_out_n(ll_eof_out_n), + .ll_src_rdy_out_n(ll_src_rdy_out_n), + .ll_dst_rdy_in_n(ll_dst_rdy_in_n) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_mux_4.py b/fpga/lib/eth/lib/axis/tb/test_axis_mux_4.py new file mode 100755 index 000000000..e4c7791ae --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_mux_4.py @@ -0,0 +1,433 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_mux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + m_axis_tready = Signal(bool(0)) + + enable = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_axis_tready_list = [s_axis_tready(i) for i in range(S_COUNT)] + + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tready=s_axis_tready_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + enable=enable, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=2, + dest=1 + ) + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=2 + ) + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + select.next = 2 + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_mux_4.v b/fpga/lib/eth/lib/axis/tb/test_axis_mux_4.v new file mode 100644 index 000000000..a72c24446 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_mux_4.v @@ -0,0 +1,146 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_mux + */ +module test_axis_mux_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; + +reg m_axis_tready = 0; + +reg enable = 0; +reg [1:0] select = 0; + +// Outputs +wire [S_COUNT-1:0] s_axis_tready; + +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready, + enable, + select + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_mux_4.lxt"); + $dumpvars(0, test_axis_mux_4); +end + +axis_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Control + .enable(enable), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_mux_4_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_mux_4_64.py new file mode 100755 index 000000000..31d4a267a --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_mux_4_64.py @@ -0,0 +1,433 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_mux' +testbench = 'test_%s_4_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + m_axis_tready = Signal(bool(0)) + + enable = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_axis_tready_list = [s_axis_tready(i) for i in range(S_COUNT)] + + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tready=s_axis_tready_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + enable=enable, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=2, + dest=1 + ) + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=2 + ) + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + select.next = 2 + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + + while s_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_mux_4_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_mux_4_64.v new file mode 100644 index 000000000..a8d9c280b --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_mux_4_64.v @@ -0,0 +1,146 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_mux + */ +module test_axis_mux_4_64; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; + +reg m_axis_tready = 0; + +reg enable = 0; +reg [1:0] select = 0; + +// Outputs +wire [S_COUNT-1:0] s_axis_tready; + +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready, + enable, + select + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_mux_4_64.lxt"); + $dumpvars(0, test_axis_mux_4_64); +end + +axis_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Control + .enable(enable), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit.py b/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit.py new file mode 100755 index 000000000..522075091 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit.py @@ -0,0 +1,526 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_rate_limit' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + rate_num = Signal(intbv(0)[8:]) + rate_denom = Signal(intbv(0)[8:]) + rate_by_frame = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + rate_num=rate_num, + rate_denom=rate_denom, + rate_by_frame=rate_by_frame + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + reset_stats = Signal(bool(False)) + cur_frame = Signal(bool(False)) + tick_count = Signal(intbv(0)) + byte_count = Signal(intbv(0)) + frame_count = Signal(intbv(0)) + + @always(clk.posedge) + def monitor(): + ctc = int(tick_count) + cbc = int(byte_count) + cfc = int(frame_count) + if reset_stats: + ctc = 0 + cbc = 0 + cfc = 0 + reset_stats.next = 0 + ctc += len(m_axis_tkeep) + if m_axis_tready and m_axis_tvalid: + cbc += bin(m_axis_tkeep).count('1') + if m_axis_tlast: + cur_frame.next = False + elif not cur_frame: + cfc += 1 + cur_frame.next = True + tick_count.next = ctc + byte_count.next = cbc + frame_count.next = cfc + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + rate_num.next = 1 + rate_denom.next = 4 + rate_by_frame.next = 1 + + for frame_mode in (True, False): + print("test frame mode %s" % frame_mode) + rate_by_frame.next = frame_mode + + rate_num.next = 1 + rate_denom.next = 4 + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + yield clk.posedge + print("test 8: various lengths and delays") + current_test.next = 8 + + for rate in ((1,1), (1,2), (1,10), (2,3)): + print("test 8 rate %d / %d" % rate) + rate_num.next = rate[0] + rate_denom.next = rate[1] + + yield delay(100) + + reset_stats.next = 1 + yield clk.posedge + start_time = now() + + lens = [32, 48, 64, 96, 128, 256] + test_frame = [] + + for i in range(len(lens)): + test_frame.append(axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(lens[i])), + id=i, + dest=1 + )) + + for f in test_frame: + source.send(f) + + rx_frame = [] + + for i in range(len(lens)): + yield sink.wait() + rx_frame.append(sink.recv()) + + yield clk.posedge + + while not s_axis_tready: + yield clk.posedge + + stop_time = now() + + assert len(rx_frame) == len(test_frame) + + for i in range(len(lens)): + assert rx_frame[i] == test_frame[i] + + cycle = (stop_time - start_time) / 8 + + print("cycles %d" % cycle) + print("tick count %d" % tick_count) + print("byte count %d" % byte_count) + print("frame count %d" % frame_count) + + assert tick_count == cycle*len(m_axis_tkeep) + assert byte_count == sum(len(f.data) for f in test_frame) + assert frame_count == len(test_frame) + + test_rate = float(rate_num) / float(rate_denom) + meas_rate = float(byte_count) / float(tick_count) + error = (test_rate - meas_rate) / test_rate + + print("test rate %f" % test_rate) + print("meas rate %f" % meas_rate) + print("error %f%%" % (error*100)) + + assert abs(error) < 0.1 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit.v b/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit.v new file mode 100644 index 000000000..79f37ecda --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit.v @@ -0,0 +1,146 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_rate_limit + */ +module test_axis_rate_limit; + +// Parameters +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; +reg [7:0] rate_num = 0; +reg [7:0] rate_denom = 0; +reg rate_by_frame = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready, + rate_num, + rate_denom, + rate_by_frame + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_rate_limit.lxt"); + $dumpvars(0, test_axis_rate_limit); +end + +axis_rate_limit #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Configuration + .rate_num(rate_num), + .rate_denom(rate_denom), + .rate_by_frame(rate_by_frame) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit_64.py new file mode 100755 index 000000000..2ef4ae9f4 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit_64.py @@ -0,0 +1,526 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_rate_limit' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + rate_num = Signal(intbv(0)[8:]) + rate_denom = Signal(intbv(0)[8:]) + rate_by_frame = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + rate_num=rate_num, + rate_denom=rate_denom, + rate_by_frame=rate_by_frame + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + reset_stats = Signal(bool(False)) + cur_frame = Signal(bool(False)) + tick_count = Signal(intbv(0)) + byte_count = Signal(intbv(0)) + frame_count = Signal(intbv(0)) + + @always(clk.posedge) + def monitor(): + ctc = int(tick_count) + cbc = int(byte_count) + cfc = int(frame_count) + if reset_stats: + ctc = 0 + cbc = 0 + cfc = 0 + reset_stats.next = 0 + ctc += len(m_axis_tkeep) + if m_axis_tready and m_axis_tvalid: + cbc += bin(m_axis_tkeep).count('1') + if m_axis_tlast: + cur_frame.next = False + elif not cur_frame: + cfc += 1 + cur_frame.next = True + tick_count.next = ctc + byte_count.next = cbc + frame_count.next = cfc + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + rate_num.next = 1 + rate_denom.next = 4 + rate_by_frame.next = 1 + + for frame_mode in (True, False): + print("test frame mode %s" % frame_mode) + rate_by_frame.next = frame_mode + + rate_num.next = 1 + rate_denom.next = 4 + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + yield clk.posedge + print("test 8: various lengths and delays") + current_test.next = 8 + + for rate in ((1,1), (1,2), (1,10), (2,3)): + print("test 8 rate %d / %d" % rate) + rate_num.next = rate[0] + rate_denom.next = rate[1] + + yield delay(100) + + reset_stats.next = 1 + yield clk.posedge + start_time = now() + + lens = [32, 48, 64, 96, 128, 256] + test_frame = [] + + for i in range(len(lens)): + test_frame.append(axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(lens[i])), + id=i, + dest=1 + )) + + for f in test_frame: + source.send(f) + + rx_frame = [] + + for i in range(len(lens)): + yield sink.wait() + rx_frame.append(sink.recv()) + + yield clk.posedge + + while not s_axis_tready: + yield clk.posedge + + stop_time = now() + + assert len(rx_frame) == len(test_frame) + + for i in range(len(lens)): + assert rx_frame[i] == test_frame[i] + + cycle = (stop_time - start_time) / 8 + + print("cycles %d" % cycle) + print("tick count %d" % tick_count) + print("byte count %d" % byte_count) + print("frame count %d" % frame_count) + + assert tick_count == cycle*len(m_axis_tkeep) + assert byte_count == sum(len(f.data) for f in test_frame) + assert frame_count == len(test_frame) + + test_rate = float(rate_num) / float(rate_denom) + meas_rate = float(byte_count) / float(tick_count) + error = (test_rate - meas_rate) / test_rate + + print("test rate %f" % test_rate) + print("meas rate %f" % meas_rate) + print("error %f%%" % (error*100)) + + assert abs(error) < 0.1 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit_64.v new file mode 100644 index 000000000..1688d88d2 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_rate_limit_64.v @@ -0,0 +1,146 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_rate_limit + */ +module test_axis_rate_limit_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; +reg [7:0] rate_num = 0; +reg [7:0] rate_denom = 0; +reg rate_by_frame = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready, + rate_num, + rate_denom, + rate_by_frame + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_rate_limit_64.lxt"); + $dumpvars(0, test_axis_rate_limit_64); +end + +axis_rate_limit #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Configuration + .rate_num(rate_num), + .rate_denom(rate_denom), + .rate_by_frame(rate_by_frame) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_register.py b/fpga/lib/eth/lib/axis/tb/test_axis_register.py new file mode 100755 index 000000000..f9bcea205 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_register.py @@ -0,0 +1,409 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_register' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + REG_TYPE = 2 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_register.v b/fpga/lib/eth/lib/axis/tb/test_axis_register.v new file mode 100644 index 000000000..52a52c999 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_register.v @@ -0,0 +1,138 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_register + */ +module test_axis_register; + +// Parameters +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter REG_TYPE = 2; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_register.lxt"); + $dumpvars(0, test_axis_register); +end + +axis_register #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .REG_TYPE(REG_TYPE) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_register_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_register_64.py new file mode 100755 index 000000000..9098131ae --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_register_64.py @@ -0,0 +1,409 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_register' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + REG_TYPE = 2 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_register_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_register_64.v new file mode 100644 index 000000000..cf523b601 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_register_64.v @@ -0,0 +1,138 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_register + */ +module test_axis_register_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter REG_TYPE = 2; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_register_64.lxt"); + $dumpvars(0, test_axis_register_64); +end + +axis_register #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .REG_TYPE(REG_TYPE) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo.py b/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo.py new file mode 100755 index 000000000..ea3df9959 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo.py @@ -0,0 +1,469 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_srl_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DEPTH = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(1)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + count = Signal(intbv(0)[3:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + count=count + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + yield clk.posedge + print("test 8: initial sink pause") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + b'\x01\x02\x03', + id=8, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 9: initial sink pause, reset") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + b'\x01\x02\x03', + id=9, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rst.next = 1 + yield clk.posedge + rst.next = 0 + + sink_pause.next = 0 + + yield delay(100) + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo.v b/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo.v new file mode 100644 index 000000000..7b76f852c --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo.v @@ -0,0 +1,143 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_srl_fifo + */ +module test_axis_srl_fifo; + +// Parameters +parameter DEPTH = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +wire [2:0] count; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + count + ); + + // dump file + $dumpfile("test_axis_srl_fifo.lxt"); + $dumpvars(0, test_axis_srl_fifo); +end + +axis_srl_fifo #( + .DEPTH(DEPTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .count(count) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo_64.py new file mode 100755 index 000000000..9458d188e --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo_64.py @@ -0,0 +1,469 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_srl_fifo' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DEPTH = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(1)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + count = Signal(intbv(0)[3:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + count=count + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + yield clk.posedge + print("test 8: initial sink pause") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(24)), + id=8, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 9: initial sink pause, reset") + current_test.next = 9 + + test_frame = axis_ep.AXIStreamFrame( + bytearray(range(24)), + id=9, + dest=1 + ) + + sink_pause.next = 1 + source.send(test_frame) + yield clk.posedge + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rst.next = 1 + yield clk.posedge + rst.next = 0 + + sink_pause.next = 0 + + yield delay(100) + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo_64.v new file mode 100644 index 000000000..33bf0fb78 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_srl_fifo_64.v @@ -0,0 +1,143 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_srl_fifo + */ +module test_axis_srl_fifo_64; + +// Parameters +parameter DEPTH = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +wire [2:0] count; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + count + ); + + // dump file + $dumpfile("test_axis_srl_fifo_64.lxt"); + $dumpvars(0, test_axis_srl_fifo_64); +end + +axis_srl_fifo #( + .DEPTH(DEPTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .count(count) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_srl_register.py b/fpga/lib/eth/lib/axis/tb/test_axis_srl_register.py new file mode 100755 index 000000000..0528bd4ef --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_srl_register.py @@ -0,0 +1,408 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_srl_register' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(1)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_srl_register.v b/fpga/lib/eth/lib/axis/tb/test_axis_srl_register.v new file mode 100644 index 000000000..93f9c1f4f --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_srl_register.v @@ -0,0 +1,136 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_srl_register + */ +module test_axis_srl_register; + +// Parameters +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_srl_register.lxt"); + $dumpvars(0, test_axis_srl_register); +end + +axis_srl_register #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_srl_register_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_srl_register_64.py new file mode 100755 index 000000000..abdd4f7a6 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_srl_register_64.py @@ -0,0 +1,408 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_srl_register' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + LAST_ENABLE = 1 + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(1)) + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tid=s_axis_tid, + tdest=s_axis_tdest, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_srl_register_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_srl_register_64.v new file mode 100644 index 000000000..eb58dbfd3 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_srl_register_64.v @@ -0,0 +1,136 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_srl_register + */ +module test_axis_srl_register_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter LAST_ENABLE = 1; +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_srl_register_64.lxt"); + $dumpvars(0, test_axis_srl_register_64); +end + +axis_srl_register #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_stat_counter.py b/fpga/lib/eth/lib/axis/tb/test_axis_stat_counter.py new file mode 100755 index 000000000..907fca6ef --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_stat_counter.py @@ -0,0 +1,790 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os +import struct + +import axis_ep + +module = 'axis_stat_counter' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + TAG_ENABLE = 1 + TAG_WIDTH = 16 + TICK_COUNT_ENABLE = 1 + TICK_COUNT_WIDTH = 32 + BYTE_COUNT_ENABLE = 1 + BYTE_COUNT_WIDTH = 32 + FRAME_COUNT_ENABLE = 1 + FRAME_COUNT_WIDTH = 32 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + monitor_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + monitor_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + monitor_axis_tvalid = Signal(bool(0)) + monitor_axis_tready = Signal(bool(0)) + monitor_axis_tlast = Signal(bool(0)) + monitor_axis_tuser = Signal(bool(0)) + m_axis_tready = Signal(bool(0)) + + tag = Signal(intbv(16)[TAG_WIDTH:]) + trigger = Signal(bool(0)) + + # Outputs + m_axis_tdata = Signal(intbv(0)[8:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + monitor_sink_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=monitor_axis_tdata, + tkeep=monitor_axis_tkeep, + tvalid=monitor_axis_tvalid, + tready=monitor_axis_tready, + tlast=monitor_axis_tlast, + tuser=monitor_axis_tuser, + pause=source_pause, + name='source' + ) + + monitor_sink = axis_ep.AXIStreamSink() + + monitor_sink_logic = monitor_sink.create_logic( + clk, + rst, + tdata=monitor_axis_tdata, + tkeep=monitor_axis_tkeep, + tvalid=monitor_axis_tvalid, + tready=monitor_axis_tready, + tlast=monitor_axis_tlast, + tuser=monitor_axis_tuser, + pause=monitor_sink_pause, + name='monitor_sink' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + monitor_axis_tdata=monitor_axis_tdata, + monitor_axis_tkeep=monitor_axis_tkeep, + monitor_axis_tvalid=monitor_axis_tvalid, + monitor_axis_tready=monitor_axis_tready, + monitor_axis_tlast=monitor_axis_tlast, + monitor_axis_tuser=monitor_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser, + + tag=tag, + trigger=trigger, + busy=busy + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + tag.next = 1 + + yield clk.posedge + print("test 1: test tick timer") + current_test.next = 1 + + yield clk.posedge + start_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + for i in range(100-1): + yield clk.posedge + + stop_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + # discard first trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame = sink.recv() + + rx_frame_values = struct.unpack(">HLLL", bytes(rx_frame.data)) + cycles = (stop_time - start_time) / 8 + print(rx_frame_values) + + assert rx_frame_values[0] == 1 + assert rx_frame_values[1] == cycles*8 + assert rx_frame_values[1] == 100*8 + + yield delay(100) + + yield clk.posedge + print("test 2: pause sink") + current_test.next = 2 + + yield clk.posedge + start_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + for i in range(100-1): + yield clk.posedge + + stop_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + while trigger or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield clk.posedge + yield clk.posedge + + # discard first trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame = sink.recv() + + rx_frame_values = struct.unpack(">HLLL", bytes(rx_frame.data)) + cycles = (stop_time - start_time) / 8 + print(rx_frame_values) + + assert rx_frame_values[0] == 1 + assert rx_frame_values[1] == cycles*8 + assert rx_frame_values[1] == 100*8 + + yield delay(100) + + yield clk.posedge + print("test 3: test packet") + current_test.next = 3 + + yield clk.posedge + start_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + + yield clk.posedge + stop_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + # discard first trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame = sink.recv() + + rx_frame_values = struct.unpack(">HLLL", bytes(rx_frame.data)) + cycles = (stop_time - start_time) / 8 + print(rx_frame_values) + + assert rx_frame_values[0] == 1 + assert rx_frame_values[1] == cycles*8 + assert rx_frame_values[2] == len(test_frame.data) + assert rx_frame_values[3] == 1 + + yield delay(100) + + yield clk.posedge + print("test 4: longer packet") + current_test.next = 4 + + yield clk.posedge + start_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)) + ) + + source.send(test_frame) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + + yield clk.posedge + stop_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + # discard first trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame = sink.recv() + + rx_frame_values = struct.unpack(">HLLL", bytes(rx_frame.data)) + cycles = (stop_time - start_time) / 8 + print(rx_frame_values) + + assert rx_frame_values[0] == 1 + assert rx_frame_values[1] == cycles*8 + assert rx_frame_values[2] == len(test_frame.data) + assert rx_frame_values[3] == 1 + + yield delay(100) + + yield clk.posedge + print("test 5: test packet with pauses") + current_test.next = 5 + + yield clk.posedge + start_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)) + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + monitor_sink_pause.next = True + yield delay(32) + yield clk.posedge + monitor_sink_pause.next = False + + while monitor_axis_tvalid: + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + + yield clk.posedge + stop_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + # discard first trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame = sink.recv() + + rx_frame_values = struct.unpack(">HLLL", bytes(rx_frame.data)) + cycles = (stop_time - start_time) / 8 + print(rx_frame_values) + + assert rx_frame_values[0] == 1 + assert rx_frame_values[1] == cycles*8 + assert rx_frame_values[2] == len(test_frame.data) + assert rx_frame_values[3] == 1 + + yield delay(100) + + yield clk.posedge + print("test 6: back-to-back packets") + current_test.next = 6 + + yield clk.posedge + start_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + + yield clk.posedge + stop_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + # discard first trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame = sink.recv() + + rx_frame_values = struct.unpack(">HLLL", bytes(rx_frame.data)) + cycles = (stop_time - start_time) / 8 + print(rx_frame_values) + + assert rx_frame_values[0] == 1 + assert rx_frame_values[1] == cycles*8 + assert rx_frame_values[2] == len(test_frame1.data) + len(test_frame2.data) + assert rx_frame_values[3] == 2 + + yield delay(100) + + yield clk.posedge + print("test 7: alternate pause source") + current_test.next = 7 + + yield clk.posedge + start_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + while m_axis_tvalid: + yield clk.posedge + + yield clk.posedge + stop_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + # discard first trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame = sink.recv() + + rx_frame_values = struct.unpack(">HLLL", bytes(rx_frame.data)) + cycles = (stop_time - start_time) / 8 + print(rx_frame_values) + + assert rx_frame_values[0] == 1 + assert rx_frame_values[1] == cycles*8 + assert rx_frame_values[2] == len(test_frame1.data) + len(test_frame2.data) + assert rx_frame_values[3] == 2 + + yield delay(100) + + yield clk.posedge + print("test 8: alternate pause sink") + current_test.next = 8 + + yield clk.posedge + start_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10' + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while monitor_axis_tvalid: + monitor_sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + monitor_sink_pause.next = False + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + + yield clk.posedge + stop_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + # discard first trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame = sink.recv() + + rx_frame_values = struct.unpack(">HLLL", bytes(rx_frame.data)) + cycles = (stop_time - start_time) / 8 + print(rx_frame_values) + + assert rx_frame_values[0] == 1 + assert rx_frame_values[1] == cycles*8 + assert rx_frame_values[2] == len(test_frame1.data) + len(test_frame2.data) + assert rx_frame_values[3] == 2 + + yield delay(100) + + yield clk.posedge + print("test 9: various length packets") + current_test.next = 9 + + yield clk.posedge + start_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + lens = [32, 48, 96, 128, 256] + test_frame = [] + + for i in range(len(lens)): + test_frame.append(axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(lens[i]))) + ) + + for f in test_frame: + source.send(f) + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + + yield clk.posedge + stop_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + # discard first trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame = sink.recv() + + rx_frame_values = struct.unpack(">HLLL", bytes(rx_frame.data)) + cycles = (stop_time - start_time) / 8 + print(rx_frame_values) + + assert rx_frame_values[0] == 1 + assert rx_frame_values[1] == cycles*8 + assert rx_frame_values[2] == sum(len(f.data) for f in test_frame) + assert rx_frame_values[3] == len(test_frame) + + yield delay(100) + + yield clk.posedge + print("test 10: various length packets with intermediate trigger") + current_test.next = 10 + + yield clk.posedge + start_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + + lens = [32, 48, 96, 128, 256] + test_frame = [] + + for i in range(len(lens)): + test_frame.append(axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(lens[i]))) + ) + + for f in test_frame: + source.send(f) + yield clk.posedge + + yield delay(200) + + yield clk.posedge + trigger_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + yield clk.posedge + + while monitor_axis_tvalid: + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + + yield clk.posedge + stop_time = now() + trigger.next = 1 + yield clk.posedge + trigger.next = 0 + yield clk.posedge + + while m_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + # discard first trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame = sink.recv() + + # check second trigger output + rx_frame2 = sink.recv() + + rx_frame_values = struct.unpack(">HLLL", bytes(rx_frame.data)) + cycles = (stop_time - start_time) / 8 + cycles1 = (trigger_time - start_time) / 8 + print(rx_frame_values) + + rx_frame2_values = struct.unpack(">HLLL", bytes(rx_frame2.data)) + cycles2 = (stop_time - trigger_time) / 8 + print(rx_frame2_values) + + assert rx_frame_values[0] == 1 + assert rx_frame2_values[0] == 1 + assert rx_frame_values[1] == cycles1*8 + assert rx_frame2_values[1] == cycles2*8 + assert rx_frame_values[1] + rx_frame2_values[1] == cycles*8 + assert rx_frame_values[2] + rx_frame2_values[2] == sum(len(f.data) for f in test_frame) + assert rx_frame_values[3] + rx_frame2_values[3] == len(test_frame) + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_stat_counter.v b/fpga/lib/eth/lib/axis/tb/test_axis_stat_counter.v new file mode 100644 index 000000000..7b1d3765f --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_stat_counter.v @@ -0,0 +1,130 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for axis_stat_counter + */ +module test_axis_stat_counter; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter TAG_ENABLE = 1; +parameter TAG_WIDTH = 16; +parameter TICK_COUNT_ENABLE = 1; +parameter TICK_COUNT_WIDTH = 32; +parameter BYTE_COUNT_ENABLE = 1; +parameter BYTE_COUNT_WIDTH = 32; +parameter FRAME_COUNT_ENABLE = 1; +parameter FRAME_COUNT_WIDTH = 32; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] monitor_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] monitor_axis_tkeep = 0; +reg monitor_axis_tvalid = 0; +reg monitor_axis_tready = 0; +reg monitor_axis_tlast = 0; +reg monitor_axis_tuser = 0; +reg m_axis_tready = 0; +reg [TAG_WIDTH-1:0] tag = 0; +reg trigger = 0; + +// Outputs +wire [7:0] m_axis_tdata; +wire m_axis_tvalid; +wire m_axis_tlast; +wire m_axis_tuser; +wire busy; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + monitor_axis_tdata, + monitor_axis_tkeep, + monitor_axis_tvalid, + monitor_axis_tready, + monitor_axis_tlast, + monitor_axis_tuser, + m_axis_tready, + tag, + trigger + ); + $to_myhdl( + m_axis_tdata, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser, + busy + ); + + // dump file + $dumpfile("test_axis_stat_counter.lxt"); + $dumpvars(0, test_axis_stat_counter); +end + +axis_stat_counter #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .TAG_ENABLE(TAG_ENABLE), + .TAG_WIDTH(TAG_WIDTH), + .TICK_COUNT_ENABLE(TICK_COUNT_ENABLE), + .TICK_COUNT_WIDTH(TICK_COUNT_WIDTH), + .BYTE_COUNT_ENABLE(BYTE_COUNT_ENABLE), + .BYTE_COUNT_WIDTH(BYTE_COUNT_WIDTH), + .FRAME_COUNT_ENABLE(FRAME_COUNT_ENABLE), + .FRAME_COUNT_WIDTH(FRAME_COUNT_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // axi monitor input + .monitor_axis_tkeep(monitor_axis_tkeep), + .monitor_axis_tvalid(monitor_axis_tvalid), + .monitor_axis_tready(monitor_axis_tready), + .monitor_axis_tlast(monitor_axis_tlast), + // axi output + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + // configuration + .tag(tag), + .trigger(trigger), + // status + .busy(busy) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4.py b/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4.py new file mode 100755 index 000000000..0958f2ff8 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4.py @@ -0,0 +1,445 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_switch' +testbench = 'test_%s_4x4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_register.v") +srcs.append("../rtl/arbiter.v") +srcs.append("../rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + M_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_WIDTH = (M_COUNT+1).bit_length() + USER_ENABLE = 1 + USER_WIDTH = 1 + M_BASE = [0, 1, 2, 3] + M_TOP = [0, 1, 2, 3] + M_CONNECT = [0b1111]*M_COUNT + S_REG_TYPE = 0 + M_REG_TYPE = 2 + ARB_TYPE = "ROUND_ROBIN" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + m_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_axis_tready = ConcatSignal(*reversed(m_axis_tready_list)) + + # Outputs + s_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_axis_tready_list = [s_axis_tready(i) for i in range(S_COUNT)] + + m_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_axis_tdata_list = [m_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_axis_tkeep_list = [m_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_axis_tvalid_list = [m_axis_tvalid(i) for i in range(M_COUNT)] + m_axis_tlast_list = [m_axis_tlast(i) for i in range(M_COUNT)] + m_axis_tid_list = [m_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_axis_tdest_list = [m_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_axis_tuser_list = [m_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tready=s_axis_tready_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + for k in range(M_COUNT): + s = axis_ep.AXIStreamSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + tdata=m_axis_tdata_list[k], + tkeep=m_axis_tkeep_list[k], + tvalid=m_axis_tvalid_list[k], + tready=m_axis_tready_list[k], + tlast=m_axis_tlast_list[k], + tid=m_axis_tid_list[k], + tdest=m_axis_tdest_list[k], + tuser=m_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + def wait_pause_sink(): + while s_axis_tvalid: + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: 0123 -> 0123") + current_test.next = 1 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x01\x00\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x01\x01\x01\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x01\x02\x02\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=2, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x01\x03\x03\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=3, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + yield delay(100) + + yield clk.posedge + print("test 2: 0123 -> 3210") + current_test.next = 2 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x03\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=3) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x01\x02\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=1, dest=2) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x02\x01\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=2, dest=1) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x03\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=3, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame3 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame2 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame1 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame0 + + yield delay(100) + + yield clk.posedge + print("test 3: 0000 -> 0123") + current_test.next = 3 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x00\x01\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x00\x02\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x00\x03\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + yield delay(100) + + yield clk.posedge + print("test 4: 0123 -> 0000") + current_test.next = 4 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x01\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=1, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x02\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=2, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x03\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=3, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source_list[0].send(test_frame0) + yield clk.posedge + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + yield delay(100) + + yield clk.posedge + print("test 1: bad decoding") + current_test.next = 1 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x01\x00\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x01\x01\x01\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x01\x02\x04\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=2, dest=4) + test_frame3 = axis_ep.AXIStreamFrame(b'\x01\x03\x05\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=3, dest=5) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4.v b/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4.v new file mode 100644 index 000000000..7999b4a30 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4.v @@ -0,0 +1,148 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for axis_switch + */ +module test_axis_switch_4x4; + +// Parameters +parameter S_COUNT = 4; +parameter M_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_WIDTH = $clog2(M_COUNT+1); +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter M_BASE = {3'd3, 3'd2, 3'd1, 3'd0}; +parameter M_TOP = {3'd3, 3'd2, 3'd1, 3'd0}; +parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}; +parameter S_REG_TYPE = 0; +parameter M_REG_TYPE = 2; +parameter ARB_TYPE = "ROUND_ROBIN"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; +reg [M_COUNT-1:0] m_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_axis_tready; +wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep; +wire [M_COUNT-1:0] m_axis_tvalid; +wire [M_COUNT-1:0] m_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_switch_4x4.lxt"); + $dumpvars(0, test_axis_switch_4x4); +end + +axis_switch #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .M_BASE(M_BASE), + .M_TOP(M_TOP), + .M_CONNECT(M_CONNECT), + .S_REG_TYPE(S_REG_TYPE), + .M_REG_TYPE(M_REG_TYPE), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4_64.py new file mode 100755 index 000000000..4f2082e39 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4_64.py @@ -0,0 +1,445 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_switch' +testbench = 'test_%s_4x4_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_register.v") +srcs.append("../rtl/arbiter.v") +srcs.append("../rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + M_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_WIDTH = (M_COUNT+1).bit_length() + USER_ENABLE = 1 + USER_WIDTH = 1 + M_BASE = [0, 1, 2, 3] + M_TOP = [0, 1, 2, 3] + M_CONNECT = [0b1111]*M_COUNT + S_REG_TYPE = 0 + M_REG_TYPE = 2 + ARB_TYPE = "ROUND_ROBIN" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + m_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_axis_tready = ConcatSignal(*reversed(m_axis_tready_list)) + + # Outputs + s_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_axis_tready_list = [s_axis_tready(i) for i in range(S_COUNT)] + + m_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_axis_tdata_list = [m_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_axis_tkeep_list = [m_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_axis_tvalid_list = [m_axis_tvalid(i) for i in range(M_COUNT)] + m_axis_tlast_list = [m_axis_tlast(i) for i in range(M_COUNT)] + m_axis_tid_list = [m_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_axis_tdest_list = [m_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_axis_tuser_list = [m_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tready=s_axis_tready_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + for k in range(M_COUNT): + s = axis_ep.AXIStreamSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + tdata=m_axis_tdata_list[k], + tkeep=m_axis_tkeep_list[k], + tvalid=m_axis_tvalid_list[k], + tready=m_axis_tready_list[k], + tlast=m_axis_tlast_list[k], + tid=m_axis_tid_list[k], + tdest=m_axis_tdest_list[k], + tuser=m_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + def wait_pause_sink(): + while s_axis_tvalid: + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: 0123 -> 0123") + current_test.next = 1 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x01\x00\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x01\x01\x01\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x01\x02\x02\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=2, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x01\x03\x03\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=3, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + yield delay(100) + + yield clk.posedge + print("test 2: 0123 -> 3210") + current_test.next = 2 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x03\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=3) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x01\x02\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=1, dest=2) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x02\x01\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=2, dest=1) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x03\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=3, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame3 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame2 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame1 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame0 + + yield delay(100) + + yield clk.posedge + print("test 3: 0000 -> 0123") + current_test.next = 3 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x00\x01\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x00\x02\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x00\x03\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + yield delay(100) + + yield clk.posedge + print("test 4: 0123 -> 0000") + current_test.next = 4 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x01\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=1, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x02\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=2, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x03\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=3, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source_list[0].send(test_frame0) + yield clk.posedge + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + yield delay(100) + + yield clk.posedge + print("test 1: bad decoding") + current_test.next = 1 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x01\x00\x00\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x01\x01\x01\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x01\x02\x04\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=2, dest=4) + test_frame3 = axis_ep.AXIStreamFrame(b'\x01\x03\x05\xFF\x01\x02\x03\x04\x05\x06\x07\x08', id=3, dest=5) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4_64.v new file mode 100644 index 000000000..2c677478f --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_switch_4x4_64.v @@ -0,0 +1,148 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for axis_switch + */ +module test_axis_switch_4x4_64; + +// Parameters +parameter S_COUNT = 4; +parameter M_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_WIDTH = $clog2(M_COUNT+1); +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter M_BASE = {3'd3, 3'd2, 3'd1, 3'd0}; +parameter M_TOP = {3'd3, 3'd2, 3'd1, 3'd0}; +parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}; +parameter S_REG_TYPE = 0; +parameter M_REG_TYPE = 2; +parameter ARB_TYPE = "ROUND_ROBIN"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; +reg [M_COUNT-1:0] m_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_axis_tready; +wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep; +wire [M_COUNT-1:0] m_axis_tvalid; +wire [M_COUNT-1:0] m_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_switch_4x4_64.lxt"); + $dumpvars(0, test_axis_switch_4x4_64); +end + +axis_switch #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .M_BASE(M_BASE), + .M_TOP(M_TOP), + .M_CONNECT(M_CONNECT), + .S_REG_TYPE(S_REG_TYPE), + .M_REG_TYPE(M_REG_TYPE), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_tap.py b/fpga/lib/eth/lib/axis/tb/test_axis_tap.py new file mode 100755 index 000000000..ee89b457a --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_tap.py @@ -0,0 +1,434 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_tap' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + tap_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tap_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + tap_axis_tvalid = Signal(bool(0)) + tap_axis_tready = Signal(bool(1)) + tap_axis_tlast = Signal(bool(0)) + tap_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + tap_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + tap_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=tap_axis_tdata, + tkeep=tap_axis_tkeep, + tvalid=tap_axis_tvalid, + tready=tap_axis_tready, + tlast=tap_axis_tlast, + tid=tap_axis_tid, + tdest=tap_axis_tdest, + tuser=tap_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + tap_axis_tdata=tap_axis_tdata, + tap_axis_tkeep=tap_axis_tkeep, + tap_axis_tvalid=tap_axis_tvalid, + tap_axis_tready=tap_axis_tready, + tap_axis_tlast=tap_axis_tlast, + tap_axis_tid=tap_axis_tid, + tap_axis_tdest=tap_axis_tdest, + tap_axis_tuser=tap_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: test packet with source pause") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: test packet with sink pause") + current_test.next = 4 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=4, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.last_cycle_user + + yield delay(100) + + yield clk.posedge + print("test 5: back-to-back packets") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause source") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while tap_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: alternate pause sink") + current_test.next = 7 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=7, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while tap_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.last_cycle_user + + yield delay(100) + + yield clk.posedge + print("test 8: tuser assert") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=8, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_tap.v b/fpga/lib/eth/lib/axis/tb/test_axis_tap.v new file mode 100644 index 000000000..2bb2516aa --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_tap.v @@ -0,0 +1,138 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for axis_tap + */ +module test_axis_tap; + +// Parameters +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] tap_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] tap_axis_tkeep = 0; +reg tap_axis_tvalid = 0; +reg tap_axis_tready = 0; +reg tap_axis_tlast = 0; +reg [ID_WIDTH-1:0] tap_axis_tid = 0; +reg [DEST_WIDTH-1:0] tap_axis_tdest = 0; +reg [USER_WIDTH-1:0] tap_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + tap_axis_tdata, + tap_axis_tkeep, + tap_axis_tvalid, + tap_axis_tready, + tap_axis_tlast, + tap_axis_tid, + tap_axis_tdest, + tap_axis_tuser, + m_axis_tready + ); + $to_myhdl( + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_tap.lxt"); + $dumpvars(0, test_axis_tap); +end + +axis_tap #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI tap + .tap_axis_tdata(tap_axis_tdata), + .tap_axis_tkeep(tap_axis_tkeep), + .tap_axis_tvalid(tap_axis_tvalid), + .tap_axis_tready(tap_axis_tready), + .tap_axis_tlast(tap_axis_tlast), + .tap_axis_tid(tap_axis_tid), + .tap_axis_tdest(tap_axis_tdest), + .tap_axis_tuser(tap_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_tap_64.py b/fpga/lib/eth/lib/axis/tb/test_axis_tap_64.py new file mode 100755 index 000000000..f1edf5e77 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_tap_64.py @@ -0,0 +1,434 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'axis_tap' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + tap_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tap_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + tap_axis_tvalid = Signal(bool(0)) + tap_axis_tready = Signal(bool(1)) + tap_axis_tlast = Signal(bool(0)) + tap_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + tap_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + tap_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + m_axis_tready = Signal(bool(0)) + + # Outputs + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=tap_axis_tdata, + tkeep=tap_axis_tkeep, + tvalid=tap_axis_tvalid, + tready=tap_axis_tready, + tlast=tap_axis_tlast, + tid=tap_axis_tid, + tdest=tap_axis_tdest, + tuser=tap_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tid=m_axis_tid, + tdest=m_axis_tdest, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + tap_axis_tdata=tap_axis_tdata, + tap_axis_tkeep=tap_axis_tkeep, + tap_axis_tvalid=tap_axis_tvalid, + tap_axis_tready=tap_axis_tready, + tap_axis_tlast=tap_axis_tlast, + tap_axis_tid=tap_axis_tid, + tap_axis_tdest=tap_axis_tdest, + tap_axis_tuser=tap_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=1, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=2, + dest=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: test packet with source pause") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=3, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: test packet with sink pause") + current_test.next = 4 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=4, + dest=1 + ) + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.last_cycle_user + + yield delay(100) + + yield clk.posedge + print("test 5: back-to-back packets") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=5, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause source") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=6, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while tap_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: alternate pause sink") + current_test.next = 7 + + test_frame1 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=7, + dest=1 + ) + test_frame2 = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + bytearray(range(256)), + id=7, + dest=2 + ) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while tap_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.last_cycle_user + + yield delay(100) + + yield clk.posedge + print("test 8: tuser assert") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame( + b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10', + id=8, + dest=1, + last_cycle_user=1 + ) + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + assert rx_frame.last_cycle_user + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_axis_tap_64.v b/fpga/lib/eth/lib/axis/tb/test_axis_tap_64.v new file mode 100644 index 000000000..d4a93b1a9 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_axis_tap_64.v @@ -0,0 +1,138 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for axis_tap + */ +module test_axis_tap_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] tap_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] tap_axis_tkeep = 0; +reg tap_axis_tvalid = 0; +reg tap_axis_tready = 0; +reg tap_axis_tlast = 0; +reg [ID_WIDTH-1:0] tap_axis_tid = 0; +reg [DEST_WIDTH-1:0] tap_axis_tdest = 0; +reg [USER_WIDTH-1:0] tap_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [ID_WIDTH-1:0] m_axis_tid; +wire [DEST_WIDTH-1:0] m_axis_tdest; +wire [USER_WIDTH-1:0] m_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + tap_axis_tdata, + tap_axis_tkeep, + tap_axis_tvalid, + tap_axis_tready, + tap_axis_tlast, + tap_axis_tid, + tap_axis_tdest, + tap_axis_tuser, + m_axis_tready + ); + $to_myhdl( + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser + ); + + // dump file + $dumpfile("test_axis_tap_64.lxt"); + $dumpvars(0, test_axis_tap_64); +end + +axis_tap #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI tap + .tap_axis_tdata(tap_axis_tdata), + .tap_axis_tkeep(tap_axis_tkeep), + .tap_axis_tvalid(tap_axis_tvalid), + .tap_axis_tready(tap_axis_tready), + .tap_axis_tlast(tap_axis_tlast), + .tap_axis_tid(tap_axis_tid), + .tap_axis_tdest(tap_axis_tdest), + .tap_axis_tuser(tap_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_ll_axis_bridge.py b/fpga/lib/eth/lib/axis/tb/test_ll_axis_bridge.py new file mode 100755 index 000000000..38df65cd9 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_ll_axis_bridge.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import ll_ep + +module = 'll_axis_bridge' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + ll_data_in = Signal(intbv(0)[DATA_WIDTH:]) + ll_sof_in_n = Signal(bool(1)) + ll_eof_in_n = Signal(bool(1)) + ll_src_rdy_in_n = Signal(bool(1)) + m_axis_tready = Signal(bool(0)) + + # Outputs + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + ll_dst_rdy_out_n = Signal(bool(1)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = ll_ep.LocalLinkSource() + + source_logic = source.create_logic( + clk, + rst, + data_out=ll_data_in, + sof_out_n=ll_sof_in_n, + eof_out_n=ll_eof_in_n, + src_rdy_out_n=ll_src_rdy_in_n, + dst_rdy_in_n=ll_dst_rdy_out_n, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + ll_data_in=ll_data_in, + ll_sof_in_n=ll_sof_in_n, + ll_eof_in_n=ll_eof_in_n, + ll_src_rdy_in_n=ll_src_rdy_in_n, + ll_dst_rdy_out_n=ll_dst_rdy_out_n, + + m_axis_tdata=m_axis_tdata, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = bytearray(b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert bytearray(rx_frame) == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test packet with pauses") + current_test.next = 2 + + test_frame = bytearray(b'\xDA\xD1\xD2\xD3\xD4\xD5' + + b'\x5A\x51\x52\x53\x54\x55' + + b'\x80\x00' + + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert bytearray(rx_frame) == test_frame + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_ll_axis_bridge.v b/fpga/lib/eth/lib/axis/tb/test_ll_axis_bridge.v new file mode 100644 index 000000000..ba2572af4 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_ll_axis_bridge.v @@ -0,0 +1,97 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ll_axis_bridge + */ +module test_ll_axis_bridge; + +// Parameters +parameter DATA_WIDTH = 8; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] ll_data_in = 0; +reg ll_sof_in_n = 1; +reg ll_eof_in_n = 1; +reg ll_src_rdy_in_n = 1; +reg m_axis_tready = 0; + +// Outputs +wire ll_dst_rdy_out_n; +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire m_axis_tvalid; +wire m_axis_tlast; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + ll_data_in, + ll_sof_in_n, + ll_eof_in_n, + ll_src_rdy_in_n, + m_axis_tready + ); + $to_myhdl( + m_axis_tdata, + m_axis_tvalid, + m_axis_tlast, + ll_dst_rdy_out_n + ); + + // dump file + $dumpfile("test_ll_axis_bridge.lxt"); + $dumpvars(0, test_ll_axis_bridge); +end + +ll_axis_bridge #( + .DATA_WIDTH(DATA_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // locallink input + .ll_data_in(ll_data_in), + .ll_sof_in_n(ll_sof_in_n), + .ll_eof_in_n(ll_eof_in_n), + .ll_src_rdy_in_n(ll_src_rdy_in_n), + .ll_dst_rdy_out_n(ll_dst_rdy_out_n), + // axi output + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast) +); + +endmodule diff --git a/fpga/lib/eth/lib/axis/tb/test_priority_encoder.py b/fpga/lib/eth/lib/axis/tb/test_priority_encoder.py new file mode 100755 index 000000000..f6747e329 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_priority_encoder.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +module = 'priority_encoder' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + WIDTH = 32 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_unencoded = Signal(intbv(0)[WIDTH:]) + + # Outputs + output_valid = Signal(bool(0)) + output_encoded = Signal(intbv(0)[5:]) + output_unencoded = Signal(intbv(0)[WIDTH:]) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + input_unencoded=input_unencoded, + + output_valid=output_valid, + output_encoded=output_encoded, + output_unencoded=output_unencoded + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + print("test 1: one bit") + current_test.next = 1 + + for i in range(32): + input_unencoded.next = 1 << i + + yield clk.posedge + + assert output_encoded == i + assert output_unencoded == 1 << i + + yield delay(100) + + yield clk.posedge + + print("test 2: two bits") + current_test.next = 2 + + for i in range(32): + for j in range(32): + + input_unencoded.next = (1 << i) | (1 << j) + + yield clk.posedge + + assert output_encoded == max(i,j) + assert output_unencoded == 1 << max(i,j) + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/lib/axis/tb/test_priority_encoder.v b/fpga/lib/eth/lib/axis/tb/test_priority_encoder.v new file mode 100644 index 000000000..07c398880 --- /dev/null +++ b/fpga/lib/eth/lib/axis/tb/test_priority_encoder.v @@ -0,0 +1,78 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for priority_encoder + */ +module test_priority_encoder; + +// Parameters +localparam WIDTH = 32; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [WIDTH-1:0] input_unencoded = 0; + +// Outputs +wire output_valid; +wire [$clog2(WIDTH)-1:0] output_encoded; +wire [WIDTH-1:0] output_unencoded; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + input_unencoded + ); + $to_myhdl( + output_valid, + output_encoded, + output_unencoded + ); + + // dump file + $dumpfile("test_priority_encoder.lxt"); + $dumpvars(0, test_priority_encoder); +end + +priority_encoder #( + .WIDTH(WIDTH) +) +UUT ( + .input_unencoded(input_unencoded), + .output_valid(output_valid), + .output_encoded(output_encoded), + .output_unencoded(output_unencoded) +); + +endmodule diff --git a/fpga/lib/eth/lib/update-axis.sh b/fpga/lib/eth/lib/update-axis.sh new file mode 100755 index 000000000..ccc2eaa81 --- /dev/null +++ b/fpga/lib/eth/lib/update-axis.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +# Git subtree manager +# Alex Forencich +# This script facilitates easy management of subtrees +# included in larger repositories as this script can +# be included in the repository itself. + +# Settings +# uncomment to use --squash +#squash="yes" +# Remote repository +repo="git@github.com:alexforencich/verilog-axis.git" +# Remote name +remote="axis" +# Subdirectory to store code in +# (relative to repo root or to script location) +#subdir="axis" +rel_subdir="axis" +# Remote branch +branch="master" +# Backport branch name (only used for pushing) +backportbranch="${remote}backport" +# Add commit message +addmsg="added ${remote} as a subproject" +# Merge commit message +mergemsg="merged changes in ${remote}" + +# Usage +# add - adds subtree +# pull - default, pulls from remote +# push - pushes to remote + +# determine repo absolute path +if [ -n "$rel_subdir" ]; then + # cd to script dir + SOURCE="${BASH_SOURCE[0]}" + while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located + done + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + + cd "$DIR" + + # relative path to script dir + git-absolute-path () { + fullpath=$(readlink -f "$1") + gitroot="$(git rev-parse --show-toplevel)" || return 1 + [[ "$fullpath" =~ "$gitroot" ]] && echo "${fullpath/$gitroot\//}" + } + + subdir="$(git-absolute-path .)/$rel_subdir" +fi + +squashflag="" + +cd $(git rev-parse --show-toplevel) + +if [ $squash ]; then + squashflag="--squash" +fi + +action="pull" + +if [ ! -d "$subdir" ]; then + action="add" +fi + +if [ -n "$1" ]; then + action="$1" +fi + +# array contains value +# usage: contains array value +function contains() { + local n=$# + local value=${!n} + for ((i=1;i < $n;i++)) { + if [ "${!i}" == "${value}" ]; then + echo "y" + return 0 + fi + } + echo "n" + return 1 +} + +case "$action" in + add) + if [ $(contains $(git remote) "$remote") != "y" ]; then + git remote add "$remote" "$repo" + fi + git fetch "$remote" + git subtree add -P "$subdir" $squashflag -m "$addmsg" "$remote/$branch" + ;; + pull) + if [ $(contains $(git remote) "$remote") != "y" ]; then + git remote add "$remote" "$repo" + fi + git fetch "$remote" + git subtree merge -P "$subdir" $squashflag -m "$mergemsg" "$remote/$branch" + ;; + push) + if [ $(contains $(git remote) "$remote") != "y" ]; then + git remote add "$remote" "$repo" + fi + git subtree split -P "$subdir" -b "$backportbranch" + git push "$remote" "$backportbranch:$branch" + ;; + *) + echo "Error: unknown action!" + exit 1 +esac + +exit 0 + diff --git a/fpga/lib/eth/rtl/arp.v b/fpga/lib/eth/rtl/arp.v new file mode 100644 index 000000000..47a0f3e10 --- /dev/null +++ b/fpga/lib/eth/rtl/arp.v @@ -0,0 +1,416 @@ +/* + +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 1ns / 1ps + +/* + * ARP block for IPv4, ethernet frame interface + */ +module arp #( + parameter CACHE_ADDR_WIDTH = 9, + parameter REQUEST_RETRY_COUNT = 4, + parameter REQUEST_RETRY_INTERVAL = 125000000*2, + parameter REQUEST_TIMEOUT = 125000000*30 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * ARP requests + */ + input wire arp_request_valid, + output wire arp_request_ready, + input wire [31:0] arp_request_ip, + output wire arp_response_valid, + input wire arp_response_ready, + output wire arp_response_error, + output wire [47:0] arp_response_mac, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_cache +); + +localparam [15:0] + ARP_OPER_ARP_REQUEST = 16'h0001, + ARP_OPER_ARP_REPLY = 16'h0002, + ARP_OPER_INARP_REQUEST = 16'h0008, + ARP_OPER_INARP_REPLY = 16'h0009; + +wire incoming_frame_valid; +reg incoming_frame_ready; +wire [47:0] incoming_eth_dest_mac; +wire [47:0] incoming_eth_src_mac; +wire [15:0] incoming_eth_type; +wire [15:0] incoming_arp_htype; +wire [15:0] incoming_arp_ptype; +wire [7:0] incoming_arp_hlen; +wire [7:0] incoming_arp_plen; +wire [15:0] incoming_arp_oper; +wire [47:0] incoming_arp_sha; +wire [31:0] incoming_arp_spa; +wire [47:0] incoming_arp_tha; +wire [31:0] incoming_arp_tpa; + +/* + * ARP frame processing + */ +arp_eth_rx +arp_eth_rx_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // ARP frame output + .m_frame_valid(incoming_frame_valid), + .m_frame_ready(incoming_frame_ready), + .m_eth_dest_mac(incoming_eth_dest_mac), + .m_eth_src_mac(incoming_eth_src_mac), + .m_eth_type(incoming_eth_type), + .m_arp_htype(incoming_arp_htype), + .m_arp_ptype(incoming_arp_ptype), + .m_arp_hlen(incoming_arp_hlen), + .m_arp_plen(incoming_arp_plen), + .m_arp_oper(incoming_arp_oper), + .m_arp_sha(incoming_arp_sha), + .m_arp_spa(incoming_arp_spa), + .m_arp_tha(incoming_arp_tha), + .m_arp_tpa(incoming_arp_tpa), + // Status signals + .busy(), + .error_header_early_termination(), + .error_invalid_header() +); + +reg outgoing_frame_valid_reg = 1'b0, outgoing_frame_valid_next; +wire outgoing_frame_ready; +reg [47:0] outgoing_eth_dest_mac_reg = 48'd0, outgoing_eth_dest_mac_next; +reg [15:0] outgoing_arp_oper_reg = 16'd0, outgoing_arp_oper_next; +reg [47:0] outgoing_arp_tha_reg = 48'd0, outgoing_arp_tha_next; +reg [31:0] outgoing_arp_tpa_reg = 32'd0, outgoing_arp_tpa_next; + +arp_eth_tx +arp_eth_tx_inst ( + .clk(clk), + .rst(rst), + // ARP frame input + .s_frame_valid(outgoing_frame_valid_reg), + .s_frame_ready(outgoing_frame_ready), + .s_eth_dest_mac(outgoing_eth_dest_mac_reg), + .s_eth_src_mac(local_mac), + .s_eth_type(16'h0806), + .s_arp_htype(16'h0001), + .s_arp_ptype(16'h0800), + .s_arp_oper(outgoing_arp_oper_reg), + .s_arp_sha(local_mac), + .s_arp_spa(local_ip), + .s_arp_tha(outgoing_arp_tha_reg), + .s_arp_tpa(outgoing_arp_tpa_reg), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy() +); + +reg cache_query_request_valid_reg = 1'b0, cache_query_request_valid_next; +reg [31:0] cache_query_request_ip_reg = 32'd0, cache_query_request_ip_next; +wire cache_query_response_valid; +wire cache_query_response_error; +wire [47:0] cache_query_response_mac; + +reg cache_write_request_valid_reg = 1'b0, cache_write_request_valid_next; +reg [31:0] cache_write_request_ip_reg = 32'd0, cache_write_request_ip_next; +reg [47:0] cache_write_request_mac_reg = 48'd0, cache_write_request_mac_next; + +/* + * ARP cache + */ +arp_cache #( + .CACHE_ADDR_WIDTH(CACHE_ADDR_WIDTH) +) +arp_cache_inst ( + .clk(clk), + .rst(rst), + // Query cache + .query_request_valid(cache_query_request_valid_reg), + .query_request_ready(), + .query_request_ip(cache_query_request_ip_reg), + .query_response_valid(cache_query_response_valid), + .query_response_ready(1'b1), + .query_response_error(cache_query_response_error), + .query_response_mac(cache_query_response_mac), + // Write cache + .write_request_valid(cache_write_request_valid_reg), + .write_request_ready(), + .write_request_ip(cache_write_request_ip_reg), + .write_request_mac(cache_write_request_mac_reg), + // Configuration + .clear_cache(clear_cache) +); + +reg arp_request_operation_reg = 1'b0, arp_request_operation_next; + +reg arp_request_ready_reg = 1'b0, arp_request_ready_next; +reg [31:0] arp_request_ip_reg = 32'd0, arp_request_ip_next; + +reg arp_response_valid_reg = 1'b0, arp_response_valid_next; +reg arp_response_error_reg = 1'b0, arp_response_error_next; +reg [47:0] arp_response_mac_reg = 48'd0, arp_response_mac_next; + +reg [5:0] arp_request_retry_cnt_reg = 6'd0, arp_request_retry_cnt_next; +reg [35:0] arp_request_timer_reg = 36'd0, arp_request_timer_next; + +assign arp_request_ready = arp_request_ready_reg; + +assign arp_response_valid = arp_response_valid_reg; +assign arp_response_error = arp_response_error_reg; +assign arp_response_mac = arp_response_mac_reg; + +always @* begin + incoming_frame_ready = 1'b0; + + outgoing_frame_valid_next = outgoing_frame_valid_reg && !outgoing_frame_ready; + outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg; + outgoing_arp_oper_next = outgoing_arp_oper_reg; + outgoing_arp_tha_next = outgoing_arp_tha_reg; + outgoing_arp_tpa_next = outgoing_arp_tpa_reg; + + cache_query_request_valid_next = 1'b0; + cache_query_request_ip_next = cache_query_request_ip_reg; + + cache_write_request_valid_next = 1'b0; + cache_write_request_mac_next = cache_write_request_mac_reg; + cache_write_request_ip_next = cache_write_request_ip_reg; + + arp_request_ready_next = 1'b0; + arp_request_ip_next = arp_request_ip_reg; + arp_request_operation_next = arp_request_operation_reg; + arp_request_retry_cnt_next = arp_request_retry_cnt_reg; + arp_request_timer_next = arp_request_timer_reg; + arp_response_valid_next = arp_response_valid_reg && !arp_response_ready; + arp_response_error_next = 1'b0; + arp_response_mac_next = 48'd0; + + // manage incoming frames + incoming_frame_ready = outgoing_frame_ready; + if (incoming_frame_valid && incoming_frame_ready) begin + if (incoming_eth_type == 16'h0806 && incoming_arp_htype == 16'h0001 && incoming_arp_ptype == 16'h0800) begin + // store sender addresses in cache + cache_write_request_valid_next = 1'b1; + cache_write_request_ip_next = incoming_arp_spa; + cache_write_request_mac_next = incoming_arp_sha; + if (incoming_arp_oper == ARP_OPER_ARP_REQUEST) begin + // ARP request + if (incoming_arp_tpa == local_ip) begin + // send reply frame to valid incoming request + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = incoming_eth_src_mac; + outgoing_arp_oper_next = ARP_OPER_ARP_REPLY; + outgoing_arp_tha_next = incoming_arp_sha; + outgoing_arp_tpa_next = incoming_arp_spa; + end + end else if (incoming_arp_oper == ARP_OPER_INARP_REQUEST) begin + // INARP request + if (incoming_arp_tha == local_mac) begin + // send reply frame to valid incoming request + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = incoming_eth_src_mac; + outgoing_arp_oper_next = ARP_OPER_INARP_REPLY; + outgoing_arp_tha_next = incoming_arp_sha; + outgoing_arp_tpa_next = incoming_arp_spa; + end + end + end + end + + // manage ARP lookup requests + if (arp_request_operation_reg) begin + arp_request_ready_next = 1'b0; + cache_query_request_valid_next = 1'b1; + arp_request_timer_next = arp_request_timer_reg - 1; + // if we got a response, it will go in the cache, so when the query succeds, we're done + if (cache_query_response_valid && !cache_query_response_error) begin + arp_request_operation_next = 1'b0; + cache_query_request_valid_next = 1'b0; + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b0; + arp_response_mac_next = cache_query_response_mac; + end + // timer timeout + if (arp_request_timer_reg == 0) begin + if (arp_request_retry_cnt_reg > 0) begin + // have more retries + // send ARP request frame + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = 48'hffffffffffff; + outgoing_arp_oper_next = ARP_OPER_ARP_REQUEST; + outgoing_arp_tha_next = 48'h000000000000; + outgoing_arp_tpa_next = arp_request_ip_reg; + arp_request_retry_cnt_next = arp_request_retry_cnt_reg - 1; + if (arp_request_retry_cnt_reg > 1) begin + arp_request_timer_next = REQUEST_RETRY_INTERVAL; + end else begin + arp_request_timer_next = REQUEST_TIMEOUT; + end + end else begin + // out of retries + arp_request_operation_next = 1'b0; + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b1; + cache_query_request_valid_next = 1'b0; + end + end + end else begin + arp_request_ready_next = !arp_response_valid_next; + if (cache_query_request_valid_reg) begin + cache_query_request_valid_next = 1'b1; + if (cache_query_response_valid) begin + if (cache_query_response_error) begin + arp_request_operation_next = 1'b1; + // send ARP request frame + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = 48'hffffffffffff; + outgoing_arp_oper_next = ARP_OPER_ARP_REQUEST; + outgoing_arp_tha_next = 48'h000000000000; + outgoing_arp_tpa_next = arp_request_ip_reg; + arp_request_retry_cnt_next = REQUEST_RETRY_COUNT-1; + arp_request_timer_next = REQUEST_RETRY_INTERVAL; + end else begin + cache_query_request_valid_next = 1'b0; + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b0; + arp_response_mac_next = cache_query_response_mac; + end + end + end else if (arp_request_valid && arp_request_ready) begin + if (~(arp_request_ip | subnet_mask) == 0) begin + // broadcast address + // (all bits in request IP set where subnet mask is clear) + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b0; + arp_response_mac_next = 48'hffffffffffff; + end else if (((arp_request_ip ^ gateway_ip) & subnet_mask) == 0) begin + // within subnet, look up IP directly + // (no bits differ between request IP and gateway IP where subnet mask is set) + cache_query_request_valid_next = 1'b1; + cache_query_request_ip_next = arp_request_ip; + arp_request_ip_next = arp_request_ip; + end else begin + // outside of subnet, so look up gateway address + cache_query_request_valid_next = 1'b1; + cache_query_request_ip_next = gateway_ip; + arp_request_ip_next = gateway_ip; + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + outgoing_frame_valid_reg <= 1'b0; + cache_query_request_valid_reg <= 1'b0; + cache_write_request_valid_reg <= 1'b0; + arp_request_ready_reg <= 1'b0; + arp_request_operation_reg <= 1'b0; + arp_request_retry_cnt_reg <= 6'd0; + arp_request_timer_reg <= 36'd0; + arp_response_valid_reg <= 1'b0; + end else begin + outgoing_frame_valid_reg <= outgoing_frame_valid_next; + cache_query_request_valid_reg <= cache_query_request_valid_next; + cache_write_request_valid_reg <= cache_write_request_valid_next; + arp_request_ready_reg <= arp_request_ready_next; + arp_request_operation_reg <= arp_request_operation_next; + arp_request_retry_cnt_reg <= arp_request_retry_cnt_next; + arp_request_timer_reg <= arp_request_timer_next; + arp_response_valid_reg <= arp_response_valid_next; + end + + cache_query_request_ip_reg <= cache_query_request_ip_next; + outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next; + outgoing_arp_oper_reg <= outgoing_arp_oper_next; + outgoing_arp_tha_reg <= outgoing_arp_tha_next; + outgoing_arp_tpa_reg <= outgoing_arp_tpa_next; + cache_write_request_mac_reg <= cache_write_request_mac_next; + cache_write_request_ip_reg <= cache_write_request_ip_next; + arp_request_ip_reg <= arp_request_ip_next; + arp_response_error_reg <= arp_response_error_next; + arp_response_mac_reg <= arp_response_mac_next; +end + +endmodule diff --git a/fpga/lib/eth/rtl/arp_64.v b/fpga/lib/eth/rtl/arp_64.v new file mode 100644 index 000000000..82f456b26 --- /dev/null +++ b/fpga/lib/eth/rtl/arp_64.v @@ -0,0 +1,420 @@ +/* + +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 1ns / 1ps + +/* + * ARP block for IPv4, ethernet frame interface (64 bit datapath) + */ +module arp_64 #( + parameter CACHE_ADDR_WIDTH = 9, + parameter REQUEST_RETRY_COUNT = 4, + parameter REQUEST_RETRY_INTERVAL = 156250000*2, + parameter REQUEST_TIMEOUT = 156250000*30 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * ARP requests + */ + input wire arp_request_valid, + output wire arp_request_ready, + input wire [31:0] arp_request_ip, + output wire arp_response_valid, + input wire arp_response_ready, + output wire arp_response_error, + output wire [47:0] arp_response_mac, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_cache +); + +localparam [15:0] + ARP_OPER_ARP_REQUEST = 16'h0001, + ARP_OPER_ARP_REPLY = 16'h0002, + ARP_OPER_INARP_REQUEST = 16'h0008, + ARP_OPER_INARP_REPLY = 16'h0009; + +wire incoming_frame_valid; +reg incoming_frame_ready; +wire [47:0] incoming_eth_dest_mac; +wire [47:0] incoming_eth_src_mac; +wire [15:0] incoming_eth_type; +wire [15:0] incoming_arp_htype; +wire [15:0] incoming_arp_ptype; +wire [7:0] incoming_arp_hlen; +wire [7:0] incoming_arp_plen; +wire [15:0] incoming_arp_oper; +wire [47:0] incoming_arp_sha; +wire [31:0] incoming_arp_spa; +wire [47:0] incoming_arp_tha; +wire [31:0] incoming_arp_tpa; + +/* + * ARP frame processing + */ +arp_eth_rx_64 +arp_eth_rx_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // ARP frame output + .m_frame_valid(incoming_frame_valid), + .m_frame_ready(incoming_frame_ready), + .m_eth_dest_mac(incoming_eth_dest_mac), + .m_eth_src_mac(incoming_eth_src_mac), + .m_eth_type(incoming_eth_type), + .m_arp_htype(incoming_arp_htype), + .m_arp_ptype(incoming_arp_ptype), + .m_arp_hlen(incoming_arp_hlen), + .m_arp_plen(incoming_arp_plen), + .m_arp_oper(incoming_arp_oper), + .m_arp_sha(incoming_arp_sha), + .m_arp_spa(incoming_arp_spa), + .m_arp_tha(incoming_arp_tha), + .m_arp_tpa(incoming_arp_tpa), + // Status signals + .busy(), + .error_header_early_termination(), + .error_invalid_header() +); + +reg outgoing_frame_valid_reg = 1'b0, outgoing_frame_valid_next; +wire outgoing_frame_ready; +reg [47:0] outgoing_eth_dest_mac_reg = 48'd0, outgoing_eth_dest_mac_next; +reg [15:0] outgoing_arp_oper_reg = 16'd0, outgoing_arp_oper_next; +reg [47:0] outgoing_arp_tha_reg = 48'd0, outgoing_arp_tha_next; +reg [31:0] outgoing_arp_tpa_reg = 32'd0, outgoing_arp_tpa_next; + +arp_eth_tx_64 +arp_eth_tx_inst ( + .clk(clk), + .rst(rst), + // ARP frame input + .s_frame_valid(outgoing_frame_valid_reg), + .s_frame_ready(outgoing_frame_ready), + .s_eth_dest_mac(outgoing_eth_dest_mac_reg), + .s_eth_src_mac(local_mac), + .s_eth_type(16'h0806), + .s_arp_htype(16'h0001), + .s_arp_ptype(16'h0800), + .s_arp_oper(outgoing_arp_oper_reg), + .s_arp_sha(local_mac), + .s_arp_spa(local_ip), + .s_arp_tha(outgoing_arp_tha_reg), + .s_arp_tpa(outgoing_arp_tpa_reg), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy() +); + +reg cache_query_request_valid_reg = 1'b0, cache_query_request_valid_next; +reg [31:0] cache_query_request_ip_reg = 32'd0, cache_query_request_ip_next; +wire cache_query_response_valid; +wire cache_query_response_error; +wire [47:0] cache_query_response_mac; + +reg cache_write_request_valid_reg = 1'b0, cache_write_request_valid_next; +reg [31:0] cache_write_request_ip_reg = 32'd0, cache_write_request_ip_next; +reg [47:0] cache_write_request_mac_reg = 48'd0, cache_write_request_mac_next; + +/* + * ARP cache + */ +arp_cache #( + .CACHE_ADDR_WIDTH(CACHE_ADDR_WIDTH) +) +arp_cache_inst ( + .clk(clk), + .rst(rst), + // Query cache + .query_request_valid(cache_query_request_valid_reg), + .query_request_ready(), + .query_request_ip(cache_query_request_ip_reg), + .query_response_valid(cache_query_response_valid), + .query_response_ready(1'b1), + .query_response_error(cache_query_response_error), + .query_response_mac(cache_query_response_mac), + // Write cache + .write_request_valid(cache_write_request_valid_reg), + .write_request_ready(), + .write_request_ip(cache_write_request_ip_reg), + .write_request_mac(cache_write_request_mac_reg), + // Configuration + .clear_cache(clear_cache) +); + +reg arp_request_operation_reg = 1'b0, arp_request_operation_next; + +reg arp_request_ready_reg = 1'b0, arp_request_ready_next; +reg [31:0] arp_request_ip_reg = 32'd0, arp_request_ip_next; + +reg arp_response_valid_reg = 1'b0, arp_response_valid_next; +reg arp_response_error_reg = 1'b0, arp_response_error_next; +reg [47:0] arp_response_mac_reg = 48'd0, arp_response_mac_next; + +reg [5:0] arp_request_retry_cnt_reg = 6'd0, arp_request_retry_cnt_next; +reg [35:0] arp_request_timer_reg = 36'd0, arp_request_timer_next; + +assign arp_request_ready = arp_request_ready_reg; + +assign arp_response_valid = arp_response_valid_reg; +assign arp_response_error = arp_response_error_reg; +assign arp_response_mac = arp_response_mac_reg; + +always @* begin + incoming_frame_ready = 1'b0; + + outgoing_frame_valid_next = outgoing_frame_valid_reg && !outgoing_frame_ready; + outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg; + outgoing_arp_oper_next = outgoing_arp_oper_reg; + outgoing_arp_tha_next = outgoing_arp_tha_reg; + outgoing_arp_tpa_next = outgoing_arp_tpa_reg; + + cache_query_request_valid_next = 1'b0; + cache_query_request_ip_next = cache_query_request_ip_reg; + + cache_write_request_valid_next = 1'b0; + cache_write_request_mac_next = cache_write_request_mac_reg; + cache_write_request_ip_next = cache_write_request_ip_reg; + + arp_request_ready_next = 1'b0; + arp_request_ip_next = arp_request_ip_reg; + arp_request_operation_next = arp_request_operation_reg; + arp_request_retry_cnt_next = arp_request_retry_cnt_reg; + arp_request_timer_next = arp_request_timer_reg; + arp_response_valid_next = arp_response_valid_reg && !arp_response_ready; + arp_response_error_next = 1'b0; + arp_response_mac_next = 48'd0; + + // manage incoming frames + incoming_frame_ready = outgoing_frame_ready; + if (incoming_frame_valid && incoming_frame_ready) begin + if (incoming_eth_type == 16'h0806 && incoming_arp_htype == 16'h0001 && incoming_arp_ptype == 16'h0800) begin + // store sender addresses in cache + cache_write_request_valid_next = 1'b1; + cache_write_request_ip_next = incoming_arp_spa; + cache_write_request_mac_next = incoming_arp_sha; + if (incoming_arp_oper == ARP_OPER_ARP_REQUEST) begin + // ARP request + if (incoming_arp_tpa == local_ip) begin + // send reply frame to valid incoming request + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = incoming_eth_src_mac; + outgoing_arp_oper_next = ARP_OPER_ARP_REPLY; + outgoing_arp_tha_next = incoming_arp_sha; + outgoing_arp_tpa_next = incoming_arp_spa; + end + end else if (incoming_arp_oper == ARP_OPER_INARP_REQUEST) begin + // INARP request + if (incoming_arp_tha == local_mac) begin + // send reply frame to valid incoming request + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = incoming_eth_src_mac; + outgoing_arp_oper_next = ARP_OPER_INARP_REPLY; + outgoing_arp_tha_next = incoming_arp_sha; + outgoing_arp_tpa_next = incoming_arp_spa; + end + end + end + end + + // manage ARP lookup requests + if (arp_request_operation_reg) begin + arp_request_ready_next = 1'b0; + cache_query_request_valid_next = 1'b1; + arp_request_timer_next = arp_request_timer_reg - 1; + // if we got a response, it will go in the cache, so when the query succeds, we're done + if (cache_query_response_valid && !cache_query_response_error) begin + arp_request_operation_next = 1'b0; + cache_query_request_valid_next = 1'b0; + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b0; + arp_response_mac_next = cache_query_response_mac; + end + // timer timeout + if (arp_request_timer_reg == 0) begin + if (arp_request_retry_cnt_reg > 0) begin + // have more retries + // send ARP request frame + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = 48'hffffffffffff; + outgoing_arp_oper_next = ARP_OPER_ARP_REQUEST; + outgoing_arp_tha_next = 48'h000000000000; + outgoing_arp_tpa_next = arp_request_ip_reg; + arp_request_retry_cnt_next = arp_request_retry_cnt_reg - 1; + if (arp_request_retry_cnt_reg > 1) begin + arp_request_timer_next = REQUEST_RETRY_INTERVAL; + end else begin + arp_request_timer_next = REQUEST_TIMEOUT; + end + end else begin + // out of retries + arp_request_operation_next = 1'b0; + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b1; + cache_query_request_valid_next = 1'b0; + end + end + end else begin + arp_request_ready_next = !arp_response_valid_next; + if (cache_query_request_valid_reg) begin + cache_query_request_valid_next = 1'b1; + if (cache_query_response_valid) begin + if (cache_query_response_error) begin + arp_request_operation_next = 1'b1; + // send ARP request frame + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = 48'hffffffffffff; + outgoing_arp_oper_next = ARP_OPER_ARP_REQUEST; + outgoing_arp_tha_next = 48'h000000000000; + outgoing_arp_tpa_next = arp_request_ip_reg; + arp_request_retry_cnt_next = REQUEST_RETRY_COUNT-1; + arp_request_timer_next = REQUEST_RETRY_INTERVAL; + end else begin + cache_query_request_valid_next = 1'b0; + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b0; + arp_response_mac_next = cache_query_response_mac; + end + end + end else if (arp_request_valid && arp_request_ready) begin + if (~(arp_request_ip | subnet_mask) == 0) begin + // broadcast address + // (all bits in request IP set where subnet mask is clear) + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b0; + arp_response_mac_next = 48'hffffffffffff; + end else if (((arp_request_ip ^ gateway_ip) & subnet_mask) == 0) begin + // within subnet, look up IP directly + // (no bits differ between request IP and gateway IP where subnet mask is set) + cache_query_request_valid_next = 1'b1; + cache_query_request_ip_next = arp_request_ip; + arp_request_ip_next = arp_request_ip; + end else begin + // outside of subnet, so look up gateway address + cache_query_request_valid_next = 1'b1; + cache_query_request_ip_next = gateway_ip; + arp_request_ip_next = gateway_ip; + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + outgoing_frame_valid_reg <= 1'b0; + cache_query_request_valid_reg <= 1'b0; + cache_write_request_valid_reg <= 1'b0; + arp_request_ready_reg <= 1'b0; + arp_request_operation_reg <= 1'b0; + arp_request_retry_cnt_reg <= 6'd0; + arp_request_timer_reg <= 36'd0; + arp_response_valid_reg <= 1'b0; + end else begin + outgoing_frame_valid_reg <= outgoing_frame_valid_next; + cache_query_request_valid_reg <= cache_query_request_valid_next; + cache_write_request_valid_reg <= cache_write_request_valid_next; + arp_request_ready_reg <= arp_request_ready_next; + arp_request_operation_reg <= arp_request_operation_next; + arp_request_retry_cnt_reg <= arp_request_retry_cnt_next; + arp_request_timer_reg <= arp_request_timer_next; + arp_response_valid_reg <= arp_response_valid_next; + end + + cache_query_request_ip_reg <= cache_query_request_ip_next; + outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next; + outgoing_arp_oper_reg <= outgoing_arp_oper_next; + outgoing_arp_tha_reg <= outgoing_arp_tha_next; + outgoing_arp_tpa_reg <= outgoing_arp_tpa_next; + cache_write_request_mac_reg <= cache_write_request_mac_next; + cache_write_request_ip_reg <= cache_write_request_ip_next; + arp_request_ip_reg <= arp_request_ip_next; + arp_response_error_reg <= arp_response_error_next; + arp_response_mac_reg <= arp_response_mac_next; +end + +endmodule diff --git a/fpga/lib/eth/rtl/arp_cache.v b/fpga/lib/eth/rtl/arp_cache.v new file mode 100644 index 000000000..e0c4c0662 --- /dev/null +++ b/fpga/lib/eth/rtl/arp_cache.v @@ -0,0 +1,243 @@ +/* + +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 1ns / 1ps + +/* + * ARP cache + */ +module arp_cache #( + parameter CACHE_ADDR_WIDTH = 9 +) +( + input wire clk, + input wire rst, + + /* + * Cache query + */ + input wire query_request_valid, + output wire query_request_ready, + input wire [31:0] query_request_ip, + + output wire query_response_valid, + input wire query_response_ready, + output wire query_response_error, + output wire [47:0] query_response_mac, + + /* + * Cache write + */ + input wire write_request_valid, + output wire write_request_ready, + input wire [31:0] write_request_ip, + input wire [47:0] write_request_mac, + + /* + * Configuration + */ + input wire clear_cache +); + +reg mem_write = 0; +reg store_query = 0; +reg store_write = 0; + +reg query_ip_valid_reg = 0, query_ip_valid_next; +reg [31:0] query_ip_reg = 0; +reg write_ip_valid_reg = 0, write_ip_valid_next; +reg [31:0] write_ip_reg = 0; +reg [47:0] write_mac_reg = 0; +reg clear_cache_reg = 0, clear_cache_next; + +reg [CACHE_ADDR_WIDTH-1:0] wr_ptr_reg = {CACHE_ADDR_WIDTH{1'b0}}, wr_ptr_next; +reg [CACHE_ADDR_WIDTH-1:0] rd_ptr_reg = {CACHE_ADDR_WIDTH{1'b0}}, rd_ptr_next; + +reg valid_mem[(2**CACHE_ADDR_WIDTH)-1:0]; +reg [31:0] ip_addr_mem[(2**CACHE_ADDR_WIDTH)-1:0]; +reg [47:0] mac_addr_mem[(2**CACHE_ADDR_WIDTH)-1:0]; + +reg query_request_ready_reg = 0, query_request_ready_next; + +reg query_response_valid_reg = 0, query_response_valid_next; +reg query_response_error_reg = 0, query_response_error_next; +reg [47:0] query_response_mac_reg = 0; + +reg write_request_ready_reg = 0, write_request_ready_next; + +wire [31:0] query_request_hash; +wire [31:0] write_request_hash; + +assign query_request_ready = query_request_ready_reg; + +assign query_response_valid = query_response_valid_reg; +assign query_response_error = query_response_error_reg; +assign query_response_mac = query_response_mac_reg; + +assign write_request_ready = write_request_ready_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +rd_hash ( + .data_in(query_request_ip), + .state_in(32'hffffffff), + .data_out(), + .state_out(query_request_hash) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +wr_hash ( + .data_in(write_request_ip), + .state_in(32'hffffffff), + .data_out(), + .state_out(write_request_hash) +); + +integer i; + +initial begin + for (i = 0; i < 2**CACHE_ADDR_WIDTH; i = i + 1) begin + valid_mem[i] = 1'b0; + ip_addr_mem[i] = 32'd0; + mac_addr_mem[i] = 48'd0; + end +end + +always @* begin + mem_write = 1'b0; + store_query = 1'b0; + store_write = 1'b0; + + wr_ptr_next = wr_ptr_reg; + rd_ptr_next = rd_ptr_reg; + + clear_cache_next = clear_cache_reg | clear_cache; + + query_ip_valid_next = query_ip_valid_reg; + + query_request_ready_next = (~query_ip_valid_reg || ~query_request_valid || query_response_ready) && !clear_cache_next; + + query_response_valid_next = query_response_valid_reg & ~query_response_ready; + query_response_error_next = query_response_error_reg; + + if (query_ip_valid_reg && (~query_request_valid || query_response_ready)) begin + query_response_valid_next = 1; + query_ip_valid_next = 0; + if (valid_mem[rd_ptr_reg] && ip_addr_mem[rd_ptr_reg] == query_ip_reg) begin + query_response_error_next = 0; + end else begin + query_response_error_next = 1; + end + end + + if (query_request_valid && query_request_ready && (~query_ip_valid_reg || ~query_request_valid || query_response_ready)) begin + store_query = 1; + query_ip_valid_next = 1; + rd_ptr_next = query_request_hash[CACHE_ADDR_WIDTH-1:0]; + end + + write_ip_valid_next = write_ip_valid_reg; + + write_request_ready_next = !clear_cache_next; + + if (write_ip_valid_reg) begin + write_ip_valid_next = 0; + mem_write = 1; + end + + if (write_request_valid && write_request_ready) begin + store_write = 1; + write_ip_valid_next = 1; + wr_ptr_next = write_request_hash[CACHE_ADDR_WIDTH-1:0]; + end + + if (clear_cache) begin + clear_cache_next = 1'b1; + wr_ptr_next = 0; + end else if (clear_cache_reg) begin + wr_ptr_next = wr_ptr_reg + 1; + clear_cache_next = wr_ptr_next != 0; + mem_write = 1; + end +end + +always @(posedge clk) begin + if (rst) begin + query_ip_valid_reg <= 1'b0; + query_request_ready_reg <= 1'b0; + query_response_valid_reg <= 1'b0; + write_ip_valid_reg <= 1'b0; + write_request_ready_reg <= 1'b0; + clear_cache_reg <= 1'b1; + wr_ptr_reg <= 0; + end else begin + query_ip_valid_reg <= query_ip_valid_next; + query_request_ready_reg <= query_request_ready_next; + query_response_valid_reg <= query_response_valid_next; + write_ip_valid_reg <= write_ip_valid_next; + write_request_ready_reg <= write_request_ready_next; + clear_cache_reg <= clear_cache_next; + wr_ptr_reg <= wr_ptr_next; + end + + query_response_error_reg <= query_response_error_next; + + if (store_query) begin + query_ip_reg <= query_request_ip; + end + + if (store_write) begin + write_ip_reg <= write_request_ip; + write_mac_reg <= write_request_mac; + end + + rd_ptr_reg <= rd_ptr_next; + + query_response_mac_reg <= mac_addr_mem[rd_ptr_reg]; + + if (mem_write) begin + valid_mem[wr_ptr_reg] <= !clear_cache_reg; + ip_addr_mem[wr_ptr_reg] <= write_ip_reg; + mac_addr_mem[wr_ptr_reg] <= write_mac_reg; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/arp_eth_rx.v b/fpga/lib/eth/rtl/arp_eth_rx.v new file mode 100644 index 000000000..381098e87 --- /dev/null +++ b/fpga/lib/eth/rtl/arp_eth_rx.v @@ -0,0 +1,391 @@ +/* + +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 1ns / 1ps + +/* + * ARP ethernet frame receiver (Ethernet frame in, ARP frame out) + */ +module arp_eth_rx +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * ARP frame output + */ + output wire m_frame_valid, + input wire m_frame_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [15:0] m_arp_htype, + output wire [15:0] m_arp_ptype, + output wire [7:0] m_arp_hlen, + output wire [7:0] m_arp_plen, + output wire [15:0] m_arp_oper, + output wire [47:0] m_arp_sha, + output wire [31:0] m_arp_spa, + output wire [47:0] m_arp_tha, + output wire [31:0] m_arp_tpa, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_invalid_header +); + +/* + +ARP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0806) 2 octets + HTYPE (1) 2 octets + PTYPE (0x0800) 2 octets + HLEN (6) 1 octets + PLEN (4) 1 octets + OPER 2 octets + SHA Sender MAC 6 octets + SPA Sender IP 4 octets + THA Target MAC 6 octets + TPA Target IP 4 octets + +This module receives an Ethernet frame with header fields in parallel and +payload on an AXI stream interface, decodes the ARP packet fields, and +produces the frame fields in parallel. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_WAIT_LAST = 3'd2; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_eth_hdr; +reg store_arp_htype_0; +reg store_arp_htype_1; +reg store_arp_ptype_0; +reg store_arp_ptype_1; +reg store_arp_hlen; +reg store_arp_plen; +reg store_arp_oper_0; +reg store_arp_oper_1; +reg store_arp_sha_0; +reg store_arp_sha_1; +reg store_arp_sha_2; +reg store_arp_sha_3; +reg store_arp_sha_4; +reg store_arp_sha_5; +reg store_arp_spa_0; +reg store_arp_spa_1; +reg store_arp_spa_2; +reg store_arp_spa_3; +reg store_arp_tha_0; +reg store_arp_tha_1; +reg store_arp_tha_2; +reg store_arp_tha_3; +reg store_arp_tha_4; +reg store_arp_tha_5; +reg store_arp_tpa_0; +reg store_arp_tpa_1; +reg store_arp_tpa_2; +reg store_arp_tpa_3; + +reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg m_frame_valid_reg = 1'b0, m_frame_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [15:0] m_arp_htype_reg = 16'd0; +reg [15:0] m_arp_ptype_reg = 16'd0; +reg [7:0] m_arp_hlen_reg = 8'd0; +reg [7:0] m_arp_plen_reg = 8'd0; +reg [15:0] m_arp_oper_reg = 16'd0; +reg [47:0] m_arp_sha_reg = 48'd0; +reg [31:0] m_arp_spa_reg = 32'd0; +reg [47:0] m_arp_tha_reg = 48'd0; +reg [31:0] m_arp_tpa_reg = 32'd0; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_invalid_header_reg = 1'b0, error_invalid_header_next; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign m_frame_valid = m_frame_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_arp_htype = m_arp_htype_reg; +assign m_arp_ptype = m_arp_ptype_reg; +assign m_arp_hlen = m_arp_hlen_reg; +assign m_arp_plen = m_arp_plen_reg; +assign m_arp_oper = m_arp_oper_reg; +assign m_arp_sha = m_arp_sha_reg; +assign m_arp_spa = m_arp_spa_reg; +assign m_arp_tha = m_arp_tha_reg; +assign m_arp_tpa = m_arp_tpa_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_invalid_header = error_invalid_header_reg; + +always @* begin + state_next = STATE_IDLE; + + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b0; + + store_eth_hdr = 1'b0; + store_arp_htype_0 = 1'b0; + store_arp_htype_1 = 1'b0; + store_arp_ptype_0 = 1'b0; + store_arp_ptype_1 = 1'b0; + store_arp_hlen = 1'b0; + store_arp_plen = 1'b0; + store_arp_oper_0 = 1'b0; + store_arp_oper_1 = 1'b0; + store_arp_sha_0 = 1'b0; + store_arp_sha_1 = 1'b0; + store_arp_sha_2 = 1'b0; + store_arp_sha_3 = 1'b0; + store_arp_sha_4 = 1'b0; + store_arp_sha_5 = 1'b0; + store_arp_spa_0 = 1'b0; + store_arp_spa_1 = 1'b0; + store_arp_spa_2 = 1'b0; + store_arp_spa_3 = 1'b0; + store_arp_tha_0 = 1'b0; + store_arp_tha_1 = 1'b0; + store_arp_tha_2 = 1'b0; + store_arp_tha_3 = 1'b0; + store_arp_tha_4 = 1'b0; + store_arp_tha_5 = 1'b0; + store_arp_tpa_0 = 1'b0; + store_arp_tpa_1 = 1'b0; + store_arp_tpa_2 = 1'b0; + store_arp_tpa_3 = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + m_frame_valid_next = m_frame_valid_reg && !m_frame_ready; + + error_header_early_termination_next = 1'b0; + error_invalid_header_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 8'd0; + s_eth_hdr_ready_next = !m_frame_valid_next; + + if (s_eth_hdr_ready && s_eth_hdr_valid) begin + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b1; + store_eth_hdr = 1'b1; + state_next = STATE_READ_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header state + s_eth_payload_axis_tready_next = 1'b1; + + if (s_eth_payload_axis_tvalid) begin + // word transfer in - store it + frame_ptr_next = frame_ptr_reg + 8'd1; + state_next = STATE_READ_HEADER; + case (frame_ptr_reg) + 8'h00: store_arp_htype_1 = 1'b1; + 8'h01: store_arp_htype_0 = 1'b1; + 8'h02: store_arp_ptype_1 = 1'b1; + 8'h03: store_arp_ptype_0 = 1'b1; + 8'h04: store_arp_hlen = 1'b1; + 8'h05: store_arp_plen = 1'b1; + 8'h06: store_arp_oper_1 = 1'b1; + 8'h07: store_arp_oper_0 = 1'b1; + 8'h08: store_arp_sha_5 = 1'b1; + 8'h09: store_arp_sha_4 = 1'b1; + 8'h0A: store_arp_sha_3 = 1'b1; + 8'h0B: store_arp_sha_2 = 1'b1; + 8'h0C: store_arp_sha_1 = 1'b1; + 8'h0D: store_arp_sha_0 = 1'b1; + 8'h0E: store_arp_spa_3 = 1'b1; + 8'h0F: store_arp_spa_2 = 1'b1; + 8'h10: store_arp_spa_1 = 1'b1; + 8'h11: store_arp_spa_0 = 1'b1; + 8'h12: store_arp_tha_5 = 1'b1; + 8'h13: store_arp_tha_4 = 1'b1; + 8'h14: store_arp_tha_3 = 1'b1; + 8'h15: store_arp_tha_2 = 1'b1; + 8'h16: store_arp_tha_1 = 1'b1; + 8'h17: store_arp_tha_0 = 1'b1; + 8'h18: store_arp_tpa_3 = 1'b1; + 8'h19: store_arp_tpa_2 = 1'b1; + 8'h1A: store_arp_tpa_1 = 1'b1; + 8'h1B: begin + store_arp_tpa_0 = 1'b1; + state_next = STATE_WAIT_LAST; + end + endcase + if (s_eth_payload_axis_tlast) begin + // end of frame + if (frame_ptr_reg != 8'h1B) begin + // don't have the whole header + error_header_early_termination_next = 1'b1; + end else if (m_arp_hlen != 4'd6 || m_arp_plen != 4'd4) begin + // lengths not valid + error_invalid_header_next = 1'b1; + end else begin + // otherwise, transfer tuser + m_frame_valid_next = !s_eth_payload_axis_tuser; + end + s_eth_hdr_ready_next = !m_frame_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_WAIT_LAST: begin + // wait for end of frame; read and discard + s_eth_payload_axis_tready_next = 1'b1; + + if (s_eth_payload_axis_tvalid) begin + if (s_eth_payload_axis_tlast) begin + if (m_arp_hlen != 4'd6 || m_arp_plen != 4'd4) begin + // lengths not valid + error_invalid_header_next = 1'b1; + end else begin + // otherwise, transfer tuser + m_frame_valid_next = !s_eth_payload_axis_tuser; + end + s_eth_hdr_ready_next = !m_frame_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + // wait for end of frame; read and discard + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 8'd0; + s_eth_payload_axis_tready_reg <= 1'b0; + m_frame_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_invalid_header_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + + frame_ptr_reg <= frame_ptr_next; + + m_frame_valid_reg <= m_frame_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_invalid_header_reg <= error_invalid_header_next; + + busy_reg <= state_next != STATE_IDLE; + end + + // datapath + if (store_eth_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + end + + if (store_arp_htype_0) m_arp_htype_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_arp_htype_1) m_arp_htype_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_arp_ptype_0) m_arp_ptype_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_arp_ptype_1) m_arp_ptype_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_arp_hlen) m_arp_hlen_reg <= s_eth_payload_axis_tdata; + if (store_arp_plen) m_arp_plen_reg <= s_eth_payload_axis_tdata; + if (store_arp_oper_0) m_arp_oper_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_arp_oper_1) m_arp_oper_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_arp_sha_0) m_arp_sha_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_arp_sha_1) m_arp_sha_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_arp_sha_2) m_arp_sha_reg[23:16] <= s_eth_payload_axis_tdata; + if (store_arp_sha_3) m_arp_sha_reg[31:24] <= s_eth_payload_axis_tdata; + if (store_arp_sha_4) m_arp_sha_reg[39:32] <= s_eth_payload_axis_tdata; + if (store_arp_sha_5) m_arp_sha_reg[47:40] <= s_eth_payload_axis_tdata; + if (store_arp_spa_0) m_arp_spa_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_arp_spa_1) m_arp_spa_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_arp_spa_2) m_arp_spa_reg[23:16] <= s_eth_payload_axis_tdata; + if (store_arp_spa_3) m_arp_spa_reg[31:24] <= s_eth_payload_axis_tdata; + if (store_arp_tha_0) m_arp_tha_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_arp_tha_1) m_arp_tha_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_arp_tha_2) m_arp_tha_reg[23:16] <= s_eth_payload_axis_tdata; + if (store_arp_tha_3) m_arp_tha_reg[31:24] <= s_eth_payload_axis_tdata; + if (store_arp_tha_4) m_arp_tha_reg[39:32] <= s_eth_payload_axis_tdata; + if (store_arp_tha_5) m_arp_tha_reg[47:40] <= s_eth_payload_axis_tdata; + if (store_arp_tpa_0) m_arp_tpa_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_arp_tpa_1) m_arp_tpa_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_arp_tpa_2) m_arp_tpa_reg[23:16] <= s_eth_payload_axis_tdata; + if (store_arp_tpa_3) m_arp_tpa_reg[31:24] <= s_eth_payload_axis_tdata; +end + +endmodule diff --git a/fpga/lib/eth/rtl/arp_eth_rx_64.v b/fpga/lib/eth/rtl/arp_eth_rx_64.v new file mode 100644 index 000000000..4568349aa --- /dev/null +++ b/fpga/lib/eth/rtl/arp_eth_rx_64.v @@ -0,0 +1,324 @@ +/* + +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 1ns / 1ps + +/* + * ARP ethernet frame receiver (Ethernet frame in, ARP frame out, 64 bit datapath) + */ +module arp_eth_rx_64 +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * ARP frame output + */ + output wire m_frame_valid, + input wire m_frame_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [15:0] m_arp_htype, + output wire [15:0] m_arp_ptype, + output wire [7:0] m_arp_hlen, + output wire [7:0] m_arp_plen, + output wire [15:0] m_arp_oper, + output wire [47:0] m_arp_sha, + output wire [31:0] m_arp_spa, + output wire [47:0] m_arp_tha, + output wire [31:0] m_arp_tpa, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_invalid_header +); + +/* + +ARP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0806) 2 octets + HTYPE (1) 2 octets + PTYPE (0x0800) 2 octets + HLEN (6) 1 octets + PLEN (4) 1 octets + OPER 2 octets + SHA Sender MAC 6 octets + SPA Sender IP 4 octets + THA Target MAC 6 octets + TPA Target IP 4 octets + +This module receives an Ethernet frame with header fields in parallel and +payload on an AXI stream interface, decodes the ARP packet fields, and +produces the frame fields in parallel. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_WAIT_LAST = 3'd2; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_eth_hdr; +reg store_arp_hdr_word_0; +reg store_arp_hdr_word_1; +reg store_arp_hdr_word_2; +reg store_arp_hdr_word_3; + +reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg m_frame_valid_reg = 1'b0, m_frame_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [15:0] m_arp_htype_reg = 16'd0; +reg [15:0] m_arp_ptype_reg = 16'd0; +reg [7:0] m_arp_hlen_reg = 8'd0; +reg [7:0] m_arp_plen_reg = 8'd0; +reg [15:0] m_arp_oper_reg = 16'd0; +reg [47:0] m_arp_sha_reg = 48'd0; +reg [31:0] m_arp_spa_reg = 32'd0; +reg [47:0] m_arp_tha_reg = 48'd0; +reg [31:0] m_arp_tpa_reg = 32'd0; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_invalid_header_reg = 1'b0, error_invalid_header_next; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign m_frame_valid = m_frame_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_arp_htype = m_arp_htype_reg; +assign m_arp_ptype = m_arp_ptype_reg; +assign m_arp_hlen = m_arp_hlen_reg; +assign m_arp_plen = m_arp_plen_reg; +assign m_arp_oper = m_arp_oper_reg; +assign m_arp_sha = m_arp_sha_reg; +assign m_arp_spa = m_arp_spa_reg; +assign m_arp_tha = m_arp_tha_reg; +assign m_arp_tpa = m_arp_tpa_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_invalid_header = error_invalid_header_reg; + +always @* begin + state_next = STATE_IDLE; + + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b0; + + store_eth_hdr = 1'b0; + store_arp_hdr_word_0 = 1'b0; + store_arp_hdr_word_1 = 1'b0; + store_arp_hdr_word_2 = 1'b0; + store_arp_hdr_word_3 = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + m_frame_valid_next = m_frame_valid_reg && !m_frame_ready; + + error_header_early_termination_next = 1'b0; + error_invalid_header_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 8'd0; + s_eth_hdr_ready_next = !m_frame_valid_next; + + if (s_eth_hdr_ready && s_eth_hdr_valid) begin + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b1; + store_eth_hdr = 1'b1; + state_next = STATE_READ_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header state + s_eth_payload_axis_tready_next = 1'b1; + + if (s_eth_payload_axis_tvalid) begin + // word transfer in - store it + frame_ptr_next = frame_ptr_reg + 8'd1; + state_next = STATE_READ_HEADER; + case (frame_ptr_reg) + 8'h00: store_arp_hdr_word_0 = 1'b1; + 8'h01: store_arp_hdr_word_1 = 1'b1; + 8'h02: store_arp_hdr_word_2 = 1'b1; + 8'h03: begin + store_arp_hdr_word_3 = 1'b1; + state_next = STATE_WAIT_LAST; + end + endcase + if (s_eth_payload_axis_tlast) begin + if (frame_ptr_reg != 8'h03 || (s_eth_payload_axis_tkeep & 8'h0F) != 8'h0F) begin + error_header_early_termination_next = 1'b1; + end else if (m_arp_hlen != 4'd6 || m_arp_plen != 4'd4) begin + error_invalid_header_next = 1'b1; + end else begin + m_frame_valid_next = !s_eth_payload_axis_tuser; + end + s_eth_hdr_ready_next = !m_frame_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_WAIT_LAST: begin + // wait for end of frame; read and discard + s_eth_payload_axis_tready_next = 1'b1; + + if (s_eth_payload_axis_tvalid) begin + if (s_eth_payload_axis_tlast) begin + if (m_arp_hlen != 4'd6 || m_arp_plen != 4'd4) begin + // lengths not valid + error_invalid_header_next = 1'b1; + end else begin + // otherwise, transfer tuser + m_frame_valid_next = !s_eth_payload_axis_tuser; + end + s_eth_hdr_ready_next = !m_frame_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + // wait for end of frame; read and discard + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 8'd0; + s_eth_payload_axis_tready_reg <= 1'b0; + m_frame_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_invalid_header_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + + frame_ptr_reg <= frame_ptr_next; + + m_frame_valid_reg <= m_frame_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_invalid_header_reg <= error_invalid_header_next; + + busy_reg <= state_next != STATE_IDLE; + end + + // datapath + if (store_eth_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + end + + if (store_arp_hdr_word_0) begin + m_arp_htype_reg[15: 8] <= s_eth_payload_axis_tdata[ 7: 0]; + m_arp_htype_reg[ 7: 0] <= s_eth_payload_axis_tdata[15: 8]; + m_arp_ptype_reg[15: 8] <= s_eth_payload_axis_tdata[23:16]; + m_arp_ptype_reg[ 7: 0] <= s_eth_payload_axis_tdata[31:24]; + m_arp_hlen_reg <= s_eth_payload_axis_tdata[39:32]; + m_arp_plen_reg <= s_eth_payload_axis_tdata[47:40]; + m_arp_oper_reg[15: 8] <= s_eth_payload_axis_tdata[55:48]; + m_arp_oper_reg[ 7: 0] <= s_eth_payload_axis_tdata[63:56]; + end + if (store_arp_hdr_word_1) begin + m_arp_sha_reg[47:40] <= s_eth_payload_axis_tdata[ 7: 0]; + m_arp_sha_reg[39:32] <= s_eth_payload_axis_tdata[15: 8]; + m_arp_sha_reg[31:24] <= s_eth_payload_axis_tdata[23:16]; + m_arp_sha_reg[23:16] <= s_eth_payload_axis_tdata[31:24]; + m_arp_sha_reg[15: 8] <= s_eth_payload_axis_tdata[39:32]; + m_arp_sha_reg[ 7: 0] <= s_eth_payload_axis_tdata[47:40]; + m_arp_spa_reg[31:24] <= s_eth_payload_axis_tdata[55:48]; + m_arp_spa_reg[23:16] <= s_eth_payload_axis_tdata[63:56]; + end + if (store_arp_hdr_word_2) begin + m_arp_spa_reg[15: 8] <= s_eth_payload_axis_tdata[ 7: 0]; + m_arp_spa_reg[ 7: 0] <= s_eth_payload_axis_tdata[15: 8]; + m_arp_tha_reg[47:40] <= s_eth_payload_axis_tdata[23:16]; + m_arp_tha_reg[39:32] <= s_eth_payload_axis_tdata[31:24]; + m_arp_tha_reg[31:24] <= s_eth_payload_axis_tdata[39:32]; + m_arp_tha_reg[23:16] <= s_eth_payload_axis_tdata[47:40]; + m_arp_tha_reg[15: 8] <= s_eth_payload_axis_tdata[55:48]; + m_arp_tha_reg[ 7: 0] <= s_eth_payload_axis_tdata[63:56]; + end + if (store_arp_hdr_word_3) begin + m_arp_tpa_reg[31:24] <= s_eth_payload_axis_tdata[ 7: 0]; + m_arp_tpa_reg[23:16] <= s_eth_payload_axis_tdata[15: 8]; + m_arp_tpa_reg[15: 8] <= s_eth_payload_axis_tdata[23:16]; + m_arp_tpa_reg[ 7: 0] <= s_eth_payload_axis_tdata[31:24]; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/arp_eth_tx.v b/fpga/lib/eth/rtl/arp_eth_tx.v new file mode 100644 index 000000000..888f3cf62 --- /dev/null +++ b/fpga/lib/eth/rtl/arp_eth_tx.v @@ -0,0 +1,340 @@ +/* + +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 1ns / 1ps + +/* + * ARP ethernet frame transmitter (ARP frame in, Ethernet frame out) + */ +module arp_eth_tx +( + input wire clk, + input wire rst, + + /* + * ARP frame input + */ + input wire s_frame_valid, + output wire s_frame_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [15:0] s_arp_htype, + input wire [15:0] s_arp_ptype, + input wire [15:0] s_arp_oper, + input wire [47:0] s_arp_sha, + input wire [31:0] s_arp_spa, + input wire [47:0] s_arp_tha, + input wire [31:0] s_arp_tpa, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy +); + +/* + +ARP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0806) 2 octets + HTYPE (1) 2 octets + PTYPE (0x0800) 2 octets + HLEN (6) 1 octets + PLEN (4) 1 octets + OPER 2 octets + SHA Sender MAC 6 octets + SPA Sender IP 4 octets + THA Target MAC 6 octets + TPA Target IP 4 octets + +This module receives an ARP frame with header fields in parallel and +transmits the complete Ethernet payload on an AXI interface. + +*/ + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_WRITE_HEADER = 2'd1; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_frame; + +reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next; + +reg [15:0] arp_htype_reg = 16'd0; +reg [15:0] arp_ptype_reg = 16'd0; +reg [15:0] arp_oper_reg = 16'd0; +reg [47:0] arp_sha_reg = 48'd0; +reg [31:0] arp_spa_reg = 32'd0; +reg [47:0] arp_tha_reg = 48'd0; +reg [31:0] arp_tpa_reg = 32'd0; + +reg s_frame_ready_reg = 1'b0, s_frame_ready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; + +reg busy_reg = 1'b0; + +// internal datapath +reg [7:0] m_eth_payload_axis_tdata_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_frame_ready = s_frame_ready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + s_frame_ready_next = 1'b0; + + store_frame = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + + m_eth_payload_axis_tdata_int = 8'd0; + m_eth_payload_axis_tvalid_int = 1'b0; + m_eth_payload_axis_tlast_int = 1'b0; + m_eth_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 8'd0; + s_frame_ready_next = !m_eth_hdr_valid_next; + + if (s_frame_ready && s_frame_valid) begin + store_frame = 1'b1; + s_frame_ready_next = 1'b0; + m_eth_hdr_valid_next = 1'b1; + if (m_eth_payload_axis_tready_int_reg) begin + m_eth_payload_axis_tvalid_int = 1'b1; + m_eth_payload_axis_tdata_int = s_arp_htype[15: 8]; + frame_ptr_next = 8'd1; + end + state_next = STATE_WRITE_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // read header state + if (m_eth_payload_axis_tready_int_reg) begin + // word transfer out + frame_ptr_next = frame_ptr_reg + 8'd1; + m_eth_payload_axis_tvalid_int = 1'b1; + state_next = STATE_WRITE_HEADER; + case (frame_ptr_reg) + 8'h00: m_eth_payload_axis_tdata_int = arp_htype_reg[15: 8]; + 8'h01: m_eth_payload_axis_tdata_int = arp_htype_reg[ 7: 0]; + 8'h02: m_eth_payload_axis_tdata_int = arp_ptype_reg[15: 8]; + 8'h03: m_eth_payload_axis_tdata_int = arp_ptype_reg[ 7: 0]; + 8'h04: m_eth_payload_axis_tdata_int = 8'd6; // hlen + 8'h05: m_eth_payload_axis_tdata_int = 8'd4; // plen + 8'h06: m_eth_payload_axis_tdata_int = arp_oper_reg[15: 8]; + 8'h07: m_eth_payload_axis_tdata_int = arp_oper_reg[ 7: 0]; + 8'h08: m_eth_payload_axis_tdata_int = arp_sha_reg[47:40]; + 8'h09: m_eth_payload_axis_tdata_int = arp_sha_reg[39:32]; + 8'h0A: m_eth_payload_axis_tdata_int = arp_sha_reg[31:24]; + 8'h0B: m_eth_payload_axis_tdata_int = arp_sha_reg[23:16]; + 8'h0C: m_eth_payload_axis_tdata_int = arp_sha_reg[15: 8]; + 8'h0D: m_eth_payload_axis_tdata_int = arp_sha_reg[ 7: 0]; + 8'h0E: m_eth_payload_axis_tdata_int = arp_spa_reg[31:24]; + 8'h0F: m_eth_payload_axis_tdata_int = arp_spa_reg[23:16]; + 8'h10: m_eth_payload_axis_tdata_int = arp_spa_reg[15: 8]; + 8'h11: m_eth_payload_axis_tdata_int = arp_spa_reg[ 7: 0]; + 8'h12: m_eth_payload_axis_tdata_int = arp_tha_reg[47:40]; + 8'h13: m_eth_payload_axis_tdata_int = arp_tha_reg[39:32]; + 8'h14: m_eth_payload_axis_tdata_int = arp_tha_reg[31:24]; + 8'h15: m_eth_payload_axis_tdata_int = arp_tha_reg[23:16]; + 8'h16: m_eth_payload_axis_tdata_int = arp_tha_reg[15: 8]; + 8'h17: m_eth_payload_axis_tdata_int = arp_tha_reg[ 7: 0]; + 8'h18: m_eth_payload_axis_tdata_int = arp_tpa_reg[31:24]; + 8'h19: m_eth_payload_axis_tdata_int = arp_tpa_reg[23:16]; + 8'h1A: m_eth_payload_axis_tdata_int = arp_tpa_reg[15: 8]; + 8'h1B: begin + m_eth_payload_axis_tdata_int = arp_tpa_reg[ 7: 0]; + m_eth_payload_axis_tlast_int = 1'b1; + s_frame_ready_next = !m_eth_hdr_valid_next; + state_next = STATE_IDLE; + end + endcase + end else begin + state_next = STATE_WRITE_HEADER; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 8'd0; + s_frame_ready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_frame_ready_reg <= s_frame_ready_next; + + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + end + + if (store_frame) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + arp_htype_reg <= s_arp_htype; + arp_ptype_reg <= s_arp_ptype; + arp_oper_reg <= s_arp_oper; + arp_sha_reg <= s_arp_sha; + arp_spa_reg <= s_arp_spa; + arp_tha_reg <= s_arp_tha; + arp_tpa_reg <= s_arp_tpa; + end +end + +// output datapath logic +reg [7:0] m_eth_payload_axis_tdata_reg = 8'd0; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg m_eth_payload_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_eth_payload_axis_tdata_reg = 8'd0; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg temp_m_eth_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_eth_payload_int_to_output; +reg store_eth_payload_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_eth_payload_int_to_output = 1'b0; + store_eth_payload_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_eth_payload_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_eth_payload_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/arp_eth_tx_64.v b/fpga/lib/eth/rtl/arp_eth_tx_64.v new file mode 100644 index 000000000..0270cca1a --- /dev/null +++ b/fpga/lib/eth/rtl/arp_eth_tx_64.v @@ -0,0 +1,371 @@ +/* + +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 1ns / 1ps + +/* + * ARP ethernet frame transmitter (ARP frame in, Ethernet frame out, 64 bit datapath) + */ +module arp_eth_tx_64 +( + input wire clk, + input wire rst, + + /* + * ARP frame input + */ + input wire s_frame_valid, + output wire s_frame_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [15:0] s_arp_htype, + input wire [15:0] s_arp_ptype, + input wire [15:0] s_arp_oper, + input wire [47:0] s_arp_sha, + input wire [31:0] s_arp_spa, + input wire [47:0] s_arp_tha, + input wire [31:0] s_arp_tpa, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy +); + +/* + +ARP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0806) 2 octets + HTYPE (1) 2 octets + PTYPE (0x0800) 2 octets + HLEN (6) 1 octets + PLEN (4) 1 octets + OPER 2 octets + SHA Sender MAC 6 octets + SPA Sender IP 4 octets + THA Target MAC 6 octets + TPA Target IP 4 octets + +This module receives an ARP frame with header fields in parallel and +transmits the complete Ethernet payload on an AXI interface. + +*/ + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_WRITE_HEADER = 2'd1; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_frame; + +reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next; + +reg [15:0] arp_htype_reg = 16'd0; +reg [15:0] arp_ptype_reg = 16'd0; +reg [15:0] arp_oper_reg = 16'd0; +reg [47:0] arp_sha_reg = 48'd0; +reg [31:0] arp_spa_reg = 32'd0; +reg [47:0] arp_tha_reg = 48'd0; +reg [31:0] arp_tpa_reg = 32'd0; + +reg s_frame_ready_reg = 1'b0, s_frame_ready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; + +reg busy_reg = 1'b0; + +// internal datapath +reg [63:0] m_eth_payload_axis_tdata_int; +reg [7:0] m_eth_payload_axis_tkeep_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_frame_ready = s_frame_ready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + s_frame_ready_next = 1'b0; + + store_frame = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + + m_eth_payload_axis_tdata_int = 64'd0; + m_eth_payload_axis_tkeep_int = 8'd0; + m_eth_payload_axis_tvalid_int = 1'b0; + m_eth_payload_axis_tlast_int = 1'b0; + m_eth_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 8'd0; + s_frame_ready_next = !m_eth_hdr_valid_next; + + if (s_frame_ready && s_frame_valid) begin + store_frame = 1'b1; + s_frame_ready_next = 1'b0; + m_eth_hdr_valid_next = 1'b1; + if (m_eth_payload_axis_tready_int_reg) begin + m_eth_payload_axis_tvalid_int = 1'b1; + m_eth_payload_axis_tdata_int[ 7: 0] = s_arp_htype[15: 8]; + m_eth_payload_axis_tdata_int[15: 8] = s_arp_htype[ 7: 0]; + m_eth_payload_axis_tdata_int[23:16] = s_arp_ptype[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = s_arp_ptype[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = 8'd6; // hlen + m_eth_payload_axis_tdata_int[47:40] = 8'd4; // plen + m_eth_payload_axis_tdata_int[55:48] = s_arp_oper[15: 8]; + m_eth_payload_axis_tdata_int[63:56] = s_arp_oper[ 7: 0]; + m_eth_payload_axis_tkeep_int = 8'hff; + frame_ptr_next = 8'd8; + end + state_next = STATE_WRITE_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // read header state + if (m_eth_payload_axis_tready_int_reg) begin + // word transfer out + frame_ptr_next = frame_ptr_reg + 8'd8; + m_eth_payload_axis_tvalid_int = 1'b1; + state_next = STATE_WRITE_HEADER; + case (frame_ptr_reg) + 8'h00: begin + m_eth_payload_axis_tdata_int[ 7: 0] = arp_htype_reg[15: 8]; + m_eth_payload_axis_tdata_int[15: 8] = arp_htype_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[23:16] = arp_ptype_reg[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = arp_ptype_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = 8'd6; // hlen + m_eth_payload_axis_tdata_int[47:40] = 8'd4; // plen + m_eth_payload_axis_tdata_int[55:48] = arp_oper_reg[15: 8]; + m_eth_payload_axis_tdata_int[63:56] = arp_oper_reg[ 7: 0]; + m_eth_payload_axis_tkeep_int = 8'hff; + end + 8'h08: begin + m_eth_payload_axis_tdata_int[ 7: 0] = arp_sha_reg[47:40]; + m_eth_payload_axis_tdata_int[15: 8] = arp_sha_reg[39:32]; + m_eth_payload_axis_tdata_int[23:16] = arp_sha_reg[31:24]; + m_eth_payload_axis_tdata_int[31:24] = arp_sha_reg[23:16]; + m_eth_payload_axis_tdata_int[39:32] = arp_sha_reg[15: 8]; + m_eth_payload_axis_tdata_int[47:40] = arp_sha_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[55:48] = arp_spa_reg[31:24]; + m_eth_payload_axis_tdata_int[63:56] = arp_spa_reg[23:16]; + m_eth_payload_axis_tkeep_int = 8'hff; + end + 8'h10: begin + m_eth_payload_axis_tdata_int[ 7: 0] = arp_spa_reg[15: 8]; + m_eth_payload_axis_tdata_int[15: 8] = arp_spa_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[23:16] = arp_tha_reg[47:40]; + m_eth_payload_axis_tdata_int[31:24] = arp_tha_reg[39:32]; + m_eth_payload_axis_tdata_int[39:32] = arp_tha_reg[31:24]; + m_eth_payload_axis_tdata_int[47:40] = arp_tha_reg[23:16]; + m_eth_payload_axis_tdata_int[55:48] = arp_tha_reg[15: 8]; + m_eth_payload_axis_tdata_int[63:56] = arp_tha_reg[ 7: 0]; + m_eth_payload_axis_tkeep_int = 8'hff; + end + 8'h18: begin + m_eth_payload_axis_tdata_int[ 7: 0] = arp_tpa_reg[31:24]; + m_eth_payload_axis_tdata_int[15: 8] = arp_tpa_reg[23:16]; + m_eth_payload_axis_tdata_int[23:16] = arp_tpa_reg[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = arp_tpa_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = 0; + m_eth_payload_axis_tdata_int[47:40] = 0; + m_eth_payload_axis_tdata_int[55:48] = 0; + m_eth_payload_axis_tdata_int[63:56] = 0; + m_eth_payload_axis_tkeep_int = 8'h0f; + m_eth_payload_axis_tlast_int = 1'b1; + s_frame_ready_next = !m_eth_hdr_valid_next; + state_next = STATE_IDLE; + end + endcase + end else begin + state_next = STATE_WRITE_HEADER; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 8'd0; + s_frame_ready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_frame_ready_reg <= s_frame_ready_next; + + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + end + + if (store_frame) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + arp_htype_reg <= s_arp_htype; + arp_ptype_reg <= s_arp_ptype; + arp_oper_reg <= s_arp_oper; + arp_sha_reg <= s_arp_sha; + arp_spa_reg <= s_arp_spa; + arp_tha_reg <= s_arp_tha; + arp_tpa_reg <= s_arp_tpa; + end +end + +// output datapath logic +reg [63:0] m_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] m_eth_payload_axis_tkeep_reg = 8'd0; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg m_eth_payload_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_eth_payload_axis_tkeep_reg = 8'd0; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg temp_m_eth_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_eth_payload_int_to_output; +reg store_eth_payload_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tkeep = m_eth_payload_axis_tkeep_reg; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_eth_payload_int_to_output = 1'b0; + store_eth_payload_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_eth_payload_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_eth_payload_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_baser_rx_64.v b/fpga/lib/eth/rtl/axis_baser_rx_64.v new file mode 100644 index 000000000..533fdcfb2 --- /dev/null +++ b/fpga/lib/eth/rtl/axis_baser_rx_64.v @@ -0,0 +1,615 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * AXI4-Stream 10GBASE-R frame receiver (10GBASE-R in, AXI out) + */ +module axis_baser_rx_64 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * 10GBASE-R encoded input + */ + input wire [DATA_WIDTH-1:0] encoded_rx_data, + input wire [HDR_WIDTH-1:0] encoded_rx_hdr, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Status + */ + output wire [1:0] start_packet, + output wire error_bad_frame, + output wire error_bad_fcs, + output wire rx_bad_block +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [6:0] + CTRL_IDLE = 7'h00, + CTRL_LPI = 7'h06, + CTRL_ERROR = 7'h1e, + CTRL_RES_0 = 7'h2d, + CTRL_RES_1 = 7'h33, + CTRL_RES_2 = 7'h4b, + CTRL_RES_3 = 7'h55, + CTRL_RES_4 = 7'h66, + CTRL_RES_5 = 7'h78; + +localparam [3:0] + O_SEQ_OS = 4'h0, + O_SIG_OS = 4'hf; + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +localparam [7:0] + BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT + BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT + BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT + BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT + BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT + BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT + BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT + BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT + BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT + BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT + +localparam [3:0] + INPUT_TYPE_IDLE = 4'd0, + INPUT_TYPE_ERROR = 4'd1, + INPUT_TYPE_START_0 = 4'd2, + INPUT_TYPE_START_4 = 4'd3, + INPUT_TYPE_DATA = 4'd4, + INPUT_TYPE_TERM_0 = 4'd8, + INPUT_TYPE_TERM_1 = 4'd9, + INPUT_TYPE_TERM_2 = 4'd10, + INPUT_TYPE_TERM_3 = 4'd11, + INPUT_TYPE_TERM_4 = 4'd12, + INPUT_TYPE_TERM_5 = 4'd13, + INPUT_TYPE_TERM_6 = 4'd14, + INPUT_TYPE_TERM_7 = 4'd15; + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1, + STATE_LAST = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc_last; + +reg lanes_swapped = 1'b0; +reg [31:0] swap_data = 32'd0; + +reg delay_type_valid = 1'b0; +reg [3:0] delay_type = INPUT_TYPE_IDLE; + +reg [DATA_WIDTH-1:0] input_data_d0 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] input_data_d1 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] input_data_crc = {DATA_WIDTH{1'b0}}; + +reg [3:0] input_type_d0 = INPUT_TYPE_IDLE; +reg [3:0] input_type_d1 = INPUT_TYPE_IDLE; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}, m_axis_tdata_next; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, m_axis_tkeep_next; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next; +reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next; + +reg [1:0] start_packet_reg = 2'b00; +reg error_bad_frame_reg = 1'b0, error_bad_frame_next; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; +reg rx_bad_block_reg = 1'b0; + +reg [PTP_TS_WIDTH-1:0] ptp_ts_reg = 0; + +reg [31:0] crc_state = 32'hFFFFFFFF; +reg [31:0] crc_state3 = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next7; + +wire crc_valid0 = crc_next0 == ~32'h2144df1c; +wire crc_valid1 = crc_next1 == ~32'h2144df1c; +wire crc_valid2 = crc_next2 == ~32'h2144df1c; +wire crc_valid3 = crc_next3 == ~32'h2144df1c; +wire crc_valid7 = crc_next7 == ~32'h2144df1c; + +reg crc_valid7_save = 1'b0; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = PTP_TS_ENABLE ? {ptp_ts_reg, m_axis_tuser_reg} : m_axis_tuser_reg; + +assign start_packet = start_packet_reg; +assign error_bad_frame = error_bad_frame_reg; +assign error_bad_fcs = error_bad_fcs_reg; +assign rx_bad_block = rx_bad_block_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(input_data_crc[7:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(input_data_crc[15:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(input_data_crc[23:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(input_data_crc[31:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(input_data_d0[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc_last = 1'b0; + + m_axis_tdata_next = input_data_d1; + m_axis_tkeep_next = 8'd0; + m_axis_tvalid_next = 1'b0; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (input_type_d1 == INPUT_TYPE_START_0) begin + // start condition + reset_crc = 1'b0; + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + m_axis_tdata_next = input_data_d1; + m_axis_tkeep_next = 8'hff; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + if (input_type_d0 == INPUT_TYPE_DATA) begin + state_next = STATE_PAYLOAD; + end else if (input_type_d0[3]) begin + // INPUT_TYPE_TERM_* + if (input_type_d0 <= INPUT_TYPE_TERM_4) begin + // end this cycle + reset_crc = 1'b1; + case (input_type_d0) + INPUT_TYPE_TERM_0: m_axis_tkeep_next = 8'b00001111; + INPUT_TYPE_TERM_1: m_axis_tkeep_next = 8'b00011111; + INPUT_TYPE_TERM_2: m_axis_tkeep_next = 8'b00111111; + INPUT_TYPE_TERM_3: m_axis_tkeep_next = 8'b01111111; + INPUT_TYPE_TERM_4: m_axis_tkeep_next = 8'b11111111; + endcase + m_axis_tlast_next = 1'b1; + if ((input_type_d0 == INPUT_TYPE_TERM_0 && crc_valid7_save) || + (input_type_d0 == INPUT_TYPE_TERM_1 && crc_valid0) || + (input_type_d0 == INPUT_TYPE_TERM_2 && crc_valid1) || + (input_type_d0 == INPUT_TYPE_TERM_3 && crc_valid2) || + (input_type_d0 == INPUT_TYPE_TERM_4 && crc_valid3)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + // need extra cycle + update_crc_last = 1'b1; + state_next = STATE_LAST; + end + end else begin + // control or error characters in packet + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + reset_crc = 1'b1; + state_next = STATE_IDLE; + end + end + STATE_LAST: begin + // last cycle of packet + m_axis_tdata_next = input_data_d1; + m_axis_tkeep_next = 8'hff; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b0; + + reset_crc = 1'b1; + + case (input_type_d1) + INPUT_TYPE_TERM_5: m_axis_tkeep_next = 8'b00000001; + INPUT_TYPE_TERM_6: m_axis_tkeep_next = 8'b00000011; + INPUT_TYPE_TERM_7: m_axis_tkeep_next = 8'b00000111; + endcase + + if ((input_type_d1 == INPUT_TYPE_TERM_5 && crc_valid0) || + (input_type_d1 == INPUT_TYPE_TERM_6 && crc_valid1) || + (input_type_d1 == INPUT_TYPE_TERM_7 && crc_valid2)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + + state_next = STATE_IDLE; + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_reg <= 2'b00; + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + rx_bad_block_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + crc_state3 <= 32'hFFFFFFFF; + + input_type_d0 <= INPUT_TYPE_IDLE; + input_type_d1 <= INPUT_TYPE_IDLE; + + lanes_swapped <= 1'b0; + + delay_type_valid <= 1'b0; + delay_type <= INPUT_TYPE_IDLE; + end else begin + state_reg <= state_next; + + m_axis_tvalid_reg <= m_axis_tvalid_next; + + start_packet_reg <= 2'b00; + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + rx_bad_block_reg <= 1'b0; + + delay_type_valid <= 1'b0; + + if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_0) begin + lanes_swapped <= 1'b0; + start_packet_reg <= 2'b01; + input_type_d0 <= INPUT_TYPE_START_0; + end else if (encoded_rx_hdr == SYNC_CTRL && (encoded_rx_data[7:0] == BLOCK_TYPE_START_4 || encoded_rx_data[7:0] == BLOCK_TYPE_OS_START)) begin + lanes_swapped <= 1'b1; + start_packet_reg <= 2'b10; + delay_type_valid <= 1'b1; + if (delay_type_valid) begin + input_type_d0 <= delay_type; + end else begin + input_type_d0 <= INPUT_TYPE_IDLE; + end + end else if (lanes_swapped) begin + if (delay_type_valid) begin + input_type_d0 <= delay_type; + end else if (encoded_rx_hdr == SYNC_DATA) begin + input_type_d0 <= INPUT_TYPE_DATA; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_TERM_0: input_type_d0 <= INPUT_TYPE_TERM_4; + BLOCK_TYPE_TERM_1: input_type_d0 <= INPUT_TYPE_TERM_5; + BLOCK_TYPE_TERM_2: input_type_d0 <= INPUT_TYPE_TERM_6; + BLOCK_TYPE_TERM_3: input_type_d0 <= INPUT_TYPE_TERM_7; + BLOCK_TYPE_TERM_4: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + BLOCK_TYPE_TERM_5: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + BLOCK_TYPE_TERM_6: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + BLOCK_TYPE_TERM_7: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + default: begin + rx_bad_block_reg <= 1'b1; + input_type_d0 <= INPUT_TYPE_ERROR; + end + endcase + end else begin + rx_bad_block_reg <= 1'b1; + input_type_d0 <= INPUT_TYPE_ERROR; + end + end else begin + if (encoded_rx_hdr == SYNC_DATA) begin + input_type_d0 <= INPUT_TYPE_DATA; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_TERM_0: input_type_d0 <= INPUT_TYPE_TERM_0; + BLOCK_TYPE_TERM_1: input_type_d0 <= INPUT_TYPE_TERM_1; + BLOCK_TYPE_TERM_2: input_type_d0 <= INPUT_TYPE_TERM_2; + BLOCK_TYPE_TERM_3: input_type_d0 <= INPUT_TYPE_TERM_3; + BLOCK_TYPE_TERM_4: input_type_d0 <= INPUT_TYPE_TERM_4; + BLOCK_TYPE_TERM_5: input_type_d0 <= INPUT_TYPE_TERM_5; + BLOCK_TYPE_TERM_6: input_type_d0 <= INPUT_TYPE_TERM_6; + BLOCK_TYPE_TERM_7: input_type_d0 <= INPUT_TYPE_TERM_7; + default: begin + rx_bad_block_reg <= 1'b1; + input_type_d0 <= INPUT_TYPE_ERROR; + end + endcase + end else begin + rx_bad_block_reg <= 1'b1; + input_type_d0 <= INPUT_TYPE_ERROR; + end + end + + input_type_d1 <= input_type_d0; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else begin + crc_state <= crc_next7; + end + + if (update_crc_last) begin + crc_state3 <= crc_next3; + end else begin + crc_state3 <= crc_next7; + end + end + + if (PTP_TS_WIDTH == 96 && $signed({1'b0, ptp_ts_reg[45:16]}) - $signed(31'd1000000000) > 0) begin + // ns field rollover + ptp_ts_reg[45:16] <= $signed({1'b0, ptp_ts_reg[45:16]}) - $signed(31'd1000000000); + ptp_ts_reg[95:48] <= ptp_ts_reg[95:48] + 1; + end + + if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_0) begin + if (PTP_TS_WIDTH == 96) begin + ptp_ts_reg[45:0] <= ptp_ts[45:0] + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + ptp_ts_reg[95:48] <= ptp_ts[95:48]; + end else begin + ptp_ts_reg <= ptp_ts + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + end + end else if (encoded_rx_hdr == SYNC_CTRL && (encoded_rx_data[7:0] == BLOCK_TYPE_START_4 || encoded_rx_data[7:0] == BLOCK_TYPE_OS_START)) begin + if (PTP_TS_WIDTH == 96) begin + ptp_ts_reg[45:0] <= ptp_ts[45:0] + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + ptp_ts_reg[95:48] <= ptp_ts[95:48]; + end else begin + ptp_ts_reg <= ptp_ts + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + end + end + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tkeep_reg <= m_axis_tkeep_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + if (encoded_rx_hdr == SYNC_DATA) begin + swap_data <= encoded_rx_data[63:32]; + end else begin + swap_data <= {8'd0, encoded_rx_data[63:40]}; + end + + if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_0) begin + input_data_d0 <= encoded_rx_data; + input_data_crc <= encoded_rx_data; + end else if (encoded_rx_hdr == SYNC_CTRL && (encoded_rx_data[7:0] == BLOCK_TYPE_START_4 || encoded_rx_data[7:0] == BLOCK_TYPE_OS_START)) begin + input_data_d0 <= {encoded_rx_data[31:0], swap_data}; + input_data_crc <= {encoded_rx_data[31:0], swap_data}; + end else if (lanes_swapped) begin + if (encoded_rx_hdr == SYNC_DATA) begin + input_data_d0 <= {encoded_rx_data[31:0], swap_data}; + input_data_crc <= {encoded_rx_data[31:0], swap_data}; + end else begin + input_data_d0 <= {encoded_rx_data[39:8], swap_data}; + input_data_crc <= {encoded_rx_data[39:8], swap_data}; + end + end else begin + if (encoded_rx_hdr == SYNC_DATA) begin + input_data_d0 <= encoded_rx_data; + input_data_crc <= encoded_rx_data; + end else begin + input_data_d0 <= {8'd0, encoded_rx_data[63:8]}; + input_data_crc <= {8'd0, encoded_rx_data[63:8]}; + end + end + + crc_valid7_save <= crc_valid7; + + if (state_next == STATE_LAST) begin + input_data_crc[31:0] <= input_data_crc[63:32]; + end + + input_data_d1 <= input_data_d0; + + if (encoded_rx_hdr == SYNC_DATA) begin + delay_type <= INPUT_TYPE_DATA; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_START_4: delay_type <= INPUT_TYPE_START_0; + BLOCK_TYPE_TERM_0: delay_type <= INPUT_TYPE_TERM_4; + BLOCK_TYPE_TERM_1: delay_type <= INPUT_TYPE_TERM_5; + BLOCK_TYPE_TERM_2: delay_type <= INPUT_TYPE_TERM_6; + BLOCK_TYPE_TERM_3: delay_type <= INPUT_TYPE_TERM_7; + BLOCK_TYPE_TERM_4: delay_type <= INPUT_TYPE_TERM_0; + BLOCK_TYPE_TERM_5: delay_type <= INPUT_TYPE_TERM_1; + BLOCK_TYPE_TERM_6: delay_type <= INPUT_TYPE_TERM_2; + BLOCK_TYPE_TERM_7: delay_type <= INPUT_TYPE_TERM_3; + default: delay_type <= INPUT_TYPE_ERROR; + endcase + end else begin + delay_type <= INPUT_TYPE_ERROR; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_baser_tx_64.v b/fpga/lib/eth/rtl/axis_baser_tx_64.v new file mode 100644 index 000000000..35b86809f --- /dev/null +++ b/fpga/lib/eth/rtl/axis_baser_tx_64.v @@ -0,0 +1,916 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * AXI4-Stream 10GBASE-R frame transmitter (AXI in, 10GBASE-R out) + */ +module axis_baser_tx_64 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * 10GBASE-R encoded interface + */ + output wire [DATA_WIDTH-1:0] encoded_tx_data, + output wire [HDR_WIDTH-1:0] encoded_tx_hdr, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire [1:0] start_packet, + output wire error_underflow +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam MIN_FL_NOCRC = MIN_FRAME_LENGTH-4; +localparam MIN_FL_NOCRC_MS = MIN_FL_NOCRC & 16'hfff8; +localparam MIN_FL_NOCRC_LS = MIN_FL_NOCRC & 16'h0007; + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [6:0] + CTRL_IDLE = 7'h00, + CTRL_LPI = 7'h06, + CTRL_ERROR = 7'h1e, + CTRL_RES_0 = 7'h2d, + CTRL_RES_1 = 7'h33, + CTRL_RES_2 = 7'h4b, + CTRL_RES_3 = 7'h55, + CTRL_RES_4 = 7'h66, + CTRL_RES_5 = 7'h78; + +localparam [3:0] + O_SEQ_OS = 4'h0, + O_SIG_OS = 4'hf; + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +localparam [7:0] + BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT + BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT + BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT + BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT + BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT + BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT + BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT + BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT + BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT + BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT + +localparam [3:0] + OUTPUT_TYPE_IDLE = 4'd0, + OUTPUT_TYPE_ERROR = 4'd1, + OUTPUT_TYPE_START_0 = 4'd2, + OUTPUT_TYPE_START_4 = 4'd3, + OUTPUT_TYPE_DATA = 4'd4, + OUTPUT_TYPE_TERM_0 = 4'd8, + OUTPUT_TYPE_TERM_1 = 4'd9, + OUTPUT_TYPE_TERM_2 = 4'd10, + OUTPUT_TYPE_TERM_3 = 4'd11, + OUTPUT_TYPE_TERM_4 = 4'd12, + OUTPUT_TYPE_TERM_5 = 4'd13, + OUTPUT_TYPE_TERM_6 = 4'd14, + OUTPUT_TYPE_TERM_7 = 4'd15; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PAYLOAD = 3'd1, + STATE_PAD = 3'd2, + STATE_FCS_1 = 3'd3, + STATE_FCS_2 = 3'd4, + STATE_IFG = 3'd5, + STATE_WAIT_END = 3'd6; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg swap_lanes; +reg unswap_lanes; + +reg lanes_swapped = 1'b0; +reg [31:0] swap_data = 32'd0; + +reg delay_type_valid = 1'b0; +reg [3:0] delay_type = OUTPUT_TYPE_IDLE; + +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +reg [DATA_WIDTH-1:0] s_tdata_reg = {DATA_WIDTH{1'b0}}, s_tdata_next; +reg [7:0] s_tkeep_reg = 8'd0, s_tkeep_next; + +reg [DATA_WIDTH-1:0] fcs_output_data_0; +reg [DATA_WIDTH-1:0] fcs_output_data_1; +reg [3:0] fcs_output_type_0; +reg [3:0] fcs_output_type_1; + +reg [7:0] ifg_offset; + +reg extra_cycle; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] ifg_count_reg = 8'd0, ifg_count_next; +reg [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg = 0, m_axis_ptp_ts_next; +reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg = 0, m_axis_ptp_ts_tag_next; +reg m_axis_ptp_ts_valid_reg = 1'b0, m_axis_ptp_ts_valid_next; +reg m_axis_ptp_ts_valid_int_reg = 1'b0, m_axis_ptp_ts_valid_int_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next4; +wire [31:0] crc_next5; +wire [31:0] crc_next6; +wire [31:0] crc_next7; + +reg [DATA_WIDTH-1:0] encoded_tx_data_reg = {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL}; +reg [HDR_WIDTH-1:0] encoded_tx_hdr_reg = SYNC_CTRL; + +reg [DATA_WIDTH-1:0] output_data_reg = {DATA_WIDTH{1'b0}}, output_data_next; +reg [3:0] output_type_reg = OUTPUT_TYPE_IDLE, output_type_next; + +reg [1:0] start_packet_reg = 2'b00, start_packet_next; +reg error_underflow_reg = 1'b0, error_underflow_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign encoded_tx_data = encoded_tx_data_reg; +assign encoded_tx_hdr = encoded_tx_hdr_reg; + +assign m_axis_ptp_ts = PTP_TS_ENABLE ? m_axis_ptp_ts_reg : 0; +assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : 0; +assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0; + +assign start_packet = start_packet_reg; +assign error_underflow = error_underflow_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_tdata_reg[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(s_tdata_reg[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(s_tdata_reg[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(s_tdata_reg[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(40), + .STYLE("AUTO") +) +eth_crc_40 ( + .data_in(s_tdata_reg[39:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next4) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(48), + .STYLE("AUTO") +) +eth_crc_48 ( + .data_in(s_tdata_reg[47:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next5) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(56), + .STYLE("AUTO") +) +eth_crc_56 ( + .data_in(s_tdata_reg[55:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next6) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(s_tdata_reg[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 8; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = s_axis_tkeep[j] ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (s_tkeep_reg) + 8'bzzzzzz01: begin + fcs_output_data_0 = {24'd0, ~crc_next0[31:0], s_tdata_reg[7:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_TERM_5; + fcs_output_type_1 = OUTPUT_TYPE_IDLE; + ifg_offset = 8'd3; + extra_cycle = 1'b0; + end + 8'bzzzzz011: begin + fcs_output_data_0 = {16'd0, ~crc_next1[31:0], s_tdata_reg[15:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_TERM_6; + fcs_output_type_1 = OUTPUT_TYPE_IDLE; + ifg_offset = 8'd2; + extra_cycle = 1'b0; + end + 8'bzzzz0111: begin + fcs_output_data_0 = {8'd0, ~crc_next2[31:0], s_tdata_reg[23:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_TERM_7; + fcs_output_type_1 = OUTPUT_TYPE_IDLE; + ifg_offset = 8'd1; + extra_cycle = 1'b0; + end + 8'bzzz01111: begin + fcs_output_data_0 = {~crc_next3[31:0], s_tdata_reg[31:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_0; + ifg_offset = 8'd8; + extra_cycle = 1'b1; + end + 8'bzz011111: begin + fcs_output_data_0 = {~crc_next4[23:0], s_tdata_reg[39:0]}; + fcs_output_data_1 = {56'd0, ~crc_next4[31:24]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_1; + ifg_offset = 8'd7; + extra_cycle = 1'b1; + end + 8'bz0111111: begin + fcs_output_data_0 = {~crc_next5[15:0], s_tdata_reg[47:0]}; + fcs_output_data_1 = {48'd0, ~crc_next5[31:16]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_2; + ifg_offset = 8'd6; + extra_cycle = 1'b1; + end + 8'b01111111: begin + fcs_output_data_0 = {~crc_next6[7:0], s_tdata_reg[55:0]}; + fcs_output_data_1 = {40'd0, ~crc_next6[31:8]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_3; + ifg_offset = 8'd5; + extra_cycle = 1'b1; + end + 8'b11111111: begin + fcs_output_data_0 = s_tdata_reg; + fcs_output_data_1 = {32'd0, ~crc_next7[31:0]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_4; + ifg_offset = 8'd4; + extra_cycle = 1'b1; + end + default: begin + fcs_output_data_0 = 64'd0; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_ERROR; + fcs_output_type_1 = OUTPUT_TYPE_ERROR; + ifg_offset = 8'd0; + extra_cycle = 1'b1; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + swap_lanes = 1'b0; + unswap_lanes = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + ifg_count_next = ifg_count_reg; + deficit_idle_count_next = deficit_idle_count_reg; + + s_axis_tready_next = 1'b0; + + s_tdata_next = s_tdata_reg; + s_tkeep_next = s_tkeep_reg; + + m_axis_ptp_ts_next = m_axis_ptp_ts_reg; + m_axis_ptp_ts_tag_next = m_axis_ptp_ts_tag_reg; + m_axis_ptp_ts_valid_next = 1'b0; + m_axis_ptp_ts_valid_int_next = 1'b0; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_IDLE; + + start_packet_next = 2'b00; + error_underflow_next = 1'b0; + + if (m_axis_ptp_ts_valid_int_reg) begin + m_axis_ptp_ts_valid_next = 1'b1; + if (PTP_TS_WIDTH == 96 && $signed({1'b0, m_axis_ptp_ts_reg[45:16]}) - $signed(31'd1000000000) > 0) begin + // ns field rollover + m_axis_ptp_ts_next[45:16] = $signed({1'b0, m_axis_ptp_ts_reg[45:16]}) - $signed(31'd1000000000); + m_axis_ptp_ts_next[95:48] = m_axis_ptp_ts_reg[95:48] + 1; + end + end + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 16'd8; + reset_crc = 1'b1; + s_axis_tready_next = 1'b1; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_IDLE; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + // XGMII start and preamble + if (ifg_count_reg > 8'd0) begin + // need to send more idles - swap lanes + swap_lanes = 1'b1; + if (PTP_TS_WIDTH == 96) begin + m_axis_ptp_ts_next[45:0] = ptp_ts[45:0] + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + m_axis_ptp_ts_next[95:48] = ptp_ts[95:48]; + end else begin + m_axis_ptp_ts_next = ptp_ts + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + end + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_int_next = 1'b1; + start_packet_next = 2'b10; + end else begin + // no more idles - unswap + unswap_lanes = 1'b1; + if (PTP_TS_WIDTH == 96) begin + m_axis_ptp_ts_next[45:0] = ptp_ts[45:0] + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + m_axis_ptp_ts_next[95:48] = ptp_ts[95:48]; + end else begin + m_axis_ptp_ts_next = ptp_ts + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + end + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_int_next = 1'b1; + start_packet_next = 2'b01; + end + output_data_next = {ETH_SFD, {7{ETH_PRE}}}; + output_type_next = OUTPUT_TYPE_START_0; + s_axis_tready_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + ifg_count_next = 8'd0; + deficit_idle_count_next = 2'd0; + unswap_lanes = 1'b1; + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + update_crc = 1'b1; + s_axis_tready_next = 1'b1; + + frame_ptr_next = frame_ptr_reg + 16'd8; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_DATA; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + frame_ptr_next = frame_ptr_reg + keep2count(s_axis_tkeep); + s_axis_tready_next = 1'b0; + if (s_axis_tuser[0]) begin + output_type_next = OUTPUT_TYPE_ERROR; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b0; + + if (ENABLE_PADDING && (frame_ptr_reg < MIN_FL_NOCRC_MS || (frame_ptr_reg == MIN_FL_NOCRC_MS && keep2count(s_axis_tkeep) < MIN_FL_NOCRC_LS))) begin + s_tkeep_next = 8'hff; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-8)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end else begin + state_next = STATE_FCS_1; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + output_type_next = OUTPUT_TYPE_ERROR; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + error_underflow_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + STATE_PAD: begin + // pad frame to MIN_FRAME_LENGTH + s_axis_tready_next = 1'b0; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_DATA; + + s_tdata_next = 64'd0; + s_tkeep_next = 8'hff; + + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-8)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end + STATE_FCS_1: begin + // last cycle + s_axis_tready_next = 1'b0; + + output_data_next = fcs_output_data_0; + output_type_next = fcs_output_type_0; + + frame_ptr_next = 16'd0; + + ifg_count_next = (ifg_delay > 8'd12 ? ifg_delay : 8'd12) - ifg_offset + (lanes_swapped ? 8'd4 : 8'd0) + deficit_idle_count_reg; + if (extra_cycle) begin + state_next = STATE_FCS_2; + end else begin + state_next = STATE_IFG; + end + end + STATE_FCS_2: begin + // last cycle + s_axis_tready_next = 1'b0; + + output_data_next = fcs_output_data_1; + output_type_next = fcs_output_type_1; + + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end + STATE_IFG: begin + // send IFG + if (ifg_count_reg > 8'd8) begin + ifg_count_next = ifg_count_reg - 8'd8; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end + STATE_WAIT_END: begin + // wait for end of frame + s_axis_tready_next = 1'b1; + + if (ifg_count_reg > 8'd4) begin + ifg_count_next = ifg_count_reg - 8'd4; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = 1'b0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + ifg_count_reg <= 8'd0; + deficit_idle_count_reg <= 2'd0; + + s_axis_tready_reg <= 1'b0; + + m_axis_ptp_ts_valid_reg <= 1'b0; + m_axis_ptp_ts_valid_int_reg <= 1'b0; + + encoded_tx_data_reg <= {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + + output_data_reg <= {DATA_WIDTH{1'b0}}; + output_type_reg <= OUTPUT_TYPE_IDLE; + + start_packet_reg <= 2'b00; + error_underflow_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + lanes_swapped <= 1'b0; + + delay_type_valid <= 1'b0; + delay_type <= OUTPUT_TYPE_IDLE; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + ifg_count_reg <= ifg_count_next; + deficit_idle_count_reg <= deficit_idle_count_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next; + m_axis_ptp_ts_valid_int_reg <= m_axis_ptp_ts_valid_int_next; + + start_packet_reg <= start_packet_next; + error_underflow_reg <= error_underflow_next; + + delay_type_valid <= 1'b0; + + if (swap_lanes || (lanes_swapped && !unswap_lanes)) begin + lanes_swapped <= 1'b1; + output_data_reg <= {output_data_next[31:0], swap_data}; + if (delay_type_valid) begin + output_type_reg <= delay_type; + end else if (output_type_next == OUTPUT_TYPE_START_0) begin + output_type_reg <= OUTPUT_TYPE_START_4; + end else if (output_type_next[3]) begin + // OUTPUT_TYPE_TERM_* + if (output_type_next[2]) begin + delay_type_valid <= 1'b1; + output_type_reg <= OUTPUT_TYPE_DATA; + end else begin + output_type_reg <= output_type_next ^ 4'd4; + end + end else begin + output_type_reg <= output_type_next; + end + end else begin + lanes_swapped <= 1'b0; + output_data_reg <= output_data_next; + output_type_reg <= output_type_next; + end + + case (output_type_reg) + OUTPUT_TYPE_IDLE: begin + encoded_tx_data_reg <= {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_ERROR: begin + encoded_tx_data_reg <= {{8{CTRL_ERROR}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_START_0: begin + encoded_tx_data_reg <= {output_data_reg[63:8], BLOCK_TYPE_START_0}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_START_4: begin + encoded_tx_data_reg <= {output_data_reg[63:40], 4'd0, {4{CTRL_IDLE}}, BLOCK_TYPE_START_4}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_DATA: begin + encoded_tx_data_reg <= output_data_reg; + encoded_tx_hdr_reg <= SYNC_DATA; + end + OUTPUT_TYPE_TERM_0: begin + encoded_tx_data_reg <= {{7{CTRL_IDLE}}, 7'd0, BLOCK_TYPE_TERM_0}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_1: begin + encoded_tx_data_reg <= {{6{CTRL_IDLE}}, 6'd0, output_data_reg[7:0], BLOCK_TYPE_TERM_1}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_2: begin + encoded_tx_data_reg <= {{5{CTRL_IDLE}}, 5'd0, output_data_reg[15:0], BLOCK_TYPE_TERM_2}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_3: begin + encoded_tx_data_reg <= {{4{CTRL_IDLE}}, 4'd0, output_data_reg[23:0], BLOCK_TYPE_TERM_3}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_4: begin + encoded_tx_data_reg <= {{3{CTRL_IDLE}}, 3'd0, output_data_reg[31:0], BLOCK_TYPE_TERM_4}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_5: begin + encoded_tx_data_reg <= {{2{CTRL_IDLE}}, 2'd0, output_data_reg[39:0], BLOCK_TYPE_TERM_5}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_6: begin + encoded_tx_data_reg <= {{1{CTRL_IDLE}}, 1'd0, output_data_reg[47:0], BLOCK_TYPE_TERM_6}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_7: begin + encoded_tx_data_reg <= {output_data_reg[55:0], BLOCK_TYPE_TERM_7}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + default: begin + encoded_tx_data_reg <= {{8{CTRL_ERROR}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + endcase + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next7; + end + end + + s_tdata_reg <= s_tdata_next; + s_tkeep_reg <= s_tkeep_next; + + m_axis_ptp_ts_reg <= m_axis_ptp_ts_next; + m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next; + + swap_data <= output_data_next[63:32]; + + delay_type <= output_type_next ^ 4'd4; +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_eth_fcs.v b/fpga/lib/eth/rtl/axis_eth_fcs.v new file mode 100644 index 000000000..16e6a193f --- /dev/null +++ b/fpga/lib/eth/rtl/axis_eth_fcs.v @@ -0,0 +1,98 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS Generator + */ +module axis_eth_fcs +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * FCS output + */ + output wire [31:0] output_fcs, + output wire output_fcs_valid +); + +reg [31:0] crc_state = 32'hFFFFFFFF; +reg [31:0] fcs_reg = 32'h00000000; +reg fcs_valid_reg = 1'b0; + +wire [31:0] crc_next; + +assign s_axis_tready = 1; +assign output_fcs = fcs_reg; +assign output_fcs_valid = fcs_valid_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_axis_tdata), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @(posedge clk) begin + if (rst) begin + crc_state <= 32'hFFFFFFFF; + fcs_reg <= 32'h00000000; + fcs_valid_reg <= 1'b0; + end else begin + fcs_valid_reg <= 1'b0; + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + crc_state <= 32'hFFFFFFFF; + fcs_reg <= ~crc_next; + fcs_valid_reg <= 1'b1; + end else begin + crc_state <= crc_next; + end + end + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_eth_fcs_64.v b/fpga/lib/eth/rtl/axis_eth_fcs_64.v new file mode 100644 index 000000000..e2fa4e371 --- /dev/null +++ b/fpga/lib/eth/rtl/axis_eth_fcs_64.v @@ -0,0 +1,227 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS Generator (64 bit datapath) + */ +module axis_eth_fcs_64 +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [63:0] s_axis_tdata, + input wire [7:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * FCS output + */ + output wire [31:0] output_fcs, + output wire output_fcs_valid +); + +reg [31:0] crc_state = 32'hFFFFFFFF; +reg [31:0] fcs_reg = 32'h00000000; +reg fcs_valid_reg = 1'b0; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next4; +wire [31:0] crc_next5; +wire [31:0] crc_next6; +wire [31:0] crc_next7; + +assign s_axis_tready = 1'b1; +assign output_fcs = fcs_reg; +assign output_fcs_valid = fcs_valid_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_axis_tdata[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(s_axis_tdata[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(s_axis_tdata[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(s_axis_tdata[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(40), + .STYLE("AUTO") +) +eth_crc_40 ( + .data_in(s_axis_tdata[39:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next4) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(48), + .STYLE("AUTO") +) +eth_crc_48 ( + .data_in(s_axis_tdata[47:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next5) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(56), + .STYLE("AUTO") +) +eth_crc_56 ( + .data_in(s_axis_tdata[55:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next6) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(s_axis_tdata[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +always @(posedge clk) begin + if (rst) begin + crc_state <= 32'hFFFFFFFF; + fcs_reg <= 1'b0; + fcs_valid_reg <= 1'b0; + end else begin + fcs_valid_reg <= 1'b0; + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + crc_state <= 32'hFFFFFFFF; + case (s_axis_tkeep) + 8'b00000001: fcs_reg <= ~crc_next0; + 8'b00000011: fcs_reg <= ~crc_next1; + 8'b00000111: fcs_reg <= ~crc_next2; + 8'b00001111: fcs_reg <= ~crc_next3; + 8'b00011111: fcs_reg <= ~crc_next4; + 8'b00111111: fcs_reg <= ~crc_next5; + 8'b01111111: fcs_reg <= ~crc_next6; + 8'b11111111: fcs_reg <= ~crc_next7; + endcase + fcs_valid_reg <= 1'b1; + end else begin + crc_state <= crc_next7; + end + end + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_eth_fcs_check.v b/fpga/lib/eth/rtl/axis_eth_fcs_check.v new file mode 100644 index 000000000..00625238e --- /dev/null +++ b/fpga/lib/eth/rtl/axis_eth_fcs_check.v @@ -0,0 +1,341 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS checker + */ +module axis_eth_fcs_check +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status + */ + output wire busy, + output wire error_bad_fcs +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; +reg shift_in; +reg shift_reset; + +reg [7:0] s_axis_tdata_d0 = 8'd0; +reg [7:0] s_axis_tdata_d1 = 8'd0; +reg [7:0] s_axis_tdata_d2 = 8'd0; +reg [7:0] s_axis_tdata_d3 = 8'd0; + +reg s_axis_tvalid_d0 = 1'b0; +reg s_axis_tvalid_d1 = 1'b0; +reg s_axis_tvalid_d2 = 1'b0; +reg s_axis_tvalid_d3 = 1'b0; + +reg busy_reg = 1'b0; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign busy = busy_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_axis_tdata_d3), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + shift_in = 1'b0; + shift_reset = 1'b0; + + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + error_bad_fcs_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + s_axis_tready_next = m_axis_tready_int_early; + reset_crc = 1'b1; + + m_axis_tdata_int = s_axis_tdata_d3; + m_axis_tvalid_int = s_axis_tvalid_d3 && s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + shift_in = 1'b1; + + if (s_axis_tvalid_d3) begin + reset_crc = 1'b0; + update_crc = 1'b1; + if (s_axis_tlast) begin + shift_reset = 1'b1; + reset_crc = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = s_axis_tuser; + if ({s_axis_tdata, s_axis_tdata_d0, s_axis_tdata_d1, s_axis_tdata_d2} != ~crc_next) begin + m_axis_tuser_int = 1'b1; + error_bad_fcs_next = 1'b1; + end + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_axis_tdata_d3; + m_axis_tvalid_int = s_axis_tvalid_d3 && s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + shift_in = 1'b1; + update_crc = 1'b1; + if (s_axis_tlast) begin + shift_reset = 1'b1; + reset_crc = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = s_axis_tuser; + if ({s_axis_tdata, s_axis_tdata_d0, s_axis_tdata_d1, s_axis_tdata_d2} != ~crc_next) begin + m_axis_tuser_int = 1'b1; + error_bad_fcs_next = 1'b1; + end + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + s_axis_tready_reg <= 1'b0; + + busy_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + s_axis_tvalid_d0 <= 1'b0; + s_axis_tvalid_d1 <= 1'b0; + s_axis_tvalid_d2 <= 1'b0; + s_axis_tvalid_d3 <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + s_axis_tready_reg <= s_axis_tready_next; + + busy_reg <= state_next != STATE_IDLE; + error_bad_fcs_reg <= error_bad_fcs_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + + if (shift_reset) begin + s_axis_tvalid_d0 <= 1'b0; + s_axis_tvalid_d1 <= 1'b0; + s_axis_tvalid_d2 <= 1'b0; + s_axis_tvalid_d3 <= 1'b0; + end else if (shift_in) begin + s_axis_tvalid_d0 <= s_axis_tvalid; + s_axis_tvalid_d1 <= s_axis_tvalid_d0; + s_axis_tvalid_d2 <= s_axis_tvalid_d1; + s_axis_tvalid_d3 <= s_axis_tvalid_d2; + end + end + + if (shift_in) begin + s_axis_tdata_d0 <= s_axis_tdata; + s_axis_tdata_d1 <= s_axis_tdata_d0; + s_axis_tdata_d2 <= s_axis_tdata_d1; + s_axis_tdata_d3 <= s_axis_tdata_d2; + end +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_eth_fcs_check_64.v b/fpga/lib/eth/rtl/axis_eth_fcs_check_64.v new file mode 100644 index 000000000..1fd172066 --- /dev/null +++ b/fpga/lib/eth/rtl/axis_eth_fcs_check_64.v @@ -0,0 +1,478 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS checker (64 bit datapath) + */ +module axis_eth_fcs_check_64 +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [63:0] s_axis_tdata, + input wire [7:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [63:0] m_axis_tdata, + output wire [7:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status + */ + output wire busy, + output wire error_bad_fcs +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1, + STATE_LAST = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; +reg shift_in; +reg shift_reset; + +reg [7:0] last_cycle_tkeep_reg = 8'd0, last_cycle_tkeep_next; +reg last_cycle_tuser_reg = 1'b0, last_cycle_tuser_next; + +reg [63:0] s_axis_tdata_d0 = 64'd0; +reg [7:0] s_axis_tkeep_d0 = 8'd0; +reg s_axis_tvalid_d0 = 1'b0; +reg s_axis_tuser_d0 = 1'b0; + +reg busy_reg = 1'b0; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +reg [31:0] crc_state3 = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next7; + +wire crc_valid0 = crc_next0 == ~32'h2144df1c; +wire crc_valid1 = crc_next1 == ~32'h2144df1c; +wire crc_valid2 = crc_next2 == ~32'h2144df1c; +wire crc_valid3 = crc_next3 == ~32'h2144df1c; + +// internal datapath +reg [63:0] m_axis_tdata_int; +reg [7:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign busy = busy_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +wire last_cycle = state_reg == STATE_LAST; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(last_cycle ? s_axis_tdata_d0[39:32] : s_axis_tdata[7:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(last_cycle ? s_axis_tdata_d0[47:32] : s_axis_tdata[15:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(last_cycle ? s_axis_tdata_d0[55:32] : s_axis_tdata[23:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(last_cycle ? s_axis_tdata_d0[63:32] : s_axis_tdata[31:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(s_axis_tdata[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + shift_in = 1'b0; + shift_reset = 1'b0; + + last_cycle_tkeep_next = last_cycle_tkeep_reg; + last_cycle_tuser_next = last_cycle_tuser_reg; + + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = 64'd0; + m_axis_tkeep_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + error_bad_fcs_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + s_axis_tready_next = m_axis_tready_int_early; + reset_crc = 1'b1; + + m_axis_tdata_int = s_axis_tdata_d0; + m_axis_tkeep_int = s_axis_tkeep_d0; + m_axis_tvalid_int = s_axis_tvalid_d0 && s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + shift_in = 1'b1; + reset_crc = 1'b0; + update_crc = 1'b1; + if (s_axis_tlast) begin + if (s_axis_tkeep[7:4] == 0) begin + shift_reset = 1'b1; + reset_crc = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = s_axis_tuser; + m_axis_tkeep_int = {s_axis_tkeep[3:0], 4'b1111}; + if ((s_axis_tkeep[3:0] == 4'b0001 && crc_valid0) || + (s_axis_tkeep[3:0] == 4'b0011 && crc_valid1) || + (s_axis_tkeep[3:0] == 4'b0111 && crc_valid2) || + (s_axis_tkeep[3:0] == 4'b1111 && crc_valid3)) begin + // CRC valid + end else begin + m_axis_tuser_int = 1'b1; + error_bad_fcs_next = 1'b1; + end + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end else begin + last_cycle_tkeep_next = {4'b0000, s_axis_tkeep[7:4]}; + last_cycle_tuser_next = s_axis_tuser; + s_axis_tready_next = 1'b0; + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_axis_tdata_d0; + m_axis_tkeep_int = s_axis_tkeep_d0; + m_axis_tvalid_int = s_axis_tvalid_d0 && s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + shift_in = 1'b1; + update_crc = 1'b1; + if (s_axis_tlast) begin + if (s_axis_tkeep[7:4] == 0) begin + shift_reset = 1'b1; + reset_crc = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = s_axis_tuser; + m_axis_tkeep_int = {s_axis_tkeep[3:0], 4'b1111}; + if ((s_axis_tkeep[3:0] == 4'b0001 && crc_valid0) || + (s_axis_tkeep[3:0] == 4'b0011 && crc_valid1) || + (s_axis_tkeep[3:0] == 4'b0111 && crc_valid2) || + (s_axis_tkeep[3:0] == 4'b1111 && crc_valid3)) begin + // CRC valid + end else begin + m_axis_tuser_int = 1'b1; + error_bad_fcs_next = 1'b1; + end + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end else begin + last_cycle_tkeep_next = {4'b0000, s_axis_tkeep[7:4]}; + last_cycle_tuser_next = s_axis_tuser; + s_axis_tready_next = 1'b0; + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_LAST: begin + // last cycle + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = s_axis_tdata_d0; + m_axis_tkeep_int = last_cycle_tkeep_reg; + m_axis_tvalid_int = s_axis_tvalid_d0; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = last_cycle_tuser_reg; + + if ((s_axis_tkeep_d0[7:4] == 4'b0001 && crc_valid0) || + (s_axis_tkeep_d0[7:4] == 4'b0011 && crc_valid1) || + (s_axis_tkeep_d0[7:4] == 4'b0111 && crc_valid2) || + (s_axis_tkeep_d0[7:4] == 4'b1111 && crc_valid3)) begin + // CRC valid + end else begin + m_axis_tuser_int = 1'b1; + error_bad_fcs_next = 1'b1; + end + + if (m_axis_tready_int_reg) begin + shift_reset = 1'b1; + reset_crc = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end else begin + state_next = STATE_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + s_axis_tready_reg <= 1'b0; + + busy_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + s_axis_tvalid_d0 <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + crc_state3 <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + s_axis_tready_reg <= s_axis_tready_next; + + busy_reg <= state_next != STATE_IDLE; + error_bad_fcs_reg <= error_bad_fcs_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + crc_state3 <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next7; + crc_state3 <= crc_next3; + end + + if (shift_reset) begin + s_axis_tvalid_d0 <= 1'b0; + end else if (shift_in) begin + s_axis_tvalid_d0 <= s_axis_tvalid; + end + end + + last_cycle_tkeep_reg <= last_cycle_tkeep_next; + last_cycle_tuser_reg <= last_cycle_tuser_next; + + if (shift_in) begin + s_axis_tdata_d0 <= s_axis_tdata; + s_axis_tkeep_d0 <= s_axis_tkeep; + s_axis_tuser_d0 <= s_axis_tuser; + end +end + +// output datapath logic +reg [63:0] m_axis_tdata_reg = 64'd0; +reg [7:0] m_axis_tkeep_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_axis_tkeep_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_eth_fcs_insert.v b/fpga/lib/eth/rtl/axis_eth_fcs_insert.v new file mode 100644 index 000000000..a73baab0b --- /dev/null +++ b/fpga/lib/eth/rtl/axis_eth_fcs_insert.v @@ -0,0 +1,368 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS inserter + */ +module axis_eth_fcs_insert # +( + parameter ENABLE_PADDING = 0, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status + */ + output wire busy +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1, + STATE_PAD = 2'd2, + STATE_FCS = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg busy_reg = 1'b0; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign busy = busy_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(m_axis_tdata_int), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + reset_crc = 1'b1; + + m_axis_tdata_int = s_axis_tdata; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + frame_ptr_next = 16'd1; + reset_crc = 1'b0; + update_crc = 1'b1; + if (s_axis_tlast) begin + if (s_axis_tuser) begin + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = 1'b1; + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + if (ENABLE_PADDING && frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_axis_tdata; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + frame_ptr_next = frame_ptr_reg + 16'd1; + update_crc = 1'b1; + if (s_axis_tlast) begin + if (s_axis_tuser) begin + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = 1'b1; + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + if (ENABLE_PADDING && frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_PAD: begin + // insert padding + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (m_axis_tready_int_reg) begin + frame_ptr_next = frame_ptr_reg + 16'd1; + update_crc = 1'b1; + if (frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end else begin + state_next = STATE_PAD; + end + end + STATE_FCS: begin + // send FCS + s_axis_tready_next = 1'b0; + + case (frame_ptr_reg) + 2'd0: m_axis_tdata_int = ~crc_state[7:0]; + 2'd1: m_axis_tdata_int = ~crc_state[15:8]; + 2'd2: m_axis_tdata_int = ~crc_state[23:16]; + 2'd3: m_axis_tdata_int = ~crc_state[31:24]; + endcase + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (m_axis_tready_int_reg) begin + frame_ptr_next = frame_ptr_reg + 16'd1; + + if (frame_ptr_reg < 16'd3) begin + state_next = STATE_FCS; + end else begin + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_FCS; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 1'b0; + + s_axis_tready_reg <= 1'b0; + + busy_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + + busy_reg <= state_next != STATE_IDLE; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + end +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_eth_fcs_insert_64.v b/fpga/lib/eth/rtl/axis_eth_fcs_insert_64.v new file mode 100644 index 000000000..887cc4315 --- /dev/null +++ b/fpga/lib/eth/rtl/axis_eth_fcs_insert_64.v @@ -0,0 +1,717 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS inserter (64 bit datapath) + */ +module axis_eth_fcs_insert_64 # +( + parameter ENABLE_PADDING = 0, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [63:0] s_axis_tdata, + input wire [7:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [63:0] m_axis_tdata, + output wire [7:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status + */ + output wire busy +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1, + STATE_PAD = 2'd2, + STATE_FCS = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [63:0] s_axis_tdata_masked; + +reg [63:0] fcs_s_tdata; +reg [7:0] fcs_s_tkeep; + +reg [63:0] fcs_m_tdata_0; +reg [63:0] fcs_m_tdata_1; +reg [7:0] fcs_m_tkeep_0; +reg [7:0] fcs_m_tkeep_1; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [63:0] last_cycle_tdata_reg = 64'd0, last_cycle_tdata_next; +reg [7:0] last_cycle_tkeep_reg = 8'd0, last_cycle_tkeep_next; + +reg busy_reg = 1'b0; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next4; +wire [31:0] crc_next5; +wire [31:0] crc_next6; +wire [31:0] crc_next7; + +// internal datapath +reg [63:0] m_axis_tdata_int; +reg [7:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign busy = busy_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(fcs_s_tdata[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(fcs_s_tdata[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(fcs_s_tdata[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(fcs_s_tdata[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(40), + .STYLE("AUTO") +) +eth_crc_40 ( + .data_in(fcs_s_tdata[39:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next4) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(48), + .STYLE("AUTO") +) +eth_crc_48 ( + .data_in(fcs_s_tdata[47:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next5) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(56), + .STYLE("AUTO") +) +eth_crc_56 ( + .data_in(fcs_s_tdata[55:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next6) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(fcs_s_tdata[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 8; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = s_axis_tkeep[j] ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (fcs_s_tkeep) + 8'bzzzzzz01: begin + fcs_m_tdata_0 = {24'd0, ~crc_next0[31:0], fcs_s_tdata[7:0]}; + fcs_m_tdata_1 = 64'd0; + fcs_m_tkeep_0 = 8'b00011111; + fcs_m_tkeep_1 = 8'b00000000; + end + 8'bzzzzz011: begin + fcs_m_tdata_0 = {16'd0, ~crc_next1[31:0], fcs_s_tdata[15:0]}; + fcs_m_tdata_1 = 64'd0; + fcs_m_tkeep_0 = 8'b00111111; + fcs_m_tkeep_1 = 8'b00000000; + end + 8'bzzzz0111: begin + fcs_m_tdata_0 = {8'd0, ~crc_next2[31:0], fcs_s_tdata[23:0]}; + fcs_m_tdata_1 = 64'd0; + fcs_m_tkeep_0 = 8'b01111111; + fcs_m_tkeep_1 = 8'b00000000; + end + 8'bzzz01111: begin + fcs_m_tdata_0 = {~crc_next3[31:0], fcs_s_tdata[31:0]}; + fcs_m_tdata_1 = 64'd0; + fcs_m_tkeep_0 = 8'b11111111; + fcs_m_tkeep_1 = 8'b00000000; + end + 8'bzz011111: begin + fcs_m_tdata_0 = {~crc_next4[23:0], fcs_s_tdata[39:0]}; + fcs_m_tdata_1 = {56'd0, ~crc_next4[31:24]}; + fcs_m_tkeep_0 = 8'b11111111; + fcs_m_tkeep_1 = 8'b00000001; + end + 8'bz0111111: begin + fcs_m_tdata_0 = {~crc_next5[15:0], fcs_s_tdata[47:0]}; + fcs_m_tdata_1 = {48'd0, ~crc_next5[31:16]}; + fcs_m_tkeep_0 = 8'b11111111; + fcs_m_tkeep_1 = 8'b00000011; + end + 8'b01111111: begin + fcs_m_tdata_0 = {~crc_next6[7:0], fcs_s_tdata[55:0]}; + fcs_m_tdata_1 = {40'd0, ~crc_next6[31:8]}; + fcs_m_tkeep_0 = 8'b11111111; + fcs_m_tkeep_1 = 8'b00000111; + end + 8'b11111111: begin + fcs_m_tdata_0 = fcs_s_tdata; + fcs_m_tdata_1 = {32'd0, ~crc_next7[31:0]}; + fcs_m_tkeep_0 = 8'b11111111; + fcs_m_tkeep_1 = 8'b00001111; + end + default: begin + fcs_m_tdata_0 = 64'd0; + fcs_m_tdata_1 = 64'd0; + fcs_m_tkeep_0 = 8'd0; + fcs_m_tkeep_1 = 8'd0; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + last_cycle_tdata_next = last_cycle_tdata_reg; + last_cycle_tkeep_next = last_cycle_tkeep_reg; + + s_axis_tready_next = 1'b0; + + fcs_s_tdata = 64'd0; + fcs_s_tkeep = 8'd0; + + m_axis_tdata_int = 64'd0; + m_axis_tkeep_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + reset_crc = 1'b1; + + m_axis_tdata_int = s_axis_tdata_masked; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + fcs_s_tdata = s_axis_tdata_masked; + fcs_s_tkeep = s_axis_tkeep; + + if (s_axis_tready && s_axis_tvalid) begin + reset_crc = 1'b0; + update_crc = 1'b1; + frame_ptr_next = keep2count(s_axis_tkeep); + if (s_axis_tlast) begin + if (s_axis_tuser) begin + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = 1'b1; + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + if (ENABLE_PADDING && frame_ptr_next < MIN_FRAME_LENGTH-4) begin + m_axis_tkeep_int = 8'hff; + fcs_s_tkeep = 8'hff; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_next < MIN_FRAME_LENGTH-4) begin + s_axis_tready_next = 1'b0; + state_next = STATE_PAD; + end else begin + m_axis_tkeep_int = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + fcs_s_tkeep = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + + m_axis_tdata_int = fcs_m_tdata_0; + last_cycle_tdata_next = fcs_m_tdata_1; + m_axis_tkeep_int = fcs_m_tkeep_0; + last_cycle_tkeep_next = fcs_m_tkeep_1; + + reset_crc = 1'b1; + + if (fcs_m_tkeep_1 == 8'd0) begin + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 1'b0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + state_next = STATE_FCS; + end + end + end else begin + m_axis_tdata_int = fcs_m_tdata_0; + last_cycle_tdata_next = fcs_m_tdata_1; + m_axis_tkeep_int = fcs_m_tkeep_0; + last_cycle_tkeep_next = fcs_m_tkeep_1; + + reset_crc = 1'b1; + + if (fcs_m_tkeep_1 == 8'd0) begin + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + state_next = STATE_FCS; + end + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_axis_tdata_masked; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + fcs_s_tdata = s_axis_tdata_masked; + fcs_s_tkeep = s_axis_tkeep; + + if (s_axis_tready && s_axis_tvalid) begin + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + keep2count(s_axis_tkeep); + if (s_axis_tlast) begin + if (s_axis_tuser) begin + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = 1'b1; + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + if (ENABLE_PADDING && frame_ptr_next < MIN_FRAME_LENGTH-4) begin + m_axis_tkeep_int = 8'hff; + fcs_s_tkeep = 8'hff; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_next < MIN_FRAME_LENGTH-4) begin + s_axis_tready_next = 1'b0; + state_next = STATE_PAD; + end else begin + m_axis_tkeep_int = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + fcs_s_tkeep = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + + m_axis_tdata_int = fcs_m_tdata_0; + last_cycle_tdata_next = fcs_m_tdata_1; + m_axis_tkeep_int = fcs_m_tkeep_0; + last_cycle_tkeep_next = fcs_m_tkeep_1; + + reset_crc = 1'b1; + + if (fcs_m_tkeep_1 == 8'd0) begin + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + state_next = STATE_FCS; + end + end + end else begin + m_axis_tdata_int = fcs_m_tdata_0; + last_cycle_tdata_next = fcs_m_tdata_1; + m_axis_tkeep_int = fcs_m_tkeep_0; + last_cycle_tkeep_next = fcs_m_tkeep_1; + + reset_crc = 1'b1; + + if (fcs_m_tkeep_1 == 8'd0) begin + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + state_next = STATE_FCS; + end + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_PAD: begin + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = 64'd0; + m_axis_tkeep_int = 8'hff; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + fcs_s_tdata = 64'd0; + fcs_s_tkeep = 8'hff; + + if (m_axis_tready_int_reg) begin + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_next < MIN_FRAME_LENGTH-4) begin + state_next = STATE_PAD; + end else begin + m_axis_tkeep_int = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + fcs_s_tkeep = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + + m_axis_tdata_int = fcs_m_tdata_0; + last_cycle_tdata_next = fcs_m_tdata_1; + m_axis_tkeep_int = fcs_m_tkeep_0; + last_cycle_tkeep_next = fcs_m_tkeep_1; + + reset_crc = 1'b1; + + if (fcs_m_tkeep_1 == 8'd0) begin + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + state_next = STATE_FCS; + end + end + end else begin + state_next = STATE_PAD; + end + end + STATE_FCS: begin + // last cycle + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = last_cycle_tdata_reg; + m_axis_tkeep_int = last_cycle_tkeep_reg; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = 1'b0; + + if (m_axis_tready_int_reg) begin + reset_crc = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_FCS; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 1'b0; + + s_axis_tready_reg <= 1'b0; + + busy_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + + busy_reg <= state_next != STATE_IDLE; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next7; + end + end + + last_cycle_tdata_reg <= last_cycle_tdata_next; + last_cycle_tkeep_reg <= last_cycle_tkeep_next; +end + +// output datapath logic +reg [63:0] m_axis_tdata_reg = 64'd0; +reg [7:0] m_axis_tkeep_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_axis_tkeep_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_gmii_rx.v b/fpga/lib/eth/rtl/axis_gmii_rx.v new file mode 100644 index 000000000..b09240162 --- /dev/null +++ b/fpga/lib/eth/rtl/axis_gmii_rx.v @@ -0,0 +1,351 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream GMII frame receiver (GMII in, AXI out) + */ +module axis_gmii_rx # +( + parameter DATA_WIDTH = 8, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * GMII input + */ + input wire [DATA_WIDTH-1:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Control + */ + input wire clk_enable, + input wire mii_select, + + /* + * Status + */ + output wire start_packet, + output wire error_bad_frame, + output wire error_bad_fcs +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 8) begin + $error("Error: Interface width must be 8"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PAYLOAD = 3'd1, + STATE_WAIT_LAST = 3'd2; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg mii_odd = 1'b0; +reg mii_locked = 1'b0; + +reg [DATA_WIDTH-1:0] gmii_rxd_d0 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d1 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d2 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d3 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d4 = {DATA_WIDTH{1'b0}}; + +reg gmii_rx_dv_d0 = 1'b0; +reg gmii_rx_dv_d1 = 1'b0; +reg gmii_rx_dv_d2 = 1'b0; +reg gmii_rx_dv_d3 = 1'b0; +reg gmii_rx_dv_d4 = 1'b0; + +reg gmii_rx_er_d0 = 1'b0; +reg gmii_rx_er_d1 = 1'b0; +reg gmii_rx_er_d2 = 1'b0; +reg gmii_rx_er_d3 = 1'b0; +reg gmii_rx_er_d4 = 1'b0; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}, m_axis_tdata_next; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next; +reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next; + +reg start_packet_reg = 1'b0, start_packet_next; +reg error_bad_frame_reg = 1'b0, error_bad_frame_next; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg [PTP_TS_WIDTH-1:0] ptp_ts_reg = 0, ptp_ts_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = PTP_TS_ENABLE ? {ptp_ts_reg, m_axis_tuser_reg} : m_axis_tuser_reg; + +assign start_packet = start_packet_reg; +assign error_bad_frame = error_bad_frame_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(gmii_rxd_d4), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tvalid_next = 1'b0; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + start_packet_next = 1'b0; + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + ptp_ts_next = ptp_ts_reg; + + if (!clk_enable) begin + // clock disabled - hold state + state_next = state_reg; + end else if (mii_select && !mii_odd) begin + // MII even cycle - hold state + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (gmii_rx_dv_d4 && !gmii_rx_er_d4 && gmii_rxd_d4 == ETH_SFD) begin + ptp_ts_next = ptp_ts; + start_packet_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + update_crc = 1'b1; + + m_axis_tdata_next = gmii_rxd_d4; + m_axis_tvalid_next = 1'b1; + + if (gmii_rx_dv_d4 && gmii_rx_er_d4) begin + // error + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_WAIT_LAST; + end else if (!gmii_rx_dv) begin + // end of packet + m_axis_tlast_next = 1'b1; + if (gmii_rx_er_d0 || gmii_rx_er_d1 || gmii_rx_er_d2 || gmii_rx_er_d3) begin + // error received in FCS bytes + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + end else if ({gmii_rxd_d0, gmii_rxd_d1, gmii_rxd_d2, gmii_rxd_d3} == ~crc_next) begin + // FCS good + m_axis_tuser_next = 1'b0; + end else begin + // FCS bad + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_WAIT_LAST: begin + // wait for end of packet + + if (~gmii_rx_dv) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_reg <= 1'b0; + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + mii_locked <= 1'b0; + mii_odd <= 1'b0; + + gmii_rx_dv_d0 <= 1'b0; + gmii_rx_dv_d1 <= 1'b0; + gmii_rx_dv_d2 <= 1'b0; + gmii_rx_dv_d3 <= 1'b0; + gmii_rx_dv_d4 <= 1'b0; + end else begin + state_reg <= state_next; + + m_axis_tvalid_reg <= m_axis_tvalid_next; + + start_packet_reg <= start_packet_next; + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + + if (clk_enable) begin + if (mii_select) begin + mii_odd <= !mii_odd; + + if (mii_locked) begin + mii_locked <= gmii_rx_dv; + end else if (gmii_rx_dv && {gmii_rxd[3:0], gmii_rxd_d0[7:4]} == ETH_SFD) begin + mii_locked <= 1'b1; + mii_odd <= 1'b1; + end + + if (mii_odd) begin + gmii_rx_dv_d0 <= gmii_rx_dv & gmii_rx_dv_d0; + gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv; + gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv; + gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv; + gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv; + end else begin + gmii_rx_dv_d0 <= gmii_rx_dv; + end + end else begin + gmii_rx_dv_d0 <= gmii_rx_dv; + gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv; + gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv; + gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv; + gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv; + end + end + end + + ptp_ts_reg <= ptp_ts_next; + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + // delay input + if (clk_enable) begin + if (mii_select) begin + gmii_rxd_d0 <= {gmii_rxd[3:0], gmii_rxd_d0[7:4]}; + + if (mii_odd) begin + gmii_rxd_d1 <= gmii_rxd_d0; + gmii_rxd_d2 <= gmii_rxd_d1; + gmii_rxd_d3 <= gmii_rxd_d2; + gmii_rxd_d4 <= gmii_rxd_d3; + + gmii_rx_er_d0 <= gmii_rx_er | gmii_rx_er_d0; + gmii_rx_er_d1 <= gmii_rx_er_d0; + gmii_rx_er_d2 <= gmii_rx_er_d1; + gmii_rx_er_d3 <= gmii_rx_er_d2; + gmii_rx_er_d4 <= gmii_rx_er_d3; + end else begin + gmii_rx_er_d0 <= gmii_rx_er; + end + end else begin + gmii_rxd_d0 <= gmii_rxd; + gmii_rxd_d1 <= gmii_rxd_d0; + gmii_rxd_d2 <= gmii_rxd_d1; + gmii_rxd_d3 <= gmii_rxd_d2; + gmii_rxd_d4 <= gmii_rxd_d3; + + gmii_rx_er_d0 <= gmii_rx_er; + gmii_rx_er_d1 <= gmii_rx_er_d0; + gmii_rx_er_d2 <= gmii_rx_er_d1; + gmii_rx_er_d3 <= gmii_rx_er_d2; + gmii_rx_er_d4 <= gmii_rx_er_d3; + end + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_gmii_tx.v b/fpga/lib/eth/rtl/axis_gmii_tx.v new file mode 100644 index 000000000..de4c43998 --- /dev/null +++ b/fpga/lib/eth/rtl/axis_gmii_tx.v @@ -0,0 +1,448 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * AXI4-Stream GMII frame transmitter (AXI in, GMII out) + */ +module axis_gmii_tx # +( + parameter DATA_WIDTH = 8, + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * GMII output + */ + output wire [DATA_WIDTH-1:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Control + */ + input wire clk_enable, + input wire mii_select, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire start_packet, + output wire error_underflow +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 8) begin + $error("Error: Interface width must be 8"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PREAMBLE = 3'd1, + STATE_PAYLOAD = 3'd2, + STATE_LAST = 3'd3, + STATE_PAD = 3'd4, + STATE_FCS = 3'd5, + STATE_WAIT_END = 3'd6, + STATE_IFG = 3'd7; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [7:0] s_tdata_reg = 8'd0, s_tdata_next; + +reg mii_odd_reg = 1'b0, mii_odd_next; +reg [3:0] mii_msn_reg = 4'b0, mii_msn_next; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] gmii_txd_reg = 8'd0, gmii_txd_next; +reg gmii_tx_en_reg = 1'b0, gmii_tx_en_next; +reg gmii_tx_er_reg = 1'b0, gmii_tx_er_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg = 0, m_axis_ptp_ts_next; +reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg = 0, m_axis_ptp_ts_tag_next; +reg m_axis_ptp_ts_valid_reg = 1'b0, m_axis_ptp_ts_valid_next; + +reg start_packet_reg = 1'b0, start_packet_next; +reg error_underflow_reg = 1'b0, error_underflow_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign gmii_txd = gmii_txd_reg; +assign gmii_tx_en = gmii_tx_en_reg; +assign gmii_tx_er = gmii_tx_er_reg; + +assign m_axis_ptp_ts = PTP_TS_ENABLE ? m_axis_ptp_ts_reg : 0; +assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : 0; +assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0; + +assign start_packet = start_packet_reg; +assign error_underflow = error_underflow_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_tdata_reg), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + mii_odd_next = mii_odd_reg; + mii_msn_next = mii_msn_reg; + + frame_ptr_next = frame_ptr_reg; + + s_axis_tready_next = 1'b0; + + s_tdata_next = s_tdata_reg; + + m_axis_ptp_ts_next = m_axis_ptp_ts_reg; + m_axis_ptp_ts_tag_next = m_axis_ptp_ts_tag_reg; + m_axis_ptp_ts_valid_next = 1'b0; + + gmii_txd_next = {DATA_WIDTH{1'b0}}; + gmii_tx_en_next = 1'b0; + gmii_tx_er_next = 1'b0; + + start_packet_next = 1'b0; + error_underflow_next = 1'b0; + + if (!clk_enable) begin + // clock disabled - hold state and outputs + gmii_txd_next = gmii_txd_reg; + gmii_tx_en_next = gmii_tx_en_reg; + gmii_tx_er_next = gmii_tx_er_reg; + state_next = state_reg; + end else if (mii_select && mii_odd_reg) begin + // MII odd cycle - hold state, output MSN + mii_odd_next = 1'b0; + gmii_txd_next = {4'd0, mii_msn_reg}; + gmii_tx_en_next = gmii_tx_en_reg; + gmii_tx_er_next = gmii_tx_er_reg; + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + mii_odd_next = 1'b0; + + if (s_axis_tvalid) begin + mii_odd_next = 1'b1; + frame_ptr_next = 16'd1; + gmii_txd_next = ETH_PRE; + gmii_tx_en_next = 1'b1; + state_next = STATE_PREAMBLE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + // send preamble + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = ETH_PRE; + gmii_tx_en_next = 1'b1; + + if (frame_ptr_reg == 16'd6) begin + s_axis_tready_next = 1'b1; + s_tdata_next = s_axis_tdata; + state_next = STATE_PREAMBLE; + end else if (frame_ptr_reg == 16'd7) begin + // end of preamble; start payload + frame_ptr_next = 16'd0; + if (s_axis_tready_reg) begin + s_axis_tready_next = 1'b1; + s_tdata_next = s_axis_tdata; + end + gmii_txd_next = ETH_SFD; + m_axis_ptp_ts_next = ptp_ts; + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_next = 1'b1; + start_packet_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_PREAMBLE; + end + end + STATE_PAYLOAD: begin + // send payload + + update_crc = 1'b1; + s_axis_tready_next = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = s_tdata_reg; + gmii_tx_en_next = 1'b1; + + s_tdata_next = s_axis_tdata; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = !s_axis_tready_reg; + if (s_axis_tuser[0]) begin + gmii_tx_er_next = 1'b1; + frame_ptr_next = 1'b0; + state_next = STATE_IFG; + end else begin + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + gmii_tx_er_next = 1'b1; + frame_ptr_next = 16'd0; + error_underflow_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + STATE_LAST: begin + // last payload word + + update_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = s_tdata_reg; + gmii_tx_en_next = 1'b1; + + if (ENABLE_PADDING && frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + s_tdata_next = 8'd0; + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + STATE_PAD: begin + // send padding + + update_crc = 1'b1; + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = 8'd0; + gmii_tx_en_next = 1'b1; + + s_tdata_next = 8'd0; + + if (frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + STATE_FCS: begin + // send FCS + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + case (frame_ptr_reg) + 2'd0: gmii_txd_next = ~crc_state[7:0]; + 2'd1: gmii_txd_next = ~crc_state[15:8]; + 2'd2: gmii_txd_next = ~crc_state[23:16]; + 2'd3: gmii_txd_next = ~crc_state[31:24]; + endcase + gmii_tx_en_next = 1'b1; + + if (frame_ptr_reg < 3) begin + state_next = STATE_FCS; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_IFG; + end + end + STATE_WAIT_END: begin + // wait for end of frame + + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + s_axis_tready_next = 1'b1; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = 1'b0; + if (frame_ptr_reg < ifg_delay-1) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + STATE_IFG: begin + // send IFG + + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + if (frame_ptr_reg < ifg_delay-1) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + endcase + + if (mii_select) begin + mii_msn_next = gmii_txd_next[7:4]; + gmii_txd_next[7:4] = 4'd0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + s_axis_tready_reg <= 1'b0; + + m_axis_ptp_ts_valid_reg <= 1'b0; + + gmii_tx_en_reg <= 1'b0; + gmii_tx_er_reg <= 1'b0; + + start_packet_reg <= 1'b0; + error_underflow_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next; + + gmii_tx_en_reg <= gmii_tx_en_next; + gmii_tx_er_reg <= gmii_tx_er_next; + + start_packet_reg <= start_packet_next; + error_underflow_reg <= error_underflow_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + end + + m_axis_ptp_ts_reg <= m_axis_ptp_ts_next; + m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next; + + mii_odd_reg <= mii_odd_next; + mii_msn_reg <= mii_msn_next; + + s_tdata_reg <= s_tdata_next; + + gmii_txd_reg <= gmii_txd_next; +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_xgmii_rx_32.v b/fpga/lib/eth/rtl/axis_xgmii_rx_32.v new file mode 100644 index 000000000..a295b020c --- /dev/null +++ b/fpga/lib/eth/rtl/axis_xgmii_rx_32.v @@ -0,0 +1,435 @@ +/* + +Copyright (c) 2015-2017 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 1ns / 1ps + +/* + * AXI4-Stream XGMII frame receiver (XGMII in, AXI out) + */ +module axis_xgmii_rx_32 # +( + parameter DATA_WIDTH = 32, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * XGMII input + */ + input wire [DATA_WIDTH-1:0] xgmii_rxd, + input wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Status + */ + output wire start_packet, + output wire error_bad_frame, + output wire error_bad_fcs +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 32) begin + $error("Error: Interface width must be 32"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PREAMBLE = 2'd1, + STATE_PAYLOAD = 2'd2, + STATE_LAST = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; + +reg [3:0] last_cycle_tkeep_reg = 4'd0, last_cycle_tkeep_next; + +reg [DATA_WIDTH-1:0] xgmii_rxd_d0 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] xgmii_rxd_d1 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] xgmii_rxd_d2 = {DATA_WIDTH{1'b0}}; + +reg [CTRL_WIDTH-1:0] xgmii_rxc_d0 = {CTRL_WIDTH{1'b0}}; +reg [CTRL_WIDTH-1:0] xgmii_rxc_d1 = {CTRL_WIDTH{1'b0}}; +reg [CTRL_WIDTH-1:0] xgmii_rxc_d2 = {CTRL_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}, m_axis_tdata_next; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, m_axis_tkeep_next; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next; +reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next; + +reg start_packet_reg = 1'b0, start_packet_next; +reg error_bad_frame_reg = 1'b0, error_bad_frame_next; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg [PTP_TS_WIDTH-1:0] ptp_ts_reg = 0, ptp_ts_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; + +wire crc_valid0 = crc_next0 == ~32'h2144df1c; +wire crc_valid1 = crc_next1 == ~32'h2144df1c; +wire crc_valid2 = crc_next2 == ~32'h2144df1c; +wire crc_valid3 = crc_next3 == ~32'h2144df1c; + +reg crc_valid0_save = 1'b0; +reg crc_valid1_save = 1'b0; +reg crc_valid2_save = 1'b0; +reg crc_valid3_save = 1'b0; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = PTP_TS_ENABLE ? {ptp_ts_reg, m_axis_tuser_reg} : m_axis_tuser_reg; + +assign start_packet = start_packet_reg; +assign error_bad_frame = error_bad_frame_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +wire last_cycle = state_reg == STATE_LAST; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(xgmii_rxd_d0[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(xgmii_rxd_d0[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(xgmii_rxd_d0[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(xgmii_rxd_d0[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +// detect control characters +reg [3:0] detect_term = 4'd0; + +reg [3:0] detect_term_save; + +integer i; + +// mask errors to within packet +reg [3:0] control_masked; +reg [3:0] tkeep_mask; + +always @* begin + casez (detect_term) + 4'b0000: begin + control_masked = xgmii_rxc_d0; + tkeep_mask = 4'b1111; + end + 4'bzzz1: begin + control_masked = 0; + tkeep_mask = 4'b0000; + end + 4'bzz10: begin + control_masked = xgmii_rxc_d0[0]; + tkeep_mask = 4'b0001; + end + 4'bz100: begin + control_masked = xgmii_rxc_d0[1:0]; + tkeep_mask = 4'b0011; + end + 4'b1000: begin + control_masked = xgmii_rxc_d0[2:0]; + tkeep_mask = 4'b0111; + end + default: begin + control_masked = xgmii_rxc_d0; + tkeep_mask = 4'b1111; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + + last_cycle_tkeep_next = last_cycle_tkeep_reg; + + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_next = {KEEP_WIDTH{1'b1}}; + m_axis_tvalid_next = 1'b0; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + start_packet_next = 1'b0; + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + ptp_ts_next = ptp_ts_reg; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (xgmii_rxc_d2[0] && xgmii_rxd_d2[7:0] == XGMII_START) begin + // start condition + if (control_masked) begin + // control or error characters in first data word + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_next = 4'h1; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_IDLE; + end else begin + reset_crc = 1'b0; + state_next = STATE_PREAMBLE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + // drop preamble + ptp_ts_next = ptp_ts; + start_packet_next = 1'b1; + state_next = STATE_PAYLOAD; + end + STATE_PAYLOAD: begin + // read payload + m_axis_tdata_next = xgmii_rxd_d2; + m_axis_tkeep_next = {KEEP_WIDTH{1'b1}}; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + last_cycle_tkeep_next = tkeep_mask; + + if (control_masked) begin + // control or error characters in packet + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + reset_crc = 1'b1; + state_next = STATE_IDLE; + end else if (detect_term) begin + if (detect_term[0]) begin + // end this cycle + reset_crc = 1'b1; + m_axis_tkeep_next = 4'b1111; + m_axis_tlast_next = 1'b1; + if (detect_term[0] && crc_valid3_save) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + // need extra cycle + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_LAST: begin + // last cycle of packet + m_axis_tdata_next = xgmii_rxd_d2; + m_axis_tkeep_next = last_cycle_tkeep_reg; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b0; + + reset_crc = 1'b1; + + if ((detect_term_save[1] && crc_valid0_save) || + (detect_term_save[2] && crc_valid1_save) || + (detect_term_save[3] && crc_valid2_save)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + + state_next = STATE_IDLE; + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_reg <= 1'b0; + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + xgmii_rxc_d0 <= {CTRL_WIDTH{1'b0}}; + xgmii_rxc_d1 <= {CTRL_WIDTH{1'b0}}; + end else begin + state_reg <= state_next; + + m_axis_tvalid_reg <= m_axis_tvalid_next; + + start_packet_reg <= start_packet_next; + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + + xgmii_rxc_d0 <= xgmii_rxc; + xgmii_rxc_d1 <= xgmii_rxc_d0; + xgmii_rxc_d2 <= xgmii_rxc_d1; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else begin + crc_state <= crc_next3; + end + end + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tkeep_reg <= m_axis_tkeep_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + ptp_ts_reg <= ptp_ts_next; + + last_cycle_tkeep_reg <= last_cycle_tkeep_next; + + for (i = 0; i < 4; i = i + 1) begin + detect_term[i] <= xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM); + end + + detect_term_save <= detect_term; + + crc_valid0_save <= crc_valid0; + crc_valid1_save <= crc_valid1; + crc_valid2_save <= crc_valid2; + crc_valid3_save <= crc_valid3; + + xgmii_rxd_d0 <= xgmii_rxd; + xgmii_rxd_d1 <= xgmii_rxd_d0; + xgmii_rxd_d2 <= xgmii_rxd_d1; +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_xgmii_rx_64.v b/fpga/lib/eth/rtl/axis_xgmii_rx_64.v new file mode 100644 index 000000000..8ef42e16b --- /dev/null +++ b/fpga/lib/eth/rtl/axis_xgmii_rx_64.v @@ -0,0 +1,555 @@ +/* + +Copyright (c) 2015-2017 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 1ns / 1ps + +/* + * AXI4-Stream XGMII frame receiver (XGMII in, AXI out) + */ +module axis_xgmii_rx_64 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * XGMII input + */ + input wire [DATA_WIDTH-1:0] xgmii_rxd, + input wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Status + */ + output wire [1:0] start_packet, + output wire error_bad_frame, + output wire error_bad_fcs +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1, + STATE_LAST = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc_last; + +reg [7:0] last_cycle_tkeep_reg = 8'd0, last_cycle_tkeep_next; + +reg lanes_swapped = 1'b0; +reg [31:0] swap_rxd = 32'd0; +reg [3:0] swap_rxc = 4'd0; + +reg [DATA_WIDTH-1:0] xgmii_rxd_d0 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] xgmii_rxd_d1 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] xgmii_rxd_crc = {DATA_WIDTH{1'b0}}; + +reg [CTRL_WIDTH-1:0] xgmii_rxc_d0 = {CTRL_WIDTH{1'b0}}; +reg [CTRL_WIDTH-1:0] xgmii_rxc_d1 = {CTRL_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}, m_axis_tdata_next; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, m_axis_tkeep_next; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next; +reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next; + +reg [1:0] start_packet_reg = 2'b00; +reg error_bad_frame_reg = 1'b0, error_bad_frame_next; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg [PTP_TS_WIDTH-1:0] ptp_ts_reg = 0; + +reg [31:0] crc_state = 32'hFFFFFFFF; +reg [31:0] crc_state3 = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next7; + +wire crc_valid0 = crc_next0 == ~32'h2144df1c; +wire crc_valid1 = crc_next1 == ~32'h2144df1c; +wire crc_valid2 = crc_next2 == ~32'h2144df1c; +wire crc_valid3 = crc_next3 == ~32'h2144df1c; +wire crc_valid7 = crc_next7 == ~32'h2144df1c; + +reg crc_valid7_save = 1'b0; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = PTP_TS_ENABLE ? {ptp_ts_reg, m_axis_tuser_reg} : m_axis_tuser_reg; + +assign start_packet = start_packet_reg; +assign error_bad_frame = error_bad_frame_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(xgmii_rxd_crc[7:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(xgmii_rxd_crc[15:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(xgmii_rxd_crc[23:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(xgmii_rxd_crc[31:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(xgmii_rxd_crc[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +// detect control characters +reg [7:0] detect_term = 8'd0; + +reg [7:0] detect_term_save = 8'd0; + +integer i; + +// mask errors to within packet +reg [7:0] control_masked; +reg [7:0] tkeep_mask; + +always @* begin + casez (detect_term) + 8'b00000000: begin + control_masked = xgmii_rxc_d0; + tkeep_mask = 8'b11111111; + end + 8'bzzzzzzz1: begin + control_masked = 0; + tkeep_mask = 8'b00000000; + end + 8'bzzzzzz10: begin + control_masked = xgmii_rxc_d0[0]; + tkeep_mask = 8'b00000001; + end + 8'bzzzzz100: begin + control_masked = xgmii_rxc_d0[1:0]; + tkeep_mask = 8'b00000011; + end + 8'bzzzz1000: begin + control_masked = xgmii_rxc_d0[2:0]; + tkeep_mask = 8'b00000111; + end + 8'bzzz10000: begin + control_masked = xgmii_rxc_d0[3:0]; + tkeep_mask = 8'b00001111; + end + 8'bzz100000: begin + control_masked = xgmii_rxc_d0[4:0]; + tkeep_mask = 8'b00011111; + end + 8'bz1000000: begin + control_masked = xgmii_rxc_d0[5:0]; + tkeep_mask = 8'b00111111; + end + 8'b10000000: begin + control_masked = xgmii_rxc_d0[6:0]; + tkeep_mask = 8'b01111111; + end + default: begin + control_masked = xgmii_rxc_d0; + tkeep_mask = 8'b11111111; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc_last = 1'b0; + + last_cycle_tkeep_next = last_cycle_tkeep_reg; + + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_next = {KEEP_WIDTH{1'b1}}; + m_axis_tvalid_next = 1'b0; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (xgmii_rxc_d1[0] && xgmii_rxd_d1[7:0] == XGMII_START) begin + // start condition + if (control_masked) begin + // control or error characters in first data word + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_next = 8'h01; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_IDLE; + end else begin + reset_crc = 1'b0; + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + m_axis_tdata_next = xgmii_rxd_d1; + m_axis_tkeep_next = {KEEP_WIDTH{1'b1}}; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + last_cycle_tkeep_next = {4'b0000, tkeep_mask[7:4]}; + + if (control_masked) begin + // control or error characters in packet + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + reset_crc = 1'b1; + state_next = STATE_IDLE; + end else if (detect_term) begin + if (detect_term[4:0]) begin + // end this cycle + reset_crc = 1'b1; + m_axis_tkeep_next = {tkeep_mask[3:0], 4'b1111}; + m_axis_tlast_next = 1'b1; + if ((detect_term[0] && crc_valid7_save) || + (detect_term[1] && crc_valid0) || + (detect_term[2] && crc_valid1) || + (detect_term[3] && crc_valid2) || + (detect_term[4] && crc_valid3)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + // need extra cycle + update_crc_last = 1'b1; + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_LAST: begin + // last cycle of packet + m_axis_tdata_next = xgmii_rxd_d1; + m_axis_tkeep_next = last_cycle_tkeep_reg; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b0; + + reset_crc = 1'b1; + + if ((detect_term_save[5] && crc_valid0) || + (detect_term_save[6] && crc_valid1) || + (detect_term_save[7] && crc_valid2)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + + if (xgmii_rxc_d1[0] && xgmii_rxd_d1[7:0] == XGMII_START) begin + // start condition + if (control_masked) begin + // control or error characters in first data word + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_next = 8'h01; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_IDLE; + end else begin + reset_crc = 1'b0; + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_reg <= 2'b00; + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + crc_state3 <= 32'hFFFFFFFF; + + xgmii_rxc_d0 <= {CTRL_WIDTH{1'b0}}; + xgmii_rxc_d1 <= {CTRL_WIDTH{1'b0}}; + + lanes_swapped <= 1'b0; + end else begin + state_reg <= state_next; + + m_axis_tvalid_reg <= m_axis_tvalid_next; + + start_packet_reg <= 2'b00; + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + + if (xgmii_rxc[0] && xgmii_rxd[7:0] == XGMII_START) begin + lanes_swapped <= 1'b0; + start_packet_reg <= 2'b01; + xgmii_rxc_d0 <= xgmii_rxc; + end else if (xgmii_rxc[4] && xgmii_rxd[39:32] == XGMII_START) begin + lanes_swapped <= 1'b1; + start_packet_reg <= 2'b10; + xgmii_rxc_d0 <= {xgmii_rxc[3:0], swap_rxc}; + end else if (lanes_swapped) begin + xgmii_rxc_d0 <= {xgmii_rxc[3:0], swap_rxc}; + end else begin + xgmii_rxc_d0 <= xgmii_rxc; + end + + xgmii_rxc_d1 <= xgmii_rxc_d0; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else begin + crc_state <= crc_next7; + end + + if (update_crc_last) begin + crc_state3 <= crc_next3; + end else begin + crc_state3 <= crc_next7; + end + end + + if (PTP_TS_WIDTH == 96 && $signed({1'b0, ptp_ts_reg[45:16]}) - $signed(31'd1000000000) > 0) begin + // ns field rollover + ptp_ts_reg[45:16] <= $signed({1'b0, ptp_ts_reg[45:16]}) - $signed(31'd1000000000); + ptp_ts_reg[95:48] <= ptp_ts_reg[95:48] + 1; + end + + if (xgmii_rxc[0] && xgmii_rxd[7:0] == XGMII_START) begin + if (PTP_TS_WIDTH == 96) begin + ptp_ts_reg[45:0] <= ptp_ts[45:0] + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + ptp_ts_reg[95:48] <= ptp_ts[95:48]; + end else begin + ptp_ts_reg <= ptp_ts + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + end + end else if (xgmii_rxc[4] && xgmii_rxd[39:32] == XGMII_START) begin + if (PTP_TS_WIDTH == 96) begin + ptp_ts_reg[45:0] <= ptp_ts[45:0] + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + ptp_ts_reg[95:48] <= ptp_ts[95:48]; + end else begin + ptp_ts_reg <= ptp_ts + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + end + end + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tkeep_reg <= m_axis_tkeep_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + last_cycle_tkeep_reg <= last_cycle_tkeep_next; + + detect_term_save <= detect_term; + + swap_rxd <= xgmii_rxd[63:32]; + swap_rxc <= xgmii_rxc[7:4]; + + if (xgmii_rxc[0] && xgmii_rxd[7:0] == XGMII_START) begin + xgmii_rxd_d0 <= xgmii_rxd; + xgmii_rxd_crc <= xgmii_rxd; + + for (i = 0; i < 8; i = i + 1) begin + detect_term[i] <= xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM); + end + end else if (xgmii_rxc[4] && xgmii_rxd[39:32] == XGMII_START) begin + xgmii_rxd_d0 <= {xgmii_rxd[31:0], swap_rxd}; + xgmii_rxd_crc <= {xgmii_rxd[31:0], swap_rxd}; + + for (i = 0; i < 4; i = i + 1) begin + detect_term[i] <= swap_rxc[i] && (swap_rxd[i*8 +: 8] == XGMII_TERM); + detect_term[i+4] <= xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM); + end + end else if (lanes_swapped) begin + xgmii_rxd_d0 <= {xgmii_rxd[31:0], swap_rxd}; + xgmii_rxd_crc <= {xgmii_rxd[31:0], swap_rxd}; + + for (i = 0; i < 4; i = i + 1) begin + detect_term[i] <= swap_rxc[i] && (swap_rxd[i*8 +: 8] == XGMII_TERM); + detect_term[i+4] <= xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM); + end + end else begin + xgmii_rxd_d0 <= xgmii_rxd; + xgmii_rxd_crc <= xgmii_rxd; + + for (i = 0; i < 8; i = i + 1) begin + detect_term[i] <= xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM); + end + end + + crc_valid7_save <= crc_valid7; + + if (state_next == STATE_LAST) begin + xgmii_rxd_crc[31:0] <= xgmii_rxd_crc[63:32]; + end + + xgmii_rxd_d1 <= xgmii_rxd_d0; +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_xgmii_tx_32.v b/fpga/lib/eth/rtl/axis_xgmii_tx_32.v new file mode 100644 index 000000000..ead5affff --- /dev/null +++ b/fpga/lib/eth/rtl/axis_xgmii_tx_32.v @@ -0,0 +1,626 @@ +/* + +Copyright (c) 2015-2017 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 1ns / 1ps + +/* + * AXI4-Stream XGMII frame transmitter (AXI in, XGMII out) + */ +module axis_xgmii_tx_32 # +( + parameter DATA_WIDTH = 32, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * XGMII output + */ + output wire [DATA_WIDTH-1:0] xgmii_txd, + output wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire start_packet, + output wire error_underflow +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 32) begin + $error("Error: Interface width must be 32"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +localparam MIN_FL_NOCRC = MIN_FRAME_LENGTH-4; +localparam MIN_FL_NOCRC_MS = MIN_FL_NOCRC & 16'hfffc; +localparam MIN_FL_NOCRC_LS = MIN_FL_NOCRC & 16'h0003; + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [3:0] + STATE_IDLE = 4'd0, + STATE_PREAMBLE = 4'd1, + STATE_PAYLOAD = 4'd2, + STATE_PAD = 4'd3, + STATE_FCS_1 = 4'd4, + STATE_FCS_2 = 4'd5, + STATE_FCS_3 = 4'd6, + STATE_IFG = 4'd7, + STATE_WAIT_END = 4'd8; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +reg [DATA_WIDTH-1:0] s_tdata_reg ={DATA_WIDTH{1'b0}}, s_tdata_next; +reg [KEEP_WIDTH-1:0] s_tkeep_reg = {KEEP_WIDTH{1'b0}}, s_tkeep_next; + +reg [DATA_WIDTH-1:0] fcs_output_txd_0; +reg [DATA_WIDTH-1:0] fcs_output_txd_1; +reg [CTRL_WIDTH-1:0] fcs_output_txc_0; +reg [CTRL_WIDTH-1:0] fcs_output_txc_1; + +reg [7:0] ifg_offset; + +reg extra_cycle; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] ifg_count_reg = 8'd0, ifg_count_next; +reg [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg = 0, m_axis_ptp_ts_next; +reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg = 0, m_axis_ptp_ts_tag_next; +reg m_axis_ptp_ts_valid_reg = 1'b0, m_axis_ptp_ts_valid_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; + +reg [DATA_WIDTH-1:0] xgmii_txd_reg = {CTRL_WIDTH{XGMII_IDLE}}, xgmii_txd_next; +reg [CTRL_WIDTH-1:0] xgmii_txc_reg = {CTRL_WIDTH{1'b1}}, xgmii_txc_next; + +reg start_packet_reg = 1'b0, start_packet_next; +reg error_underflow_reg = 1'b0, error_underflow_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign xgmii_txd = xgmii_txd_reg; +assign xgmii_txc = xgmii_txc_reg; + +assign m_axis_ptp_ts = PTP_TS_ENABLE ? m_axis_ptp_ts_reg : 0; +assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : 0; +assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0; + +assign start_packet = start_packet_reg; +assign error_underflow = error_underflow_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_tdata_reg[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(s_tdata_reg[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(s_tdata_reg[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(s_tdata_reg[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +function [2:0] keep2count; + input [3:0] k; + casez (k) + 4'bzzz0: keep2count = 3'd0; + 4'bzz01: keep2count = 3'd1; + 4'bz011: keep2count = 3'd2; + 4'b0111: keep2count = 3'd3; + 4'b1111: keep2count = 3'd4; + endcase +endfunction + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 4; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = s_axis_tkeep[j] ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (s_tkeep_reg) + 4'bzz01: begin + fcs_output_txd_0 = {~crc_next0[23:0], s_tdata_reg[7:0]}; + fcs_output_txd_1 = {{2{XGMII_IDLE}}, XGMII_TERM, ~crc_next0[31:24]}; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b1110; + ifg_offset = 8'd3; + extra_cycle = 1'b0; + end + 4'bz011: begin + fcs_output_txd_0 = {~crc_next1[15:0], s_tdata_reg[15:0]}; + fcs_output_txd_1 = {XGMII_IDLE, XGMII_TERM, ~crc_next1[31:16]}; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b1100; + ifg_offset = 8'd2; + extra_cycle = 1'b0; + end + 4'b0111: begin + fcs_output_txd_0 = {~crc_next2[7:0], s_tdata_reg[23:0]}; + fcs_output_txd_1 = {XGMII_TERM, ~crc_next2[31:8]}; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b1000; + ifg_offset = 8'd1; + extra_cycle = 1'b0; + end + 4'b1111: begin + fcs_output_txd_0 = s_tdata_reg; + fcs_output_txd_1 = ~crc_next3; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b0000; + ifg_offset = 8'd4; + extra_cycle = 1'b1; + end + default: begin + fcs_output_txd_0 = {CTRL_WIDTH{XGMII_ERROR}}; + fcs_output_txd_1 = {CTRL_WIDTH{XGMII_ERROR}}; + fcs_output_txc_0 = {CTRL_WIDTH{1'b1}}; + fcs_output_txc_1 = {CTRL_WIDTH{1'b1}}; + ifg_offset = 8'd0; + extra_cycle = 1'b0; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + ifg_count_next = ifg_count_reg; + deficit_idle_count_next = deficit_idle_count_reg; + + s_axis_tready_next = 1'b0; + + s_tdata_next = s_tdata_reg; + s_tkeep_next = s_tkeep_reg; + + m_axis_ptp_ts_next = m_axis_ptp_ts_reg; + m_axis_ptp_ts_tag_next = m_axis_ptp_ts_tag_reg; + m_axis_ptp_ts_valid_next = 1'b0; + + // XGMII idle + xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_next = {CTRL_WIDTH{1'b1}}; + + start_packet_next = 1'b0; + error_underflow_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 16'd4; + reset_crc = 1'b1; + + // XGMII idle + xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_next = {CTRL_WIDTH{1'b1}}; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + // XGMII start and preamble + xgmii_txd_next = {{3{ETH_PRE}}, XGMII_START}; + xgmii_txc_next = 4'b0001; + s_axis_tready_next = 1'b1; + state_next = STATE_PREAMBLE; + end else begin + ifg_count_next = 8'd0; + deficit_idle_count_next = 2'd0; + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + // send preamble + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + xgmii_txd_next = {ETH_SFD, {3{ETH_PRE}}}; + xgmii_txc_next = 4'b0000; + s_axis_tready_next = 1'b1; + m_axis_ptp_ts_next = ptp_ts; + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_next = 1'b1; + start_packet_next = 1'b1; + state_next = STATE_PAYLOAD; + end + STATE_PAYLOAD: begin + // transfer payload + update_crc = 1'b1; + s_axis_tready_next = 1'b1; + + frame_ptr_next = frame_ptr_reg + 16'd4; + + xgmii_txd_next = s_tdata_reg; + xgmii_txc_next = 4'b0000; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + frame_ptr_next = frame_ptr_reg + keep2count(s_axis_tkeep); + s_axis_tready_next = 1'b0; + if (s_axis_tuser[0]) begin + xgmii_txd_next = {XGMII_TERM, {3{XGMII_ERROR}}}; + xgmii_txc_next = 4'b1111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd10; + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b0; + + if (ENABLE_PADDING && (frame_ptr_reg < MIN_FL_NOCRC_MS || (frame_ptr_reg == MIN_FL_NOCRC_MS && keep2count(s_axis_tkeep) < MIN_FL_NOCRC_LS))) begin + s_tkeep_next = 4'hf; + frame_ptr_next = frame_ptr_reg + 16'd4; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-4)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 4'hf >> ((4-MIN_FL_NOCRC_LS) % 4); + + state_next = STATE_FCS_1; + end + end else begin + state_next = STATE_FCS_1; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + xgmii_txd_next = {XGMII_TERM, {3{XGMII_ERROR}}}; + xgmii_txc_next = 4'b1111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd10; + error_underflow_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + STATE_PAD: begin + // pad frame to MIN_FRAME_LENGTH + s_axis_tready_next = 1'b0; + + xgmii_txd_next = s_tdata_reg; + xgmii_txc_next = {CTRL_WIDTH{1'b0}}; + + s_tdata_next = 32'd0; + s_tkeep_next = 4'hf; + + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd4; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-4)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 4'hf >> ((4-MIN_FL_NOCRC_LS) % 4); + + state_next = STATE_FCS_1; + end + end + STATE_FCS_1: begin + // last cycle + s_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_0; + xgmii_txc_next = fcs_output_txc_0; + + frame_ptr_next = 16'd0; + + ifg_count_next = (ifg_delay > 8'd12 ? ifg_delay : 8'd12) - ifg_offset + deficit_idle_count_reg; + state_next = STATE_FCS_2; + end + STATE_FCS_2: begin + // last cycle + s_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_1; + xgmii_txc_next = fcs_output_txc_1; + + frame_ptr_next = 16'd0; + + if (extra_cycle) begin + state_next = STATE_FCS_3; + end else begin + state_next = STATE_IFG; + end + end + STATE_FCS_3: begin + // last cycle + s_axis_tready_next = 1'b0; + + xgmii_txd_next = {{3{XGMII_IDLE}}, XGMII_TERM}; + xgmii_txc_next = 4'b1111; + + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd3) begin + state_next = STATE_IFG; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd0) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + end + STATE_IFG: begin + // send IFG + if (ifg_count_reg > 8'd4) begin + ifg_count_next = ifg_count_reg - 8'd4; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd3) begin + state_next = STATE_IFG; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd0) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + end + STATE_WAIT_END: begin + // wait for end of frame + s_axis_tready_next = 1'b1; + + if (ifg_count_reg > 8'd4) begin + ifg_count_next = ifg_count_reg - 8'd4; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = 1'b0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd3) begin + state_next = STATE_IFG; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd0) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + ifg_count_reg <= 8'd0; + deficit_idle_count_reg <= 2'd0; + + s_axis_tready_reg <= 1'b0; + + m_axis_ptp_ts_valid_reg <= 1'b0; + + xgmii_txd_reg <= {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_reg <= {CTRL_WIDTH{1'b1}}; + + start_packet_reg <= 1'b0; + error_underflow_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + ifg_count_reg <= ifg_count_next; + deficit_idle_count_reg <= deficit_idle_count_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next; + + xgmii_txd_reg <= xgmii_txd_next; + xgmii_txc_reg <= xgmii_txc_next; + + start_packet_reg <= start_packet_next; + error_underflow_reg <= error_underflow_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next3; + end + end + + s_tdata_reg <= s_tdata_next; + s_tkeep_reg <= s_tkeep_next; + + m_axis_ptp_ts_reg <= m_axis_ptp_ts_next; + m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next; +end + +endmodule diff --git a/fpga/lib/eth/rtl/axis_xgmii_tx_64.v b/fpga/lib/eth/rtl/axis_xgmii_tx_64.v new file mode 100644 index 000000000..4e527c64a --- /dev/null +++ b/fpga/lib/eth/rtl/axis_xgmii_tx_64.v @@ -0,0 +1,783 @@ +/* + +Copyright (c) 2015-2017 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 1ns / 1ps + +/* + * AXI4-Stream XGMII frame transmitter (AXI in, XGMII out) + */ +module axis_xgmii_tx_64 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * XGMII output + */ + output wire [DATA_WIDTH-1:0] xgmii_txd, + output wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire [1:0] start_packet, + output wire error_underflow +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +localparam MIN_FL_NOCRC = MIN_FRAME_LENGTH-4; +localparam MIN_FL_NOCRC_MS = MIN_FL_NOCRC & 16'hfff8; +localparam MIN_FL_NOCRC_LS = MIN_FL_NOCRC & 16'h0007; + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PAYLOAD = 3'd1, + STATE_PAD = 3'd2, + STATE_FCS_1 = 3'd3, + STATE_FCS_2 = 3'd4, + STATE_IFG = 3'd5, + STATE_WAIT_END = 3'd6; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg swap_lanes; +reg unswap_lanes; + +reg lanes_swapped = 1'b0; +reg [31:0] swap_txd = 32'd0; +reg [3:0] swap_txc = 4'd0; + +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +reg [DATA_WIDTH-1:0] s_tdata_reg = {DATA_WIDTH{1'b0}}, s_tdata_next; +reg [KEEP_WIDTH-1:0] s_tkeep_reg = {KEEP_WIDTH{1'b0}}, s_tkeep_next; + +reg [DATA_WIDTH-1:0] fcs_output_txd_0; +reg [DATA_WIDTH-1:0] fcs_output_txd_1; +reg [CTRL_WIDTH-1:0] fcs_output_txc_0; +reg [CTRL_WIDTH-1:0] fcs_output_txc_1; + +reg [7:0] ifg_offset; + +reg extra_cycle; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] ifg_count_reg = 8'd0, ifg_count_next; +reg [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg = 0, m_axis_ptp_ts_next; +reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg = 0, m_axis_ptp_ts_tag_next; +reg m_axis_ptp_ts_valid_reg = 1'b0, m_axis_ptp_ts_valid_next; +reg m_axis_ptp_ts_valid_int_reg = 1'b0, m_axis_ptp_ts_valid_int_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next4; +wire [31:0] crc_next5; +wire [31:0] crc_next6; +wire [31:0] crc_next7; + +reg [DATA_WIDTH-1:0] xgmii_txd_reg = {CTRL_WIDTH{XGMII_IDLE}}, xgmii_txd_next; +reg [CTRL_WIDTH-1:0] xgmii_txc_reg = {CTRL_WIDTH{1'b1}}, xgmii_txc_next; + +reg start_packet_reg = 2'b00, start_packet_next; +reg error_underflow_reg = 1'b0, error_underflow_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign xgmii_txd = xgmii_txd_reg; +assign xgmii_txc = xgmii_txc_reg; + +assign m_axis_ptp_ts = PTP_TS_ENABLE ? m_axis_ptp_ts_reg : 0; +assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : 0; +assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0; + +assign start_packet = start_packet_reg; +assign error_underflow = error_underflow_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_tdata_reg[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(s_tdata_reg[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(s_tdata_reg[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(s_tdata_reg[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(40), + .STYLE("AUTO") +) +eth_crc_40 ( + .data_in(s_tdata_reg[39:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next4) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(48), + .STYLE("AUTO") +) +eth_crc_48 ( + .data_in(s_tdata_reg[47:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next5) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(56), + .STYLE("AUTO") +) +eth_crc_56 ( + .data_in(s_tdata_reg[55:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next6) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(s_tdata_reg[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 8; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = s_axis_tkeep[j] ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (s_tkeep_reg) + 8'bzzzzzz01: begin + fcs_output_txd_0 = {{2{XGMII_IDLE}}, XGMII_TERM, ~crc_next0[31:0], s_tdata_reg[7:0]}; + fcs_output_txd_1 = {8{XGMII_IDLE}}; + fcs_output_txc_0 = 8'b11100000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd3; + extra_cycle = 1'b0; + end + 8'bzzzzz011: begin + fcs_output_txd_0 = {XGMII_IDLE, XGMII_TERM, ~crc_next1[31:0], s_tdata_reg[15:0]}; + fcs_output_txd_1 = {8{XGMII_IDLE}}; + fcs_output_txc_0 = 8'b11000000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd2; + extra_cycle = 1'b0; + end + 8'bzzzz0111: begin + fcs_output_txd_0 = {XGMII_TERM, ~crc_next2[31:0], s_tdata_reg[23:0]}; + fcs_output_txd_1 = {8{XGMII_IDLE}}; + fcs_output_txc_0 = 8'b10000000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd1; + extra_cycle = 1'b0; + end + 8'bzzz01111: begin + fcs_output_txd_0 = {~crc_next3[31:0], s_tdata_reg[31:0]}; + fcs_output_txd_1 = {{7{XGMII_IDLE}}, XGMII_TERM}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd8; + extra_cycle = 1'b1; + end + 8'bzz011111: begin + fcs_output_txd_0 = {~crc_next4[23:0], s_tdata_reg[39:0]}; + fcs_output_txd_1 = {{6{XGMII_IDLE}}, XGMII_TERM, ~crc_next4[31:24]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111110; + ifg_offset = 8'd7; + extra_cycle = 1'b1; + end + 8'bz0111111: begin + fcs_output_txd_0 = {~crc_next5[15:0], s_tdata_reg[47:0]}; + fcs_output_txd_1 = {{5{XGMII_IDLE}}, XGMII_TERM, ~crc_next5[31:16]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111100; + ifg_offset = 8'd6; + extra_cycle = 1'b1; + end + 8'b01111111: begin + fcs_output_txd_0 = {~crc_next6[7:0], s_tdata_reg[55:0]}; + fcs_output_txd_1 = {{4{XGMII_IDLE}}, XGMII_TERM, ~crc_next6[31:8]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111000; + ifg_offset = 8'd5; + extra_cycle = 1'b1; + end + 8'b11111111: begin + fcs_output_txd_0 = s_tdata_reg; + fcs_output_txd_1 = {{3{XGMII_IDLE}}, XGMII_TERM, ~crc_next7[31:0]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11110000; + ifg_offset = 8'd4; + extra_cycle = 1'b1; + end + default: begin + fcs_output_txd_0 = {CTRL_WIDTH{XGMII_ERROR}}; + fcs_output_txd_1 = {CTRL_WIDTH{XGMII_ERROR}}; + fcs_output_txc_0 = {CTRL_WIDTH{1'b1}}; + fcs_output_txc_1 = {CTRL_WIDTH{1'b1}}; + ifg_offset = 8'd0; + extra_cycle = 1'b1; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + swap_lanes = 1'b0; + unswap_lanes = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + ifg_count_next = ifg_count_reg; + deficit_idle_count_next = deficit_idle_count_reg; + + s_axis_tready_next = 1'b0; + + s_tdata_next = s_tdata_reg; + s_tkeep_next = s_tkeep_reg; + + m_axis_ptp_ts_next = m_axis_ptp_ts_reg; + m_axis_ptp_ts_tag_next = m_axis_ptp_ts_tag_reg; + m_axis_ptp_ts_valid_next = 1'b0; + m_axis_ptp_ts_valid_int_next = 1'b0; + + // XGMII idle + xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_next = {CTRL_WIDTH{1'b1}}; + + start_packet_next = 2'b00; + error_underflow_next = 1'b0; + + if (m_axis_ptp_ts_valid_int_reg) begin + m_axis_ptp_ts_valid_next = 1'b1; + if (PTP_TS_WIDTH == 96 && $signed({1'b0, m_axis_ptp_ts_reg[45:16]}) - $signed(31'd1000000000) > 0) begin + // ns field rollover + m_axis_ptp_ts_next[45:16] = $signed({1'b0, m_axis_ptp_ts_reg[45:16]}) - $signed(31'd1000000000); + m_axis_ptp_ts_next[95:48] = m_axis_ptp_ts_reg[95:48] + 1; + end + end + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 16'd8; + reset_crc = 1'b1; + s_axis_tready_next = 1'b1; + + // XGMII idle + xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_next = {CTRL_WIDTH{1'b1}}; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + // XGMII start and preamble + if (ifg_count_reg > 8'd0) begin + // need to send more idles - swap lanes + swap_lanes = 1'b1; + if (PTP_TS_WIDTH == 96) begin + m_axis_ptp_ts_next[45:0] = ptp_ts[45:0] + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + m_axis_ptp_ts_next[95:48] = ptp_ts[95:48]; + end else begin + m_axis_ptp_ts_next = ptp_ts + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + end + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_int_next = 1'b1; + start_packet_next = 2'b10; + end else begin + // no more idles - unswap + unswap_lanes = 1'b1; + if (PTP_TS_WIDTH == 96) begin + m_axis_ptp_ts_next[45:0] = ptp_ts[45:0] + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + m_axis_ptp_ts_next[95:48] = ptp_ts[95:48]; + end else begin + m_axis_ptp_ts_next = ptp_ts + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + end + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_int_next = 1'b1; + start_packet_next = 2'b01; + end + xgmii_txd_next = {ETH_SFD, {6{ETH_PRE}}, XGMII_START}; + xgmii_txc_next = 8'b00000001; + s_axis_tready_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + ifg_count_next = 8'd0; + deficit_idle_count_next = 2'd0; + unswap_lanes = 1'b1; + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + update_crc = 1'b1; + s_axis_tready_next = 1'b1; + + frame_ptr_next = frame_ptr_reg + 16'd8; + + xgmii_txd_next = s_tdata_reg; + xgmii_txc_next = 8'b00000000; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + frame_ptr_next = frame_ptr_reg + keep2count(s_axis_tkeep); + s_axis_tready_next = 1'b0; + if (s_axis_tuser[0]) begin + xgmii_txd_next = {{3{XGMII_IDLE}}, XGMII_TERM, {4{XGMII_ERROR}}}; + xgmii_txc_next = 8'b11111111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b0; + + if (ENABLE_PADDING && (frame_ptr_reg < MIN_FL_NOCRC_MS || (frame_ptr_reg == MIN_FL_NOCRC_MS && keep2count(s_axis_tkeep) < MIN_FL_NOCRC_LS))) begin + s_tkeep_next = 8'hff; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-8)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end else begin + state_next = STATE_FCS_1; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + xgmii_txd_next = {{3{XGMII_IDLE}}, XGMII_TERM, {4{XGMII_ERROR}}}; + xgmii_txc_next = 8'b11111111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + error_underflow_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + STATE_PAD: begin + // pad frame to MIN_FRAME_LENGTH + s_axis_tready_next = 1'b0; + + xgmii_txd_next = s_tdata_reg; + xgmii_txc_next = {CTRL_WIDTH{1'b0}}; + + s_tdata_next = 64'd0; + s_tkeep_next = 8'hff; + + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-8)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end + STATE_FCS_1: begin + // last cycle + s_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_0; + xgmii_txc_next = fcs_output_txc_0; + + frame_ptr_next = 16'd0; + + ifg_count_next = (ifg_delay > 8'd12 ? ifg_delay : 8'd12) - ifg_offset + (lanes_swapped ? 8'd4 : 8'd0) + deficit_idle_count_reg; + if (extra_cycle) begin + state_next = STATE_FCS_2; + end else begin + state_next = STATE_IFG; + end + end + STATE_FCS_2: begin + // last cycle + s_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_1; + xgmii_txc_next = fcs_output_txc_1; + + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end + STATE_IFG: begin + // send IFG + if (ifg_count_reg > 8'd8) begin + ifg_count_next = ifg_count_reg - 8'd8; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end + STATE_WAIT_END: begin + // wait for end of frame + s_axis_tready_next = 1'b1; + + if (ifg_count_reg > 8'd8) begin + ifg_count_next = ifg_count_reg - 8'd8; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = 1'b0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + ifg_count_reg <= 8'd0; + deficit_idle_count_reg <= 2'd0; + + s_axis_tready_reg <= 1'b0; + + m_axis_ptp_ts_valid_reg <= 1'b0; + m_axis_ptp_ts_valid_int_reg <= 1'b0; + + xgmii_txd_reg <= {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_reg <= {CTRL_WIDTH{1'b1}}; + + start_packet_reg <= 2'b00; + error_underflow_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + lanes_swapped <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + ifg_count_reg <= ifg_count_next; + deficit_idle_count_reg <= deficit_idle_count_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next; + m_axis_ptp_ts_valid_int_reg <= m_axis_ptp_ts_valid_int_next; + + start_packet_reg <= start_packet_next; + error_underflow_reg <= error_underflow_next; + + if (swap_lanes || (lanes_swapped && !unswap_lanes)) begin + lanes_swapped <= 1'b1; + xgmii_txd_reg <= {xgmii_txd_next[31:0], swap_txd}; + xgmii_txc_reg <= {xgmii_txc_next[3:0], swap_txc}; + end else begin + lanes_swapped <= 1'b0; + xgmii_txd_reg <= xgmii_txd_next; + xgmii_txc_reg <= xgmii_txc_next; + end + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next7; + end + end + + s_tdata_reg <= s_tdata_next; + s_tkeep_reg <= s_tkeep_next; + + m_axis_ptp_ts_reg <= m_axis_ptp_ts_next; + m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next; + + swap_txd <= xgmii_txd_next[63:32]; + swap_txc <= xgmii_txc_next[7:4]; +end + +endmodule diff --git a/fpga/lib/eth/rtl/eth_arb_mux.v b/fpga/lib/eth/rtl/eth_arb_mux.v new file mode 100644 index 000000000..a4d6d7cf9 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_arb_mux.v @@ -0,0 +1,316 @@ +/* + +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 1ns / 1ps + +/* + * Ethernet arbitrated multiplexer + */ +module eth_arb_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame inputs + */ + input wire [S_COUNT-1:0] s_eth_hdr_valid, + output wire [S_COUNT-1:0] s_eth_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*DATA_WIDTH-1:0] s_eth_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_eth_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_eth_payload_axis_tready, + input wire [S_COUNT-1:0] s_eth_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_eth_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_eth_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_eth_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_eth_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg frame_reg = 1'b0, frame_next; + +reg s_eth_hdr_ready_mask_reg = 1'b0, s_eth_hdr_ready_mask_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; + +wire [S_COUNT-1:0] request; +wire [S_COUNT-1:0] acknowledge; +wire [S_COUNT-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT-1:0] grant_encoded; + +// internal datapath +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_eth_hdr_ready = (!s_eth_hdr_ready_mask_reg && grant_valid) << grant_encoded; + +assign s_eth_payload_axis_tready = (m_eth_payload_axis_tready_int_reg && grant_valid) << grant_encoded; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_eth_payload_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_eth_payload_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_eth_payload_axis_tvalid[grant_encoded]; +wire current_s_tready = s_eth_payload_axis_tready[grant_encoded]; +wire current_s_tlast = s_eth_payload_axis_tlast[grant_encoded]; +wire [ID_WIDTH-1:0] current_s_tid = s_eth_payload_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_eth_payload_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_eth_payload_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + +// arbiter instance +arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +generate + genvar n; + + for (n = 0; n < S_COUNT; n = n + 1) begin + assign request[n] = s_eth_hdr_valid[n] && !grant[n]; + assign acknowledge[n] = grant[n] && s_eth_payload_axis_tvalid[n] && s_eth_payload_axis_tready[n] && s_eth_payload_axis_tlast[n]; + end +endgenerate + +always @* begin + frame_next = frame_reg; + + s_eth_hdr_ready_mask_next = s_eth_hdr_ready_mask_reg; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + + if (s_eth_payload_axis_tvalid[grant_encoded] && s_eth_payload_axis_tready[grant_encoded]) begin + // end of frame detection + if (s_eth_payload_axis_tlast[grant_encoded]) begin + frame_next = 1'b0; + s_eth_hdr_ready_mask_next = 1'b0; + end + end + + if (!frame_reg && grant_valid) begin + // start of frame + frame_next = 1'b1; + + s_eth_hdr_ready_mask_next = 1'b1; + + m_eth_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[grant_encoded*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[grant_encoded*48 +: 48]; + m_eth_type_next = s_eth_type[grant_encoded*16 +: 16]; + end + + // pass through selected packet data + m_eth_payload_axis_tdata_int = current_s_tdata; + m_eth_payload_axis_tkeep_int = current_s_tkeep; + m_eth_payload_axis_tvalid_int = current_s_tvalid && m_eth_payload_axis_tready_int_reg && grant_valid; + m_eth_payload_axis_tlast_int = current_s_tlast; + m_eth_payload_axis_tid_int = current_s_tid; + m_eth_payload_axis_tdest_int = current_s_tdest; + m_eth_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + frame_reg <= 1'b0; + s_eth_hdr_ready_mask_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + end else begin + frame_reg <= frame_next; + s_eth_hdr_ready_mask_reg <= s_eth_hdr_ready_mask_next; + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tkeep = KEEP_ENABLE ? m_eth_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tid = ID_ENABLE ? m_eth_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_eth_payload_axis_tdest = DEST_ENABLE ? m_eth_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_eth_payload_axis_tuser = USER_ENABLE ? m_eth_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tid_reg <= temp_m_eth_payload_axis_tid_reg; + m_eth_payload_axis_tdest_reg <= temp_m_eth_payload_axis_tdest_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + temp_m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/eth_axis_rx.v b/fpga/lib/eth/rtl/eth_axis_rx.v new file mode 100644 index 000000000..f2e399aaf --- /dev/null +++ b/fpga/lib/eth/rtl/eth_axis_rx.v @@ -0,0 +1,370 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream ethernet frame receiver (AXI in, Ethernet frame out) + */ +module eth_axis_rx +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination +); + +/* + +Ethernet frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype 2 octets + +This module receives an Ethernet frame on an AXI stream interface, decodes +and strips the headers, then produces the header fields in parallel along +with the payload in a separate AXI stream. + +*/ + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_READ_HEADER = 2'd1, + STATE_READ_PAYLOAD = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_eth_dest_mac_0; +reg store_eth_dest_mac_1; +reg store_eth_dest_mac_2; +reg store_eth_dest_mac_3; +reg store_eth_dest_mac_4; +reg store_eth_dest_mac_5; +reg store_eth_src_mac_0; +reg store_eth_src_mac_1; +reg store_eth_src_mac_2; +reg store_eth_src_mac_3; +reg store_eth_src_mac_4; +reg store_eth_src_mac_5; +reg store_eth_type_0; +reg store_eth_type_1; + +reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; + +// internal datapath +reg [7:0] m_eth_payload_axis_tdata_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; + +always @* begin + state_next = STATE_IDLE; + + s_axis_tready_next = 1'b0; + + store_eth_dest_mac_0 = 1'b0; + store_eth_dest_mac_1 = 1'b0; + store_eth_dest_mac_2 = 1'b0; + store_eth_dest_mac_3 = 1'b0; + store_eth_dest_mac_4 = 1'b0; + store_eth_dest_mac_5 = 1'b0; + store_eth_src_mac_0 = 1'b0; + store_eth_src_mac_1 = 1'b0; + store_eth_src_mac_2 = 1'b0; + store_eth_src_mac_3 = 1'b0; + store_eth_src_mac_4 = 1'b0; + store_eth_src_mac_5 = 1'b0; + store_eth_type_0 = 1'b0; + store_eth_type_1 = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + + error_header_early_termination_next = 1'b0; + + m_eth_payload_axis_tdata_int = 8'd0; + m_eth_payload_axis_tvalid_int = 1'b0; + m_eth_payload_axis_tlast_int = 1'b0; + m_eth_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 8'd0; + s_axis_tready_next = !m_eth_hdr_valid_reg; + + if (s_axis_tready && s_axis_tvalid) begin + // got first word of packet + if (s_axis_tlast) begin + // tlast asserted on first word + error_header_early_termination_next = 1'b1; + state_next = STATE_IDLE; + end else begin + // move to read header state + frame_ptr_next = 1'b1; + store_eth_dest_mac_5 = 1'b1; + state_next = STATE_READ_HEADER; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store it + frame_ptr_next = frame_ptr_reg + 8'd1; + state_next = STATE_READ_HEADER; + case (frame_ptr_reg) + 8'h00: store_eth_dest_mac_5 = 1'b1; + 8'h01: store_eth_dest_mac_4 = 1'b1; + 8'h02: store_eth_dest_mac_3 = 1'b1; + 8'h03: store_eth_dest_mac_2 = 1'b1; + 8'h04: store_eth_dest_mac_1 = 1'b1; + 8'h05: store_eth_dest_mac_0 = 1'b1; + 8'h06: store_eth_src_mac_5 = 1'b1; + 8'h07: store_eth_src_mac_4 = 1'b1; + 8'h08: store_eth_src_mac_3 = 1'b1; + 8'h09: store_eth_src_mac_2 = 1'b1; + 8'h0A: store_eth_src_mac_1 = 1'b1; + 8'h0B: store_eth_src_mac_0 = 1'b1; + 8'h0C: store_eth_type_1 = 1'b1; + 8'h0D: begin + store_eth_type_0 = 1'b1; + m_eth_hdr_valid_next = 1'b1; + s_axis_tready_next = m_eth_payload_axis_tready_int_early; + state_next = STATE_READ_PAYLOAD; + end + endcase + if (s_axis_tlast) begin + error_header_early_termination_next = 1'b1; + s_axis_tready_next = !m_eth_hdr_valid_reg; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_READ_PAYLOAD: begin + // read payload + s_axis_tready_next = m_eth_payload_axis_tready_int_early; + + m_eth_payload_axis_tdata_int = s_axis_tdata; + m_eth_payload_axis_tvalid_int = s_axis_tvalid; + m_eth_payload_axis_tlast_int = s_axis_tlast; + m_eth_payload_axis_tuser_int = s_axis_tuser; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer through + if (s_axis_tlast) begin + s_axis_tready_next = !m_eth_hdr_valid_reg; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD; + end + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 8'd0; + s_axis_tready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + + busy_reg <= state_next != STATE_IDLE; + end + + // datapath + if (store_eth_dest_mac_0) m_eth_dest_mac_reg[ 7: 0] <= s_axis_tdata; + if (store_eth_dest_mac_1) m_eth_dest_mac_reg[15: 8] <= s_axis_tdata; + if (store_eth_dest_mac_2) m_eth_dest_mac_reg[23:16] <= s_axis_tdata; + if (store_eth_dest_mac_3) m_eth_dest_mac_reg[31:24] <= s_axis_tdata; + if (store_eth_dest_mac_4) m_eth_dest_mac_reg[39:32] <= s_axis_tdata; + if (store_eth_dest_mac_5) m_eth_dest_mac_reg[47:40] <= s_axis_tdata; + if (store_eth_src_mac_0) m_eth_src_mac_reg[ 7: 0] <= s_axis_tdata; + if (store_eth_src_mac_1) m_eth_src_mac_reg[15: 8] <= s_axis_tdata; + if (store_eth_src_mac_2) m_eth_src_mac_reg[23:16] <= s_axis_tdata; + if (store_eth_src_mac_3) m_eth_src_mac_reg[31:24] <= s_axis_tdata; + if (store_eth_src_mac_4) m_eth_src_mac_reg[39:32] <= s_axis_tdata; + if (store_eth_src_mac_5) m_eth_src_mac_reg[47:40] <= s_axis_tdata; + if (store_eth_type_0) m_eth_type_reg[ 7: 0] <= s_axis_tdata; + if (store_eth_type_1) m_eth_type_reg[15: 8] <= s_axis_tdata; +end + +// output datapath logic +reg [7:0] m_eth_payload_axis_tdata_reg = 8'd0; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg m_eth_payload_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_eth_payload_axis_tdata_reg = 8'd0; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg temp_m_eth_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_eth_payload_int_to_output; +reg store_eth_payload_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_eth_payload_int_to_output = 1'b0; + store_eth_payload_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_eth_payload_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_eth_payload_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/eth_axis_rx_64.v b/fpga/lib/eth/rtl/eth_axis_rx_64.v new file mode 100644 index 000000000..2f815cccf --- /dev/null +++ b/fpga/lib/eth/rtl/eth_axis_rx_64.v @@ -0,0 +1,417 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream ethernet frame receiver (AXI in, Ethernet frame out, 64 bit datapath) + */ +module eth_axis_rx_64 +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [63:0] s_axis_tdata, + input wire [7:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination +); + +/* + +Ethernet frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype 2 octets + +This module receives an Ethernet frame on an AXI stream interface, decodes +and strips the headers, then produces the header fields in parallel along +with the payload in a separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_READ_PAYLOAD = 3'd2; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_hdr_word_0; +reg store_hdr_word_1; + +reg flush_save; +reg transfer_in_save; + +reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; + +reg [63:0] save_axis_tdata_reg = 64'd0; +reg [7:0] save_axis_tkeep_reg = 8'd0; +reg save_axis_tlast_reg = 1'b0; +reg save_axis_tuser_reg = 1'b0; + +reg [63:0] shift_axis_tdata; +reg [7:0] shift_axis_tkeep; +reg shift_axis_tvalid; +reg shift_axis_tlast; +reg shift_axis_tuser; +reg shift_axis_s_tready; +reg shift_axis_extra_cycle_reg = 1'b0; + +// internal datapath +reg [63:0] m_eth_payload_axis_tdata_int; +reg [7:0] m_eth_payload_axis_tkeep_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; + +always @* begin + shift_axis_tdata[15:0] = save_axis_tdata_reg[63:48]; + shift_axis_tkeep[1:0] = save_axis_tkeep_reg[7:6]; + + if (shift_axis_extra_cycle_reg) begin + shift_axis_tdata[63:16] = 48'd0; + shift_axis_tkeep[7:2] = 6'd0; + shift_axis_tvalid = 1'b1; + shift_axis_tlast = save_axis_tlast_reg; + shift_axis_tuser = save_axis_tuser_reg; + shift_axis_s_tready = flush_save; + end else begin + shift_axis_tdata[63:16] = s_axis_tdata[47:0]; + shift_axis_tkeep[7:2] = s_axis_tkeep[5:0]; + shift_axis_tvalid = s_axis_tvalid; + shift_axis_tlast = (s_axis_tlast && (s_axis_tkeep[7:6] == 0)); + shift_axis_tuser = (s_axis_tuser && (s_axis_tkeep[7:6] == 0)); + shift_axis_s_tready = !(s_axis_tlast && s_axis_tvalid && transfer_in_save); + end +end + +always @* begin + state_next = STATE_IDLE; + + s_axis_tready_next = 1'b0; + + flush_save = 1'b0; + transfer_in_save = 1'b0; + + store_hdr_word_0 = 1'b0; + store_hdr_word_1 = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + + error_header_early_termination_next = 1'b0; + + m_eth_payload_axis_tdata_int = 64'd0; + m_eth_payload_axis_tkeep_int = 8'd0; + m_eth_payload_axis_tvalid_int = 1'b0; + m_eth_payload_axis_tlast_int = 1'b0; + m_eth_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 8'd0; + flush_save = 1'b1; + s_axis_tready_next = !m_eth_hdr_valid_reg; + + if (s_axis_tready && s_axis_tvalid) begin + // got first word of packet + if (s_axis_tlast) begin + // tlast asserted on first word + error_header_early_termination_next = 1'b1; + state_next = STATE_IDLE; + end else begin + // move to read header state + frame_ptr_next = 8'd8; + store_hdr_word_0 = 1'b1; + transfer_in_save = 1'b1; + state_next = STATE_READ_HEADER; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header state + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store it + frame_ptr_next = frame_ptr_reg + 8'd8; + transfer_in_save = 1'b1; + state_next = STATE_READ_HEADER; + case (frame_ptr_reg) + 8'h00: store_hdr_word_0 = 1'b1; + 8'h08: begin + store_hdr_word_1 = 1'b1; + m_eth_hdr_valid_next = 1'b1; + s_axis_tready_next = m_eth_payload_axis_tready_int_early; + state_next = STATE_READ_PAYLOAD; + end + endcase + if (s_axis_tlast) begin + if (s_axis_tkeep[7:6] != 2'd0) begin + s_axis_tready_next = 1'b0; + state_next = STATE_READ_PAYLOAD; + end else begin + flush_save = 1'b1; + m_eth_hdr_valid_next = 1'b0; + error_header_early_termination_next = 1'b1; + s_axis_tready_next = !m_eth_hdr_valid_reg; + state_next = STATE_IDLE; + end + end + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_READ_PAYLOAD: begin + // read payload + s_axis_tready_next = m_eth_payload_axis_tready_int_early && shift_axis_s_tready; + + m_eth_payload_axis_tdata_int = shift_axis_tdata; + m_eth_payload_axis_tkeep_int = shift_axis_tkeep; + m_eth_payload_axis_tvalid_int = shift_axis_tvalid; + m_eth_payload_axis_tlast_int = shift_axis_tlast; + m_eth_payload_axis_tuser_int = shift_axis_tuser; + + if (m_eth_payload_axis_tready_int_reg && shift_axis_tvalid) begin + // word transfer through + transfer_in_save = 1'b1; + if (shift_axis_tlast) begin + flush_save = 1'b1; + s_axis_tready_next = !m_eth_hdr_valid_reg; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD; + end + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 8'd0; + s_axis_tready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + save_axis_tlast_reg <= 1'b0; + shift_axis_extra_cycle_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + + busy_reg <= state_next != STATE_IDLE; + + if (flush_save) begin + save_axis_tlast_reg <= 1'b0; + shift_axis_extra_cycle_reg <= 1'b0; + end else if (transfer_in_save) begin + save_axis_tlast_reg <= s_axis_tlast; + shift_axis_extra_cycle_reg <= s_axis_tlast && (s_axis_tkeep[7:6] != 0); + end + end + + // datapath + if (store_hdr_word_0) begin + m_eth_dest_mac_reg[47:40] <= s_axis_tdata[ 7: 0]; + m_eth_dest_mac_reg[39:32] <= s_axis_tdata[15: 8]; + m_eth_dest_mac_reg[31:24] <= s_axis_tdata[23:16]; + m_eth_dest_mac_reg[23:16] <= s_axis_tdata[31:24]; + m_eth_dest_mac_reg[15: 8] <= s_axis_tdata[39:32]; + m_eth_dest_mac_reg[ 7: 0] <= s_axis_tdata[47:40]; + m_eth_src_mac_reg[47:40] <= s_axis_tdata[55:48]; + m_eth_src_mac_reg[39:32] <= s_axis_tdata[63:56]; + end + if (store_hdr_word_1) begin + m_eth_src_mac_reg[31:24] <= s_axis_tdata[ 7: 0]; + m_eth_src_mac_reg[23:16] <= s_axis_tdata[15: 8]; + m_eth_src_mac_reg[15: 8] <= s_axis_tdata[23:16]; + m_eth_src_mac_reg[ 7: 0] <= s_axis_tdata[31:24]; + m_eth_type_reg[15:8] <= s_axis_tdata[39:32]; + m_eth_type_reg[ 7:0] <= s_axis_tdata[47:40]; + end + + if (transfer_in_save) begin + save_axis_tdata_reg <= s_axis_tdata; + save_axis_tkeep_reg <= s_axis_tkeep; + save_axis_tuser_reg <= s_axis_tuser; + end +end + +// output datapath logic +reg [63:0] m_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] m_eth_payload_axis_tkeep_reg = 8'd0; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg m_eth_payload_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_eth_payload_axis_tkeep_reg = 8'd0; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg temp_m_eth_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_eth_payload_int_to_output; +reg store_eth_payload_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tkeep = m_eth_payload_axis_tkeep_reg; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_eth_payload_int_to_output = 1'b0; + store_eth_payload_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_eth_payload_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_eth_payload_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/eth_axis_tx.v b/fpga/lib/eth/rtl/eth_axis_tx.v new file mode 100644 index 000000000..55388f278 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_axis_tx.v @@ -0,0 +1,312 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream ethernet frame transmitter (Ethernet frame in, AXI out) + */ +module eth_axis_tx +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status signals + */ + output wire busy +); + +/* + +Ethernet frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype 2 octets + +This module receives an Ethernet frame with header fields in parallel along +with the payload in an AXI stream, combines the header with the payload, and +transmits the complete Ethernet frame on the output AXI stream interface. + +*/ + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_WRITE_HEADER = 2'd1, + STATE_WRITE_PAYLOAD = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_eth_hdr; + +reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next; + +reg [47:0] eth_dest_mac_reg = 48'd0; +reg [47:0] eth_src_mac_reg = 48'd0; +reg [15:0] eth_type_reg = 16'd0; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg busy_reg = 1'b0; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b0; + + store_eth_hdr = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 8'd0; + s_eth_hdr_ready_next = 1'b1; + + if (s_eth_hdr_ready && s_eth_hdr_valid) begin + store_eth_hdr = 1'b1; + s_eth_hdr_ready_next = 1'b0; + if (m_axis_tready_int_reg) begin + m_axis_tvalid_int = 1'b1; + m_axis_tdata_int = s_eth_dest_mac[47:40]; + frame_ptr_next = 1'b1; + end + state_next = STATE_WRITE_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // write header + if (m_axis_tready_int_reg) begin + frame_ptr_next = frame_ptr_reg+1; + m_axis_tvalid_int = 1'b1; + state_next = STATE_WRITE_HEADER; + case (frame_ptr_reg) + 8'h00: m_axis_tdata_int = eth_dest_mac_reg[47:40]; + 8'h01: m_axis_tdata_int = eth_dest_mac_reg[39:32]; + 8'h02: m_axis_tdata_int = eth_dest_mac_reg[31:24]; + 8'h03: m_axis_tdata_int = eth_dest_mac_reg[23:16]; + 8'h04: m_axis_tdata_int = eth_dest_mac_reg[15: 8]; + 8'h05: m_axis_tdata_int = eth_dest_mac_reg[ 7: 0]; + 8'h06: m_axis_tdata_int = eth_src_mac_reg[47:40]; + 8'h07: m_axis_tdata_int = eth_src_mac_reg[39:32]; + 8'h08: m_axis_tdata_int = eth_src_mac_reg[31:24]; + 8'h09: m_axis_tdata_int = eth_src_mac_reg[23:16]; + 8'h0A: m_axis_tdata_int = eth_src_mac_reg[15: 8]; + 8'h0B: m_axis_tdata_int = eth_src_mac_reg[ 7: 0]; + 8'h0C: m_axis_tdata_int = eth_type_reg[15: 8]; + 8'h0D: begin + m_axis_tdata_int = eth_type_reg[ 7: 0]; + s_eth_payload_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_WRITE_PAYLOAD; + end + endcase + end else begin + state_next = STATE_WRITE_HEADER; + end + end + STATE_WRITE_PAYLOAD: begin + // write payload + s_eth_payload_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_eth_payload_axis_tdata; + m_axis_tvalid_int = s_eth_payload_axis_tvalid; + m_axis_tlast_int = s_eth_payload_axis_tlast; + m_axis_tuser_int = s_eth_payload_axis_tuser; + + if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin + // word transfer through + if (s_eth_payload_axis_tlast) begin + s_eth_payload_axis_tready_next = 1'b0; + s_eth_hdr_ready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 8'd0; + s_eth_hdr_ready_reg <= 1'b0; + s_eth_payload_axis_tready_reg <= 1'b0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + + busy_reg <= state_next != STATE_IDLE; + end + + // datapath + if (store_eth_hdr) begin + eth_dest_mac_reg <= s_eth_dest_mac; + eth_src_mac_reg <= s_eth_src_mac; + eth_type_reg <= s_eth_type; + end +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/eth_axis_tx_64.v b/fpga/lib/eth/rtl/eth_axis_tx_64.v new file mode 100644 index 000000000..e414d2cd2 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_axis_tx_64.v @@ -0,0 +1,428 @@ +/* + +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 1ns / 1ps + +/* + * AXI4-Stream ethernet frame transmitter (Ethernet frame in, AXI out, 64 bit datapath) + */ +module eth_axis_tx_64 +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * AXI output + */ + output wire [63:0] m_axis_tdata, + output wire [7:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status signals + */ + output wire busy +); + +/* + +Ethernet frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype 2 octets + +This module receives an Ethernet frame with header fields in parallel along +with the payload in an AXI stream, combines the header with the payload, and +transmits the complete Ethernet frame on the output AXI stream interface. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_WRITE_HEADER = 3'd1, + STATE_WRITE_HEADER_LAST = 3'd2, + STATE_WRITE_PAYLOAD = 3'd3; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_eth_hdr; + +reg flush_save; +reg transfer_in_save; + +reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next; + +reg [47:0] eth_dest_mac_reg = 48'd0; +reg [47:0] eth_src_mac_reg = 48'd0; +reg [15:0] eth_type_reg = 16'd0; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg busy_reg = 1'b0; + +reg [63:0] save_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] save_eth_payload_axis_tkeep_reg = 8'd0; +reg save_eth_payload_axis_tlast_reg = 1'b0; +reg save_eth_payload_axis_tuser_reg = 1'b0; + +reg [63:0] shift_eth_payload_axis_tdata; +reg [7:0] shift_eth_payload_axis_tkeep; +reg shift_eth_payload_axis_tvalid; +reg shift_eth_payload_axis_tlast; +reg shift_eth_payload_axis_tuser; +reg shift_eth_payload_s_tready; +reg shift_eth_payload_extra_cycle_reg = 1'b0; + +// internal datapath +reg [63:0] m_axis_tdata_int; +reg [7:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign busy = busy_reg; + +always @* begin + shift_eth_payload_axis_tdata[47:0] = save_eth_payload_axis_tdata_reg[63:16]; + shift_eth_payload_axis_tkeep[5:0] = save_eth_payload_axis_tkeep_reg[7:2]; + + if (shift_eth_payload_extra_cycle_reg) begin + shift_eth_payload_axis_tdata[63:48] = 16'd0; + shift_eth_payload_axis_tkeep[7:6] = 2'd0; + shift_eth_payload_axis_tvalid = 1'b1; + shift_eth_payload_axis_tlast = save_eth_payload_axis_tlast_reg; + shift_eth_payload_axis_tuser = save_eth_payload_axis_tuser_reg; + shift_eth_payload_s_tready = flush_save; + end else begin + shift_eth_payload_axis_tdata[63:48] = s_eth_payload_axis_tdata[15:0]; + shift_eth_payload_axis_tkeep[7:6] = s_eth_payload_axis_tkeep[1:0]; + shift_eth_payload_axis_tvalid = s_eth_payload_axis_tvalid; + shift_eth_payload_axis_tlast = (s_eth_payload_axis_tlast && (s_eth_payload_axis_tkeep[7:2] == 0)); + shift_eth_payload_axis_tuser = (s_eth_payload_axis_tuser && (s_eth_payload_axis_tkeep[7:2] == 0)); + shift_eth_payload_s_tready = !(s_eth_payload_axis_tlast && s_eth_payload_axis_tvalid && transfer_in_save); + end +end + +always @* begin + state_next = STATE_IDLE; + + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b0; + + store_eth_hdr = 1'b0; + + flush_save = 1'b0; + transfer_in_save = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + m_axis_tdata_int = 64'd0; + m_axis_tkeep_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 8'd0; + flush_save = 1'b1; + s_eth_hdr_ready_next = 1'b1; + + if (s_eth_hdr_ready && s_eth_hdr_valid) begin + store_eth_hdr = 1'b1; + s_eth_hdr_ready_next = 1'b0; + state_next = STATE_WRITE_HEADER; + if (m_axis_tready_int_reg) begin + m_axis_tvalid_int = 1'b1; + m_axis_tdata_int[ 7: 0] = s_eth_dest_mac[47:40]; + m_axis_tdata_int[15: 8] = s_eth_dest_mac[39:32]; + m_axis_tdata_int[23:16] = s_eth_dest_mac[31:24]; + m_axis_tdata_int[31:24] = s_eth_dest_mac[23:16]; + m_axis_tdata_int[39:32] = s_eth_dest_mac[15: 8]; + m_axis_tdata_int[47:40] = s_eth_dest_mac[ 7: 0]; + m_axis_tdata_int[55:48] = s_eth_src_mac[47:40]; + m_axis_tdata_int[63:56] = s_eth_src_mac[39:32]; + m_axis_tkeep_int = 8'hff; + frame_ptr_next = 8'd8; + s_eth_payload_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_WRITE_HEADER_LAST; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // write header + if (m_axis_tready_int_reg) begin + frame_ptr_next = frame_ptr_reg + 8'd8; + m_axis_tvalid_int = 1'b1; + state_next = STATE_WRITE_HEADER; + case (frame_ptr_reg) + 5'd00: begin + m_axis_tdata_int[ 7: 0] = eth_dest_mac_reg[47:40]; + m_axis_tdata_int[15: 8] = eth_dest_mac_reg[39:32]; + m_axis_tdata_int[23:16] = eth_dest_mac_reg[31:24]; + m_axis_tdata_int[31:24] = eth_dest_mac_reg[23:16]; + m_axis_tdata_int[39:32] = eth_dest_mac_reg[15: 8]; + m_axis_tdata_int[47:40] = eth_dest_mac_reg[ 7: 0]; + m_axis_tdata_int[55:48] = eth_src_mac_reg[47:40]; + m_axis_tdata_int[63:56] = eth_src_mac_reg[39:32]; + m_axis_tkeep_int = 8'hff; + s_eth_payload_axis_tready_next = m_axis_tready_int_early && shift_eth_payload_s_tready; + state_next = STATE_WRITE_HEADER_LAST; + end + endcase + end else begin + state_next = STATE_WRITE_HEADER; + end + end + STATE_WRITE_HEADER_LAST: begin + // last header word requires first payload word; process accordingly + s_eth_payload_axis_tready_next = m_axis_tready_int_early && shift_eth_payload_s_tready; + + if (s_eth_payload_axis_tready && shift_eth_payload_axis_tvalid) begin + frame_ptr_next = frame_ptr_reg + 8'd8; + m_axis_tvalid_int = 1'b1; + transfer_in_save = 1'b1; + + m_axis_tdata_int[ 7: 0] = eth_src_mac_reg[31:24]; + m_axis_tdata_int[15: 8] = eth_src_mac_reg[23:16]; + m_axis_tdata_int[23:16] = eth_src_mac_reg[15: 8]; + m_axis_tdata_int[31:24] = eth_src_mac_reg[ 7: 0]; + m_axis_tdata_int[39:32] = eth_type_reg[15: 8]; + m_axis_tdata_int[47:40] = eth_type_reg[ 7: 0]; + m_axis_tdata_int[55:48] = shift_eth_payload_axis_tdata[55:48]; + m_axis_tdata_int[63:56] = shift_eth_payload_axis_tdata[63:56]; + m_axis_tkeep_int = {shift_eth_payload_axis_tkeep[7:6], 6'h3F}; + m_axis_tlast_int = shift_eth_payload_axis_tlast; + m_axis_tuser_int = shift_eth_payload_axis_tuser; + + if (shift_eth_payload_axis_tlast) begin + s_eth_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_eth_hdr_ready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end else begin + state_next = STATE_WRITE_HEADER_LAST; + end + end + STATE_WRITE_PAYLOAD: begin + // write payload + s_eth_payload_axis_tready_next = m_axis_tready_int_early && shift_eth_payload_s_tready; + + m_axis_tdata_int = shift_eth_payload_axis_tdata; + m_axis_tkeep_int = shift_eth_payload_axis_tkeep; + m_axis_tvalid_int = shift_eth_payload_axis_tvalid; + m_axis_tlast_int = shift_eth_payload_axis_tlast; + m_axis_tuser_int = shift_eth_payload_axis_tuser; + + if (m_axis_tready_int_reg && shift_eth_payload_axis_tvalid) begin + // word transfer through + transfer_in_save = 1'b1; + if (shift_eth_payload_axis_tlast) begin + s_eth_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_eth_hdr_ready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 8'd0; + s_eth_hdr_ready_reg <= 1'b0; + s_eth_payload_axis_tready_reg <= 1'b0; + save_eth_payload_axis_tlast_reg <= 1'b0; + shift_eth_payload_extra_cycle_reg <= 1'b0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + + busy_reg <= state_next != STATE_IDLE; + + // datapath + if (store_eth_hdr) begin + eth_dest_mac_reg <= s_eth_dest_mac; + eth_src_mac_reg <= s_eth_src_mac; + eth_type_reg <= s_eth_type; + end + + if (flush_save) begin + save_eth_payload_axis_tlast_reg <= 1'b0; + shift_eth_payload_extra_cycle_reg <= 1'b0; + end else if (transfer_in_save) begin + save_eth_payload_axis_tlast_reg <= s_eth_payload_axis_tlast; + shift_eth_payload_extra_cycle_reg <= s_eth_payload_axis_tlast && (s_eth_payload_axis_tkeep[7:2] != 0); + end + end + + // datapath + if (store_eth_hdr) begin + eth_dest_mac_reg <= s_eth_dest_mac; + eth_src_mac_reg <= s_eth_src_mac; + eth_type_reg <= s_eth_type; + end + + if (transfer_in_save) begin + save_eth_payload_axis_tdata_reg <= s_eth_payload_axis_tdata; + save_eth_payload_axis_tkeep_reg <= s_eth_payload_axis_tkeep; + save_eth_payload_axis_tuser_reg <= s_eth_payload_axis_tuser; + end +end + +// output datapath logic +reg [63:0] m_axis_tdata_reg = 64'd0; +reg [7:0] m_axis_tkeep_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_axis_tkeep_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/eth_demux.v b/fpga/lib/eth/rtl/eth_demux.v new file mode 100644 index 000000000..0433b9ef6 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_demux.v @@ -0,0 +1,305 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * Ethernet demultiplexer + */ +module eth_demux # +( + parameter M_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [DATA_WIDTH-1:0] s_eth_payload_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire [ID_WIDTH-1:0] s_eth_payload_axis_tid, + input wire [DEST_WIDTH-1:0] s_eth_payload_axis_tdest, + input wire [USER_WIDTH-1:0] s_eth_payload_axis_tuser, + + /* + * Ethernet frame outputs + */ + output wire [M_COUNT-1:0] m_eth_hdr_valid, + input wire [M_COUNT-1:0] m_eth_hdr_ready, + output wire [M_COUNT*48-1:0] m_eth_dest_mac, + output wire [M_COUNT*48-1:0] m_eth_src_mac, + output wire [M_COUNT*16-1:0] m_eth_type, + output wire [M_COUNT*DATA_WIDTH-1:0] m_eth_payload_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep, + output wire [M_COUNT-1:0] m_eth_payload_axis_tvalid, + input wire [M_COUNT-1:0] m_eth_payload_axis_tready, + output wire [M_COUNT-1:0] m_eth_payload_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_eth_payload_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_eth_payload_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_eth_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [$clog2(M_COUNT)-1:0] select +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +reg [CL_M_COUNT-1:0] select_reg = {CL_M_COUNT{1'b0}}, select_ctl, select_next; +reg drop_reg = 1'b0, drop_ctl, drop_next; +reg frame_reg = 1'b0, frame_ctl, frame_next; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; + +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg [M_COUNT-1:0] m_eth_hdr_valid_reg = 0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_int; +reg [M_COUNT-1:0] m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg && enable; + +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg && enable; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = {M_COUNT{m_eth_dest_mac_reg}}; +assign m_eth_src_mac = {M_COUNT{m_eth_src_mac_reg}}; +assign m_eth_type = {M_COUNT{m_eth_type_reg}}; + +integer i; + +always @* begin + select_next = select_reg; + select_ctl = select_reg; + drop_next = drop_reg; + drop_ctl = drop_reg; + frame_next = frame_reg; + frame_ctl = frame_reg; + + s_eth_hdr_ready_next = 1'b0; + + s_eth_payload_axis_tready_next = 1'b0; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg & ~m_eth_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + + if (s_eth_payload_axis_tvalid && s_eth_payload_axis_tready) begin + // end of frame detection + if (s_eth_payload_axis_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + + if (!frame_reg && s_eth_hdr_valid && s_eth_hdr_ready) begin + // start of frame, grab select value + select_ctl = select; + drop_ctl = drop; + frame_ctl = 1'b1; + + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = frame_ctl; + + s_eth_hdr_ready_next = 1'b0; + + m_eth_hdr_valid_next = (!drop_ctl) << select_ctl; + m_eth_dest_mac_next = s_eth_dest_mac; + m_eth_src_mac_next = s_eth_src_mac; + m_eth_type_next = s_eth_type; + end + + s_eth_hdr_ready_next = !frame_next && !m_eth_hdr_valid_next; + + s_eth_payload_axis_tready_next = (m_eth_payload_axis_tready_int_early || drop_ctl) && frame_ctl; + + m_eth_payload_axis_tdata_int = s_eth_payload_axis_tdata; + m_eth_payload_axis_tkeep_int = s_eth_payload_axis_tkeep; + m_eth_payload_axis_tvalid_int = (s_eth_payload_axis_tvalid && s_eth_payload_axis_tready && !drop_ctl) << select_ctl; + m_eth_payload_axis_tlast_int = s_eth_payload_axis_tlast; + m_eth_payload_axis_tid_int = s_eth_payload_axis_tid; + m_eth_payload_axis_tdest_int = s_eth_payload_axis_tdest; + m_eth_payload_axis_tuser_int = s_eth_payload_axis_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 2'd0; + drop_reg <= 1'b0; + frame_reg <= 1'b0; + s_eth_hdr_ready_reg <= 1'b0; + s_eth_payload_axis_tready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 0; + end else begin + select_reg <= select_next; + drop_reg <= drop_next; + frame_reg <= frame_next; + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_eth_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] temp_m_eth_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = {M_COUNT{m_eth_payload_axis_tdata_reg}}; +assign m_eth_payload_axis_tkeep = KEEP_ENABLE ? {M_COUNT{m_eth_payload_axis_tkeep_reg}} : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = {M_COUNT{m_eth_payload_axis_tlast_reg}}; +assign m_eth_payload_axis_tid = ID_ENABLE ? {M_COUNT{m_eth_payload_axis_tid_reg}} : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_eth_payload_axis_tdest = DEST_ENABLE ? {M_COUNT{m_eth_payload_axis_tdest_reg}} : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_eth_payload_axis_tuser = USER_ENABLE ? {M_COUNT{m_eth_payload_axis_tuser_reg}} : {M_COUNT*USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = (m_eth_payload_axis_tready & m_eth_payload_axis_tvalid) || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if ((m_eth_payload_axis_tready & m_eth_payload_axis_tvalid) || !m_eth_payload_axis_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready & m_eth_payload_axis_tvalid) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= {M_COUNT{1'b0}}; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tid_reg <= temp_m_eth_payload_axis_tid_reg; + m_eth_payload_axis_tdest_reg <= temp_m_eth_payload_axis_tdest_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + temp_m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_10g.v b/fpga/lib/eth/rtl/eth_mac_10g.v new file mode 100644 index 000000000..187389bce --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_10g.v @@ -0,0 +1,252 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * 10G Ethernet MAC + */ +module eth_mac_10g # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter TX_PTP_TS_ENABLE = 0, + parameter TX_PTP_TS_WIDTH = 96, + parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE, + parameter TX_PTP_TAG_WIDTH = 16, + parameter RX_PTP_TS_ENABLE = 0, + parameter RX_PTP_TS_WIDTH = 96, + parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1, + parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire [KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire [TX_USER_WIDTH-1:0] tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire [KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire [RX_USER_WIDTH-1:0] rx_axis_tuser, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_rxd, + input wire [CTRL_WIDTH-1:0] xgmii_rxc, + output wire [DATA_WIDTH-1:0] xgmii_txd, + output wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * PTP + */ + input wire [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts, + input wire [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts, + output wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts, + output wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag, + output wire tx_axis_ptp_ts_valid, + + /* + * Status + */ + output wire [1:0] tx_start_packet, + output wire tx_error_underflow, + output wire [1:0] rx_start_packet, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 32 && DATA_WIDTH != 64) begin + $error("Error: Interface width must be 32 or 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +generate + +if (DATA_WIDTH == 64) begin + +axis_xgmii_rx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .USER_WIDTH(RX_USER_WIDTH) +) +axis_xgmii_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tuser(rx_axis_tuser), + .ptp_ts(rx_ptp_ts), + .start_packet(rx_start_packet), + .error_bad_frame(rx_error_bad_frame), + .error_bad_fcs(rx_error_bad_fcs) +); + +axis_xgmii_tx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .USER_WIDTH(TX_USER_WIDTH) +) +axis_xgmii_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .ptp_ts(tx_ptp_ts), + .m_axis_ptp_ts(tx_axis_ptp_ts), + .m_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .ifg_delay(ifg_delay), + .start_packet(tx_start_packet), + .error_underflow(tx_error_underflow) +); + +end else begin + +axis_xgmii_rx_32 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .USER_WIDTH(RX_USER_WIDTH) +) +axis_xgmii_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tuser(rx_axis_tuser), + .ptp_ts(rx_ptp_ts), + .start_packet(rx_start_packet[0]), + .error_bad_frame(rx_error_bad_frame), + .error_bad_fcs(rx_error_bad_fcs) +); + +assign rx_start_packet[1] = 1'b0; + +axis_xgmii_tx_32 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .USER_WIDTH(TX_USER_WIDTH) +) +axis_xgmii_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .ptp_ts(tx_ptp_ts), + .m_axis_ptp_ts(tx_axis_ptp_ts), + .m_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .ifg_delay(ifg_delay), + .start_packet(tx_start_packet[0]) +); + +assign tx_start_packet[1] = 1'b0; + +end + +endgenerate + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_10g_fifo.v b/fpga/lib/eth/rtl/eth_mac_10g_fifo.v new file mode 100644 index 000000000..b478dff58 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_10g_fifo.v @@ -0,0 +1,308 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * 10G Ethernet MAC with TX and RX FIFOs + */ +module eth_mac_10g_fifo # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_ADDR_WIDTH = 9, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_ADDR_WIDTH = 9, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire [KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire [KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_rxd, + input wire [CTRL_WIDTH-1:0] xgmii_rxc, + output wire [DATA_WIDTH-1:0] xgmii_txd, + output wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [DATA_WIDTH-1:0] tx_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] tx_fifo_axis_tkeep; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [DATA_WIDTH-1:0] rx_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_fifo_axis_tkeep; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +eth_mac_10g #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_10g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tkeep(tx_fifo_axis_tkeep), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tkeep(rx_fifo_axis_tkeep), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo #( + .ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(1), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // Common reset + .async_rst(logic_rst | tx_rst), + // AXI input + .s_clk(logic_clk), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(tx_fifo_axis_tkeep), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo #( + .ADDR_WIDTH(RX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(1), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(RX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(RX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(RX_DROP_WHEN_FULL) +) +rx_fifo ( + // Common reset + .async_rst(rx_rst | logic_rst), + // AXI input + .s_clk(rx_clk), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(rx_fifo_axis_tkeep), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_1g.v b/fpga/lib/eth/rtl/eth_mac_1g.v new file mode 100644 index 000000000..4f1075c19 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_1g.v @@ -0,0 +1,167 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * 1G Ethernet MAC + */ +module eth_mac_1g # +( + parameter DATA_WIDTH = 8, + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_PTP_TS_ENABLE = 0, + parameter TX_PTP_TS_WIDTH = 96, + parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE, + parameter TX_PTP_TAG_WIDTH = 16, + parameter RX_PTP_TS_ENABLE = 0, + parameter RX_PTP_TS_WIDTH = 96, + parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1, + parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire [TX_USER_WIDTH-1:0] tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire [RX_USER_WIDTH-1:0] rx_axis_tuser, + + /* + * GMII interface + */ + input wire [DATA_WIDTH-1:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + output wire [DATA_WIDTH-1:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * PTP + */ + input wire [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts, + input wire [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts, + output wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts, + output wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag, + output wire tx_axis_ptp_ts_valid, + + /* + * Control + */ + input wire rx_clk_enable, + input wire tx_clk_enable, + input wire rx_mii_select, + input wire tx_mii_select, + + /* + * Status + */ + output wire tx_start_packet, + output wire tx_error_underflow, + output wire rx_start_packet, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +axis_gmii_rx #( + .DATA_WIDTH(DATA_WIDTH), + .PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .USER_WIDTH(RX_USER_WIDTH) +) +axis_gmii_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tuser(rx_axis_tuser), + .ptp_ts(rx_ptp_ts), + .clk_enable(rx_clk_enable), + .mii_select(rx_mii_select), + .start_packet(rx_start_packet), + .error_bad_frame(rx_error_bad_frame), + .error_bad_fcs(rx_error_bad_fcs) +); + +axis_gmii_tx #( + .DATA_WIDTH(DATA_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .USER_WIDTH(TX_USER_WIDTH) +) +axis_gmii_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .ptp_ts(tx_ptp_ts), + .m_axis_ptp_ts(tx_axis_ptp_ts), + .m_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .clk_enable(tx_clk_enable), + .mii_select(tx_mii_select), + .ifg_delay(ifg_delay), + .start_packet(tx_start_packet), + .error_underflow(tx_error_underflow) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_1g_fifo.v b/fpga/lib/eth/rtl/eth_mac_1g_fifo.v new file mode 100644 index 000000000..b96c5a862 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_1g_fifo.v @@ -0,0 +1,308 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * 1G Ethernet MAC with TX and RX FIFOs + */ +module eth_mac_1g_fifo # +( + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_ADDR_WIDTH = 12, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_ADDR_WIDTH = 12, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * GMII interface + */ + input wire [7:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + output wire [7:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * Control + */ + input wire rx_clk_enable, + input wire tx_clk_enable, + input wire rx_mii_select, + input wire tx_mii_select, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [7:0] tx_fifo_axis_tdata; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [7:0] rx_fifo_axis_tdata; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +eth_mac_1g #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .rx_clk_enable(rx_clk_enable), + .tx_clk_enable(tx_clk_enable), + .rx_mii_select(rx_mii_select), + .tx_mii_select(tx_mii_select), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo #( + .ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // Common reset + .async_rst(logic_rst | tx_rst), + // AXI input + .s_clk(logic_clk), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo #( + .ADDR_WIDTH(RX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +rx_fifo ( + // Common reset + .async_rst(rx_rst | logic_rst), + // AXI input + .s_clk(rx_clk), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_1g_gmii.v b/fpga/lib/eth/rtl/eth_mac_1g_gmii.v new file mode 100644 index 000000000..dc0927c83 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_1g_gmii.v @@ -0,0 +1,248 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * 1G Ethernet MAC with GMII interface + */ +module eth_mac_1g_gmii # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire gtx_clk, + input wire gtx_rst, + output wire rx_clk, + output wire rx_rst, + output wire tx_clk, + output wire tx_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * GMII interface + */ + input wire gmii_rx_clk, + input wire [7:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + input wire mii_tx_clk, + output wire gmii_tx_clk, + output wire [7:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * Status + */ + output wire tx_error_underflow, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [7:0] mac_gmii_rxd; +wire mac_gmii_rx_dv; +wire mac_gmii_rx_er; +wire [7:0] mac_gmii_txd; +wire mac_gmii_tx_en; +wire mac_gmii_tx_er; + +reg [1:0] speed_reg = 2'b10; +reg mii_select_reg = 1'b0; + +(* srl_style = "register" *) +reg [1:0] tx_mii_select_sync = 2'd0; + +always @(posedge tx_clk) begin + tx_mii_select_sync <= {tx_mii_select_sync[0], mii_select_reg}; +end + +(* srl_style = "register" *) +reg [1:0] rx_mii_select_sync = 2'd0; + +always @(posedge rx_clk) begin + rx_mii_select_sync <= {rx_mii_select_sync[0], mii_select_reg}; +end + +// PHY speed detection +reg [2:0] rx_prescale = 3'd0; + +always @(posedge rx_clk) begin + rx_prescale <= rx_prescale + 3'd1; +end + +(* srl_style = "register" *) +reg [2:0] rx_prescale_sync = 3'd0; + +always @(posedge gtx_clk) begin + rx_prescale_sync <= {rx_prescale_sync[1:0], rx_prescale[2]}; +end + +reg [6:0] rx_speed_count_1 = 0; +reg [1:0] rx_speed_count_2 = 0; + +always @(posedge gtx_clk) begin + if (gtx_rst) begin + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b10; + mii_select_reg <= 1'b0; + end else begin + rx_speed_count_1 <= rx_speed_count_1 + 1; + + if (rx_prescale_sync[1] ^ rx_prescale_sync[2]) begin + rx_speed_count_2 <= rx_speed_count_2 + 1; + end + + if (&rx_speed_count_1) begin + // reference count overflow - 10M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b00; + mii_select_reg <= 1'b1; + end + + if (&rx_speed_count_2) begin + // prescaled count overflow - 100M or 1000M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + if (rx_speed_count_1[6:5]) begin + // large reference count - 100M + speed_reg <= 2'b01; + mii_select_reg <= 1'b1; + end else begin + // small reference count - 1000M + speed_reg <= 2'b10; + mii_select_reg <= 1'b0; + end + end + end +end + +assign speed = speed_reg; + +gmii_phy_if #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE) +) +gmii_phy_if_inst ( + .clk(gtx_clk), + .rst(gtx_rst), + + .mac_gmii_rx_clk(rx_clk), + .mac_gmii_rx_rst(rx_rst), + .mac_gmii_rxd(mac_gmii_rxd), + .mac_gmii_rx_dv(mac_gmii_rx_dv), + .mac_gmii_rx_er(mac_gmii_rx_er), + .mac_gmii_tx_clk(tx_clk), + .mac_gmii_tx_rst(tx_rst), + .mac_gmii_txd(mac_gmii_txd), + .mac_gmii_tx_en(mac_gmii_tx_en), + .mac_gmii_tx_er(mac_gmii_tx_er), + + .phy_gmii_rx_clk(gmii_rx_clk), + .phy_gmii_rxd(gmii_rxd), + .phy_gmii_rx_dv(gmii_rx_dv), + .phy_gmii_rx_er(gmii_rx_er), + .phy_mii_tx_clk(mii_tx_clk), + .phy_gmii_tx_clk(gmii_tx_clk), + .phy_gmii_txd(gmii_txd), + .phy_gmii_tx_en(gmii_tx_en), + .phy_gmii_tx_er(gmii_tx_er), + + .mii_select(mii_select_reg) +); + +eth_mac_1g #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_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_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rxd(mac_gmii_rxd), + .gmii_rx_dv(mac_gmii_rx_dv), + .gmii_rx_er(mac_gmii_rx_er), + .gmii_txd(mac_gmii_txd), + .gmii_tx_en(mac_gmii_tx_en), + .gmii_tx_er(mac_gmii_tx_er), + .rx_clk_enable(1'b1), + .tx_clk_enable(1'b1), + .rx_mii_select(rx_mii_select_sync[1]), + .tx_mii_select(tx_mii_select_sync[1]), + .tx_error_underflow(tx_error_underflow), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_1g_gmii_fifo.v b/fpga/lib/eth/rtl/eth_mac_1g_gmii_fifo.v new file mode 100644 index 000000000..dc853732c --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_1g_gmii_fifo.v @@ -0,0 +1,335 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * 1G Ethernet MAC with GMII interface and TX and RX FIFOs + */ +module eth_mac_1g_gmii_fifo # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_ADDR_WIDTH = 12, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_ADDR_WIDTH = 12, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire gtx_clk, + input wire gtx_rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * GMII interface + */ + input wire gmii_rx_clk, + input wire [7:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + input wire mii_tx_clk, + output wire gmii_tx_clk, + output wire [7:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire tx_clk; +wire rx_clk; +wire tx_rst; +wire rx_rst; + +wire [7:0] tx_fifo_axis_tdata; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [7:0] rx_fifo_axis_tdata; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +wire [1:0] speed_int; + +reg [1:0] speed_sync_reg_1 = 2'b10; +reg [1:0] speed_sync_reg_2 = 2'b10; + +assign speed = speed_sync_reg_2; + +always @(posedge logic_clk) begin + speed_sync_reg_1 <= speed_int; + speed_sync_reg_2 <= speed_sync_reg_1; +end + +eth_mac_1g_gmii #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_gmii_inst ( + .gtx_clk(gtx_clk), + .gtx_rst(gtx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .gmii_rx_clk(gmii_rx_clk), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_tx_clk(gmii_tx_clk), + .mii_tx_clk(mii_tx_clk), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .speed(speed_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo #( + .ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // Common reset + .async_rst(logic_rst | tx_rst), + // AXI input + .s_clk(logic_clk), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo #( + .ADDR_WIDTH(RX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +rx_fifo ( + // Common reset + .async_rst(rx_rst | logic_rst), + // AXI input + .s_clk(rx_clk), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_1g_rgmii.v b/fpga/lib/eth/rtl/eth_mac_1g_rgmii.v new file mode 100644 index 000000000..a29d576ed --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_1g_rgmii.v @@ -0,0 +1,249 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * 1G Ethernet MAC with RGMII interface + */ +module eth_mac_1g_rgmii # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Use 90 degree clock for RGMII transmit ("TRUE", "FALSE") + parameter USE_CLK90 = "TRUE", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire gtx_clk, + input wire gtx_clk90, + input wire gtx_rst, + output wire rx_clk, + output wire rx_rst, + output wire tx_clk, + output wire tx_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * RGMII interface + */ + input wire rgmii_rx_clk, + input wire [3:0] rgmii_rxd, + input wire rgmii_rx_ctl, + output wire rgmii_tx_clk, + output wire [3:0] rgmii_txd, + output wire rgmii_tx_ctl, + + /* + * Status + */ + output wire tx_error_underflow, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [7:0] mac_gmii_rxd; +wire mac_gmii_rx_dv; +wire mac_gmii_rx_er; +wire mac_gmii_tx_clk_en; +wire [7:0] mac_gmii_txd; +wire mac_gmii_tx_en; +wire mac_gmii_tx_er; + +reg [1:0] speed_reg = 2'b10; +reg mii_select_reg = 1'b0; + +(* srl_style = "register" *) +reg [1:0] tx_mii_select_sync = 2'd0; + +always @(posedge tx_clk) begin + tx_mii_select_sync <= {tx_mii_select_sync[0], mii_select_reg}; +end + +(* srl_style = "register" *) +reg [1:0] rx_mii_select_sync = 2'd0; + +always @(posedge rx_clk) begin + rx_mii_select_sync <= {rx_mii_select_sync[0], mii_select_reg}; +end + +// PHY speed detection +reg [2:0] rx_prescale = 3'd0; + +always @(posedge rx_clk) begin + rx_prescale <= rx_prescale + 3'd1; +end + +(* srl_style = "register" *) +reg [2:0] rx_prescale_sync = 3'd0; + +always @(posedge gtx_clk) begin + rx_prescale_sync <= {rx_prescale_sync[1:0], rx_prescale[2]}; +end + +reg [6:0] rx_speed_count_1 = 0; +reg [1:0] rx_speed_count_2 = 0; + +always @(posedge gtx_clk) begin + if (gtx_rst) begin + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b10; + mii_select_reg <= 1'b0; + end else begin + rx_speed_count_1 <= rx_speed_count_1 + 1; + + if (rx_prescale_sync[1] ^ rx_prescale_sync[2]) begin + rx_speed_count_2 <= rx_speed_count_2 + 1; + end + + if (&rx_speed_count_1) begin + // reference count overflow - 10M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b00; + mii_select_reg <= 1'b1; + end + + if (&rx_speed_count_2) begin + // prescaled count overflow - 100M or 1000M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + if (rx_speed_count_1[6:5]) begin + // large reference count - 100M + speed_reg <= 2'b01; + mii_select_reg <= 1'b1; + end else begin + // small reference count - 1000M + speed_reg <= 2'b10; + mii_select_reg <= 1'b0; + end + end + end +end + +assign speed = speed_reg; + +rgmii_phy_if #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .USE_CLK90(USE_CLK90) +) +rgmii_phy_if_inst ( + .clk(gtx_clk), + .clk90(gtx_clk90), + .rst(gtx_rst), + + .mac_gmii_rx_clk(rx_clk), + .mac_gmii_rx_rst(rx_rst), + .mac_gmii_rxd(mac_gmii_rxd), + .mac_gmii_rx_dv(mac_gmii_rx_dv), + .mac_gmii_rx_er(mac_gmii_rx_er), + .mac_gmii_tx_clk(tx_clk), + .mac_gmii_tx_rst(tx_rst), + .mac_gmii_tx_clk_en(mac_gmii_tx_clk_en), + .mac_gmii_txd(mac_gmii_txd), + .mac_gmii_tx_en(mac_gmii_tx_en), + .mac_gmii_tx_er(mac_gmii_tx_er), + + .phy_rgmii_rx_clk(rgmii_rx_clk), + .phy_rgmii_rxd(rgmii_rxd), + .phy_rgmii_rx_ctl(rgmii_rx_ctl), + .phy_rgmii_tx_clk(rgmii_tx_clk), + .phy_rgmii_txd(rgmii_txd), + .phy_rgmii_tx_ctl(rgmii_tx_ctl), + + .speed(speed) +); + +eth_mac_1g #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_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_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rxd(mac_gmii_rxd), + .gmii_rx_dv(mac_gmii_rx_dv), + .gmii_rx_er(mac_gmii_rx_er), + .gmii_txd(mac_gmii_txd), + .gmii_tx_en(mac_gmii_tx_en), + .gmii_tx_er(mac_gmii_tx_er), + .rx_clk_enable(1'b1), + .tx_clk_enable(mac_gmii_tx_clk_en), + .rx_mii_select(rx_mii_select_sync[1]), + .tx_mii_select(tx_mii_select_sync[1]), + .tx_error_underflow(tx_error_underflow), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_1g_rgmii_fifo.v b/fpga/lib/eth/rtl/eth_mac_1g_rgmii_fifo.v new file mode 100644 index 000000000..ce7b85746 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_1g_rgmii_fifo.v @@ -0,0 +1,334 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * 1G Ethernet MAC with RGMII interface and TX and RX FIFOs + */ +module eth_mac_1g_rgmii_fifo # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Use 90 degree clock for RGMII transmit ("TRUE", "FALSE") + parameter USE_CLK90 = "TRUE", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_ADDR_WIDTH = 12, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_ADDR_WIDTH = 12, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire gtx_clk, + input wire gtx_clk90, + input wire gtx_rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * RGMII interface + */ + input wire rgmii_rx_clk, + input wire [3:0] rgmii_rxd, + input wire rgmii_rx_ctl, + output wire rgmii_tx_clk, + output wire [3:0] rgmii_txd, + output wire rgmii_tx_ctl, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire tx_clk; +wire rx_clk; +wire tx_rst; +wire rx_rst; + +wire [7:0] tx_fifo_axis_tdata; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [7:0] rx_fifo_axis_tdata; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +wire [1:0] speed_int; + +reg [1:0] speed_sync_reg_1 = 2'b10; +reg [1:0] speed_sync_reg_2 = 2'b10; + +assign speed = speed_sync_reg_2; + +always @(posedge logic_clk) begin + speed_sync_reg_1 <= speed_int; + speed_sync_reg_2 <= speed_sync_reg_1; +end + +eth_mac_1g_rgmii #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .USE_CLK90(USE_CLK90), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_rgmii_inst ( + .gtx_clk(gtx_clk), + .gtx_clk90(gtx_clk90), + .gtx_rst(gtx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .rgmii_rx_clk(rgmii_rx_clk), + .rgmii_rxd(rgmii_rxd), + .rgmii_rx_ctl(rgmii_rx_ctl), + .rgmii_tx_clk(rgmii_tx_clk), + .rgmii_txd(rgmii_txd), + .rgmii_tx_ctl(rgmii_tx_ctl), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .speed(speed_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo #( + .ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // Common reset + .async_rst(logic_rst | tx_rst), + // AXI input + .s_clk(logic_clk), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo #( + .ADDR_WIDTH(RX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +rx_fifo ( + // Common reset + .async_rst(rx_rst | logic_rst), + // AXI input + .s_clk(rx_clk), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_mii.v b/fpga/lib/eth/rtl/eth_mac_mii.v new file mode 100644 index 000000000..75c1f86c9 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_mii.v @@ -0,0 +1,166 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * 10M/100M Ethernet MAC with MII interface + */ +module eth_mac_mii # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire rst, + output wire rx_clk, + output wire rx_rst, + output wire tx_clk, + output wire tx_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * MII interface + */ + input wire mii_rx_clk, + input wire [3:0] mii_rxd, + input wire mii_rx_dv, + input wire mii_rx_er, + input wire mii_tx_clk, + output wire [3:0] mii_txd, + output wire mii_tx_en, + output wire mii_tx_er, + + /* + * Status + */ + output wire tx_start_packet, + output wire tx_error_underflow, + output wire rx_start_packet, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [3:0] mac_mii_rxd; +wire mac_mii_rx_dv; +wire mac_mii_rx_er; +wire [3:0] mac_mii_txd; +wire mac_mii_tx_en; +wire mac_mii_tx_er; + +mii_phy_if #( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE) +) +mii_phy_if_inst ( + .rst(rst), + + .mac_mii_rx_clk(rx_clk), + .mac_mii_rx_rst(rx_rst), + .mac_mii_rxd(mac_mii_rxd), + .mac_mii_rx_dv(mac_mii_rx_dv), + .mac_mii_rx_er(mac_mii_rx_er), + .mac_mii_tx_clk(tx_clk), + .mac_mii_tx_rst(tx_rst), + .mac_mii_txd(mac_mii_txd), + .mac_mii_tx_en(mac_mii_tx_en), + .mac_mii_tx_er(mac_mii_tx_er), + + .phy_mii_rx_clk(mii_rx_clk), + .phy_mii_rxd(mii_rxd), + .phy_mii_rx_dv(mii_rx_dv), + .phy_mii_rx_er(mii_rx_er), + .phy_mii_tx_clk(mii_tx_clk), + .phy_mii_txd(mii_txd), + .phy_mii_tx_en(mii_tx_en), + .phy_mii_tx_er(mii_tx_er) +); + +eth_mac_1g #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_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_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rxd(mac_mii_rxd), + .gmii_rx_dv(mac_mii_rx_dv), + .gmii_rx_er(mac_mii_rx_er), + .gmii_txd(mac_mii_txd), + .gmii_tx_en(mac_mii_tx_en), + .gmii_tx_er(mac_mii_tx_er), + .rx_clk_enable(1'b1), + .tx_clk_enable(1'b1), + .rx_mii_select(1'b1), + .tx_mii_select(1'b1), + .tx_start_packet(tx_start_packet), + .tx_error_underflow(tx_error_underflow), + .rx_start_packet(rx_start_packet), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_mii_fifo.v b/fpga/lib/eth/rtl/eth_mac_mii_fifo.v new file mode 100644 index 000000000..56dd9ae99 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_mii_fifo.v @@ -0,0 +1,312 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * 10M/100M Ethernet MAC with MII interface and TX and RX FIFOs + */ +module eth_mac_mii_fifo # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_ADDR_WIDTH = 12, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_ADDR_WIDTH = 12, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * MII interface + */ + input wire mii_rx_clk, + input wire [3:0] mii_rxd, + input wire mii_rx_dv, + input wire mii_rx_er, + input wire mii_tx_clk, + output wire [3:0] mii_txd, + output wire mii_tx_en, + output wire mii_tx_er, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire tx_clk; +wire rx_clk; +wire tx_rst; +wire rx_rst; + +wire [7:0] tx_fifo_axis_tdata; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [7:0] rx_fifo_axis_tdata; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +eth_mac_mii #( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_mii_inst ( + .rst(rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .mii_rx_clk(mii_rx_clk), + .mii_rxd(mii_rxd), + .mii_rx_dv(mii_rx_dv), + .mii_rx_er(mii_rx_er), + .mii_tx_clk(mii_tx_clk), + .mii_txd(mii_txd), + .mii_tx_en(mii_tx_en), + .mii_tx_er(mii_tx_er), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo #( + .ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // Common reset + .async_rst(logic_rst | tx_rst), + // AXI input + .s_clk(logic_clk), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo #( + .ADDR_WIDTH(RX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +rx_fifo ( + // Common reset + .async_rst(rx_rst | logic_rst), + // AXI input + .s_clk(rx_clk), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_phy_10g.v b/fpga/lib/eth/rtl/eth_mac_phy_10g.v new file mode 100644 index 000000000..31390eb07 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_phy_10g.v @@ -0,0 +1,203 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * 10G Ethernet MAC/PHY combination + */ +module eth_mac_phy_10g # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter TX_PTP_TS_ENABLE = 0, + parameter TX_PTP_TS_WIDTH = 96, + parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE, + parameter TX_PTP_TAG_WIDTH = 16, + parameter RX_PTP_TS_ENABLE = 0, + parameter RX_PTP_TS_WIDTH = 96, + parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1, + parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter TX_SERDES_PIPELINE = 0, + parameter RX_SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire [KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire [TX_USER_WIDTH-1:0] tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire [KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire [RX_USER_WIDTH-1:0] rx_axis_tuser, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * PTP + */ + input wire [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts, + input wire [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts, + output wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts, + output wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag, + output wire tx_axis_ptp_ts_valid, + + /* + * Status + */ + output wire [1:0] tx_start_packet, + output wire tx_error_underflow, + output wire [1:0] rx_start_packet, + output wire [6:0] rx_error_count, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + input wire tx_prbs31_enable, + input wire rx_prbs31_enable +); + +eth_mac_phy_10g_rx #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .USER_WIDTH(RX_USER_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(RX_SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_mac_phy_10g_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tuser(rx_axis_tuser), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .ptp_ts(rx_ptp_ts), + .rx_start_packet(rx_start_packet), + .rx_error_count(rx_error_count), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_bad_block(rx_bad_block), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_prbs31_enable(rx_prbs31_enable) +); + +eth_mac_phy_10g_tx #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .USER_WIDTH(TX_USER_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(TX_SERDES_PIPELINE) +) +eth_mac_phy_10g_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .ptp_ts(tx_ptp_ts), + .m_axis_ptp_ts(tx_axis_ptp_ts), + .m_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .tx_start_packet(tx_start_packet), + .tx_error_underflow(tx_error_underflow), + .ifg_delay(ifg_delay), + .tx_prbs31_enable(tx_prbs31_enable) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_phy_10g_fifo.v b/fpga/lib/eth/rtl/eth_mac_phy_10g_fifo.v new file mode 100644 index 000000000..405ae374d --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_phy_10g_fifo.v @@ -0,0 +1,339 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * 10G Ethernet MAC/PHY combination with TX and RX FIFOs + */ +module eth_mac_phy_10g_fifo # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter TX_SERDES_PIPELINE = 0, + parameter RX_SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4, + parameter TX_FIFO_ADDR_WIDTH = 12-$clog2(KEEP_WIDTH), + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_ADDR_WIDTH = 12-$clog2(KEEP_WIDTH), + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire [KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire [KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + input wire tx_prbs31_enable, + input wire rx_prbs31_enable +); + +wire [DATA_WIDTH-1:0] tx_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] tx_fifo_axis_tkeep; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [DATA_WIDTH-1:0] rx_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_fifo_axis_tkeep; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [4:0] rx_sync_reg_1 = 5'd0; +reg [4:0] rx_sync_reg_2 = 5'd0; +reg [4:0] rx_sync_reg_3 = 5'd0; +reg [4:0] rx_sync_reg_4 = 5'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; +assign rx_bad_block = rx_sync_reg_3[2] ^ rx_sync_reg_4[2]; +assign rx_block_lock = rx_sync_reg_3[3] ^ rx_sync_reg_4[3]; +assign rx_high_ber = rx_sync_reg_3[4] ^ rx_sync_reg_4[4]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 5'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_high_ber_int, rx_block_lock_int, rx_bad_block_int, rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 5'd0; + rx_sync_reg_3 <= 5'd0; + rx_sync_reg_4 <= 5'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +eth_mac_phy_10g #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .TX_SERDES_PIPELINE(TX_SERDES_PIPELINE), + .RX_SERDES_PIPELINE(RX_SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_mac_phy_10g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tkeep(tx_fifo_axis_tkeep), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tkeep(rx_fifo_axis_tkeep), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .rx_bad_block(rx_bad_block_int), + .rx_block_lock(rx_block_lock_int), + .rx_high_ber(rx_high_ber_int), + .ifg_delay(ifg_delay), + .tx_prbs31_enable(tx_prbs31_enable), + .rx_prbs31_enable(rx_prbs31_enable) +); + +axis_async_fifo #( + .ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(1), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // Common reset + .async_rst(logic_rst | tx_rst), + // AXI input + .s_clk(logic_clk), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(tx_fifo_axis_tkeep), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo #( + .ADDR_WIDTH(RX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(1), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(RX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(RX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(RX_DROP_WHEN_FULL) +) +rx_fifo ( + // Common reset + .async_rst(rx_rst | logic_rst), + // AXI input + .s_clk(rx_clk), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(rx_fifo_axis_tkeep), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_phy_10g_rx.v b/fpga/lib/eth/rtl/eth_mac_phy_10g_rx.v new file mode 100644 index 000000000..f46dd3832 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_phy_10g_rx.v @@ -0,0 +1,165 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * 10G Ethernet MAC/PHY combination + */ +module eth_mac_phy_10g_rx # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire clk, + input wire rst, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * SERDES interface + */ + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Status + */ + output wire [1:0] rx_start_packet, + output wire [6:0] rx_error_count, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire rx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH * 32 != DATA_WIDTH) begin + $error("Error: HDR_WIDTH must be equal to DATA_WIDTH/32"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] encoded_rx_data; +wire [HDR_WIDTH-1:0] encoded_rx_hdr; + +eth_phy_10g_rx_if #( + .DATA_WIDTH(DATA_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_phy_10g_rx_if_inst ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_error_count(rx_error_count), + .rx_bad_block(rx_bad_block), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_prbs31_enable(rx_prbs31_enable) +); + +axis_baser_rx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +axis_baser_rx_inst ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + .ptp_ts(ptp_ts), + .start_packet(rx_start_packet), + .error_bad_frame(rx_error_bad_frame), + .error_bad_fcs(rx_error_bad_fcs), + .rx_bad_block(rx_bad_block) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mac_phy_10g_tx.v b/fpga/lib/eth/rtl/eth_mac_phy_10g_tx.v new file mode 100644 index 000000000..56a3713f9 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mac_phy_10g_tx.v @@ -0,0 +1,168 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * 10G Ethernet MAC/PHY combination + */ +module eth_mac_phy_10g_tx # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Status + */ + output wire [1:0] tx_start_packet, + output wire tx_error_underflow, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + input wire tx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH * 32 != DATA_WIDTH) begin + $error("Error: HDR_WIDTH must be equal to DATA_WIDTH/32"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] encoded_tx_data; +wire [HDR_WIDTH-1:0] encoded_tx_hdr; + +axis_baser_tx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .PTP_TAG_ENABLE(PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(PTP_TAG_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +axis_baser_tx_inst ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr), + .ptp_ts(ptp_ts), + .m_axis_ptp_ts(m_axis_ptp_ts), + .m_axis_ptp_ts_tag(m_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(m_axis_ptp_ts_valid), + .start_packet(tx_start_packet), + .error_underflow(tx_error_underflow), + .ifg_delay(ifg_delay) +); + +eth_phy_10g_tx_if #( + .DATA_WIDTH(DATA_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(SERDES_PIPELINE) +) +eth_phy_10g_tx_if_inst ( + .clk(clk), + .rst(rst), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .tx_prbs31_enable(tx_prbs31_enable) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_mux.v b/fpga/lib/eth/rtl/eth_mux.v new file mode 100644 index 000000000..4fe32659d --- /dev/null +++ b/fpga/lib/eth/rtl/eth_mux.v @@ -0,0 +1,299 @@ +/* + +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 1ns / 1ps + +/* + * Ethernet multiplexer + */ +module eth_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame inputs + */ + input wire [S_COUNT-1:0] s_eth_hdr_valid, + output wire [S_COUNT-1:0] s_eth_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*DATA_WIDTH-1:0] s_eth_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_eth_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_eth_payload_axis_tready, + input wire [S_COUNT-1:0] s_eth_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_eth_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_eth_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_eth_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_eth_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire [$clog2(S_COUNT)-1:0] select +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg [CL_S_COUNT-1:0] select_reg = 2'd0, select_next; +reg frame_reg = 1'b0, frame_next; + +reg [S_COUNT-1:0] s_eth_hdr_ready_reg = 0, s_eth_hdr_ready_next; + +reg [S_COUNT-1:0] s_eth_payload_axis_tready_reg = 0, s_eth_payload_axis_tready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; + +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_eth_payload_axis_tdata[select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_eth_payload_axis_tkeep[select_reg*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_eth_payload_axis_tvalid[select_reg]; +wire current_s_tready = s_eth_payload_axis_tready[select_reg]; +wire current_s_tlast = s_eth_payload_axis_tlast[select_reg]; +wire [ID_WIDTH-1:0] current_s_tid = s_eth_payload_axis_tid[select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_eth_payload_axis_tdest[select_reg*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_eth_payload_axis_tuser[select_reg*USER_WIDTH +: USER_WIDTH]; + +always @* begin + select_next = select_reg; + frame_next = frame_reg; + + s_eth_hdr_ready_next = 0; + + s_eth_payload_axis_tready_next = 0; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + + if (current_s_tvalid & current_s_tready) begin + // end of frame detection + if (current_s_tlast) begin + frame_next = 1'b0; + end + end + + if (!frame_reg && enable && !m_eth_hdr_valid && (s_eth_hdr_valid & (1 << select))) begin + // start of frame, grab select value + frame_next = 1'b1; + select_next = select; + + s_eth_hdr_ready_next = (1 << select); + + m_eth_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[select*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[select*48 +: 48]; + m_eth_type_next = s_eth_type[select*16 +: 16]; + end + + // generate ready signal on selected port + s_eth_payload_axis_tready_next = (m_eth_payload_axis_tready_int_early && frame_next) << select_next; + + // pass through selected packet data + m_eth_payload_axis_tdata_int = current_s_tdata; + m_eth_payload_axis_tkeep_int = current_s_tkeep; + m_eth_payload_axis_tvalid_int = current_s_tvalid && current_s_tready && frame_reg; + m_eth_payload_axis_tlast_int = current_s_tlast; + m_eth_payload_axis_tid_int = current_s_tid; + m_eth_payload_axis_tdest_int = current_s_tdest; + m_eth_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 0; + frame_reg <= 1'b0; + s_eth_hdr_ready_reg <= 0; + s_eth_payload_axis_tready_reg <= 0; + m_eth_hdr_valid_reg <= 1'b0; + end else begin + select_reg <= select_next; + frame_reg <= frame_next; + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tkeep = KEEP_ENABLE ? m_eth_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tid = ID_ENABLE ? m_eth_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_eth_payload_axis_tdest = DEST_ENABLE ? m_eth_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_eth_payload_axis_tuser = USER_ENABLE ? m_eth_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tid_reg <= temp_m_eth_payload_axis_tid_reg; + m_eth_payload_axis_tdest_reg <= temp_m_eth_payload_axis_tdest_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + temp_m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/eth_phy_10g.v b/fpga/lib/eth/rtl/eth_phy_10g.v new file mode 100644 index 000000000..3db237317 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_phy_10g.v @@ -0,0 +1,128 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * 10G Ethernet PHY + */ +module eth_phy_10g # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter TX_SERDES_PIPELINE = 0, + parameter RX_SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_txd, + input wire [CTRL_WIDTH-1:0] xgmii_txc, + output wire [DATA_WIDTH-1:0] xgmii_rxd, + output wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire [6:0] rx_error_count, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire tx_prbs31_enable, + input wire rx_prbs31_enable +); + +eth_phy_10g_rx #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(RX_SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_phy_10g_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_error_count(rx_error_count), + .rx_bad_block(rx_bad_block), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_prbs31_enable(rx_prbs31_enable) +); + +eth_phy_10g_tx #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(TX_SERDES_PIPELINE) +) +eth_phy_10g_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .tx_prbs31_enable(tx_prbs31_enable) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_phy_10g_rx.v b/fpga/lib/eth/rtl/eth_phy_10g_rx.v new file mode 100644 index 000000000..05bd6619e --- /dev/null +++ b/fpga/lib/eth/rtl/eth_phy_10g_rx.v @@ -0,0 +1,136 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * 10G Ethernet PHY RX + */ +module eth_phy_10g_rx # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire clk, + input wire rst, + + /* + * XGMII interface + */ + output wire [DATA_WIDTH-1:0] xgmii_rxd, + output wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * SERDES interface + */ + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire [6:0] rx_error_count, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire rx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] encoded_rx_data; +wire [HDR_WIDTH-1:0] encoded_rx_hdr; + +eth_phy_10g_rx_if #( + .DATA_WIDTH(DATA_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_phy_10g_rx_if_inst ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_error_count(rx_error_count), + .rx_bad_block(rx_bad_block), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_prbs31_enable(rx_prbs31_enable) +); + +xgmii_baser_dec_64 #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH) +) +xgmii_baser_dec_inst ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .rx_bad_block(rx_bad_block) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_phy_10g_rx_ber_mon.v b/fpga/lib/eth/rtl/eth_phy_10g_rx_ber_mon.v new file mode 100644 index 000000000..a1b5d0b37 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_phy_10g_rx_ber_mon.v @@ -0,0 +1,120 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * 10G Ethernet PHY BER monitor + */ +module eth_phy_10g_rx_ber_mon # +( + parameter HDR_WIDTH = 2, + parameter COUNT_125US = 125000/6.4 +) +( + input wire clk, + input wire rst, + + /* + * SERDES interface + */ + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + + /* + * Status + */ + output wire rx_high_ber +); + +// bus width assertions +initial begin + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +parameter COUNT_WIDTH = $clog2(COUNT_125US); + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +reg [COUNT_WIDTH-1:0] time_count_reg = 0, time_count_next; +reg [3:0] ber_count_reg = 4'd0, ber_count_next; + +reg rx_high_ber_reg = 1'b0, rx_high_ber_next; + +assign rx_high_ber = rx_high_ber_reg; + +always @* begin + if (time_count_reg > 0) begin + time_count_next = time_count_reg-1; + end else begin + time_count_next = time_count_reg; + end + ber_count_next = ber_count_reg; + + rx_high_ber_next = rx_high_ber_reg; + + if (serdes_rx_hdr == SYNC_CTRL || serdes_rx_hdr == SYNC_DATA) begin + // valid header + if (ber_count_reg != 4'd15) begin + if (time_count_reg == 0) begin + rx_high_ber_next = 1'b0; + end + end + end else begin + // invalid header + if (ber_count_reg == 4'd15) begin + rx_high_ber_next = 1'b1; + end else begin + ber_count_next = ber_count_reg + 1; + if (time_count_reg == 0) begin + rx_high_ber_next = 1'b0; + end + end + end + if (time_count_reg == 0) begin + // 125 us timer expired + ber_count_next = 4'd0; + time_count_next = COUNT_125US; + end +end + +always @(posedge clk) begin + if (rst) begin + time_count_reg <= COUNT_125US; + ber_count_reg <= 4'd0; + rx_high_ber_reg <= 1'b0; + end else begin + time_count_reg <= time_count_next; + ber_count_reg <= ber_count_next; + rx_high_ber_reg <= rx_high_ber_next; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/eth_phy_10g_rx_frame_sync.v b/fpga/lib/eth/rtl/eth_phy_10g_rx_frame_sync.v new file mode 100644 index 000000000..14fdf72b5 --- /dev/null +++ b/fpga/lib/eth/rtl/eth_phy_10g_rx_frame_sync.v @@ -0,0 +1,133 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * 10G Ethernet PHY frame sync + */ +module eth_phy_10g_rx_frame_sync # +( + parameter HDR_WIDTH = 2, + parameter SLIP_COUNT_WIDTH = 3 +) +( + input wire clk, + input wire rst, + + /* + * SERDES interface + */ + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire rx_block_lock +); + +// bus width assertions +initial begin + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +reg [5:0] sh_count_reg = 6'd0, sh_count_next; +reg [3:0] sh_invalid_count_reg = 4'd0, sh_invalid_count_next; +reg [SLIP_COUNT_WIDTH-1:0] slip_count_reg = 0, slip_count_next; + +reg serdes_rx_bitslip_reg = 1'b0, serdes_rx_bitslip_next; + +reg rx_block_lock_reg = 1'b0, rx_block_lock_next; + +assign serdes_rx_bitslip = serdes_rx_bitslip_reg; +assign rx_block_lock = rx_block_lock_reg; + +always @* begin + sh_count_next = sh_count_reg; + sh_invalid_count_next = sh_invalid_count_reg; + slip_count_next = slip_count_reg; + + serdes_rx_bitslip_next = 1'b0; + + rx_block_lock_next = rx_block_lock_reg; + + if (slip_count_reg) begin + slip_count_next = slip_count_reg-1; + end else if (serdes_rx_hdr == SYNC_CTRL || serdes_rx_hdr == SYNC_DATA) begin + // valid header + sh_count_next = sh_count_reg + 1; + if (&sh_count_reg) begin + // valid count overflow, reset + sh_count_next = 0; + sh_invalid_count_next = 0; + if (!sh_invalid_count_reg) begin + rx_block_lock_next = 1'b1; + end + end + end else begin + // invalid header + sh_count_next = sh_count_reg + 1; + sh_invalid_count_next = sh_invalid_count_reg + 1; + if (!rx_block_lock_reg || &sh_invalid_count_reg) begin + // invalid count overflow, lost block lock + sh_count_next = 0; + sh_invalid_count_next = 0; + rx_block_lock_next = 1'b0; + serdes_rx_bitslip_next = 1'b1; + slip_count_next = {SLIP_COUNT_WIDTH{1'b1}}; + end else if (&sh_count_reg) begin + // valid count overflow, reset + sh_count_next = 0; + sh_invalid_count_next = 0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + sh_count_reg <= 6'd0; + sh_invalid_count_reg <= 4'd0; + slip_count_reg <= 0; + rx_block_lock_reg <= 1'b0; + end else begin + sh_count_reg <= sh_count_next; + sh_invalid_count_reg <= sh_invalid_count_next; + slip_count_reg <= slip_count_next; + rx_block_lock_reg <= rx_block_lock_next; + end + + serdes_rx_bitslip_reg <= serdes_rx_bitslip_next; +end + +endmodule diff --git a/fpga/lib/eth/rtl/eth_phy_10g_rx_if.v b/fpga/lib/eth/rtl/eth_phy_10g_rx_if.v new file mode 100644 index 000000000..a7edf5adb --- /dev/null +++ b/fpga/lib/eth/rtl/eth_phy_10g_rx_if.v @@ -0,0 +1,243 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * 10G Ethernet PHY RX IF + */ +module eth_phy_10g_rx_if # +( + parameter DATA_WIDTH = 64, + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire clk, + input wire rst, + + /* + * 10GBASE-R encoded interface + */ + output wire [DATA_WIDTH-1:0] encoded_rx_data, + output wire [HDR_WIDTH-1:0] encoded_rx_hdr, + + /* + * SERDES interface + */ + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire [6:0] rx_error_count, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire rx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] serdes_rx_data_rev, serdes_rx_data_int; +wire [HDR_WIDTH-1:0] serdes_rx_hdr_rev, serdes_rx_hdr_int; + +generate + genvar n; + + if (BIT_REVERSE) begin + for (n = 0; n < DATA_WIDTH; n = n + 1) begin + assign serdes_rx_data_rev[n] = serdes_rx_data[DATA_WIDTH-n-1]; + end + + for (n = 0; n < HDR_WIDTH; n = n + 1) begin + assign serdes_rx_hdr_rev[n] = serdes_rx_hdr[HDR_WIDTH-n-1]; + end + end else begin + assign serdes_rx_data_rev = serdes_rx_data; + assign serdes_rx_hdr_rev = serdes_rx_hdr; + end + + if (SERDES_PIPELINE > 0) begin + (* srl_style = "register" *) + reg [DATA_WIDTH-1:0] serdes_rx_data_pipe_reg[SERDES_PIPELINE-1:0]; + (* srl_style = "register" *) + reg [HDR_WIDTH-1:0] serdes_rx_hdr_pipe_reg[SERDES_PIPELINE-1:0]; + + for (n = 0; n < SERDES_PIPELINE; n = n + 1) begin + initial begin + serdes_rx_data_pipe_reg[n] <= {DATA_WIDTH{1'b0}}; + serdes_rx_hdr_pipe_reg[n] <= {HDR_WIDTH{1'b0}}; + end + + always @(posedge clk) begin + serdes_rx_data_pipe_reg[n] <= n == 0 ? serdes_rx_data_rev : serdes_rx_data_pipe_reg[n-1]; + serdes_rx_hdr_pipe_reg[n] <= n == 0 ? serdes_rx_hdr_rev : serdes_rx_hdr_pipe_reg[n-1]; + end + end + + assign serdes_rx_data_int = serdes_rx_data_pipe_reg[SERDES_PIPELINE-1]; + assign serdes_rx_hdr_int = serdes_rx_hdr_pipe_reg[SERDES_PIPELINE-1]; + end else begin + assign serdes_rx_data_int = serdes_rx_data_rev; + assign serdes_rx_hdr_int = serdes_rx_hdr_rev; + end + +endgenerate + +wire [DATA_WIDTH-1:0] descrambled_rx_data; + +reg [DATA_WIDTH-1:0] encoded_rx_data_reg = {DATA_WIDTH{1'b0}}; +reg [HDR_WIDTH-1:0] encoded_rx_hdr_reg = {HDR_WIDTH{1'b0}}; + +reg [57:0] scrambler_state_reg = {58{1'b1}}; +wire [57:0] scrambler_state; + +reg [30:0] prbs31_state_reg = 31'h7fffffff; +wire [30:0] prbs31_state; +wire [DATA_WIDTH+HDR_WIDTH-1:0] prbs31_data; + +reg [6:0] rx_error_count_reg = 0; +reg [5:0] rx_error_count_1_reg = 0; +reg [5:0] rx_error_count_2_reg = 0; +reg [5:0] rx_error_count_1_temp = 0; +reg [5:0] rx_error_count_2_temp = 0; + +lfsr #( + .LFSR_WIDTH(58), + .LFSR_POLY(58'h8000000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(1), + .REVERSE(1), + .DATA_WIDTH(DATA_WIDTH), + .STYLE("AUTO") +) +descrambler_inst ( + .data_in(serdes_rx_data_int), + .state_in(scrambler_state_reg), + .data_out(descrambled_rx_data), + .state_out(scrambler_state) +); + +lfsr #( + .LFSR_WIDTH(31), + .LFSR_POLY(31'h10000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(1), + .REVERSE(1), + .DATA_WIDTH(DATA_WIDTH+HDR_WIDTH), + .STYLE("AUTO") +) +prbs31_check_inst ( + .data_in(~{serdes_rx_data_int, serdes_rx_hdr_int}), + .state_in(prbs31_state_reg), + .data_out(prbs31_data), + .state_out(prbs31_state) +); + +integer i; + +always @* begin + rx_error_count_1_temp = 0; + rx_error_count_2_temp = 0; + for (i = 0; i < DATA_WIDTH+HDR_WIDTH; i = i + 1) begin + if (i & 1) begin + rx_error_count_1_temp = rx_error_count_1_temp + prbs31_data[i]; + end else begin + rx_error_count_2_temp = rx_error_count_2_temp + prbs31_data[i]; + end + end +end + +always @(posedge clk) begin + scrambler_state_reg <= scrambler_state; + + encoded_rx_data_reg <= SCRAMBLER_DISABLE ? serdes_rx_data_int : descrambled_rx_data; + encoded_rx_hdr_reg <= serdes_rx_hdr_int; + + if (PRBS31_ENABLE && rx_prbs31_enable) begin + prbs31_state_reg <= prbs31_state; + + rx_error_count_1_reg <= rx_error_count_1_temp; + rx_error_count_2_reg <= rx_error_count_2_temp; + rx_error_count_reg <= rx_error_count_1_reg + rx_error_count_2_reg; + end +end + +assign encoded_rx_data = encoded_rx_data_reg; +assign encoded_rx_hdr = encoded_rx_hdr_reg; + +assign rx_error_count = rx_error_count_reg; + +wire serdes_rx_bitslip_int; +assign serdes_rx_bitslip = serdes_rx_bitslip_int && !(PRBS31_ENABLE && rx_prbs31_enable); + +eth_phy_10g_rx_frame_sync #( + .HDR_WIDTH(HDR_WIDTH), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH) +) +eth_phy_10g_rx_frame_sync_inst ( + .clk(clk), + .rst(rst), + .serdes_rx_hdr(serdes_rx_hdr_int), + .serdes_rx_bitslip(serdes_rx_bitslip_int), + .rx_block_lock(rx_block_lock) +); + +eth_phy_10g_rx_ber_mon #( + .HDR_WIDTH(HDR_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_phy_10g_rx_ber_mon_inst ( + .clk(clk), + .rst(rst), + .serdes_rx_hdr(serdes_rx_hdr_int), + .rx_high_ber(rx_high_ber) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_phy_10g_tx.v b/fpga/lib/eth/rtl/eth_phy_10g_tx.v new file mode 100644 index 000000000..86488353e --- /dev/null +++ b/fpga/lib/eth/rtl/eth_phy_10g_tx.v @@ -0,0 +1,117 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * 10G Ethernet PHY TX + */ +module eth_phy_10g_tx # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0 +) +( + input wire clk, + input wire rst, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_txd, + input wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + + /* + * Configuration + */ + input wire tx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] encoded_tx_data; +wire [HDR_WIDTH-1:0] encoded_tx_hdr; + +xgmii_baser_enc_64 #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH) +) +xgmii_baser_enc_inst ( + .clk(clk), + .rst(rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr) +); + +eth_phy_10g_tx_if #( + .DATA_WIDTH(DATA_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(SERDES_PIPELINE) +) +eth_phy_10g_tx_if_inst ( + .clk(clk), + .rst(rst), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .tx_prbs31_enable(tx_prbs31_enable) +); + +endmodule diff --git a/fpga/lib/eth/rtl/eth_phy_10g_tx_if.v b/fpga/lib/eth/rtl/eth_phy_10g_tx_if.v new file mode 100644 index 000000000..70ea70b7b --- /dev/null +++ b/fpga/lib/eth/rtl/eth_phy_10g_tx_if.v @@ -0,0 +1,179 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * 10G Ethernet PHY TX IF + */ +module eth_phy_10g_tx_if # +( + parameter DATA_WIDTH = 64, + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0 +) +( + input wire clk, + input wire rst, + + /* + * 10GBASE-R encoded interface + */ + input wire [DATA_WIDTH-1:0] encoded_tx_data, + input wire [HDR_WIDTH-1:0] encoded_tx_hdr, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + + /* + * Configuration + */ + input wire tx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +reg [57:0] scrambler_state_reg = {58{1'b1}}; +wire [57:0] scrambler_state; +wire [DATA_WIDTH-1:0] scrambled_data; + +reg [30:0] prbs31_state_reg = 31'h7fffffff; +wire [30:0] prbs31_state; +wire [DATA_WIDTH+HDR_WIDTH-1:0] prbs31_data; + +reg [DATA_WIDTH-1:0] serdes_tx_data_reg = {DATA_WIDTH{1'b0}}; +reg [HDR_WIDTH-1:0] serdes_tx_hdr_reg = {HDR_WIDTH{1'b0}}; + +wire [DATA_WIDTH-1:0] serdes_tx_data_int; +wire [HDR_WIDTH-1:0] serdes_tx_hdr_int; + +generate + genvar n; + + if (BIT_REVERSE) begin + for (n = 0; n < DATA_WIDTH; n = n + 1) begin + assign serdes_tx_data_int[n] = serdes_tx_data_reg[DATA_WIDTH-n-1]; + end + + for (n = 0; n < HDR_WIDTH; n = n + 1) begin + assign serdes_tx_hdr_int[n] = serdes_tx_hdr_reg[HDR_WIDTH-n-1]; + end + end else begin + assign serdes_tx_data_int = serdes_tx_data_reg; + assign serdes_tx_hdr_int = serdes_tx_hdr_reg; + end + + if (SERDES_PIPELINE > 0) begin + (* srl_style = "register" *) + reg [DATA_WIDTH-1:0] serdes_tx_data_pipe_reg[SERDES_PIPELINE-1:0]; + (* srl_style = "register" *) + reg [HDR_WIDTH-1:0] serdes_tx_hdr_pipe_reg[SERDES_PIPELINE-1:0]; + + for (n = 0; n < SERDES_PIPELINE; n = n + 1) begin + initial begin + serdes_tx_data_pipe_reg[n] <= {DATA_WIDTH{1'b0}}; + serdes_tx_hdr_pipe_reg[n] <= {HDR_WIDTH{1'b0}}; + end + + always @(posedge clk) begin + serdes_tx_data_pipe_reg[n] <= n == 0 ? serdes_tx_data_int : serdes_tx_data_pipe_reg[n-1]; + serdes_tx_hdr_pipe_reg[n] <= n == 0 ? serdes_tx_hdr_int : serdes_tx_hdr_pipe_reg[n-1]; + end + end + + assign serdes_tx_data = serdes_tx_data_pipe_reg[SERDES_PIPELINE-1]; + assign serdes_tx_hdr = serdes_tx_hdr_pipe_reg[SERDES_PIPELINE-1]; + end else begin + assign serdes_tx_data = serdes_tx_data_int; + assign serdes_tx_hdr = serdes_tx_hdr_int; + end + +endgenerate + +lfsr #( + .LFSR_WIDTH(58), + .LFSR_POLY(58'h8000000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(DATA_WIDTH), + .STYLE("AUTO") +) +scrambler_inst ( + .data_in(encoded_tx_data), + .state_in(scrambler_state_reg), + .data_out(scrambled_data), + .state_out(scrambler_state) +); + +lfsr #( + .LFSR_WIDTH(31), + .LFSR_POLY(31'h10000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(DATA_WIDTH+HDR_WIDTH), + .STYLE("AUTO") +) +prbs31_gen_inst ( + .data_in({DATA_WIDTH+HDR_WIDTH{1'b0}}), + .state_in(prbs31_state_reg), + .data_out(prbs31_data), + .state_out(prbs31_state) +); + +always @(posedge clk) begin + scrambler_state_reg <= scrambler_state; + + if (PRBS31_ENABLE && tx_prbs31_enable) begin + prbs31_state_reg <= prbs31_state; + + serdes_tx_data_reg <= ~prbs31_data[DATA_WIDTH+HDR_WIDTH-1:HDR_WIDTH]; + serdes_tx_hdr_reg <= ~prbs31_data[HDR_WIDTH-1:0]; + end else begin + serdes_tx_data_reg <= SCRAMBLER_DISABLE ? encoded_tx_data : scrambled_data; + serdes_tx_hdr_reg <= encoded_tx_hdr; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/gmii_phy_if.v b/fpga/lib/eth/rtl/gmii_phy_if.v new file mode 100644 index 000000000..be6ea4ec3 --- /dev/null +++ b/fpga/lib/eth/rtl/gmii_phy_if.v @@ -0,0 +1,148 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * GMII PHY interface + */ +module gmii_phy_if # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2" +) +( + input wire clk, + input wire rst, + + /* + * GMII interface to MAC + */ + output wire mac_gmii_rx_clk, + output wire mac_gmii_rx_rst, + output wire [7:0] mac_gmii_rxd, + output wire mac_gmii_rx_dv, + output wire mac_gmii_rx_er, + output wire mac_gmii_tx_clk, + output wire mac_gmii_tx_rst, + input wire [7:0] mac_gmii_txd, + input wire mac_gmii_tx_en, + input wire mac_gmii_tx_er, + + /* + * GMII interface to PHY + */ + input wire phy_gmii_rx_clk, + input wire [7:0] phy_gmii_rxd, + input wire phy_gmii_rx_dv, + input wire phy_gmii_rx_er, + input wire phy_mii_tx_clk, + output wire phy_gmii_tx_clk, + output wire [7:0] phy_gmii_txd, + output wire phy_gmii_tx_en, + output wire phy_gmii_tx_er, + + /* + * Control + */ + input wire mii_select +); + +ssio_sdr_in # +( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .WIDTH(10) +) +rx_ssio_sdr_inst ( + .input_clk(phy_gmii_rx_clk), + .input_d({phy_gmii_rxd, phy_gmii_rx_dv, phy_gmii_rx_er}), + .output_clk(mac_gmii_rx_clk), + .output_q({mac_gmii_rxd, mac_gmii_rx_dv, mac_gmii_rx_er}) +); + +ssio_sdr_out # +( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(10) +) +tx_ssio_sdr_inst ( + .clk(mac_gmii_tx_clk), + .input_d({mac_gmii_txd, mac_gmii_tx_en, mac_gmii_tx_er}), + .output_clk(phy_gmii_tx_clk), + .output_q({phy_gmii_txd, phy_gmii_tx_en, phy_gmii_tx_er}) +); + +generate + +if (TARGET == "XILINX") begin + BUFGMUX + gmii_bufgmux_inst ( + .I0(clk), + .I1(phy_mii_tx_clk), + .S(mii_select), + .O(mac_gmii_tx_clk) + ); +end else begin + assign mac_gmii_tx_clk = mii_select ? phy_mii_tx_clk : clk; +end + +endgenerate + +// reset sync +reg [3:0] tx_rst_reg = 4'hf; +assign mac_gmii_tx_rst = tx_rst_reg[0]; + +always @(posedge mac_gmii_tx_clk or posedge rst) begin + if (rst) begin + tx_rst_reg <= 4'hf; + end else begin + tx_rst_reg <= {1'b0, tx_rst_reg[3:1]}; + end +end + +reg [3:0] rx_rst_reg = 4'hf; +assign mac_gmii_rx_rst = rx_rst_reg[0]; + +always @(posedge mac_gmii_rx_clk or posedge rst) begin + if (rst) begin + rx_rst_reg <= 4'hf; + end else begin + rx_rst_reg <= {1'b0, rx_rst_reg[3:1]}; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/iddr.v b/fpga/lib/eth/rtl/iddr.v new file mode 100644 index 000000000..473a06d35 --- /dev/null +++ b/fpga/lib/eth/rtl/iddr.v @@ -0,0 +1,151 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Generic IDDR module + */ +module iddr # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + + input wire [WIDTH-1:0] d, + + output wire [WIDTH-1:0] q1, + output wire [WIDTH-1:0] q2 +); + +/* + +Provides a consistent input DDR flip flop across multiple FPGA families + _____ _____ _____ _____ ____ + clk ____/ \_____/ \_____/ \_____/ \_____/ + _ _____ _____ _____ _____ _____ _____ _____ _____ _____ _ + d _X_D0__X_D1__X_D2__X_D3__X_D4__X_D5__X_D6__X_D7__X_D8__X_ + _______ ___________ ___________ ___________ ___________ _ + q1 _______X___________X____D0_____X____D2_____X____D4_____X_ + _______ ___________ ___________ ___________ ___________ _ + q2 _______X___________X____D1_____X____D3_____X____D5_____X_ + +*/ + +genvar n; + +generate + +if (TARGET == "XILINX") begin + for (n = 0; n < WIDTH; n = n + 1) begin : iddr + if (IODDR_STYLE == "IODDR") begin + IDDR #( + .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), + .SRTYPE("ASYNC") + ) + iddr_inst ( + .Q1(q1[n]), + .Q2(q2[n]), + .C(clk), + .CE(1'b1), + .D(d[n]), + .R(1'b0), + .S(1'b0) + ); + end else if (IODDR_STYLE == "IODDR2") begin + IDDR2 #( + .DDR_ALIGNMENT("C0") + ) + iddr_inst ( + .Q0(q1[n]), + .Q1(q2[n]), + .C0(clk), + .C1(~clk), + .CE(1'b1), + .D(d[n]), + .R(1'b0), + .S(1'b0) + ); + end + end +end else if (TARGET == "ALTERA") begin + wire [WIDTH-1:0] q1_int; + reg [WIDTH-1:0] q1_delay; + + altddio_in #( + .WIDTH(WIDTH), + .POWER_UP_HIGH("OFF") + ) + altddio_in_inst ( + .aset(1'b0), + .datain(d), + .inclocken(1'b1), + .inclock(clk), + .aclr(1'b0), + .dataout_h(q1_int), + .dataout_l(q2) + ); + + always @(posedge clk) begin + q1_delay <= q1_int; + end + + assign q1 = q1_delay; +end else begin + reg [WIDTH-1:0] d_reg_1 = {WIDTH{1'b0}}; + reg [WIDTH-1:0] d_reg_2 = {WIDTH{1'b0}}; + + reg [WIDTH-1:0] q_reg_1 = {WIDTH{1'b0}}; + reg [WIDTH-1:0] q_reg_2 = {WIDTH{1'b0}}; + + always @(posedge clk) begin + d_reg_1 <= d; + end + + always @(negedge clk) begin + d_reg_2 <= d; + end + + always @(posedge clk) begin + q_reg_1 <= d_reg_1; + q_reg_2 <= d_reg_2; + end + + assign q1 = q_reg_1; + assign q2 = q_reg_2; +end + +endgenerate + +endmodule diff --git a/fpga/lib/eth/rtl/ip.v b/fpga/lib/eth/rtl/ip.v new file mode 100644 index 000000000..6c884ce35 --- /dev/null +++ b/fpga/lib/eth/rtl/ip.v @@ -0,0 +1,341 @@ +/* + +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 1ns / 1ps + +/* + * IPv4 block, ethernet frame interface + */ +module ip +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * ARP requests + */ + output wire arp_request_valid, + input wire arp_request_ready, + output wire [31:0] arp_request_ip, + input wire arp_response_valid, + output wire arp_response_ready, + input wire arp_response_error, + input wire [47:0] arp_response_mac, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire rx_error_invalid_header, + output wire rx_error_invalid_checksum, + output wire tx_error_payload_early_termination, + output wire tx_error_arp_failed, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_ARP_QUERY = 2'd1, + STATE_WAIT_PACKET = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg outgoing_ip_hdr_valid_reg = 1'b0, outgoing_ip_hdr_valid_next; +wire outgoing_ip_hdr_ready; +reg [47:0] outgoing_eth_dest_mac_reg = 48'h000000000000, outgoing_eth_dest_mac_next; +wire outgoing_ip_payload_axis_tready; + +/* + * IP frame processing + */ +ip_eth_rx +ip_eth_rx_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_ip_eth_dest_mac), + .m_eth_src_mac(m_ip_eth_src_mac), + .m_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(rx_busy), + .error_header_early_termination(rx_error_header_early_termination), + .error_payload_early_termination(rx_error_payload_early_termination), + .error_invalid_header(rx_error_invalid_header), + .error_invalid_checksum(rx_error_invalid_checksum) +); + +ip_eth_tx +ip_eth_tx_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(outgoing_ip_hdr_valid_reg), + .s_ip_hdr_ready(outgoing_ip_hdr_ready), + .s_eth_dest_mac(outgoing_eth_dest_mac_reg), + .s_eth_src_mac(local_mac), + .s_eth_type(16'h0800), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(16'd0), + .s_ip_flags(3'b010), + .s_ip_fragment_offset(13'd0), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(outgoing_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy(tx_busy), + .error_payload_early_termination(tx_error_payload_early_termination) +); + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; + +reg arp_request_valid_reg = 1'b0, arp_request_valid_next; + +reg arp_response_ready_reg = 1'b0, arp_response_ready_next; + +reg drop_packet_reg = 1'b0, drop_packet_next; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = outgoing_ip_payload_axis_tready || drop_packet_reg; + +assign arp_request_valid = arp_request_valid_reg; +assign arp_request_ip = s_ip_dest_ip; +assign arp_response_ready = arp_response_ready_reg; + +assign tx_error_arp_failed = arp_response_error; + +always @* begin + state_next = STATE_IDLE; + + arp_request_valid_next = arp_request_valid_reg && !arp_request_ready; + arp_response_ready_next = 1'b0; + drop_packet_next = 1'b0; + + s_ip_hdr_ready_next = 1'b0; + + outgoing_ip_hdr_valid_next = outgoing_ip_hdr_valid_reg && !outgoing_ip_hdr_ready; + outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg; + + case (state_reg) + STATE_IDLE: begin + // wait for outgoing packet + if (s_ip_hdr_valid) begin + // initiate ARP request + arp_request_valid_next = 1'b1; + arp_response_ready_next = 1'b1; + state_next = STATE_ARP_QUERY; + end else begin + state_next = STATE_IDLE; + end + end + STATE_ARP_QUERY: begin + arp_response_ready_next = 1'b1; + + if (arp_response_valid) begin + // wait for ARP reponse + if (arp_response_error) begin + // did not get MAC address; drop packet + s_ip_hdr_ready_next = 1'b1; + drop_packet_next = 1'b1; + state_next = STATE_WAIT_PACKET; + end else begin + // got MAC address; send packet + s_ip_hdr_ready_next = 1'b1; + outgoing_ip_hdr_valid_next = 1'b1; + outgoing_eth_dest_mac_next = arp_response_mac; + state_next = STATE_WAIT_PACKET; + end + end else begin + state_next = STATE_ARP_QUERY; + end + end + STATE_WAIT_PACKET: begin + drop_packet_next = drop_packet_reg; + + // wait for packet transfer to complete + if (s_ip_payload_axis_tlast && s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_PACKET; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + arp_request_valid_reg <= 1'b0; + arp_response_ready_reg <= 1'b0; + drop_packet_reg <= 1'b0; + s_ip_hdr_ready_reg <= 1'b0; + outgoing_ip_hdr_valid_reg <= 1'b0; + end else begin + state_reg <= state_next; + + arp_request_valid_reg <= arp_request_valid_next; + arp_response_ready_reg <= arp_response_ready_next; + drop_packet_reg <= drop_packet_next; + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + + outgoing_ip_hdr_valid_reg <= outgoing_ip_hdr_valid_next; + end + + outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next; +end + +endmodule diff --git a/fpga/lib/eth/rtl/ip_64.v b/fpga/lib/eth/rtl/ip_64.v new file mode 100644 index 000000000..be1464f4a --- /dev/null +++ b/fpga/lib/eth/rtl/ip_64.v @@ -0,0 +1,349 @@ +/* + +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 1ns / 1ps + +/* + * IPv4 block, ethernet frame interface (64 bit datapath) + */ +module ip_64 +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * ARP requests + */ + output wire arp_request_valid, + input wire arp_request_ready, + output wire [31:0] arp_request_ip, + input wire arp_response_valid, + output wire arp_response_ready, + input wire arp_response_error, + input wire [47:0] arp_response_mac, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire rx_error_invalid_header, + output wire rx_error_invalid_checksum, + output wire tx_error_payload_early_termination, + output wire tx_error_arp_failed, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_ARP_QUERY = 2'd1, + STATE_WAIT_PACKET = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg outgoing_ip_hdr_valid_reg = 1'b0, outgoing_ip_hdr_valid_next; +wire outgoing_ip_hdr_ready; +reg [47:0] outgoing_eth_dest_mac_reg = 48'h000000000000, outgoing_eth_dest_mac_next; +wire outgoing_ip_payload_axis_tready; + +/* + * IP frame processing + */ +ip_eth_rx_64 +ip_eth_rx_64_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_ip_eth_dest_mac), + .m_eth_src_mac(m_ip_eth_src_mac), + .m_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(rx_busy), + .error_header_early_termination(rx_error_header_early_termination), + .error_payload_early_termination(rx_error_payload_early_termination), + .error_invalid_header(rx_error_invalid_header), + .error_invalid_checksum(rx_error_invalid_checksum) +); + +ip_eth_tx_64 +ip_eth_tx_64_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(outgoing_ip_hdr_valid_reg), + .s_ip_hdr_ready(outgoing_ip_hdr_ready), + .s_eth_dest_mac(outgoing_eth_dest_mac_reg), + .s_eth_src_mac(local_mac), + .s_eth_type(16'h0800), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(16'd0), + .s_ip_flags(3'b010), + .s_ip_fragment_offset(13'd0), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(outgoing_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy(tx_busy), + .error_payload_early_termination(tx_error_payload_early_termination) +); + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; + +reg arp_request_valid_reg = 1'b0, arp_request_valid_next; + +reg arp_response_ready_reg = 1'b0, arp_response_ready_next; + +reg drop_packet_reg = 1'b0, drop_packet_next; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = outgoing_ip_payload_axis_tready || drop_packet_reg; + +assign arp_request_valid = arp_request_valid_reg; +assign arp_request_ip = s_ip_dest_ip; +assign arp_response_ready = arp_response_ready_reg; + +assign tx_error_arp_failed = arp_response_error; + +always @* begin + state_next = STATE_IDLE; + + arp_request_valid_next = arp_request_valid_reg && !arp_request_ready; + arp_response_ready_next = 1'b0; + drop_packet_next = 1'b0; + + s_ip_hdr_ready_next = 1'b0; + + outgoing_ip_hdr_valid_next = outgoing_ip_hdr_valid_reg && !outgoing_ip_hdr_ready; + outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg; + + case (state_reg) + STATE_IDLE: begin + // wait for outgoing packet + if (s_ip_hdr_valid) begin + // initiate ARP request + arp_request_valid_next = 1'b1; + arp_response_ready_next = 1'b1; + state_next = STATE_ARP_QUERY; + end else begin + state_next = STATE_IDLE; + end + end + STATE_ARP_QUERY: begin + arp_response_ready_next = 1'b1; + + if (arp_response_valid) begin + // wait for ARP reponse + if (arp_response_error) begin + // did not get MAC address; drop packet + s_ip_hdr_ready_next = 1'b1; + drop_packet_next = 1'b1; + state_next = STATE_WAIT_PACKET; + end else begin + // got MAC address; send packet + s_ip_hdr_ready_next = 1'b1; + outgoing_ip_hdr_valid_next = 1'b1; + outgoing_eth_dest_mac_next = arp_response_mac; + state_next = STATE_WAIT_PACKET; + end + end else begin + state_next = STATE_ARP_QUERY; + end + end + STATE_WAIT_PACKET: begin + drop_packet_next = drop_packet_reg; + + // wait for packet transfer to complete + if (s_ip_payload_axis_tlast && s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_PACKET; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + arp_request_valid_reg <= 1'b0; + arp_response_ready_reg <= 1'b0; + drop_packet_reg <= 1'b0; + s_ip_hdr_ready_reg <= 1'b0; + outgoing_ip_hdr_valid_reg <= 1'b0; + end else begin + state_reg <= state_next; + + arp_request_valid_reg <= arp_request_valid_next; + arp_response_ready_reg <= arp_response_ready_next; + drop_packet_reg <= drop_packet_next; + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + + outgoing_ip_hdr_valid_reg <= outgoing_ip_hdr_valid_next; + end + + outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next; +end + +endmodule diff --git a/fpga/lib/eth/rtl/ip_arb_mux.v b/fpga/lib/eth/rtl/ip_arb_mux.v new file mode 100644 index 000000000..13211bf3d --- /dev/null +++ b/fpga/lib/eth/rtl/ip_arb_mux.v @@ -0,0 +1,407 @@ +/* + +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 1ns / 1ps + +/* + * IP arbitrated multiplexer + */ +module ip_arb_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * IP frame inputs + */ + input wire [S_COUNT-1:0] s_ip_hdr_valid, + output wire [S_COUNT-1:0] s_ip_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*4-1:0] s_ip_version, + input wire [S_COUNT*4-1:0] s_ip_ihl, + input wire [S_COUNT*6-1:0] s_ip_dscp, + input wire [S_COUNT*2-1:0] s_ip_ecn, + input wire [S_COUNT*16-1:0] s_ip_length, + input wire [S_COUNT*16-1:0] s_ip_identification, + input wire [S_COUNT*3-1:0] s_ip_flags, + input wire [S_COUNT*13-1:0] s_ip_fragment_offset, + input wire [S_COUNT*8-1:0] s_ip_ttl, + input wire [S_COUNT*8-1:0] s_ip_protocol, + input wire [S_COUNT*16-1:0] s_ip_header_checksum, + input wire [S_COUNT*32-1:0] s_ip_source_ip, + input wire [S_COUNT*32-1:0] s_ip_dest_ip, + input wire [S_COUNT*DATA_WIDTH-1:0] s_ip_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_ip_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_ip_payload_axis_tready, + input wire [S_COUNT-1:0] s_ip_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_ip_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_ip_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_ip_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [DATA_WIDTH-1:0] m_ip_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_ip_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_ip_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg frame_reg = 1'b0, frame_next; + +reg s_ip_hdr_ready_mask_reg = 1'b0, s_ip_hdr_ready_mask_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; + +wire [S_COUNT-1:0] request; +wire [S_COUNT-1:0] acknowledge; +wire [S_COUNT-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT-1:0] grant_encoded; + +// internal datapath +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = (!s_ip_hdr_ready_mask_reg && grant_valid) << grant_encoded; + +assign s_ip_payload_axis_tready = (m_ip_payload_axis_tready_int_reg && grant_valid) << grant_encoded; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_ip_payload_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_ip_payload_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_ip_payload_axis_tvalid[grant_encoded]; +wire current_s_tready = s_ip_payload_axis_tready[grant_encoded]; +wire current_s_tlast = s_ip_payload_axis_tlast[grant_encoded]; +wire [ID_WIDTH-1:0] current_s_tid = s_ip_payload_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_ip_payload_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_ip_payload_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + +// arbiter instance +arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +generate + genvar n; + + for (n = 0; n < S_COUNT; n = n + 1) begin + assign request[n] = s_ip_hdr_valid[n] && !grant[n]; + assign acknowledge[n] = grant[n] && s_ip_payload_axis_tvalid[n] && s_ip_payload_axis_tready[n] && s_ip_payload_axis_tlast[n]; + end +endgenerate + +always @* begin + frame_next = frame_reg; + + s_ip_hdr_ready_mask_next = s_ip_hdr_ready_mask_reg; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + + if (s_ip_payload_axis_tvalid[grant_encoded] && s_ip_payload_axis_tready[grant_encoded]) begin + // end of frame detection + if (s_ip_payload_axis_tlast[grant_encoded]) begin + frame_next = 1'b0; + s_ip_hdr_ready_mask_next = 1'b0; + end + end + + if (!frame_reg && grant_valid) begin + // start of frame + frame_next = 1'b1; + + s_ip_hdr_ready_mask_next = 1'b1; + + m_ip_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[grant_encoded*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[grant_encoded*48 +: 48]; + m_eth_type_next = s_eth_type[grant_encoded*16 +: 16]; + m_ip_version_next = s_ip_version[grant_encoded*4 +: 4]; + m_ip_ihl_next = s_ip_ihl[grant_encoded*4 +: 4]; + m_ip_dscp_next = s_ip_dscp[grant_encoded*6 +: 6]; + m_ip_ecn_next = s_ip_ecn[grant_encoded*2 +: 2]; + m_ip_length_next = s_ip_length[grant_encoded*16 +: 16]; + m_ip_identification_next = s_ip_identification[grant_encoded*16 +: 16]; + m_ip_flags_next = s_ip_flags[grant_encoded*3 +: 3]; + m_ip_fragment_offset_next = s_ip_fragment_offset[grant_encoded*13 +: 13]; + m_ip_ttl_next = s_ip_ttl[grant_encoded*8 +: 8]; + m_ip_protocol_next = s_ip_protocol[grant_encoded*8 +: 8]; + m_ip_header_checksum_next = s_ip_header_checksum[grant_encoded*16 +: 16]; + m_ip_source_ip_next = s_ip_source_ip[grant_encoded*32 +: 32]; + m_ip_dest_ip_next = s_ip_dest_ip[grant_encoded*32 +: 32]; + end + + // pass through selected packet data + m_ip_payload_axis_tdata_int = current_s_tdata; + m_ip_payload_axis_tkeep_int = current_s_tkeep; + m_ip_payload_axis_tvalid_int = current_s_tvalid && m_ip_payload_axis_tready_int_reg && grant_valid; + m_ip_payload_axis_tlast_int = current_s_tlast; + m_ip_payload_axis_tid_int = current_s_tid; + m_ip_payload_axis_tdest_int = current_s_tdest; + m_ip_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + frame_reg <= 1'b0; + s_ip_hdr_ready_mask_reg <= 1'b0; + m_ip_hdr_valid_reg <= 1'b0; + end else begin + frame_reg <= frame_next; + s_ip_hdr_ready_mask_reg <= s_ip_hdr_ready_mask_next; + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tkeep = KEEP_ENABLE ? m_ip_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tid = ID_ENABLE ? m_ip_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_ip_payload_axis_tdest = DEST_ENABLE ? m_ip_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_ip_payload_axis_tuser = USER_ENABLE ? m_ip_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tid_reg <= temp_m_ip_payload_axis_tid_reg; + m_ip_payload_axis_tdest_reg <= temp_m_ip_payload_axis_tdest_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + temp_m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/ip_complete.v b/fpga/lib/eth/rtl/ip_complete.v new file mode 100644 index 000000000..f16d697b3 --- /dev/null +++ b/fpga/lib/eth/rtl/ip_complete.v @@ -0,0 +1,440 @@ +/* + +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 1ns / 1ps + +/* + * IPv4 and ARP block, ethernet frame interface + */ +module ip_complete #( + parameter ARP_CACHE_ADDR_WIDTH = 9, + parameter ARP_REQUEST_RETRY_COUNT = 4, + parameter ARP_REQUEST_RETRY_INTERVAL = 125000000*2, + parameter ARP_REQUEST_TIMEOUT = 125000000*30 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire rx_error_invalid_header, + output wire rx_error_invalid_checksum, + output wire tx_error_payload_early_termination, + output wire tx_error_arp_failed, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_arp_cache +); + +/* + +This module integrates the IP and ARP modules for a complete IP stack + +*/ + +wire arp_request_valid; +wire arp_request_ready; +wire [31:0] arp_request_ip; +wire arp_response_valid; +wire arp_response_ready; +wire arp_response_error; +wire [47:0] arp_response_mac; + +wire ip_rx_eth_hdr_valid; +wire ip_rx_eth_hdr_ready; +wire [47:0] ip_rx_eth_dest_mac; +wire [47:0] ip_rx_eth_src_mac; +wire [15:0] ip_rx_eth_type; +wire [7:0] ip_rx_eth_payload_axis_tdata; +wire ip_rx_eth_payload_axis_tvalid; +wire ip_rx_eth_payload_axis_tready; +wire ip_rx_eth_payload_axis_tlast; +wire ip_rx_eth_payload_axis_tuser; + +wire ip_tx_eth_hdr_valid; +wire ip_tx_eth_hdr_ready; +wire [47:0] ip_tx_eth_dest_mac; +wire [47:0] ip_tx_eth_src_mac; +wire [15:0] ip_tx_eth_type; +wire [7:0] ip_tx_eth_payload_axis_tdata; +wire ip_tx_eth_payload_axis_tvalid; +wire ip_tx_eth_payload_axis_tready; +wire ip_tx_eth_payload_axis_tlast; +wire ip_tx_eth_payload_axis_tuser; + +wire arp_rx_eth_hdr_valid; +wire arp_rx_eth_hdr_ready; +wire [47:0] arp_rx_eth_dest_mac; +wire [47:0] arp_rx_eth_src_mac; +wire [15:0] arp_rx_eth_type; +wire [7:0] arp_rx_eth_payload_axis_tdata; +wire arp_rx_eth_payload_axis_tvalid; +wire arp_rx_eth_payload_axis_tready; +wire arp_rx_eth_payload_axis_tlast; +wire arp_rx_eth_payload_axis_tuser; + +wire arp_tx_eth_hdr_valid; +wire arp_tx_eth_hdr_ready; +wire [47:0] arp_tx_eth_dest_mac; +wire [47:0] arp_tx_eth_src_mac; +wire [15:0] arp_tx_eth_type; +wire [7:0] arp_tx_eth_payload_axis_tdata; +wire arp_tx_eth_payload_axis_tvalid; +wire arp_tx_eth_payload_axis_tready; +wire arp_tx_eth_payload_axis_tlast; +wire arp_tx_eth_payload_axis_tuser; + +/* + * Input classifier (eth_type) + */ +wire s_select_ip = (s_eth_type == 16'h0800); +wire s_select_arp = (s_eth_type == 16'h0806); +wire s_select_none = !(s_select_ip || s_select_arp); + +reg s_select_ip_reg = 1'b0; +reg s_select_arp_reg = 1'b0; +reg s_select_none_reg = 1'b0; + +always @(posedge clk) begin + if (rst) begin + s_select_ip_reg <= 1'b0; + s_select_arp_reg <= 1'b0; + s_select_none_reg <= 1'b0; + end else begin + if (s_eth_payload_axis_tvalid) begin + if ((!s_select_ip_reg && !s_select_arp_reg && !s_select_none_reg) || + (s_eth_payload_axis_tvalid && s_eth_payload_axis_tready && s_eth_payload_axis_tlast)) begin + s_select_ip_reg <= s_select_ip; + s_select_arp_reg <= s_select_arp; + s_select_none_reg <= s_select_none; + end + end else begin + s_select_ip_reg <= 1'b0; + s_select_arp_reg <= 1'b0; + s_select_none_reg <= 1'b0; + end + end +end + +assign ip_rx_eth_hdr_valid = s_select_ip && s_eth_hdr_valid; +assign ip_rx_eth_dest_mac = s_eth_dest_mac; +assign ip_rx_eth_src_mac = s_eth_src_mac; +assign ip_rx_eth_type = 16'h0800; +assign ip_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata; +assign ip_rx_eth_payload_axis_tvalid = s_select_ip_reg && s_eth_payload_axis_tvalid; +assign ip_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast; +assign ip_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser; + +assign arp_rx_eth_hdr_valid = s_select_arp && s_eth_hdr_valid; +assign arp_rx_eth_dest_mac = s_eth_dest_mac; +assign arp_rx_eth_src_mac = s_eth_src_mac; +assign arp_rx_eth_type = 16'h0806; +assign arp_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata; +assign arp_rx_eth_payload_axis_tvalid = s_select_arp_reg && s_eth_payload_axis_tvalid; +assign arp_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast; +assign arp_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser; + +assign s_eth_hdr_ready = (s_select_ip && ip_rx_eth_hdr_ready) || + (s_select_arp && arp_rx_eth_hdr_ready) || + (s_select_none); + +assign s_eth_payload_axis_tready = (s_select_ip_reg && ip_rx_eth_payload_axis_tready) || + (s_select_arp_reg && arp_rx_eth_payload_axis_tready) || + s_select_none_reg; + +/* + * Output arbiter + */ +eth_arb_mux #( + .S_COUNT(2), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +eth_arb_mux_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_eth_hdr_valid({ip_tx_eth_hdr_valid, arp_tx_eth_hdr_valid}), + .s_eth_hdr_ready({ip_tx_eth_hdr_ready, arp_tx_eth_hdr_ready}), + .s_eth_dest_mac({ip_tx_eth_dest_mac, arp_tx_eth_dest_mac}), + .s_eth_src_mac({ip_tx_eth_src_mac, arp_tx_eth_src_mac}), + .s_eth_type({ip_tx_eth_type, arp_tx_eth_type}), + .s_eth_payload_axis_tdata({ip_tx_eth_payload_axis_tdata, arp_tx_eth_payload_axis_tdata}), + .s_eth_payload_axis_tkeep(0), + .s_eth_payload_axis_tvalid({ip_tx_eth_payload_axis_tvalid, arp_tx_eth_payload_axis_tvalid}), + .s_eth_payload_axis_tready({ip_tx_eth_payload_axis_tready, arp_tx_eth_payload_axis_tready}), + .s_eth_payload_axis_tlast({ip_tx_eth_payload_axis_tlast, arp_tx_eth_payload_axis_tlast}), + .s_eth_payload_axis_tid(0), + .s_eth_payload_axis_tdest(0), + .s_eth_payload_axis_tuser({ip_tx_eth_payload_axis_tuser, arp_tx_eth_payload_axis_tuser}), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tid(), + .m_eth_payload_axis_tdest(), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser) +); + +/* + * IP module + */ +ip +ip_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(ip_rx_eth_hdr_valid), + .s_eth_hdr_ready(ip_rx_eth_hdr_ready), + .s_eth_dest_mac(ip_rx_eth_dest_mac), + .s_eth_src_mac(ip_rx_eth_src_mac), + .s_eth_type(ip_rx_eth_type), + .s_eth_payload_axis_tdata(ip_rx_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(ip_rx_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(ip_rx_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(ip_rx_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(ip_rx_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(ip_tx_eth_hdr_valid), + .m_eth_hdr_ready(ip_tx_eth_hdr_ready), + .m_eth_dest_mac(ip_tx_eth_dest_mac), + .m_eth_src_mac(ip_tx_eth_src_mac), + .m_eth_type(ip_tx_eth_type), + .m_eth_payload_axis_tdata(ip_tx_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(ip_tx_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(ip_tx_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(ip_tx_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(ip_tx_eth_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // Status + .rx_busy(rx_busy), + .tx_busy(tx_busy), + .rx_error_header_early_termination(rx_error_header_early_termination), + .rx_error_payload_early_termination(rx_error_payload_early_termination), + .rx_error_invalid_header(rx_error_invalid_header), + .rx_error_invalid_checksum(rx_error_invalid_checksum), + .tx_error_payload_early_termination(tx_error_payload_early_termination), + .tx_error_arp_failed(tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip) +); + +/* + * ARP module + */ +arp #( + .CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT) +) +arp_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(arp_rx_eth_hdr_valid), + .s_eth_hdr_ready(arp_rx_eth_hdr_ready), + .s_eth_dest_mac(arp_rx_eth_dest_mac), + .s_eth_src_mac(arp_rx_eth_src_mac), + .s_eth_type(arp_rx_eth_type), + .s_eth_payload_axis_tdata(arp_rx_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(arp_rx_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(arp_rx_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(arp_rx_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(arp_rx_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(arp_tx_eth_hdr_valid), + .m_eth_hdr_ready(arp_tx_eth_hdr_ready), + .m_eth_dest_mac(arp_tx_eth_dest_mac), + .m_eth_src_mac(arp_tx_eth_src_mac), + .m_eth_type(arp_tx_eth_type), + .m_eth_payload_axis_tdata(arp_tx_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(arp_tx_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(arp_tx_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(arp_tx_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(arp_tx_eth_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_cache(clear_arp_cache) +); + +endmodule diff --git a/fpga/lib/eth/rtl/ip_complete_64.v b/fpga/lib/eth/rtl/ip_complete_64.v new file mode 100644 index 000000000..a6fbd0e2d --- /dev/null +++ b/fpga/lib/eth/rtl/ip_complete_64.v @@ -0,0 +1,456 @@ +/* + +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 1ns / 1ps + +/* + * IPv4 and ARP block, ethernet frame interface (64 bit datapath) + */ +module ip_complete_64 #( + parameter ARP_CACHE_ADDR_WIDTH = 9, + parameter ARP_REQUEST_RETRY_COUNT = 4, + parameter ARP_REQUEST_RETRY_INTERVAL = 156250000*2, + parameter ARP_REQUEST_TIMEOUT = 156250000*30 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire rx_error_invalid_header, + output wire rx_error_invalid_checksum, + output wire tx_error_payload_early_termination, + output wire tx_error_arp_failed, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_arp_cache +); + +/* + +This module integrates the IP and ARP modules for a complete IP stack + +*/ + +wire arp_request_valid; +wire arp_request_ready; +wire [31:0] arp_request_ip; +wire arp_response_valid; +wire arp_response_ready; +wire arp_response_error; +wire [47:0] arp_response_mac; + +wire ip_rx_eth_hdr_valid; +wire ip_rx_eth_hdr_ready; +wire [47:0] ip_rx_eth_dest_mac; +wire [47:0] ip_rx_eth_src_mac; +wire [15:0] ip_rx_eth_type; +wire [63:0] ip_rx_eth_payload_axis_tdata; +wire [7:0] ip_rx_eth_payload_axis_tkeep; +wire ip_rx_eth_payload_axis_tvalid; +wire ip_rx_eth_payload_axis_tready; +wire ip_rx_eth_payload_axis_tlast; +wire ip_rx_eth_payload_axis_tuser; + +wire ip_tx_eth_hdr_valid; +wire ip_tx_eth_hdr_ready; +wire [47:0] ip_tx_eth_dest_mac; +wire [47:0] ip_tx_eth_src_mac; +wire [15:0] ip_tx_eth_type; +wire [63:0] ip_tx_eth_payload_axis_tdata; +wire [7:0] ip_tx_eth_payload_axis_tkeep; +wire ip_tx_eth_payload_axis_tvalid; +wire ip_tx_eth_payload_axis_tready; +wire ip_tx_eth_payload_axis_tlast; +wire ip_tx_eth_payload_axis_tuser; + +wire arp_rx_eth_hdr_valid; +wire arp_rx_eth_hdr_ready; +wire [47:0] arp_rx_eth_dest_mac; +wire [47:0] arp_rx_eth_src_mac; +wire [15:0] arp_rx_eth_type; +wire [63:0] arp_rx_eth_payload_axis_tdata; +wire [7:0] arp_rx_eth_payload_axis_tkeep; +wire arp_rx_eth_payload_axis_tvalid; +wire arp_rx_eth_payload_axis_tready; +wire arp_rx_eth_payload_axis_tlast; +wire arp_rx_eth_payload_axis_tuser; + +wire arp_tx_eth_hdr_valid; +wire arp_tx_eth_hdr_ready; +wire [47:0] arp_tx_eth_dest_mac; +wire [47:0] arp_tx_eth_src_mac; +wire [15:0] arp_tx_eth_type; +wire [63:0] arp_tx_eth_payload_axis_tdata; +wire [7:0] arp_tx_eth_payload_axis_tkeep; +wire arp_tx_eth_payload_axis_tvalid; +wire arp_tx_eth_payload_axis_tready; +wire arp_tx_eth_payload_axis_tlast; +wire arp_tx_eth_payload_axis_tuser; + +/* + * Input classifier (eth_type) + */ +wire s_select_ip = (s_eth_type == 16'h0800); +wire s_select_arp = (s_eth_type == 16'h0806); +wire s_select_none = !(s_select_ip || s_select_arp); + +reg s_select_ip_reg = 1'b0; +reg s_select_arp_reg = 1'b0; +reg s_select_none_reg = 1'b0; + +always @(posedge clk) begin + if (rst) begin + s_select_ip_reg <= 1'b0; + s_select_arp_reg <= 1'b0; + s_select_none_reg <= 1'b0; + end else begin + if (s_eth_payload_axis_tvalid) begin + if ((!s_select_ip_reg && !s_select_arp_reg && !s_select_none_reg) || + (s_eth_payload_axis_tvalid && s_eth_payload_axis_tready && s_eth_payload_axis_tlast)) begin + s_select_ip_reg <= s_select_ip; + s_select_arp_reg <= s_select_arp; + s_select_none_reg <= s_select_none; + end + end else begin + s_select_ip_reg <= 1'b0; + s_select_arp_reg <= 1'b0; + s_select_none_reg <= 1'b0; + end + end +end + +assign ip_rx_eth_hdr_valid = s_select_ip && s_eth_hdr_valid; +assign ip_rx_eth_dest_mac = s_eth_dest_mac; +assign ip_rx_eth_src_mac = s_eth_src_mac; +assign ip_rx_eth_type = 16'h0800; +assign ip_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata; +assign ip_rx_eth_payload_axis_tkeep = s_eth_payload_axis_tkeep; +assign ip_rx_eth_payload_axis_tvalid = s_select_ip_reg && s_eth_payload_axis_tvalid; +assign ip_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast; +assign ip_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser; + +assign arp_rx_eth_hdr_valid = s_select_arp && s_eth_hdr_valid; +assign arp_rx_eth_dest_mac = s_eth_dest_mac; +assign arp_rx_eth_src_mac = s_eth_src_mac; +assign arp_rx_eth_type = 16'h0806; +assign arp_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata; +assign arp_rx_eth_payload_axis_tkeep = s_eth_payload_axis_tkeep; +assign arp_rx_eth_payload_axis_tvalid = s_select_arp_reg && s_eth_payload_axis_tvalid; +assign arp_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast; +assign arp_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser; + +assign s_eth_hdr_ready = (s_select_ip && ip_rx_eth_hdr_ready) || + (s_select_arp && arp_rx_eth_hdr_ready) || + (s_select_none); + +assign s_eth_payload_axis_tready = (s_select_ip_reg && ip_rx_eth_payload_axis_tready) || + (s_select_arp_reg && arp_rx_eth_payload_axis_tready) || + s_select_none_reg; + +/* + * Output arbiter + */ +eth_arb_mux #( + .S_COUNT(2), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +eth_arb_mux_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_eth_hdr_valid({ip_tx_eth_hdr_valid, arp_tx_eth_hdr_valid}), + .s_eth_hdr_ready({ip_tx_eth_hdr_ready, arp_tx_eth_hdr_ready}), + .s_eth_dest_mac({ip_tx_eth_dest_mac, arp_tx_eth_dest_mac}), + .s_eth_src_mac({ip_tx_eth_src_mac, arp_tx_eth_src_mac}), + .s_eth_type({ip_tx_eth_type, arp_tx_eth_type}), + .s_eth_payload_axis_tdata({ip_tx_eth_payload_axis_tdata, arp_tx_eth_payload_axis_tdata}), + .s_eth_payload_axis_tkeep({ip_tx_eth_payload_axis_tkeep, arp_tx_eth_payload_axis_tkeep}), + .s_eth_payload_axis_tvalid({ip_tx_eth_payload_axis_tvalid, arp_tx_eth_payload_axis_tvalid}), + .s_eth_payload_axis_tready({ip_tx_eth_payload_axis_tready, arp_tx_eth_payload_axis_tready}), + .s_eth_payload_axis_tlast({ip_tx_eth_payload_axis_tlast, arp_tx_eth_payload_axis_tlast}), + .s_eth_payload_axis_tid(0), + .s_eth_payload_axis_tdest(0), + .s_eth_payload_axis_tuser({ip_tx_eth_payload_axis_tuser, arp_tx_eth_payload_axis_tuser}), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tid(), + .m_eth_payload_axis_tdest(), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser) +); + +/* + * IP module + */ +ip_64 +ip_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(ip_rx_eth_hdr_valid), + .s_eth_hdr_ready(ip_rx_eth_hdr_ready), + .s_eth_dest_mac(ip_rx_eth_dest_mac), + .s_eth_src_mac(ip_rx_eth_src_mac), + .s_eth_type(ip_rx_eth_type), + .s_eth_payload_axis_tdata(ip_rx_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(ip_rx_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(ip_rx_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(ip_rx_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(ip_rx_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(ip_rx_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(ip_tx_eth_hdr_valid), + .m_eth_hdr_ready(ip_tx_eth_hdr_ready), + .m_eth_dest_mac(ip_tx_eth_dest_mac), + .m_eth_src_mac(ip_tx_eth_src_mac), + .m_eth_type(ip_tx_eth_type), + .m_eth_payload_axis_tdata(ip_tx_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(ip_tx_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(ip_tx_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(ip_tx_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(ip_tx_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(ip_tx_eth_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // Status + .rx_busy(rx_busy), + .tx_busy(tx_busy), + .rx_error_header_early_termination(rx_error_header_early_termination), + .rx_error_payload_early_termination(rx_error_payload_early_termination), + .rx_error_invalid_header(rx_error_invalid_header), + .rx_error_invalid_checksum(rx_error_invalid_checksum), + .tx_error_payload_early_termination(tx_error_payload_early_termination), + .tx_error_arp_failed(tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip) +); + +/* + * ARP module + */ +arp_64 #( + .CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT) +) +arp_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(arp_rx_eth_hdr_valid), + .s_eth_hdr_ready(arp_rx_eth_hdr_ready), + .s_eth_dest_mac(arp_rx_eth_dest_mac), + .s_eth_src_mac(arp_rx_eth_src_mac), + .s_eth_type(arp_rx_eth_type), + .s_eth_payload_axis_tdata(arp_rx_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(arp_rx_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(arp_rx_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(arp_rx_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(arp_rx_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(arp_rx_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(arp_tx_eth_hdr_valid), + .m_eth_hdr_ready(arp_tx_eth_hdr_ready), + .m_eth_dest_mac(arp_tx_eth_dest_mac), + .m_eth_src_mac(arp_tx_eth_src_mac), + .m_eth_type(arp_tx_eth_type), + .m_eth_payload_axis_tdata(arp_tx_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(arp_tx_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(arp_tx_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(arp_tx_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(arp_tx_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(arp_tx_eth_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_cache(clear_arp_cache) +); + +endmodule diff --git a/fpga/lib/eth/rtl/ip_demux.v b/fpga/lib/eth/rtl/ip_demux.v new file mode 100644 index 000000000..b86e60bd9 --- /dev/null +++ b/fpga/lib/eth/rtl/ip_demux.v @@ -0,0 +1,396 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * IP demultiplexer + */ +module ip_demux # +( + parameter M_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [DATA_WIDTH-1:0] s_ip_payload_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire [ID_WIDTH-1:0] s_ip_payload_axis_tid, + input wire [DEST_WIDTH-1:0] s_ip_payload_axis_tdest, + input wire [USER_WIDTH-1:0] s_ip_payload_axis_tuser, + + /* + * IP frame outputs + */ + output wire [M_COUNT-1:0] m_ip_hdr_valid, + input wire [M_COUNT-1:0] m_ip_hdr_ready, + output wire [M_COUNT*48-1:0] m_eth_dest_mac, + output wire [M_COUNT*48-1:0] m_eth_src_mac, + output wire [M_COUNT*16-1:0] m_eth_type, + output wire [M_COUNT*4-1:0] m_ip_version, + output wire [M_COUNT*4-1:0] m_ip_ihl, + output wire [M_COUNT*6-1:0] m_ip_dscp, + output wire [M_COUNT*2-1:0] m_ip_ecn, + output wire [M_COUNT*16-1:0] m_ip_length, + output wire [M_COUNT*16-1:0] m_ip_identification, + output wire [M_COUNT*3-1:0] m_ip_flags, + output wire [M_COUNT*13-1:0] m_ip_fragment_offset, + output wire [M_COUNT*8-1:0] m_ip_ttl, + output wire [M_COUNT*8-1:0] m_ip_protocol, + output wire [M_COUNT*16-1:0] m_ip_header_checksum, + output wire [M_COUNT*32-1:0] m_ip_source_ip, + output wire [M_COUNT*32-1:0] m_ip_dest_ip, + output wire [M_COUNT*DATA_WIDTH-1:0] m_ip_payload_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep, + output wire [M_COUNT-1:0] m_ip_payload_axis_tvalid, + input wire [M_COUNT-1:0] m_ip_payload_axis_tready, + output wire [M_COUNT-1:0] m_ip_payload_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_ip_payload_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_ip_payload_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_ip_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [$clog2(M_COUNT)-1:0] select +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +reg [CL_M_COUNT-1:0] select_reg = {CL_M_COUNT{1'b0}}, select_ctl, select_next; +reg drop_reg = 1'b0, drop_ctl, drop_next; +reg frame_reg = 1'b0, frame_ctl, frame_next; + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; + +reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next; + +reg [M_COUNT-1:0] m_ip_hdr_valid_reg = 0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_int; +reg [M_COUNT-1:0] m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg && enable; + +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg && enable; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = {M_COUNT{m_eth_dest_mac_reg}}; +assign m_eth_src_mac = {M_COUNT{m_eth_src_mac_reg}}; +assign m_eth_type = {M_COUNT{m_eth_type_reg}}; +assign m_ip_version = {M_COUNT{m_ip_version_reg}}; +assign m_ip_ihl = {M_COUNT{m_ip_ihl_reg}}; +assign m_ip_dscp = {M_COUNT{m_ip_dscp_reg}}; +assign m_ip_ecn = {M_COUNT{m_ip_ecn_reg}}; +assign m_ip_length = {M_COUNT{m_ip_length_reg}}; +assign m_ip_identification = {M_COUNT{m_ip_identification_reg}}; +assign m_ip_flags = {M_COUNT{m_ip_flags_reg}}; +assign m_ip_fragment_offset = {M_COUNT{m_ip_fragment_offset_reg}}; +assign m_ip_ttl = {M_COUNT{m_ip_ttl_reg}}; +assign m_ip_protocol = {M_COUNT{m_ip_protocol_reg}}; +assign m_ip_header_checksum = {M_COUNT{m_ip_header_checksum_reg}}; +assign m_ip_source_ip = {M_COUNT{m_ip_source_ip_reg}}; +assign m_ip_dest_ip = {M_COUNT{m_ip_dest_ip_reg}}; + +integer i; + +always @* begin + select_next = select_reg; + select_ctl = select_reg; + drop_next = drop_reg; + drop_ctl = drop_reg; + frame_next = frame_reg; + frame_ctl = frame_reg; + + s_ip_hdr_ready_next = 1'b0; + + s_ip_payload_axis_tready_next = 1'b0; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg & ~m_ip_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + + if (s_ip_payload_axis_tvalid && s_ip_payload_axis_tready) begin + // end of frame detection + if (s_ip_payload_axis_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + + if (!frame_reg && s_ip_hdr_valid && s_ip_hdr_ready) begin + // start of frame, grab select value + select_ctl = select; + drop_ctl = drop; + frame_ctl = 1'b1; + + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = frame_ctl; + + s_ip_hdr_ready_next = 1'b0; + + m_ip_hdr_valid_next = (!drop_ctl) << select_ctl; + m_eth_dest_mac_next = s_eth_dest_mac; + m_eth_src_mac_next = s_eth_src_mac; + m_eth_type_next = s_eth_type; + m_ip_version_next = s_ip_version; + m_ip_ihl_next = s_ip_ihl; + m_ip_dscp_next = s_ip_dscp; + m_ip_ecn_next = s_ip_ecn; + m_ip_length_next = s_ip_length; + m_ip_identification_next = s_ip_identification; + m_ip_flags_next = s_ip_flags; + m_ip_fragment_offset_next = s_ip_fragment_offset; + m_ip_ttl_next = s_ip_ttl; + m_ip_protocol_next = s_ip_protocol; + m_ip_header_checksum_next = s_ip_header_checksum; + m_ip_source_ip_next = s_ip_source_ip; + m_ip_dest_ip_next = s_ip_dest_ip; + end + + s_ip_hdr_ready_next = !frame_next && !m_ip_hdr_valid_next; + + s_ip_payload_axis_tready_next = (m_ip_payload_axis_tready_int_early || drop_ctl) && frame_ctl; + + m_ip_payload_axis_tdata_int = s_ip_payload_axis_tdata; + m_ip_payload_axis_tkeep_int = s_ip_payload_axis_tkeep; + m_ip_payload_axis_tvalid_int = (s_ip_payload_axis_tvalid && s_ip_payload_axis_tready && !drop_ctl) << select_ctl; + m_ip_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_ip_payload_axis_tid_int = s_ip_payload_axis_tid; + m_ip_payload_axis_tdest_int = s_ip_payload_axis_tdest; + m_ip_payload_axis_tuser_int = s_ip_payload_axis_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 2'd0; + drop_reg <= 1'b0; + frame_reg <= 1'b0; + s_ip_hdr_ready_reg <= 1'b0; + s_ip_payload_axis_tready_reg <= 1'b0; + m_ip_hdr_valid_reg <= 0; + end else begin + select_reg <= select_next; + drop_reg <= drop_next; + frame_reg <= frame_next; + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_ip_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] temp_m_ip_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = {M_COUNT{m_ip_payload_axis_tdata_reg}}; +assign m_ip_payload_axis_tkeep = KEEP_ENABLE ? {M_COUNT{m_ip_payload_axis_tkeep_reg}} : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = {M_COUNT{m_ip_payload_axis_tlast_reg}}; +assign m_ip_payload_axis_tid = ID_ENABLE ? {M_COUNT{m_ip_payload_axis_tid_reg}} : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_ip_payload_axis_tdest = DEST_ENABLE ? {M_COUNT{m_ip_payload_axis_tdest_reg}} : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_ip_payload_axis_tuser = USER_ENABLE ? {M_COUNT{m_ip_payload_axis_tuser_reg}} : {M_COUNT*USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = (m_ip_payload_axis_tready & m_ip_payload_axis_tvalid) || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if ((m_ip_payload_axis_tready & m_ip_payload_axis_tvalid) || !m_ip_payload_axis_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready & m_ip_payload_axis_tvalid) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= {M_COUNT{1'b0}}; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tid_reg <= temp_m_ip_payload_axis_tid_reg; + m_ip_payload_axis_tdest_reg <= temp_m_ip_payload_axis_tdest_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + temp_m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/ip_eth_rx.v b/fpga/lib/eth/rtl/ip_eth_rx.v new file mode 100644 index 000000000..5d0579148 --- /dev/null +++ b/fpga/lib/eth/rtl/ip_eth_rx.v @@ -0,0 +1,577 @@ +/* + +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 1ns / 1ps + +/* + * IP ethernet frame receiver (Ethernet frame in, IP frame out) + */ +module ip_eth_rx +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_payload_early_termination, + output wire error_invalid_header, + output wire error_invalid_checksum +); + +/* + +IP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + payload length octets + +This module receives an Ethernet frame with header fields in parallel and +payload on an AXI stream interface, decodes and strips the IP header fields, +then produces the header fields in parallel along with the IP payload in a +separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_READ_PAYLOAD = 3'd2, + STATE_READ_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_eth_hdr; +reg store_ip_version_ihl; +reg store_ip_dscp_ecn; +reg store_ip_length_0; +reg store_ip_length_1; +reg store_ip_identification_0; +reg store_ip_identification_1; +reg store_ip_flags_fragment_offset_0; +reg store_ip_flags_fragment_offset_1; +reg store_ip_ttl; +reg store_ip_protocol; +reg store_ip_header_checksum_0; +reg store_ip_header_checksum_1; +reg store_ip_source_ip_0; +reg store_ip_source_ip_1; +reg store_ip_source_ip_2; +reg store_ip_source_ip_3; +reg store_ip_dest_ip_0; +reg store_ip_dest_ip_1; +reg store_ip_dest_ip_2; +reg store_ip_dest_ip_3; +reg store_last_word; + +reg [5:0] hdr_ptr_reg = 6'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [15:0] hdr_sum_reg = 16'd0, hdr_sum_next; + +reg [7:0] last_word_data_reg = 8'd0; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; +reg error_invalid_header_reg = 1'b0, error_invalid_header_next; +reg error_invalid_checksum_reg = 1'b0, error_invalid_checksum_next; + +// internal datapath +reg [7:0] m_ip_payload_axis_tdata_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; +assign error_invalid_header = error_invalid_header_reg; +assign error_invalid_checksum = error_invalid_checksum_reg; + +function [15:0] add1c16b; + input [15:0] a, b; + reg [16:0] t; + begin + t = a+b; + add1c16b = t[15:0] + t[16]; + end +endfunction + +always @* begin + state_next = STATE_IDLE; + + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b0; + + store_eth_hdr = 1'b0; + store_ip_version_ihl = 1'b0; + store_ip_dscp_ecn = 1'b0; + store_ip_length_0 = 1'b0; + store_ip_length_1 = 1'b0; + store_ip_identification_0 = 1'b0; + store_ip_identification_1 = 1'b0; + store_ip_flags_fragment_offset_0 = 1'b0; + store_ip_flags_fragment_offset_1 = 1'b0; + store_ip_ttl = 1'b0; + store_ip_protocol = 1'b0; + store_ip_header_checksum_0 = 1'b0; + store_ip_header_checksum_1 = 1'b0; + store_ip_source_ip_0 = 1'b0; + store_ip_source_ip_1 = 1'b0; + store_ip_source_ip_2 = 1'b0; + store_ip_source_ip_3 = 1'b0; + store_ip_dest_ip_0 = 1'b0; + store_ip_dest_ip_1 = 1'b0; + store_ip_dest_ip_2 = 1'b0; + store_ip_dest_ip_3 = 1'b0; + + store_last_word = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + hdr_sum_next = hdr_sum_reg; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + + error_header_early_termination_next = 1'b0; + error_payload_early_termination_next = 1'b0; + error_invalid_header_next = 1'b0; + error_invalid_checksum_next = 1'b0; + + m_ip_payload_axis_tdata_int = 8'd0; + m_ip_payload_axis_tvalid_int = 1'b0; + m_ip_payload_axis_tlast_int = 1'b0; + m_ip_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for header + hdr_ptr_next = 16'd0; + hdr_sum_next = 16'd0; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + + if (s_eth_hdr_ready && s_eth_hdr_valid) begin + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b1; + store_eth_hdr = 1'b1; + state_next = STATE_READ_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header + s_eth_payload_axis_tready_next = 1'b1; + word_count_next = m_ip_length_reg - 5*4; + + if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin + // word transfer in - store it + hdr_ptr_next = hdr_ptr_reg + 6'd1; + state_next = STATE_READ_HEADER; + + if (hdr_ptr_reg[0]) begin + hdr_sum_next = add1c16b(hdr_sum_reg, {8'd0, s_eth_payload_axis_tdata}); + end else begin + hdr_sum_next = add1c16b(hdr_sum_reg, {s_eth_payload_axis_tdata, 8'd0}); + end + + case (hdr_ptr_reg) + 6'h00: store_ip_version_ihl = 1'b1; + 6'h01: store_ip_dscp_ecn = 1'b1; + 6'h02: store_ip_length_1 = 1'b1; + 6'h03: store_ip_length_0 = 1'b1; + 6'h04: store_ip_identification_1 = 1'b1; + 6'h05: store_ip_identification_0 = 1'b1; + 6'h06: store_ip_flags_fragment_offset_1 = 1'b1; + 6'h07: store_ip_flags_fragment_offset_0 = 1'b1; + 6'h08: store_ip_ttl = 1'b1; + 6'h09: store_ip_protocol = 1'b1; + 6'h0A: store_ip_header_checksum_1 = 1'b1; + 6'h0B: store_ip_header_checksum_0 = 1'b1; + 6'h0C: store_ip_source_ip_3 = 1'b1; + 6'h0D: store_ip_source_ip_2 = 1'b1; + 6'h0E: store_ip_source_ip_1 = 1'b1; + 6'h0F: store_ip_source_ip_0 = 1'b1; + 6'h10: store_ip_dest_ip_3 = 1'b1; + 6'h11: store_ip_dest_ip_2 = 1'b1; + 6'h12: store_ip_dest_ip_1 = 1'b1; + 6'h13: begin + store_ip_dest_ip_0 = 1'b1; + if (m_ip_version_reg != 4'd4 || m_ip_ihl_reg != 4'd5) begin + error_invalid_header_next = 1'b1; + state_next = STATE_WAIT_LAST; + end else if (hdr_sum_next != 16'hffff) begin + error_invalid_checksum_next = 1'b1; + state_next = STATE_WAIT_LAST; + end else begin + m_ip_hdr_valid_next = 1'b1; + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + state_next = STATE_READ_PAYLOAD; + end + end + endcase + + if (s_eth_payload_axis_tlast) begin + error_header_early_termination_next = 1'b1; + m_ip_hdr_valid_next = 1'b0; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end + + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_READ_PAYLOAD: begin + // read payload + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = s_eth_payload_axis_tdata; + m_ip_payload_axis_tvalid_int = s_eth_payload_axis_tvalid; + m_ip_payload_axis_tlast_int = s_eth_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_eth_payload_axis_tuser; + + if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd1; + if (s_eth_payload_axis_tlast) begin + if (word_count_reg > 16'd1) begin + // end of frame, but length does not match + m_ip_payload_axis_tuser_int = 1'b1; + error_payload_early_termination_next = 1'b1; + end + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + if (word_count_reg == 16'd1) begin + store_last_word = 1'b1; + m_ip_payload_axis_tvalid_int = 1'b0; + state_next = STATE_READ_PAYLOAD_LAST; + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + STATE_READ_PAYLOAD_LAST: begin + // read and discard until end of frame + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = last_word_data_reg; + m_ip_payload_axis_tvalid_int = s_eth_payload_axis_tvalid && s_eth_payload_axis_tlast; + m_ip_payload_axis_tlast_int = s_eth_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_eth_payload_axis_tuser; + + if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin + if (s_eth_payload_axis_tlast) begin + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // read and discard until end of frame + s_eth_payload_axis_tready_next = 1'b1; + + if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin + if (s_eth_payload_axis_tlast) begin + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_eth_hdr_ready_reg <= 1'b0; + s_eth_payload_axis_tready_reg <= 1'b0; + m_ip_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + error_invalid_header_reg <= 1'b0; + error_invalid_checksum_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_payload_early_termination_reg <= error_payload_early_termination_next; + error_invalid_header_reg <= error_invalid_header_next; + error_invalid_checksum_reg <= error_invalid_checksum_next; + + busy_reg <= state_next != STATE_IDLE; + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + hdr_sum_reg <= hdr_sum_next; + + // datapath + if (store_eth_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + end + + if (store_last_word) begin + last_word_data_reg <= m_ip_payload_axis_tdata_int; + end + + if (store_ip_version_ihl) {m_ip_version_reg, m_ip_ihl_reg} <= s_eth_payload_axis_tdata; + if (store_ip_dscp_ecn) {m_ip_dscp_reg, m_ip_ecn_reg} <= s_eth_payload_axis_tdata; + if (store_ip_length_0) m_ip_length_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_ip_length_1) m_ip_length_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_ip_identification_0) m_ip_identification_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_ip_identification_1) m_ip_identification_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_ip_flags_fragment_offset_0) m_ip_fragment_offset_reg[ 7:0] <= s_eth_payload_axis_tdata; + if (store_ip_flags_fragment_offset_1) {m_ip_flags_reg, m_ip_fragment_offset_reg[12:8]} <= s_eth_payload_axis_tdata; + if (store_ip_ttl) m_ip_ttl_reg <= s_eth_payload_axis_tdata; + if (store_ip_protocol) m_ip_protocol_reg <= s_eth_payload_axis_tdata; + if (store_ip_header_checksum_0) m_ip_header_checksum_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_ip_header_checksum_1) m_ip_header_checksum_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_ip_source_ip_0) m_ip_source_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_ip_source_ip_1) m_ip_source_ip_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_ip_source_ip_2) m_ip_source_ip_reg[23:16] <= s_eth_payload_axis_tdata; + if (store_ip_source_ip_3) m_ip_source_ip_reg[31:24] <= s_eth_payload_axis_tdata; + if (store_ip_dest_ip_0) m_ip_dest_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_ip_dest_ip_1) m_ip_dest_ip_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_ip_dest_ip_2) m_ip_dest_ip_reg[23:16] <= s_eth_payload_axis_tdata; + if (store_ip_dest_ip_3) m_ip_dest_ip_reg[31:24] <= s_eth_payload_axis_tdata; +end + +// output datapath logic +reg [7:0] m_ip_payload_axis_tdata_reg = 8'd0; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg m_ip_payload_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_ip_payload_axis_tdata_reg = 8'd0; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg temp_m_ip_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_ip_payload_int_to_output; +reg store_ip_payload_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_ip_payload_int_to_output = 1'b0; + store_ip_payload_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_ip_payload_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_ip_payload_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/ip_eth_rx_64.v b/fpga/lib/eth/rtl/ip_eth_rx_64.v new file mode 100644 index 000000000..47554645c --- /dev/null +++ b/fpga/lib/eth/rtl/ip_eth_rx_64.v @@ -0,0 +1,686 @@ +/* + +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 1ns / 1ps + +/* + * IP ethernet frame receiver (Ethernet frame in, IP frame out, 64 bit datapath) + */ +module ip_eth_rx_64 +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_payload_early_termination, + output wire error_invalid_header, + output wire error_invalid_checksum +); + +/* + +IP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + payload length octets + +This module receives an Ethernet frame with header fields in parallel and +payload on an AXI stream interface, decodes and strips the IP header fields, +then produces the header fields in parallel along with the IP payload in a +separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_READ_PAYLOAD = 3'd2, + STATE_READ_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_eth_hdr; +reg store_hdr_word_0; +reg store_hdr_word_1; +reg store_hdr_word_2; +reg store_last_word; + +reg flush_save; +reg transfer_in_save; + +reg [5:0] hdr_ptr_reg = 6'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [16:0] hdr_sum_high_reg = 17'd0; +reg [16:0] hdr_sum_low_reg = 17'd0; +reg [19:0] hdr_sum_temp; +reg [19:0] hdr_sum_reg = 20'd0, hdr_sum_next; +reg check_hdr_reg = 1'b0, check_hdr_next; + +reg [63:0] last_word_data_reg = 64'd0; +reg [7:0] last_word_keep_reg = 8'd0; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; +reg error_invalid_header_reg = 1'b0, error_invalid_header_next; +reg error_invalid_checksum_reg = 1'b0, error_invalid_checksum_next; + +reg [63:0] save_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] save_eth_payload_axis_tkeep_reg = 8'd0; +reg save_eth_payload_axis_tlast_reg = 1'b0; +reg save_eth_payload_axis_tuser_reg = 1'b0; + +reg [63:0] shift_eth_payload_axis_tdata; +reg [7:0] shift_eth_payload_axis_tkeep; +reg shift_eth_payload_axis_tvalid; +reg shift_eth_payload_axis_tlast; +reg shift_eth_payload_axis_tuser; +reg shift_eth_payload_s_tready; +reg shift_eth_payload_extra_cycle_reg = 1'b0; + +// internal datapath +reg [63:0] m_ip_payload_axis_tdata_int; +reg [7:0] m_ip_payload_axis_tkeep_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; +assign error_invalid_header = error_invalid_header_reg; +assign error_invalid_checksum = error_invalid_checksum_reg; + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +always @* begin + shift_eth_payload_axis_tdata[31:0] = save_eth_payload_axis_tdata_reg[63:32]; + shift_eth_payload_axis_tkeep[3:0] = save_eth_payload_axis_tkeep_reg[7:4]; + + if (shift_eth_payload_extra_cycle_reg) begin + shift_eth_payload_axis_tdata[63:32] = 32'd0; + shift_eth_payload_axis_tkeep[7:4] = 4'd0; + shift_eth_payload_axis_tvalid = 1'b1; + shift_eth_payload_axis_tlast = save_eth_payload_axis_tlast_reg; + shift_eth_payload_axis_tuser = save_eth_payload_axis_tuser_reg; + shift_eth_payload_s_tready = flush_save; + end else begin + shift_eth_payload_axis_tdata[63:32] = s_eth_payload_axis_tdata[31:0]; + shift_eth_payload_axis_tkeep[7:4] = s_eth_payload_axis_tkeep[3:0]; + shift_eth_payload_axis_tvalid = s_eth_payload_axis_tvalid; + shift_eth_payload_axis_tlast = (s_eth_payload_axis_tlast && (s_eth_payload_axis_tkeep[7:4] == 0)); + shift_eth_payload_axis_tuser = (s_eth_payload_axis_tuser && (s_eth_payload_axis_tkeep[7:4] == 0)); + shift_eth_payload_s_tready = !(s_eth_payload_axis_tlast && s_eth_payload_axis_tvalid && transfer_in_save); + end +end + +always @* begin + state_next = STATE_IDLE; + + flush_save = 1'b0; + transfer_in_save = 1'b0; + + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b0; + + store_eth_hdr = 1'b0; + store_hdr_word_0 = 1'b0; + store_hdr_word_1 = 1'b0; + store_hdr_word_2 = 1'b0; + + store_last_word = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + hdr_sum_temp = 32'd0; + hdr_sum_next = hdr_sum_reg; + check_hdr_next = check_hdr_reg; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + + error_header_early_termination_next = 1'b0; + error_payload_early_termination_next = 1'b0; + error_invalid_header_next = 1'b0; + error_invalid_checksum_next = 1'b0; + + m_ip_payload_axis_tdata_int = 64'd0; + m_ip_payload_axis_tkeep_int = 8'd0; + m_ip_payload_axis_tvalid_int = 1'b0; + m_ip_payload_axis_tlast_int = 1'b0; + m_ip_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for header + hdr_ptr_next = 6'd0; + hdr_sum_next = 32'd0; + flush_save = 1'b1; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + + if (s_eth_hdr_ready && s_eth_hdr_valid) begin + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b1; + store_eth_hdr = 1'b1; + state_next = STATE_READ_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header + s_eth_payload_axis_tready_next = shift_eth_payload_s_tready; + word_count_next = m_ip_length_reg - 5*4; + + if (s_eth_payload_axis_tvalid) begin + // word transfer in - store it + hdr_ptr_next = hdr_ptr_reg + 6'd8; + transfer_in_save = 1'b1; + state_next = STATE_READ_HEADER; + + case (hdr_ptr_reg) + 6'h00: begin + store_hdr_word_0 = 1'b1; + end + 6'h08: begin + store_hdr_word_1 = 1'b1; + hdr_sum_next = hdr_sum_high_reg + hdr_sum_low_reg; + end + 6'h10: begin + store_hdr_word_2 = 1'b1; + hdr_sum_next = hdr_sum_reg + hdr_sum_high_reg + hdr_sum_low_reg; + + // check header checksum on next cycle for improved timing + check_hdr_next = 1'b1; + + if (m_ip_version_reg != 4'd4 || m_ip_ihl_reg != 4'd5) begin + error_invalid_header_next = 1'b1; + s_eth_payload_axis_tready_next = shift_eth_payload_s_tready; + state_next = STATE_WAIT_LAST; + end else begin + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early && shift_eth_payload_s_tready; + state_next = STATE_READ_PAYLOAD; + end + end + endcase + + if (shift_eth_payload_axis_tlast) begin + error_header_early_termination_next = 1'b1; + error_invalid_header_next = 1'b0; + error_invalid_checksum_next = 1'b0; + m_ip_hdr_valid_next = 1'b0; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end + + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_READ_PAYLOAD: begin + // read payload + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early && shift_eth_payload_s_tready; + + m_ip_payload_axis_tdata_int = shift_eth_payload_axis_tdata; + m_ip_payload_axis_tkeep_int = shift_eth_payload_axis_tkeep; + m_ip_payload_axis_tvalid_int = shift_eth_payload_axis_tvalid; + m_ip_payload_axis_tlast_int = shift_eth_payload_axis_tlast; + m_ip_payload_axis_tuser_int = shift_eth_payload_axis_tuser; + + store_last_word = 1'b1; + + if (m_ip_payload_axis_tready_int_reg && shift_eth_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd8; + transfer_in_save = 1'b1; + if (word_count_reg <= 8) begin + // have entire payload + m_ip_payload_axis_tkeep_int = shift_eth_payload_axis_tkeep & count2keep(word_count_reg); + if (shift_eth_payload_axis_tlast) begin + if (keep2count(shift_eth_payload_axis_tkeep) < word_count_reg[4:0]) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_ip_payload_axis_tuser_int = 1'b1; + end + s_eth_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_eth_hdr_ready_next = !m_ip_hdr_valid_reg && !check_hdr_reg; + state_next = STATE_IDLE; + end else begin + m_ip_payload_axis_tvalid_int = 1'b0; + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + if (shift_eth_payload_axis_tlast) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_ip_payload_axis_tuser_int = 1'b1; + s_eth_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_eth_hdr_ready_next = !m_ip_hdr_valid_reg && !check_hdr_reg; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + end else begin + state_next = STATE_READ_PAYLOAD; + end + + if (check_hdr_reg) begin + check_hdr_next = 1'b0; + + hdr_sum_temp = hdr_sum_reg[15:0] + hdr_sum_reg[19:16] + hdr_sum_low_reg; + + if (hdr_sum_temp != 19'h0ffff && hdr_sum_temp != 19'h1fffe) begin + // bad checksum + error_invalid_checksum_next = 1'b1; + m_ip_payload_axis_tvalid_int = 1'b0; + if (shift_eth_payload_axis_tlast && shift_eth_payload_axis_tvalid) begin + // only one payload cycle; return to idle now + s_eth_hdr_ready_next = !m_ip_hdr_valid_reg && !check_hdr_reg; + state_next = STATE_IDLE; + end else begin + // drop payload + s_eth_payload_axis_tready_next = shift_eth_payload_s_tready; + state_next = STATE_WAIT_LAST; + end + end else begin + // good checksum; transfer header + m_ip_hdr_valid_next = 1'b1; + end + end + end + STATE_READ_PAYLOAD_LAST: begin + // read and discard until end of frame + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early && shift_eth_payload_s_tready; + + m_ip_payload_axis_tdata_int = last_word_data_reg; + m_ip_payload_axis_tkeep_int = last_word_keep_reg; + m_ip_payload_axis_tvalid_int = shift_eth_payload_axis_tvalid && shift_eth_payload_axis_tlast; + m_ip_payload_axis_tlast_int = shift_eth_payload_axis_tlast; + m_ip_payload_axis_tuser_int = shift_eth_payload_axis_tuser; + + if (m_ip_payload_axis_tready_int_reg && shift_eth_payload_axis_tvalid) begin + transfer_in_save = 1'b1; + if (shift_eth_payload_axis_tlast) begin + s_eth_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // read and discard until end of frame + s_eth_payload_axis_tready_next = shift_eth_payload_s_tready; + + if (shift_eth_payload_axis_tvalid) begin + transfer_in_save = 1'b1; + if (shift_eth_payload_axis_tlast) begin + s_eth_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_eth_hdr_ready_reg <= 1'b0; + s_eth_payload_axis_tready_reg <= 1'b0; + m_ip_hdr_valid_reg <= 1'b0; + save_eth_payload_axis_tlast_reg <= 1'b0; + shift_eth_payload_extra_cycle_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + error_invalid_header_reg <= 1'b0; + error_invalid_checksum_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_payload_early_termination_reg <= error_payload_early_termination_next; + error_invalid_header_reg <= error_invalid_header_next; + error_invalid_checksum_reg <= error_invalid_checksum_next; + + busy_reg <= state_next != STATE_IDLE; + + // datapath + if (flush_save) begin + save_eth_payload_axis_tlast_reg <= 1'b0; + shift_eth_payload_extra_cycle_reg <= 1'b0; + end else if (transfer_in_save) begin + save_eth_payload_axis_tlast_reg <= s_eth_payload_axis_tlast; + shift_eth_payload_extra_cycle_reg <= s_eth_payload_axis_tlast && (s_eth_payload_axis_tkeep[7:4] != 0); + end + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + hdr_sum_reg <= hdr_sum_next; + check_hdr_reg <= check_hdr_next; + + if (s_eth_payload_axis_tvalid) begin + hdr_sum_low_reg <= s_eth_payload_axis_tdata[15:0] + s_eth_payload_axis_tdata[31:16]; + hdr_sum_high_reg <= s_eth_payload_axis_tdata[47:32] + s_eth_payload_axis_tdata[63:48]; + end + + // datapath + if (store_eth_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + end + + if (store_last_word) begin + last_word_data_reg <= m_ip_payload_axis_tdata_int; + last_word_keep_reg <= m_ip_payload_axis_tkeep_int; + end + + if (store_hdr_word_0) begin + {m_ip_version_reg, m_ip_ihl_reg} <= s_eth_payload_axis_tdata[ 7: 0]; + {m_ip_dscp_reg, m_ip_ecn_reg} <= s_eth_payload_axis_tdata[15: 8]; + m_ip_length_reg[15: 8] <= s_eth_payload_axis_tdata[23:16]; + m_ip_length_reg[ 7: 0] <= s_eth_payload_axis_tdata[31:24]; + m_ip_identification_reg[15: 8] <= s_eth_payload_axis_tdata[39:32]; + m_ip_identification_reg[ 7: 0] <= s_eth_payload_axis_tdata[47:40]; + {m_ip_flags_reg, m_ip_fragment_offset_reg[12:8]} <= s_eth_payload_axis_tdata[55:48]; + m_ip_fragment_offset_reg[ 7:0] <= s_eth_payload_axis_tdata[63:56]; + end + + if (store_hdr_word_1) begin + m_ip_ttl_reg <= s_eth_payload_axis_tdata[ 7: 0]; + m_ip_protocol_reg <= s_eth_payload_axis_tdata[15: 8]; + m_ip_header_checksum_reg[15: 8] <= s_eth_payload_axis_tdata[23:16]; + m_ip_header_checksum_reg[ 7: 0] <= s_eth_payload_axis_tdata[31:24]; + m_ip_source_ip_reg[31:24] <= s_eth_payload_axis_tdata[39:32]; + m_ip_source_ip_reg[23:16] <= s_eth_payload_axis_tdata[47:40]; + m_ip_source_ip_reg[15: 8] <= s_eth_payload_axis_tdata[55:48]; + m_ip_source_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata[63:56]; + end + + if (store_hdr_word_2) begin + m_ip_dest_ip_reg[31:24] <= s_eth_payload_axis_tdata[ 7: 0]; + m_ip_dest_ip_reg[23:16] <= s_eth_payload_axis_tdata[15: 8]; + m_ip_dest_ip_reg[15: 8] <= s_eth_payload_axis_tdata[23:16]; + m_ip_dest_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata[31:24]; + end + + if (transfer_in_save) begin + save_eth_payload_axis_tdata_reg <= s_eth_payload_axis_tdata; + save_eth_payload_axis_tkeep_reg <= s_eth_payload_axis_tkeep; + save_eth_payload_axis_tuser_reg <= s_eth_payload_axis_tuser; + end +end + +// output datapath logic +reg [63:0] m_ip_payload_axis_tdata_reg = 64'd0; +reg [7:0] m_ip_payload_axis_tkeep_reg = 8'd0; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg m_ip_payload_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_ip_payload_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_ip_payload_axis_tkeep_reg = 8'd0; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg temp_m_ip_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_ip_payload_int_to_output; +reg store_ip_payload_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tkeep = m_ip_payload_axis_tkeep_reg; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_ip_payload_int_to_output = 1'b0; + store_ip_payload_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_ip_payload_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_ip_payload_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/ip_eth_tx.v b/fpga/lib/eth/rtl/ip_eth_tx.v new file mode 100644 index 000000000..14619f88f --- /dev/null +++ b/fpga/lib/eth/rtl/ip_eth_tx.v @@ -0,0 +1,497 @@ +/* + +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 1ns / 1ps + +/* + * IP ethernet frame transmitter (IP frame in, Ethernet frame out) + */ +module ip_eth_tx +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_payload_early_termination +); + +/* + +IP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + payload length octets + +This module receives an IP frame with header fields in parallel along with the +payload in an AXI stream, combines the header with the payload, passes through +the Ethernet headers, and transmits the complete Ethernet payload on an AXI +interface. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_WRITE_HEADER = 3'd1, + STATE_WRITE_PAYLOAD = 3'd2, + STATE_WRITE_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_ip_hdr; +reg store_last_word; + +reg [5:0] hdr_ptr_reg = 6'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [15:0] hdr_sum_reg = 16'd0, hdr_sum_next; + +reg [7:0] last_word_data_reg = 8'd0; + +reg [5:0] ip_dscp_reg = 6'd0; +reg [1:0] ip_ecn_reg = 2'd0; +reg [15:0] ip_length_reg = 16'd0; +reg [15:0] ip_identification_reg = 16'd0; +reg [2:0] ip_flags_reg = 3'd0; +reg [12:0] ip_fragment_offset_reg = 13'd0; +reg [7:0] ip_ttl_reg = 8'd0; +reg [7:0] ip_protocol_reg = 8'd0; +reg [31:0] ip_source_ip_reg = 32'd0; +reg [31:0] ip_dest_ip_reg = 32'd0; + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; +reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; + +reg busy_reg = 1'b0; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +// internal datapath +reg [7:0] m_eth_payload_axis_tdata_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +assign busy = busy_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +function [15:0] add1c16b; + input [15:0] a, b; + reg [16:0] t; + begin + t = a+b; + add1c16b = t[15:0] + t[16]; + end +endfunction + +always @* begin + state_next = STATE_IDLE; + + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b0; + + store_ip_hdr = 1'b0; + + store_last_word = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + hdr_sum_next = hdr_sum_reg; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + + error_payload_early_termination_next = 1'b0; + + m_eth_payload_axis_tdata_int = 8'd0; + m_eth_payload_axis_tvalid_int = 1'b0; + m_eth_payload_axis_tlast_int = 1'b0; + m_eth_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + hdr_ptr_next = 6'd0; + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + + if (s_ip_hdr_ready && s_ip_hdr_valid) begin + store_ip_hdr = 1'b1; + s_ip_hdr_ready_next = 1'b0; + m_eth_hdr_valid_next = 1'b1; + if (m_eth_payload_axis_tready_int_reg) begin + m_eth_payload_axis_tvalid_int = 1'b1; + m_eth_payload_axis_tdata_int = {4'd4, 4'd5}; // ip_version, ip_ihl + hdr_ptr_next = 6'd1; + end + state_next = STATE_WRITE_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // write header + word_count_next = ip_length_reg - 5*4; + + if (m_eth_payload_axis_tready_int_reg) begin + hdr_ptr_next = hdr_ptr_reg + 6'd1; + m_eth_payload_axis_tvalid_int = 1; + state_next = STATE_WRITE_HEADER; + case (hdr_ptr_reg) + 6'h00: begin + m_eth_payload_axis_tdata_int = {4'd4, 4'd5}; // ip_version, ip_ihl + end + 6'h01: begin + m_eth_payload_axis_tdata_int = {ip_dscp_reg, ip_ecn_reg}; + hdr_sum_next = {4'd4, 4'd5, ip_dscp_reg, ip_ecn_reg}; + end + 6'h02: begin + m_eth_payload_axis_tdata_int = ip_length_reg[15: 8]; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_length_reg); + end + 6'h03: begin + m_eth_payload_axis_tdata_int = ip_length_reg[ 7: 0]; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_identification_reg); + end + 6'h04: begin + m_eth_payload_axis_tdata_int = ip_identification_reg[15: 8]; + hdr_sum_next = add1c16b(hdr_sum_reg, {ip_flags_reg, ip_fragment_offset_reg}); + end + 6'h05: begin + m_eth_payload_axis_tdata_int = ip_identification_reg[ 7: 0]; + hdr_sum_next = add1c16b(hdr_sum_reg, {ip_ttl_reg, ip_protocol_reg}); + end + 6'h06: begin + m_eth_payload_axis_tdata_int = {ip_flags_reg, ip_fragment_offset_reg[12:8]}; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_source_ip_reg[31:16]); + end + 6'h07: begin + m_eth_payload_axis_tdata_int = ip_fragment_offset_reg[ 7: 0]; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_source_ip_reg[15:0]); + end + 6'h08: begin + m_eth_payload_axis_tdata_int = ip_ttl_reg; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_dest_ip_reg[31:16]); + end + 6'h09: begin + m_eth_payload_axis_tdata_int = ip_protocol_reg; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_dest_ip_reg[15:0]); + end + 6'h0A: m_eth_payload_axis_tdata_int = ~hdr_sum_reg[15: 8]; + 6'h0B: m_eth_payload_axis_tdata_int = ~hdr_sum_reg[ 7: 0]; + 6'h0C: m_eth_payload_axis_tdata_int = ip_source_ip_reg[31:24]; + 6'h0D: m_eth_payload_axis_tdata_int = ip_source_ip_reg[23:16]; + 6'h0E: m_eth_payload_axis_tdata_int = ip_source_ip_reg[15: 8]; + 6'h0F: m_eth_payload_axis_tdata_int = ip_source_ip_reg[ 7: 0]; + 6'h10: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[31:24]; + 6'h11: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[23:16]; + 6'h12: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[15: 8]; + 6'h13: begin + m_eth_payload_axis_tdata_int = ip_dest_ip_reg[ 7: 0]; + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early; + state_next = STATE_WRITE_PAYLOAD; + end + endcase + end else begin + state_next = STATE_WRITE_HEADER; + end + end + STATE_WRITE_PAYLOAD: begin + // write payload + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early; + + m_eth_payload_axis_tdata_int = s_ip_payload_axis_tdata; + m_eth_payload_axis_tvalid_int = s_ip_payload_axis_tvalid; + m_eth_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_eth_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 6'd1; + if (s_ip_payload_axis_tlast) begin + if (word_count_reg != 16'd1) begin + // end of frame, but length does not match + m_eth_payload_axis_tuser_int = 1'b1; + error_payload_early_termination_next = 1'b1; + end + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + if (word_count_reg == 16'd1) begin + store_last_word = 1'b1; + m_eth_payload_axis_tvalid_int = 1'b0; + state_next = STATE_WRITE_PAYLOAD_LAST; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + STATE_WRITE_PAYLOAD_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early; + + m_eth_payload_axis_tdata_int = last_word_data_reg; + m_eth_payload_axis_tvalid_int = s_ip_payload_axis_tvalid && s_ip_payload_axis_tlast; + m_eth_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_eth_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = 1'b1; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_ip_hdr_ready_reg <= 1'b0; + s_ip_payload_axis_tready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + + error_payload_early_termination_reg <= error_payload_early_termination_next; + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + hdr_sum_reg <= hdr_sum_next; + + // datapath + if (store_ip_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + ip_dscp_reg <= s_ip_dscp; + ip_ecn_reg <= s_ip_ecn; + ip_length_reg <= s_ip_length; + ip_identification_reg <= s_ip_identification; + ip_flags_reg <= s_ip_flags; + ip_fragment_offset_reg <= s_ip_fragment_offset; + ip_ttl_reg <= s_ip_ttl; + ip_protocol_reg <= s_ip_protocol; + ip_source_ip_reg <= s_ip_source_ip; + ip_dest_ip_reg <= s_ip_dest_ip; + end + + if (store_last_word) begin + last_word_data_reg <= m_eth_payload_axis_tdata_int; + end +end + +// output datapath logic +reg [7:0] m_eth_payload_axis_tdata_reg = 8'd0; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg m_eth_payload_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_eth_payload_axis_tdata_reg = 8'd0; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg temp_m_eth_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_eth_payload_int_to_output; +reg store_eth_payload_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_eth_payload_int_to_output = 1'b0; + store_eth_payload_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_eth_payload_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_eth_payload_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/ip_eth_tx_64.v b/fpga/lib/eth/rtl/ip_eth_tx_64.v new file mode 100644 index 000000000..471cf239f --- /dev/null +++ b/fpga/lib/eth/rtl/ip_eth_tx_64.v @@ -0,0 +1,648 @@ +/* + +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 1ns / 1ps + +/* + * IP ethernet frame transmitter (IP frame in, Ethernet frame out, 64 bit datapath) + */ +module ip_eth_tx_64 +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_payload_early_termination +); + +/* + +IP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + payload length octets + +This module receives an IP frame with header fields in parallel along with the +payload in an AXI stream, combines the header with the payload, passes through +the Ethernet headers, and transmits the complete Ethernet payload on an AXI +interface. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_WRITE_HEADER = 3'd1, + STATE_WRITE_HEADER_LAST = 3'd2, + STATE_WRITE_PAYLOAD = 3'd3, + STATE_WRITE_PAYLOAD_LAST = 3'd4, + STATE_WAIT_LAST = 3'd5; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_ip_hdr; +reg store_last_word; + +reg [5:0] hdr_ptr_reg = 6'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg flush_save; +reg transfer_in_save; + +reg [19:0] hdr_sum_temp; +reg [19:0] hdr_sum_reg = 20'd0, hdr_sum_next; + +reg [63:0] last_word_data_reg = 64'd0; +reg [7:0] last_word_keep_reg = 8'd0; + +reg [5:0] ip_dscp_reg = 6'd0; +reg [1:0] ip_ecn_reg = 2'd0; +reg [15:0] ip_length_reg = 16'd0; +reg [15:0] ip_identification_reg = 16'd0; +reg [2:0] ip_flags_reg = 3'd0; +reg [12:0] ip_fragment_offset_reg = 13'd0; +reg [7:0] ip_ttl_reg = 8'd0; +reg [7:0] ip_protocol_reg = 8'd0; +reg [31:0] ip_source_ip_reg = 32'd0; +reg [31:0] ip_dest_ip_reg = 32'd0; + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; +reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; + +reg busy_reg = 1'b0; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +reg [63:0] save_ip_payload_axis_tdata_reg = 64'd0; +reg [7:0] save_ip_payload_axis_tkeep_reg = 8'd0; +reg save_ip_payload_axis_tlast_reg = 1'b0; +reg save_ip_payload_axis_tuser_reg = 1'b0; + +reg [63:0] shift_ip_payload_axis_tdata; +reg [7:0] shift_ip_payload_axis_tkeep; +reg shift_ip_payload_axis_tvalid; +reg shift_ip_payload_axis_tlast; +reg shift_ip_payload_axis_tuser; +reg shift_ip_payload_s_tready; +reg shift_ip_payload_extra_cycle_reg = 1'b0; + +// internal datapath +reg [63:0] m_eth_payload_axis_tdata_int; +reg [7:0] m_eth_payload_axis_tkeep_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +assign busy = busy_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +always @* begin + shift_ip_payload_axis_tdata[31:0] = save_ip_payload_axis_tdata_reg[63:32]; + shift_ip_payload_axis_tkeep[3:0] = save_ip_payload_axis_tkeep_reg[7:4]; + + if (shift_ip_payload_extra_cycle_reg) begin + shift_ip_payload_axis_tdata[63:32] = 32'd0; + shift_ip_payload_axis_tkeep[7:4] = 4'd0; + shift_ip_payload_axis_tvalid = 1'b1; + shift_ip_payload_axis_tlast = save_ip_payload_axis_tlast_reg; + shift_ip_payload_axis_tuser = save_ip_payload_axis_tuser_reg; + shift_ip_payload_s_tready = flush_save; + end else begin + shift_ip_payload_axis_tdata[63:32] = s_ip_payload_axis_tdata[31:0]; + shift_ip_payload_axis_tkeep[7:4] = s_ip_payload_axis_tkeep[3:0]; + shift_ip_payload_axis_tvalid = s_ip_payload_axis_tvalid; + shift_ip_payload_axis_tlast = (s_ip_payload_axis_tlast && (s_ip_payload_axis_tkeep[7:4] == 0)); + shift_ip_payload_axis_tuser = (s_ip_payload_axis_tuser && (s_ip_payload_axis_tkeep[7:4] == 0)); + shift_ip_payload_s_tready = !(s_ip_payload_axis_tlast && s_ip_payload_axis_tvalid && transfer_in_save) && !save_ip_payload_axis_tlast_reg; + end +end + +always @* begin + state_next = STATE_IDLE; + + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b0; + + store_ip_hdr = 1'b0; + + store_last_word = 1'b0; + + flush_save = 1'b0; + transfer_in_save = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + hdr_sum_temp = 20'd0; + hdr_sum_next = hdr_sum_reg; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + + error_payload_early_termination_next = 1'b0; + + m_eth_payload_axis_tdata_int = 1'b0; + m_eth_payload_axis_tkeep_int = 1'b0; + m_eth_payload_axis_tvalid_int = 1'b0; + m_eth_payload_axis_tlast_int = 1'b0; + m_eth_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + hdr_ptr_next = 6'd0; + flush_save = 1'b1; + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + + if (s_ip_hdr_ready && s_ip_hdr_valid) begin + store_ip_hdr = 1'b1; + hdr_sum_next = {4'd4, 4'd5, s_ip_dscp, s_ip_ecn} + + s_ip_length + + s_ip_identification + + {s_ip_flags, s_ip_fragment_offset} + + {s_ip_ttl, s_ip_protocol} + + s_ip_source_ip[31:16] + + s_ip_source_ip[15: 0] + + s_ip_dest_ip[31:16] + + s_ip_dest_ip[15: 0]; + s_ip_hdr_ready_next = 1'b0; + m_eth_hdr_valid_next = 1'b1; + if (m_eth_payload_axis_tready_int_reg) begin + m_eth_payload_axis_tvalid_int = 1'b1; + m_eth_payload_axis_tdata_int[ 7: 0] = {4'd4, 4'd5}; // ip_version, ip_ihl + m_eth_payload_axis_tdata_int[15: 8] = {s_ip_dscp, s_ip_ecn}; + m_eth_payload_axis_tdata_int[23:16] = s_ip_length[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = s_ip_length[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = s_ip_identification[15: 8]; + m_eth_payload_axis_tdata_int[47:40] = s_ip_identification[ 7: 0]; + m_eth_payload_axis_tdata_int[55:48] = {s_ip_flags, s_ip_fragment_offset[12: 8]}; + m_eth_payload_axis_tdata_int[63:56] = s_ip_fragment_offset[ 7: 0]; + m_eth_payload_axis_tkeep_int = 8'hff; + hdr_ptr_next = 6'd8; + end + state_next = STATE_WRITE_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // write header + word_count_next = ip_length_reg - 5*4 + 4; + + if (m_eth_payload_axis_tready_int_reg) begin + hdr_ptr_next = hdr_ptr_reg + 6'd8; + m_eth_payload_axis_tvalid_int = 1'b1; + state_next = STATE_WRITE_HEADER; + case (hdr_ptr_reg) + 6'h00: begin + m_eth_payload_axis_tdata_int[ 7: 0] = {4'd4, 4'd5}; // ip_version, ip_ihl + m_eth_payload_axis_tdata_int[15: 8] = {ip_dscp_reg, ip_ecn_reg}; + m_eth_payload_axis_tdata_int[23:16] = ip_length_reg[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = ip_length_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = ip_identification_reg[15: 8]; + m_eth_payload_axis_tdata_int[47:40] = ip_identification_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[55:48] = {ip_flags_reg, ip_fragment_offset_reg[12: 8]}; + m_eth_payload_axis_tdata_int[63:56] = ip_fragment_offset_reg[ 7: 0]; + m_eth_payload_axis_tkeep_int = 8'hff; + end + 6'h08: begin + hdr_sum_temp = hdr_sum_reg[15:0] + hdr_sum_reg[19:16]; + hdr_sum_temp = hdr_sum_temp[15:0] + hdr_sum_temp[16]; + m_eth_payload_axis_tdata_int[ 7: 0] = ip_ttl_reg; + m_eth_payload_axis_tdata_int[15: 8] = ip_protocol_reg; + m_eth_payload_axis_tdata_int[23:16] = ~hdr_sum_temp[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = ~hdr_sum_temp[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = ip_source_ip_reg[31:24]; + m_eth_payload_axis_tdata_int[47:40] = ip_source_ip_reg[23:16]; + m_eth_payload_axis_tdata_int[55:48] = ip_source_ip_reg[15: 8]; + m_eth_payload_axis_tdata_int[63:56] = ip_source_ip_reg[ 7: 0]; + m_eth_payload_axis_tkeep_int = 8'hff; + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early; + state_next = STATE_WRITE_HEADER_LAST; + end + endcase + end else begin + state_next = STATE_WRITE_HEADER; + end + end + STATE_WRITE_HEADER_LAST: begin + // last header word requires first payload word; process accordingly + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early && shift_ip_payload_s_tready; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + m_eth_payload_axis_tvalid_int = 1'b1; + transfer_in_save = 1'b1; + + m_eth_payload_axis_tdata_int[ 7: 0] = ip_dest_ip_reg[31:24]; + m_eth_payload_axis_tdata_int[15: 8] = ip_dest_ip_reg[23:16]; + m_eth_payload_axis_tdata_int[23:16] = ip_dest_ip_reg[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = ip_dest_ip_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = shift_ip_payload_axis_tdata[39:32]; + m_eth_payload_axis_tdata_int[47:40] = shift_ip_payload_axis_tdata[47:40]; + m_eth_payload_axis_tdata_int[55:48] = shift_ip_payload_axis_tdata[55:48]; + m_eth_payload_axis_tdata_int[63:56] = shift_ip_payload_axis_tdata[63:56]; + m_eth_payload_axis_tkeep_int = {shift_ip_payload_axis_tkeep[7:4], 4'hF}; + m_eth_payload_axis_tlast_int = shift_ip_payload_axis_tlast; + m_eth_payload_axis_tuser_int = shift_ip_payload_axis_tuser; + word_count_next = word_count_reg - 16'd8; + + if (keep2count(m_eth_payload_axis_tkeep_int) >= word_count_reg) begin + // have entire payload + m_eth_payload_axis_tkeep_int = count2keep(word_count_reg); + if (shift_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + store_last_word = 1'b1; + s_ip_payload_axis_tready_next = shift_ip_payload_s_tready; + m_eth_payload_axis_tvalid_int = 1'b0; + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + if (shift_ip_payload_axis_tlast) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + s_ip_payload_axis_tready_next = shift_ip_payload_s_tready; + m_eth_payload_axis_tuser_int = 1'b1; + state_next = STATE_WAIT_LAST; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + end else begin + state_next = STATE_WRITE_HEADER_LAST; + end + end + STATE_WRITE_PAYLOAD: begin + // write payload + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early && shift_ip_payload_s_tready; + + m_eth_payload_axis_tdata_int = shift_ip_payload_axis_tdata; + m_eth_payload_axis_tkeep_int = shift_ip_payload_axis_tkeep; + m_eth_payload_axis_tvalid_int = shift_ip_payload_axis_tvalid; + m_eth_payload_axis_tlast_int = shift_ip_payload_axis_tlast; + m_eth_payload_axis_tuser_int = shift_ip_payload_axis_tuser; + + store_last_word = 1'b1; + + if (m_eth_payload_axis_tready_int_reg && shift_ip_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd8; + transfer_in_save = 1'b1; + if (word_count_reg <= 8) begin + // have entire payload + m_eth_payload_axis_tkeep_int = count2keep(word_count_reg); + if (shift_ip_payload_axis_tlast) begin + if (keep2count(shift_ip_payload_axis_tkeep) < word_count_reg[4:0]) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_eth_payload_axis_tuser_int = 1'b1; + end + s_ip_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + m_eth_payload_axis_tvalid_int = 1'b0; + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + if (shift_ip_payload_axis_tlast) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_eth_payload_axis_tuser_int = 1'b1; + s_ip_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + STATE_WRITE_PAYLOAD_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early && shift_ip_payload_s_tready; + + m_eth_payload_axis_tdata_int = last_word_data_reg; + m_eth_payload_axis_tkeep_int = last_word_keep_reg; + m_eth_payload_axis_tvalid_int = shift_ip_payload_axis_tvalid && shift_ip_payload_axis_tlast; + m_eth_payload_axis_tlast_int = shift_ip_payload_axis_tlast; + m_eth_payload_axis_tuser_int = shift_ip_payload_axis_tuser; + + if (m_eth_payload_axis_tready_int_reg && shift_ip_payload_axis_tvalid) begin + transfer_in_save = 1'b1; + if (shift_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = shift_ip_payload_s_tready; + + if (shift_ip_payload_axis_tvalid) begin + transfer_in_save = 1'b1; + if (shift_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_ip_hdr_ready_reg <= 1'b0; + s_ip_payload_axis_tready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + save_ip_payload_axis_tlast_reg <= 1'b0; + shift_ip_payload_extra_cycle_reg <= 1'b0; + busy_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + + error_payload_early_termination_reg <= error_payload_early_termination_next; + + if (flush_save) begin + save_ip_payload_axis_tlast_reg <= 1'b0; + shift_ip_payload_extra_cycle_reg <= 1'b0; + end else if (transfer_in_save) begin + save_ip_payload_axis_tlast_reg <= s_ip_payload_axis_tlast; + shift_ip_payload_extra_cycle_reg <= s_ip_payload_axis_tlast && (s_ip_payload_axis_tkeep[7:4] != 0); + end + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + hdr_sum_reg <= hdr_sum_next; + + // datapath + if (store_ip_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + ip_dscp_reg <= s_ip_dscp; + ip_ecn_reg <= s_ip_ecn; + ip_length_reg <= s_ip_length; + ip_identification_reg <= s_ip_identification; + ip_flags_reg <= s_ip_flags; + ip_fragment_offset_reg <= s_ip_fragment_offset; + ip_ttl_reg <= s_ip_ttl; + ip_protocol_reg <= s_ip_protocol; + ip_source_ip_reg <= s_ip_source_ip; + ip_dest_ip_reg <= s_ip_dest_ip; + end + + if (store_last_word) begin + last_word_data_reg <= m_eth_payload_axis_tdata_int; + last_word_keep_reg <= m_eth_payload_axis_tkeep_int; + end + + if (transfer_in_save) begin + save_ip_payload_axis_tdata_reg <= s_ip_payload_axis_tdata; + save_ip_payload_axis_tkeep_reg <= s_ip_payload_axis_tkeep; + save_ip_payload_axis_tuser_reg <= s_ip_payload_axis_tuser; + end +end + +// output datapath logic +reg [63:0] m_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] m_eth_payload_axis_tkeep_reg = 8'd0; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg m_eth_payload_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_eth_payload_axis_tkeep_reg = 8'd0; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg temp_m_eth_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_eth_payload_int_to_output; +reg store_eth_payload_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tkeep = m_eth_payload_axis_tkeep_reg; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready | (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg | !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_eth_payload_int_to_output = 1'b0; + store_eth_payload_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready | !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_eth_payload_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_eth_payload_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/ip_mux.v b/fpga/lib/eth/rtl/ip_mux.v new file mode 100644 index 000000000..f05edf6b3 --- /dev/null +++ b/fpga/lib/eth/rtl/ip_mux.v @@ -0,0 +1,390 @@ +/* + +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 1ns / 1ps + +/* + * IP multiplexer + */ +module ip_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * IP frame inputs + */ + input wire [S_COUNT-1:0] s_ip_hdr_valid, + output wire [S_COUNT-1:0] s_ip_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*4-1:0] s_ip_version, + input wire [S_COUNT*4-1:0] s_ip_ihl, + input wire [S_COUNT*6-1:0] s_ip_dscp, + input wire [S_COUNT*2-1:0] s_ip_ecn, + input wire [S_COUNT*16-1:0] s_ip_length, + input wire [S_COUNT*16-1:0] s_ip_identification, + input wire [S_COUNT*3-1:0] s_ip_flags, + input wire [S_COUNT*13-1:0] s_ip_fragment_offset, + input wire [S_COUNT*8-1:0] s_ip_ttl, + input wire [S_COUNT*8-1:0] s_ip_protocol, + input wire [S_COUNT*16-1:0] s_ip_header_checksum, + input wire [S_COUNT*32-1:0] s_ip_source_ip, + input wire [S_COUNT*32-1:0] s_ip_dest_ip, + input wire [S_COUNT*DATA_WIDTH-1:0] s_ip_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_ip_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_ip_payload_axis_tready, + input wire [S_COUNT-1:0] s_ip_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_ip_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_ip_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_ip_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [DATA_WIDTH-1:0] m_ip_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_ip_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_ip_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire [$clog2(S_COUNT)-1:0] select +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg [CL_S_COUNT-1:0] select_reg = 2'd0, select_next; +reg frame_reg = 1'b0, frame_next; + +reg [S_COUNT-1:0] s_ip_hdr_ready_reg = 0, s_ip_hdr_ready_next; + +reg [S_COUNT-1:0] s_ip_payload_axis_tready_reg = 0, s_ip_payload_axis_tready_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; + +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_ip_payload_axis_tdata[select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_ip_payload_axis_tkeep[select_reg*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_ip_payload_axis_tvalid[select_reg]; +wire current_s_tready = s_ip_payload_axis_tready[select_reg]; +wire current_s_tlast = s_ip_payload_axis_tlast[select_reg]; +wire [ID_WIDTH-1:0] current_s_tid = s_ip_payload_axis_tid[select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_ip_payload_axis_tdest[select_reg*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_ip_payload_axis_tuser[select_reg*USER_WIDTH +: USER_WIDTH]; + +always @* begin + select_next = select_reg; + frame_next = frame_reg; + + s_ip_hdr_ready_next = 0; + + s_ip_payload_axis_tready_next = 0; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + + if (current_s_tvalid & current_s_tready) begin + // end of frame detection + if (current_s_tlast) begin + frame_next = 1'b0; + end + end + + if (!frame_reg && enable && !m_ip_hdr_valid && (s_ip_hdr_valid & (1 << select))) begin + // start of frame, grab select value + frame_next = 1'b1; + select_next = select; + + s_ip_hdr_ready_next = (1 << select); + + m_ip_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[select*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[select*48 +: 48]; + m_eth_type_next = s_eth_type[select*16 +: 16]; + m_ip_version_next = s_ip_version[select*4 +: 4]; + m_ip_ihl_next = s_ip_ihl[select*4 +: 4]; + m_ip_dscp_next = s_ip_dscp[select*6 +: 6]; + m_ip_ecn_next = s_ip_ecn[select*2 +: 2]; + m_ip_length_next = s_ip_length[select*16 +: 16]; + m_ip_identification_next = s_ip_identification[select*16 +: 16]; + m_ip_flags_next = s_ip_flags[select*3 +: 3]; + m_ip_fragment_offset_next = s_ip_fragment_offset[select*13 +: 13]; + m_ip_ttl_next = s_ip_ttl[select*8 +: 8]; + m_ip_protocol_next = s_ip_protocol[select*8 +: 8]; + m_ip_header_checksum_next = s_ip_header_checksum[select*16 +: 16]; + m_ip_source_ip_next = s_ip_source_ip[select*32 +: 32]; + m_ip_dest_ip_next = s_ip_dest_ip[select*32 +: 32]; + end + + // generate ready signal on selected port + s_ip_payload_axis_tready_next = (m_ip_payload_axis_tready_int_early && frame_next) << select_next; + + // pass through selected packet data + m_ip_payload_axis_tdata_int = current_s_tdata; + m_ip_payload_axis_tkeep_int = current_s_tkeep; + m_ip_payload_axis_tvalid_int = current_s_tvalid && current_s_tready && frame_reg; + m_ip_payload_axis_tlast_int = current_s_tlast; + m_ip_payload_axis_tid_int = current_s_tid; + m_ip_payload_axis_tdest_int = current_s_tdest; + m_ip_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 0; + frame_reg <= 1'b0; + s_ip_hdr_ready_reg <= 0; + s_ip_payload_axis_tready_reg <= 0; + m_ip_hdr_valid_reg <= 1'b0; + end else begin + select_reg <= select_next; + frame_reg <= frame_next; + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tkeep = KEEP_ENABLE ? m_ip_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tid = ID_ENABLE ? m_ip_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_ip_payload_axis_tdest = DEST_ENABLE ? m_ip_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_ip_payload_axis_tuser = USER_ENABLE ? m_ip_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tid_reg <= temp_m_ip_payload_axis_tid_reg; + m_ip_payload_axis_tdest_reg <= temp_m_ip_payload_axis_tdest_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + temp_m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/lfsr.v b/fpga/lib/eth/rtl/lfsr.v new file mode 100644 index 000000000..1548daef7 --- /dev/null +++ b/fpga/lib/eth/rtl/lfsr.v @@ -0,0 +1,442 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Parametrizable combinatorial parallel LFSR/CRC + */ +module lfsr # +( + // width of LFSR + parameter LFSR_WIDTH = 31, + // LFSR polynomial + parameter LFSR_POLY = 31'h10000001, + // LFSR configuration: "GALOIS", "FIBONACCI" + parameter LFSR_CONFIG = "FIBONACCI", + // LFSR feed forward enable + parameter LFSR_FEED_FORWARD = 0, + // bit-reverse input and output + parameter REVERSE = 0, + // width of data input + parameter DATA_WIDTH = 8, + // implementation style: "AUTO", "LOOP", "REDUCTION" + parameter STYLE = "AUTO" +) +( + input wire [DATA_WIDTH-1:0] data_in, + input wire [LFSR_WIDTH-1:0] state_in, + output wire [DATA_WIDTH-1:0] data_out, + output wire [LFSR_WIDTH-1:0] state_out +); + +/* + +Fully parametrizable combinatorial parallel LFSR/CRC module. Implements an unrolled LFSR +next state computation, shifting DATA_WIDTH bits per pass through the module. Input data +is XORed with LFSR feedback path, tie data_in to zero if this is not required. + +Works in two parts: statically computes a set of bit masks, then uses these bit masks to +select bits for XORing to compute the next state. + +Ports: + +data_in + +Data bits to be shifted through the LFSR (DATA_WIDTH bits) + +state_in + +LFSR/CRC current state input (LFSR_WIDTH bits) + +data_out + +Data bits shifted out of LFSR (DATA_WIDTH bits) + +state_out + +LFSR/CRC next state output (LFSR_WIDTH bits) + +Parameters: + +LFSR_WIDTH + +Specify width of LFSR/CRC register + +LFSR_POLY + +Specify the LFSR/CRC polynomial in hex format. For example, the polynomial + +x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 + +would be represented as + +32'h04c11db7 + +Note that the largest term (x^32) is suppressed. This term is generated automatically based +on LFSR_WIDTH. + +LFSR_CONFIG + +Specify the LFSR configuration, either Fibonacci or Galois. Fibonacci is generally used +for linear-feedback shift registers (LFSR) for pseudorandom binary sequence (PRBS) generators, +scramblers, and descrambers, while Galois is generally used for cyclic redundancy check +generators and checkers. + +Fibonacci style (example for 64b66b scrambler, 0x8000000001) + + DIN (LSB first) + | + V + (+)<---------------------------(+)<-----------------------------. + | ^ | + | .----. .----. .----. | .----. .----. .----. | + +->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--' + | '----' '----' '----' '----' '----' '----' + V + DOUT + +Galois style (example for CRC16, 0x8005) + + ,-------------------+-------------------------+----------(+)<-- DIN (MSB first) + | | | ^ + | .----. .----. V .----. .----. V .----. | + `->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |--+---> DOUT + '----' '----' '----' '----' '----' + +LFSR_FEED_FORWARD + +Generate feed forward instead of feed back LFSR. Enable this for PRBS checking and self- +synchronous descrambling. + +Fibonacci feed-forward style (example for 64b66b descrambler, 0x8000000001) + + DIN (LSB first) + | + | .----. .----. .----. .----. .----. .----. + +->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--. + | '----' '----' '----' | '----' '----' '----' | + | V | + (+)<---------------------------(+)------------------------------' + | + V + DOUT + +Galois feed-forward style + + ,-------------------+-------------------------+------------+--- DIN (MSB first) + | | | | + | .----. .----. V .----. .----. V .----. V + `->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |->(+)-> DOUT + '----' '----' '----' '----' '----' + +REVERSE + +Bit-reverse LFSR input and output. Shifts MSB first by default, set REVERSE for LSB first. + +DATA_WIDTH + +Specify width of input and output data bus. The module will perform one shift per input +data bit, so if the input data bus is not required tie data_in to zero and set DATA_WIDTH +to the required number of shifts per clock cycle. + +STYLE + +Specify implementation style. Can be "AUTO", "LOOP", or "REDUCTION". When "AUTO" +is selected, implemenation will be "LOOP" or "REDUCTION" based on synthesis translate +directives. "REDUCTION" and "LOOP" are functionally identical, however they simulate +and synthesize differently. "REDUCTION" is implemented with a loop over a Verilog +reduction operator. "LOOP" is implemented as a doubly-nested loop with no reduction +operator. "REDUCTION" is very fast for simulation in iverilog and synthesizes well in +Quartus but synthesizes poorly in ISE, likely due to large inferred XOR gates causing +problems with the optimizer. "LOOP" synthesizes will in both ISE and Quartus. "AUTO" +will default to "REDUCTION" when simulating and "LOOP" for synthesizers that obey +synthesis translate directives. + +Settings for common LFSR/CRC implementations: + +Name Configuration Length Polynomial Initial value Notes +CRC16-IBM Galois, bit-reverse 16 16'h8005 16'hffff +CRC16-CCITT Galois 16 16'h1021 16'h1d0f +CRC32 Galois, bit-reverse 32 32'h04c11db7 32'hffffffff Ethernet FCS; invert final output +PRBS6 Fibonacci 6 6'h21 any +PRBS7 Fibonacci 7 7'h41 any +PRBS9 Fibonacci 9 9'h021 any ITU V.52 +PRBS10 Fibonacci 10 10'h081 any ITU +PRBS11 Fibonacci 11 11'h201 any ITU O.152 +PRBS15 Fibonacci, inverted 15 15'h4001 any ITU O.152 +PRBS17 Fibonacci 17 17'h04001 any +PRBS20 Fibonacci 20 20'h00009 any ITU V.57 +PRBS23 Fibonacci, inverted 23 23'h040001 any ITU O.151 +PRBS29 Fibonacci, inverted 29 29'h08000001 any +PRBS31 Fibonacci, inverted 31 31'h10000001 any +64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet +128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3 + +*/ + +reg [LFSR_WIDTH-1:0] lfsr_mask_state[LFSR_WIDTH-1:0]; +reg [DATA_WIDTH-1:0] lfsr_mask_data[LFSR_WIDTH-1:0]; +reg [LFSR_WIDTH-1:0] output_mask_state[DATA_WIDTH-1:0]; +reg [DATA_WIDTH-1:0] output_mask_data[DATA_WIDTH-1:0]; + +reg [LFSR_WIDTH-1:0] state_val = 0; +reg [DATA_WIDTH-1:0] data_val = 0; + +integer i, j, k; + +initial begin + // init bit masks + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + lfsr_mask_state[i] = {LFSR_WIDTH{1'b0}}; + lfsr_mask_state[i][i] = 1'b1; + lfsr_mask_data[i] = {DATA_WIDTH{1'b0}}; + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + output_mask_state[i] = {LFSR_WIDTH{1'b0}}; + if (i < LFSR_WIDTH) begin + output_mask_state[i][i] = 1'b1; + end + output_mask_data[i] = {DATA_WIDTH{1'b0}}; + end + + // simulate shift register + if (LFSR_CONFIG == "FIBONACCI") begin + // Fibonacci configuration + for (i = DATA_WIDTH-1; i >= 0; i = i - 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_WIDTH-1]; + data_val = lfsr_mask_data[LFSR_WIDTH-1]; + data_val = data_val ^ (1 << i); + + // add XOR inputs from correct indicies + for (j = 1; j < LFSR_WIDTH; j = j + 1) begin + if (LFSR_POLY & (1 << j)) begin + state_val = lfsr_mask_state[j-1] ^ state_val; + data_val = lfsr_mask_data[j-1] ^ data_val; + end + end + + // shift + for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin + output_mask_state[j] = output_mask_state[j-1]; + output_mask_data[j] = output_mask_data[j-1]; + end + output_mask_state[0] = state_val; + output_mask_data[0] = data_val; + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = {LFSR_WIDTH{1'b0}}; + data_val = 1 << i; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + end + end else if (LFSR_CONFIG == "GALOIS") begin + // Galois configuration + for (i = DATA_WIDTH-1; i >= 0; i = i - 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_WIDTH-1]; + data_val = lfsr_mask_data[LFSR_WIDTH-1]; + data_val = data_val ^ (1 << i); + + // shift + for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin + output_mask_state[j] = output_mask_state[j-1]; + output_mask_data[j] = output_mask_data[j-1]; + end + output_mask_state[0] = state_val; + output_mask_data[0] = data_val; + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = {LFSR_WIDTH{1'b0}}; + data_val = 1 << i; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + + // add XOR inputs at correct indicies + for (j = 1; j < LFSR_WIDTH; j = j + 1) begin + if (LFSR_POLY & (1 << j)) begin + lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; + lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; + end + end + end + end else begin + $error("Error: unknown configuration setting!"); + $finish; + end + + // reverse bits if selected + if (REVERSE) begin + // reverse order + for (i = 0; i < LFSR_WIDTH/2; i = i + 1) begin + state_val = lfsr_mask_state[i]; + data_val = lfsr_mask_data[i]; + lfsr_mask_state[i] = lfsr_mask_state[LFSR_WIDTH-i-1]; + lfsr_mask_data[i] = lfsr_mask_data[LFSR_WIDTH-i-1]; + lfsr_mask_state[LFSR_WIDTH-i-1] = state_val; + lfsr_mask_data[LFSR_WIDTH-i-1] = data_val; + end + for (i = 0; i < DATA_WIDTH/2; i = i + 1) begin + state_val = output_mask_state[i]; + data_val = output_mask_data[i]; + output_mask_state[i] = output_mask_state[DATA_WIDTH-i-1]; + output_mask_data[i] = output_mask_data[DATA_WIDTH-i-1]; + output_mask_state[DATA_WIDTH-i-1] = state_val; + output_mask_data[DATA_WIDTH-i-1] = data_val; + end + // reverse bits + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + state_val = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + state_val[j] = lfsr_mask_state[i][LFSR_WIDTH-j-1]; + end + lfsr_mask_state[i] = state_val; + + data_val = 0; + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + data_val[j] = lfsr_mask_data[i][DATA_WIDTH-j-1]; + end + lfsr_mask_data[i] = data_val; + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + state_val = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + state_val[j] = output_mask_state[i][LFSR_WIDTH-j-1]; + end + output_mask_state[i] = state_val; + + data_val = 0; + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + data_val[j] = output_mask_data[i][DATA_WIDTH-j-1]; + end + output_mask_data[i] = data_val; + end + end + + // for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + // $display("%b %b", lfsr_mask_state[i], lfsr_mask_data[i]); + // end +end + +// synthesis translate_off +`define SIMULATION +// synthesis translate_on + +`ifdef SIMULATION +// "AUTO" style is "REDUCTION" for faster simulation +parameter STYLE_INT = (STYLE == "AUTO") ? "REDUCTION" : STYLE; +`else +// "AUTO" style is "LOOP" for better synthesis result +parameter STYLE_INT = (STYLE == "AUTO") ? "LOOP" : STYLE; +`endif + +genvar n; + +generate + +if (STYLE_INT == "REDUCTION") begin + + // use Verilog reduction operator + // fast in iverilog + // significantly larger than generated code with ISE (inferred wide XORs may be tripping up optimizer) + // slightly smaller than generated code with Quartus + // --> better for simulation + + for (n = 0; n < LFSR_WIDTH; n = n + 1) begin : loop1 + assign state_out[n] = ^{(state_in & lfsr_mask_state[n]), (data_in & lfsr_mask_data[n])}; + end + for (n = 0; n < DATA_WIDTH; n = n + 1) begin : loop2 + assign data_out[n] = ^{(state_in & output_mask_state[n]), (data_in & output_mask_data[n])}; + end + +end else if (STYLE_INT == "LOOP") begin + + // use nested loops + // very slow in iverilog + // slightly smaller than generated code with ISE + // same size as generated code with Quartus + // --> better for synthesis + + reg [LFSR_WIDTH-1:0] state_out_reg = 0; + reg [DATA_WIDTH-1:0] data_out_reg = 0; + + assign state_out = state_out_reg; + assign data_out = data_out_reg; + + always @* begin + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + state_out_reg[i] = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + if (lfsr_mask_state[i][j]) begin + state_out_reg[i] = state_out_reg[i] ^ state_in[j]; + end + end + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + if (lfsr_mask_data[i][j]) begin + state_out_reg[i] = state_out_reg[i] ^ data_in[j]; + end + end + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + data_out_reg[i] = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + if (output_mask_state[i][j]) begin + data_out_reg[i] = data_out_reg[i] ^ state_in[j]; + end + end + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + if (output_mask_data[i][j]) begin + data_out_reg[i] = data_out_reg[i] ^ data_in[j]; + end + end + end + end + +end else begin + + initial begin + $error("Error: unknown style setting!"); + $finish; + end + +end + +endgenerate + +endmodule diff --git a/fpga/lib/eth/rtl/mii_phy_if.v b/fpga/lib/eth/rtl/mii_phy_if.v new file mode 100644 index 000000000..0e8cb8ce2 --- /dev/null +++ b/fpga/lib/eth/rtl/mii_phy_if.v @@ -0,0 +1,137 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * MII PHY interface + */ +module mii_phy_if # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2" +) +( + input wire rst, + + /* + * MII interface to MAC + */ + output wire mac_mii_rx_clk, + output wire mac_mii_rx_rst, + output wire [3:0] mac_mii_rxd, + output wire mac_mii_rx_dv, + output wire mac_mii_rx_er, + output wire mac_mii_tx_clk, + output wire mac_mii_tx_rst, + input wire [3:0] mac_mii_txd, + input wire mac_mii_tx_en, + input wire mac_mii_tx_er, + + /* + * MII interface to PHY + */ + input wire phy_mii_rx_clk, + input wire [3:0] phy_mii_rxd, + input wire phy_mii_rx_dv, + input wire phy_mii_rx_er, + input wire phy_mii_tx_clk, + output wire [3:0] phy_mii_txd, + output wire phy_mii_tx_en, + output wire phy_mii_tx_er +); + +ssio_sdr_in # +( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .WIDTH(6) +) +rx_ssio_sdr_inst ( + .input_clk(phy_mii_rx_clk), + .input_d({phy_mii_rxd, phy_mii_rx_dv, phy_mii_rx_er}), + .output_clk(mac_mii_rx_clk), + .output_q({mac_mii_rxd, mac_mii_rx_dv, mac_mii_rx_er}) +); + +(* IOB = "TRUE" *) +reg [3:0] phy_mii_txd_reg = 4'd0; +(* IOB = "TRUE" *) +reg phy_mii_tx_en_reg = 1'b0, phy_mii_tx_er_reg = 1'b0; + +assign phy_mii_txd = phy_mii_txd_reg; +assign phy_mii_tx_en = phy_mii_tx_en_reg; +assign phy_mii_tx_er = phy_mii_tx_er_reg; + +always @(posedge mac_mii_tx_clk) begin + phy_mii_txd_reg <= mac_mii_txd; + phy_mii_tx_en_reg <= mac_mii_tx_en; + phy_mii_tx_er_reg <= mac_mii_tx_er; +end + +generate + +if (TARGET == "XILINX") begin + BUFG + mii_bufg_inst ( + .I(phy_mii_tx_clk), + .O(mac_mii_tx_clk) + ); +end else begin + assign mac_mii_tx_clk = phy_mii_tx_clk; +end + +endgenerate + +// reset sync +reg [3:0] tx_rst_reg = 4'hf; +assign mac_mii_tx_rst = tx_rst_reg[0]; + +always @(posedge mac_mii_tx_clk or posedge rst) begin + if (rst) begin + tx_rst_reg <= 4'hf; + end else begin + tx_rst_reg <= {1'b0, tx_rst_reg[3:1]}; + end +end + +reg [3:0] rx_rst_reg = 4'hf; +assign mac_mii_rx_rst = rx_rst_reg[0]; + +always @(posedge mac_mii_rx_clk or posedge rst) begin + if (rst) begin + rx_rst_reg <= 4'hf; + end else begin + rx_rst_reg <= {1'b0, rx_rst_reg[3:1]}; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/oddr.v b/fpga/lib/eth/rtl/oddr.v new file mode 100644 index 000000000..db2d14429 --- /dev/null +++ b/fpga/lib/eth/rtl/oddr.v @@ -0,0 +1,142 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Generic ODDR module + */ +module oddr # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + + input wire [WIDTH-1:0] d1, + input wire [WIDTH-1:0] d2, + + output wire [WIDTH-1:0] q +); + +/* + +Provides a consistent output DDR flip flop across multiple FPGA families + _____ _____ _____ _____ + clk ____/ \_____/ \_____/ \_____/ \_____ + _ ___________ ___________ ___________ ___________ __ + d1 _X____D0_____X____D2_____X____D4_____X____D6_____X__ + _ ___________ ___________ ___________ ___________ __ + d2 _X____D1_____X____D3_____X____D5_____X____D7_____X__ + _____ _____ _____ _____ _____ _____ _____ _____ ____ + d _____X_D0__X_D1__X_D2__X_D3__X_D4__X_D5__X_D6__X_D7_ + +*/ + +genvar n; + +generate + +if (TARGET == "XILINX") begin + for (n = 0; n < WIDTH; n = n + 1) begin : oddr + if (IODDR_STYLE == "IODDR") begin + ODDR #( + .DDR_CLK_EDGE("SAME_EDGE"), + .SRTYPE("ASYNC") + ) + oddr_inst ( + .Q(q[n]), + .C(clk), + .CE(1'b1), + .D1(d1[n]), + .D2(d2[n]), + .R(1'b0), + .S(1'b0) + ); + end else if (IODDR_STYLE == "IODDR2") begin + ODDR2 #( + .DDR_ALIGNMENT("C0"), + .SRTYPE("ASYNC") + ) + oddr_inst ( + .Q(q[n]), + .C0(clk), + .C1(~clk), + .CE(1'b1), + .D0(d1[n]), + .D1(d2[n]), + .R(1'b0), + .S(1'b0) + ); + end + end +end else if (TARGET == "ALTERA") begin + altddio_out #( + .WIDTH(WIDTH), + .POWER_UP_HIGH("OFF"), + .OE_REG("UNUSED") + ) + altddio_out_inst ( + .aset(1'b0), + .datain_h(d1), + .datain_l(d2), + .outclocken(1'b1), + .outclock(clk), + .aclr(1'b0), + .dataout(q) + ); +end else begin + reg [WIDTH-1:0] d_reg_1 = {WIDTH{1'b0}}; + reg [WIDTH-1:0] d_reg_2 = {WIDTH{1'b0}}; + + reg [WIDTH-1:0] q_reg = {WIDTH{1'b0}}; + + always @(posedge clk) begin + d_reg_1 <= d1; + d_reg_2 <= d2; + end + + always @(posedge clk) begin + q_reg <= d1; + end + + always @(negedge clk) begin + q_reg <= d_reg_2; + end + + assign q = q_reg; +end + +endgenerate + +endmodule diff --git a/fpga/lib/eth/rtl/ptp_clock.v b/fpga/lib/eth/rtl/ptp_clock.v new file mode 100644 index 000000000..f1f27d646 --- /dev/null +++ b/fpga/lib/eth/rtl/ptp_clock.v @@ -0,0 +1,246 @@ +/* + +Copyright (c) 2015-2019 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 1ns / 1ps + +/* + * PTP clock module + */ +module ptp_clock # +( + parameter PERIOD_NS_WIDTH = 4, + parameter OFFSET_NS_WIDTH = 4, + parameter DRIFT_NS_WIDTH = 4, + parameter FNS_WIDTH = 16, + parameter PERIOD_NS = 4'h6, + parameter PERIOD_FNS = 16'h6666, + parameter DRIFT_ENABLE = 1, + parameter DRIFT_NS = 4'h0, + parameter DRIFT_FNS = 16'h0002, + parameter DRIFT_RATE = 16'h0005 +) +( + input wire clk, + input wire rst, + + /* + * Timestamp inputs for synchronization + */ + input wire [95:0] input_ts_96, + input wire input_ts_96_valid, + input wire [63:0] input_ts_64, + input wire input_ts_64_valid, + + /* + * Period adjustment + */ + input wire [PERIOD_NS_WIDTH-1:0] input_period_ns, + input wire [FNS_WIDTH-1:0] input_period_fns, + input wire input_period_valid, + + /* + * Offset adjustment + */ + input wire [OFFSET_NS_WIDTH-1:0] input_adj_ns, + input wire [FNS_WIDTH-1:0] input_adj_fns, + input wire [15:0] input_adj_count, + input wire input_adj_valid, + output wire input_adj_active, + + /* + * Drift adjustment + */ + input wire [DRIFT_NS_WIDTH-1:0] input_drift_ns, + input wire [FNS_WIDTH-1:0] input_drift_fns, + input wire [15:0] input_drift_rate, + input wire input_drift_valid, + + /* + * Timestamp outputs + */ + output wire [95:0] output_ts_96, + output wire [63:0] output_ts_64, + output wire output_ts_step, + + /* + * PPS output + */ + output wire output_pps +); + +parameter INC_NS_WIDTH = $clog2(2**PERIOD_NS_WIDTH + 2**OFFSET_NS_WIDTH + 2**DRIFT_NS_WIDTH); + +reg [PERIOD_NS_WIDTH-1:0] period_ns_reg = PERIOD_NS; +reg [FNS_WIDTH-1:0] period_fns_reg = PERIOD_FNS; + +reg [OFFSET_NS_WIDTH-1:0] adj_ns_reg = 0; +reg [FNS_WIDTH-1:0] adj_fns_reg = 0; +reg [15:0] adj_count_reg = 0; +reg adj_active_reg = 0; + +reg [DRIFT_NS_WIDTH-1:0] drift_ns_reg = DRIFT_NS; +reg [FNS_WIDTH-1:0] drift_fns_reg = DRIFT_FNS; +reg [15:0] drift_rate_reg = DRIFT_RATE; + +reg [INC_NS_WIDTH-1:0] ts_inc_ns_reg = 0; +reg [FNS_WIDTH-1:0] ts_inc_fns_reg = 0; + +reg [47:0] ts_96_s_reg = 0; +reg [29:0] ts_96_ns_reg = 0; +reg [FNS_WIDTH-1:0] ts_96_fns_reg = 0; +reg [29:0] ts_96_ns_inc_reg = 0; +reg [FNS_WIDTH-1:0] ts_96_fns_inc_reg = 0; +reg [30:0] ts_96_ns_ovf_reg = 31'h7fffffff; +reg [FNS_WIDTH-1:0] ts_96_fns_ovf_reg = 16'hffff; + +reg [47:0] ts_64_ns_reg = 0; +reg [FNS_WIDTH-1:0] ts_64_fns_reg = 0; + +reg ts_step_reg = 1'b0; + +reg [15:0] drift_cnt = 0; + +reg [47:0] temp; + +reg pps_reg = 0; + +assign input_adj_active = adj_active_reg; + +assign output_ts_96[95:48] = ts_96_s_reg; +assign output_ts_96[47:46] = 2'b00; +assign output_ts_96[45:16] = ts_96_ns_reg; +assign output_ts_96[15:0] = FNS_WIDTH > 16 ? ts_96_fns_reg >> (FNS_WIDTH-16) : ts_96_fns_reg << (16-FNS_WIDTH); +assign output_ts_64[63:16] = ts_64_ns_reg; +assign output_ts_64[15:0] = FNS_WIDTH > 16 ? ts_64_fns_reg >> (FNS_WIDTH-16) : ts_64_fns_reg << (16-FNS_WIDTH); +assign output_ts_step = ts_step_reg; + +assign output_pps = pps_reg; + +always @(posedge clk) begin + ts_step_reg <= 0; + + // latch parameters + if (input_period_valid) begin + period_ns_reg <= input_period_ns; + period_fns_reg <= input_period_fns; + end + + if (input_adj_valid) begin + adj_ns_reg <= input_adj_ns; + adj_fns_reg <= input_adj_fns; + adj_count_reg <= input_adj_count; + end + + if (DRIFT_ENABLE && input_drift_valid) begin + drift_ns_reg <= input_drift_ns; + drift_fns_reg <= input_drift_fns; + drift_rate_reg <= input_drift_rate; + end + + // timestamp increment calculation + {ts_inc_ns_reg, ts_inc_fns_reg} <= $signed({period_ns_reg, period_fns_reg}) + + (adj_active_reg ? $signed({adj_ns_reg, adj_fns_reg}) : 0) + + ((DRIFT_ENABLE && drift_cnt == 0) ? $signed({drift_ns_reg, drift_fns_reg}) : 0); + + // offset adjust counter + if (adj_count_reg > 0) begin + adj_count_reg <= adj_count_reg - 1; + adj_active_reg <= 1; + ts_step_reg <= 1; + end else begin + adj_active_reg <= 0; + end + + // drift counter + if (drift_cnt == 0) begin + drift_cnt <= drift_rate_reg-1; + end else begin + drift_cnt <= drift_cnt - 1; + end + + // 96 bit timestamp + if (input_ts_96_valid) begin + // load timestamp + {ts_96_ns_inc_reg, ts_96_fns_inc_reg} <= (FNS_WIDTH > 16 ? input_ts_96[45:0] << (FNS_WIDTH-16) : input_ts_96[45:0] >> (16-FNS_WIDTH)) + {ts_inc_ns_reg, ts_inc_fns_reg}; + {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} <= (FNS_WIDTH > 16 ? input_ts_96[45:0] << (FNS_WIDTH-16) : input_ts_96[45:0] >> (16-FNS_WIDTH)) + {ts_inc_ns_reg, ts_inc_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}}; + ts_96_s_reg <= input_ts_96[95:48]; + ts_96_ns_reg <= input_ts_96[45:16]; + ts_96_fns_reg <= FNS_WIDTH > 16 ? input_ts_96[15:0] << (FNS_WIDTH-16) : input_ts_96[15:0] >> (16-FNS_WIDTH); + ts_step_reg <= 1; + end else if (!ts_96_ns_ovf_reg[30]) begin + // if the overflow lookahead did not borrow, one second has elapsed + // increment seconds field, pre-compute both normal increment and overflow values + {ts_96_ns_inc_reg, ts_96_fns_inc_reg} <= {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} + {ts_inc_ns_reg, ts_inc_fns_reg}; + {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} <= {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} + {ts_inc_ns_reg, ts_inc_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}}; + {ts_96_ns_reg, ts_96_fns_reg} <= {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg}; + ts_96_s_reg <= ts_96_s_reg + 1; + end else begin + // no increment seconds field, pre-compute both normal increment and overflow values + {ts_96_ns_inc_reg, ts_96_fns_inc_reg} <= {ts_96_ns_inc_reg, ts_96_fns_inc_reg} + {ts_inc_ns_reg, ts_inc_fns_reg}; + {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} <= {ts_96_ns_inc_reg, ts_96_fns_inc_reg} + {ts_inc_ns_reg, ts_inc_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}}; + {ts_96_ns_reg, ts_96_fns_reg} <= {ts_96_ns_inc_reg, ts_96_fns_inc_reg}; + ts_96_s_reg <= ts_96_s_reg; + end + + // 64 bit timestamp + if (input_ts_64_valid) begin + // load timestamp + {ts_64_ns_reg, ts_64_fns_reg} <= input_ts_64; + ts_step_reg <= 1; + end else begin + {ts_64_ns_reg, ts_64_fns_reg} <= {ts_64_ns_reg, ts_64_fns_reg} + {ts_inc_ns_reg, ts_inc_fns_reg}; + end + + pps_reg <= !ts_96_ns_ovf_reg[30]; + + if (rst) begin + period_ns_reg <= PERIOD_NS; + period_fns_reg <= PERIOD_FNS; + adj_ns_reg <= 0; + adj_fns_reg <= 0; + adj_count_reg <= 0; + adj_active_reg <= 0; + drift_ns_reg <= DRIFT_NS; + drift_fns_reg <= DRIFT_FNS; + drift_rate_reg <= DRIFT_RATE; + ts_inc_ns_reg <= 0; + ts_inc_fns_reg <= 0; + ts_96_s_reg <= 0; + ts_96_ns_reg <= 0; + ts_96_fns_reg <= 0; + ts_96_ns_inc_reg <= 0; + ts_96_fns_inc_reg <= 0; + ts_96_ns_ovf_reg <= 31'h7fffffff; + ts_96_fns_ovf_reg <= {FNS_WIDTH{1'b1}}; + ts_64_ns_reg <= 0; + ts_64_fns_reg <= 0; + ts_step_reg <= 0; + drift_cnt <= 0; + pps_reg <= 0; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/ptp_perout.v b/fpga/lib/eth/rtl/ptp_perout.v new file mode 100644 index 000000000..54ba07a65 --- /dev/null +++ b/fpga/lib/eth/rtl/ptp_perout.v @@ -0,0 +1,329 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * PTP period out module + */ +module ptp_perout # +( + parameter FNS_ENABLE = 1, + parameter OUT_START_S = 48'h0, + parameter OUT_START_NS = 30'h0, + parameter OUT_START_FNS = 16'h0000, + parameter OUT_PERIOD_S = 48'd1, + parameter OUT_PERIOD_NS = 30'd0, + parameter OUT_PERIOD_FNS = 16'h0000, + parameter OUT_WIDTH_S = 48'h0, + parameter OUT_WIDTH_NS = 30'd1000, + parameter OUT_WIDTH_FNS = 16'h0000 +) +( + input wire clk, + input wire rst, + + /* + * Timestamp input from PTP clock + */ + input wire [95:0] input_ts_96, + input wire input_ts_step, + + /* + * Control + */ + input wire enable, + input wire [95:0] input_start, + input wire input_start_valid, + input wire [95:0] input_period, + input wire input_period_valid, + input wire [95:0] input_width, + input wire input_width_valid, + + /* + * Status + */ + output wire locked, + output wire error, + + /* + * Pulse output + */ + output wire output_pulse +); + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_UPDATE_RISE_1 = 3'd1, + STATE_UPDATE_RISE_2 = 3'd2, + STATE_UPDATE_FALL_1 = 3'd3, + STATE_UPDATE_FALL_2 = 3'd4, + STATE_WAIT_EDGE = 3'd5; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg [47:0] time_s_reg = 0; +reg [30:0] time_ns_reg = 0; +reg [15:0] time_fns_reg = 0; + +reg [47:0] next_rise_s_reg = 0, next_rise_s_next; +reg [30:0] next_rise_ns_reg = 0, next_rise_ns_next; +reg [15:0] next_rise_fns_reg = 0, next_rise_fns_next; + +reg [47:0] next_fall_s_reg = 0, next_fall_s_next; +reg [30:0] next_fall_ns_reg = 0, next_fall_ns_next; +reg [15:0] next_fall_fns_reg = 0, next_fall_fns_next; + +reg [47:0] start_s_reg = OUT_START_S; +reg [30:0] start_ns_reg = OUT_START_NS; +reg [15:0] start_fns_reg = OUT_START_FNS; + +reg [47:0] period_s_reg = OUT_PERIOD_S; +reg [30:0] period_ns_reg = OUT_PERIOD_NS; +reg [15:0] period_fns_reg = OUT_PERIOD_FNS; + +reg [47:0] width_s_reg = OUT_WIDTH_S; +reg [30:0] width_ns_reg = OUT_WIDTH_NS; +reg [15:0] width_fns_reg = OUT_WIDTH_FNS; + +reg [29:0] ts_96_ns_inc_reg = 0, ts_96_ns_inc_next; +reg [15:0] ts_96_fns_inc_reg = 0, ts_96_fns_inc_next; +reg [30:0] ts_96_ns_ovf_reg = 0, ts_96_ns_ovf_next; +reg [15:0] ts_96_fns_ovf_reg = 0, ts_96_fns_ovf_next; + +reg locked_reg = 1'b0, locked_next; +reg error_reg = 1'b0; +reg level_reg = 1'b0, level_next; +reg output_reg = 1'b0, output_next; + +assign locked = locked_reg; +assign error = error_reg; +assign output_pulse = output_reg; + +always @* begin + state_next = STATE_IDLE; + + next_rise_s_next = next_rise_s_reg; + next_rise_ns_next = next_rise_ns_reg; + next_rise_fns_next = next_rise_fns_reg; + + next_fall_s_next = next_fall_s_reg; + next_fall_ns_next = next_fall_ns_reg; + next_fall_fns_next = next_fall_fns_reg; + + ts_96_ns_inc_next = ts_96_ns_inc_reg; + ts_96_fns_inc_next = ts_96_fns_inc_reg; + + ts_96_ns_ovf_next = ts_96_ns_ovf_reg; + ts_96_fns_ovf_next = ts_96_fns_ovf_reg; + + locked_next = locked_reg; + level_next = level_reg; + output_next = output_reg; + + case (state_reg) + STATE_IDLE: begin + // set next rise to start time + next_rise_s_next = start_s_reg; + next_rise_ns_next = start_ns_reg; + if (FNS_ENABLE) begin + next_rise_fns_next = start_fns_reg; + end + locked_next = 1'b0; + level_next = 1'b0; + output_next = 1'b0; + if (input_start_valid || input_period_valid) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_UPDATE_FALL_1; + end + end + STATE_UPDATE_RISE_1: begin + // set next rise time to next rise time plus period + {ts_96_ns_inc_next, ts_96_fns_inc_next} = {next_rise_ns_reg, next_rise_fns_reg} + {period_ns_reg, period_fns_reg}; + {ts_96_ns_ovf_next, ts_96_fns_ovf_next} = {next_rise_ns_reg, next_rise_fns_reg} + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, 16'd0}; + if (input_start_valid || input_period_valid) begin + level_next = 1'b0; + output_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_UPDATE_RISE_2; + end + end + STATE_UPDATE_RISE_2: begin + if (!ts_96_ns_ovf_reg[30]) begin + // if the overflow lookahead did not borrow, one second has elapsed + next_rise_s_next = next_rise_s_reg + period_s_reg + 1; + next_rise_ns_next = ts_96_ns_ovf_reg; + next_rise_fns_next = ts_96_fns_ovf_reg; + end else begin + // no increment seconds field + next_rise_s_next = next_rise_s_reg + period_s_reg; + next_rise_ns_next = ts_96_ns_inc_reg; + next_rise_fns_next = ts_96_fns_inc_reg; + end + if (input_start_valid || input_period_valid) begin + level_next = 1'b0; + output_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_EDGE; + end + end + STATE_UPDATE_FALL_1: begin + // set next fall time to next rise time plus width + {ts_96_ns_inc_next, ts_96_fns_inc_next} = {next_rise_ns_reg, next_rise_fns_reg} + {width_ns_reg, width_fns_reg}; + {ts_96_ns_ovf_next, ts_96_fns_ovf_next} = {next_rise_ns_reg, next_rise_fns_reg} + {width_ns_reg, width_fns_reg} - {31'd1_000_000_000, 16'd0}; + if (input_start_valid || input_period_valid) begin + level_next = 1'b0; + output_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_UPDATE_FALL_2; + end + end + STATE_UPDATE_FALL_2: begin + if (!ts_96_ns_ovf_reg[30]) begin + // if the overflow lookahead did not borrow, one second has elapsed + next_fall_s_next = next_rise_s_reg + width_s_reg + 1; + next_fall_ns_next = ts_96_ns_ovf_reg; + next_fall_fns_next = ts_96_fns_ovf_reg; + end else begin + // no increment seconds field + next_fall_s_next = next_rise_s_reg + width_s_reg; + next_fall_ns_next = ts_96_ns_inc_reg; + next_fall_fns_next = ts_96_fns_inc_reg; + end + if (input_start_valid || input_period_valid) begin + level_next = 1'b0; + output_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_EDGE; + end + end + STATE_WAIT_EDGE: begin + if (input_start_valid || input_period_valid) begin + state_next = STATE_IDLE; + end else if ((time_s_reg > next_rise_s_reg) || (time_s_reg == next_rise_s_reg && {time_ns_reg, time_fns_reg} > {next_rise_ns_reg, next_rise_fns_reg})) begin + // rising edge + level_next = 1'b1; + output_next = enable && locked_reg; + state_next = STATE_UPDATE_RISE_1; + end else if ((time_s_reg > next_fall_s_reg) || (time_s_reg == next_fall_s_reg && {time_ns_reg, time_fns_reg} > {next_fall_ns_reg, next_fall_fns_reg})) begin + // falling edge + level_next = 1'b0; + output_next = 1'b0; + state_next = STATE_UPDATE_FALL_1; + end else begin + locked_next = locked_reg || level_reg; + state_next = STATE_WAIT_EDGE; + end + end + endcase +end + +always @(posedge clk) begin + state_reg <= state_next; + + time_s_reg <= input_ts_96[95:48]; + time_ns_reg <= input_ts_96[45:16]; + if (FNS_ENABLE) begin + time_fns_reg <= input_ts_96[15:0]; + end + + if (input_start_valid) begin + start_s_reg <= input_start[95:48]; + start_ns_reg <= input_start[45:16]; + if (FNS_ENABLE) begin + start_fns_reg <= input_start[15:0]; + end + end + + if (input_period_valid) begin + period_s_reg <= input_period[95:48]; + period_ns_reg <= input_period[45:16]; + if (FNS_ENABLE) begin + period_fns_reg <= input_period[15:0]; + end + end + + if (input_width_valid) begin + width_s_reg <= input_width[95:48]; + width_ns_reg <= input_width[45:16]; + if (FNS_ENABLE) begin + width_fns_reg <= input_width[15:0]; + end + end + + next_rise_s_reg <= next_rise_s_next; + next_rise_ns_reg <= next_rise_ns_next; + if (FNS_ENABLE) begin + next_rise_fns_reg <= next_rise_fns_next; + end + + next_fall_s_reg <= next_fall_s_next; + next_fall_ns_reg <= next_fall_ns_next; + if (FNS_ENABLE) begin + next_fall_fns_reg <= next_fall_fns_next; + end + + ts_96_ns_inc_reg <= ts_96_ns_inc_next; + if (FNS_ENABLE) begin + ts_96_fns_inc_reg <= ts_96_fns_inc_next; + end + + ts_96_ns_ovf_reg <= ts_96_ns_ovf_next; + if (FNS_ENABLE) begin + ts_96_fns_ovf_reg <= ts_96_fns_ovf_next; + end + + locked_reg <= locked_next; + level_reg <= level_next; + output_reg <= output_next; + + if (rst) begin + state_reg <= STATE_IDLE; + + start_s_reg <= OUT_START_S; + start_ns_reg <= OUT_START_NS; + start_fns_reg <= OUT_START_FNS; + + period_s_reg <= OUT_PERIOD_S; + period_ns_reg <= OUT_PERIOD_NS; + period_fns_reg <= OUT_PERIOD_FNS; + + width_s_reg <= OUT_WIDTH_S; + width_ns_reg <= OUT_WIDTH_NS; + width_fns_reg <= OUT_WIDTH_FNS; + + locked_reg <= 1'b0; + error_reg <= 1'b0; + output_reg <= 1'b0; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/rgmii_phy_if.v b/fpga/lib/eth/rtl/rgmii_phy_if.v new file mode 100644 index 000000000..15f5943f5 --- /dev/null +++ b/fpga/lib/eth/rtl/rgmii_phy_if.v @@ -0,0 +1,262 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * RGMII PHY interface + */ +module rgmii_phy_if # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Use 90 degree clock for RGMII transmit ("TRUE", "FALSE") + parameter USE_CLK90 = "TRUE" +) +( + input wire clk, + input wire clk90, + input wire rst, + + /* + * GMII interface to MAC + */ + output wire mac_gmii_rx_clk, + output wire mac_gmii_rx_rst, + output wire [7:0] mac_gmii_rxd, + output wire mac_gmii_rx_dv, + output wire mac_gmii_rx_er, + output wire mac_gmii_tx_clk, + output wire mac_gmii_tx_rst, + output wire mac_gmii_tx_clk_en, + input wire [7:0] mac_gmii_txd, + input wire mac_gmii_tx_en, + input wire mac_gmii_tx_er, + + /* + * RGMII interface to PHY + */ + input wire phy_rgmii_rx_clk, + input wire [3:0] phy_rgmii_rxd, + input wire phy_rgmii_rx_ctl, + output wire phy_rgmii_tx_clk, + output wire [3:0] phy_rgmii_txd, + output wire phy_rgmii_tx_ctl, + + /* + * Control + */ + input wire [1:0] speed +); + +// receive + +wire rgmii_rx_ctl_1; +wire rgmii_rx_ctl_2; + +ssio_ddr_in # +( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(5) +) +rx_ssio_ddr_inst ( + .input_clk(phy_rgmii_rx_clk), + .input_d({phy_rgmii_rxd, phy_rgmii_rx_ctl}), + .output_clk(mac_gmii_rx_clk), + .output_q1({mac_gmii_rxd[3:0], rgmii_rx_ctl_1}), + .output_q2({mac_gmii_rxd[7:4], rgmii_rx_ctl_2}) +); + +assign mac_gmii_rx_dv = rgmii_rx_ctl_1; +assign mac_gmii_rx_er = rgmii_rx_ctl_1 ^ rgmii_rx_ctl_2; + +// transmit + +reg rgmii_tx_clk_1 = 1'b1; +reg rgmii_tx_clk_2 = 1'b0; +reg rgmii_tx_clk_rise = 1'b1; +reg rgmii_tx_clk_fall = 1'b1; + +reg [5:0] count_reg = 6'd0, count_next; + +always @(posedge clk) begin + if (rst) begin + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_rise <= 1'b1; + rgmii_tx_clk_fall <= 1'b1; + count_reg <= 0; + end else begin + rgmii_tx_clk_1 <= rgmii_tx_clk_2; + + if (speed == 2'b00) begin + // 10M + count_reg <= count_reg + 1; + rgmii_tx_clk_rise <= 1'b0; + rgmii_tx_clk_fall <= 1'b0; + if (count_reg == 24) begin + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b1; + rgmii_tx_clk_rise <= 1'b1; + end else if (count_reg >= 49) begin + rgmii_tx_clk_1 <= 1'b0; + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_fall <= 1'b1; + count_reg <= 0; + end + end else if (speed == 2'b01) begin + // 100M + count_reg <= count_reg + 1; + rgmii_tx_clk_rise <= 1'b0; + rgmii_tx_clk_fall <= 1'b0; + if (count_reg == 2) begin + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b1; + rgmii_tx_clk_rise <= 1'b1; + end else if (count_reg >= 4) begin + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_fall <= 1'b1; + count_reg <= 0; + end + end else begin + // 1000M + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_rise <= 1'b1; + rgmii_tx_clk_fall <= 1'b1; + end + end +end + +reg [3:0] rgmii_txd_1; +reg [3:0] rgmii_txd_2; +reg rgmii_tx_ctl_1; +reg rgmii_tx_ctl_2; + +reg gmii_clk_en; + +always @* begin + if (speed == 2'b00) begin + // 10M + rgmii_txd_1 = mac_gmii_txd[3:0]; + rgmii_txd_2 = mac_gmii_txd[3:0]; + if (rgmii_tx_clk_2) begin + rgmii_tx_ctl_1 = mac_gmii_tx_en; + rgmii_tx_ctl_2 = mac_gmii_tx_en; + end else begin + rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er; + rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er; + end + gmii_clk_en = rgmii_tx_clk_fall; + end else if (speed == 2'b01) begin + // 100M + rgmii_txd_1 = mac_gmii_txd[3:0]; + rgmii_txd_2 = mac_gmii_txd[3:0]; + if (rgmii_tx_clk_2) begin + rgmii_tx_ctl_1 = mac_gmii_tx_en; + rgmii_tx_ctl_2 = mac_gmii_tx_en; + end else begin + rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er; + rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er; + end + gmii_clk_en = rgmii_tx_clk_fall; + end else begin + // 1000M + rgmii_txd_1 = mac_gmii_txd[3:0]; + rgmii_txd_2 = mac_gmii_txd[7:4]; + rgmii_tx_ctl_1 = mac_gmii_tx_en; + rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er; + gmii_clk_en = 1; + end +end + +wire phy_rgmii_tx_clk_new; +wire [3:0] phy_rgmii_txd_new; +wire phy_rgmii_tx_ctl_new; + +oddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(1) +) +clk_oddr_inst ( + .clk(USE_CLK90 == "TRUE" ? clk90 : clk), + .d1(rgmii_tx_clk_1), + .d2(rgmii_tx_clk_2), + .q(phy_rgmii_tx_clk) +); + +oddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(5) +) +data_oddr_inst ( + .clk(clk), + .d1({rgmii_txd_1, rgmii_tx_ctl_1}), + .d2({rgmii_txd_2, rgmii_tx_ctl_2}), + .q({phy_rgmii_txd, phy_rgmii_tx_ctl}) +); + +assign mac_gmii_tx_clk = clk; + +assign mac_gmii_tx_clk_en = gmii_clk_en; + +// reset sync +reg [3:0] tx_rst_reg = 4'hf; +assign mac_gmii_tx_rst = tx_rst_reg[0]; + +always @(posedge mac_gmii_tx_clk or posedge rst) begin + if (rst) begin + tx_rst_reg <= 4'hf; + end else begin + tx_rst_reg <= {1'b0, tx_rst_reg[3:1]}; + end +end + +reg [3:0] rx_rst_reg = 4'hf; +assign mac_gmii_rx_rst = rx_rst_reg[0]; + +always @(posedge mac_gmii_rx_clk or posedge rst) begin + if (rst) begin + rx_rst_reg <= 4'hf; + end else begin + rx_rst_reg <= {1'b0, rx_rst_reg[3:1]}; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/ssio_ddr_in.v b/fpga/lib/eth/rtl/ssio_ddr_in.v new file mode 100644 index 000000000..4c2f6f421 --- /dev/null +++ b/fpga/lib/eth/rtl/ssio_ddr_in.v @@ -0,0 +1,171 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Generic source synchronous DDR input + */ +module ssio_ddr_in # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q1, + output wire [WIDTH-1:0] output_q2 +); + +wire clk_int; +wire clk_io; + +generate + +if (TARGET == "XILINX") begin + + // use Xilinx clocking primitives + + if (CLOCK_INPUT_STYLE == "BUFG") begin + + // buffer RX clock + BUFG + clk_bufg ( + .I(input_clk), + .O(clk_int) + ); + + // pass through RX clock to logic and input buffers + assign clk_io = clk_int; + assign output_clk = clk_int; + + end else if (CLOCK_INPUT_STYLE == "BUFR") begin + + assign clk_int = input_clk; + + // pass through RX clock to input buffers + BUFIO + clk_bufio ( + .I(clk_int), + .O(clk_io) + ); + + // pass through RX clock to logic + BUFR #( + .BUFR_DIVIDE("BYPASS") + ) + clk_bufr ( + .I(clk_int), + .O(output_clk), + .CE(1'b1), + .CLR(1'b0) + ); + + end else if (CLOCK_INPUT_STYLE == "BUFIO") begin + + assign clk_int = input_clk; + + // pass through RX clock to input buffers + BUFIO + clk_bufio ( + .I(clk_int), + .O(clk_io) + ); + + // pass through RX clock to MAC + BUFG + clk_bufg ( + .I(clk_int), + .O(output_clk) + ); + + end else if (CLOCK_INPUT_STYLE == "BUFIO2") begin + + // pass through RX clock to input buffers + BUFIO2 #( + .DIVIDE(1), + .DIVIDE_BYPASS("TRUE"), + .I_INVERT("FALSE"), + .USE_DOUBLER("FALSE") + ) + clk_bufio ( + .I(input_clk), + .DIVCLK(clk_int), + .IOCLK(clk_io), + .SERDESSTROBE() + ); + + // pass through RX clock to MAC + BUFG + clk_bufg ( + .I(clk_int), + .O(output_clk) + ); + + end + +end else begin + + // pass through RX clock to input buffers + assign clk_io = input_clk; + + // pass through RX clock to logic + assign clk_int = input_clk; + assign output_clk = clk_int; + +end + +endgenerate + +iddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(WIDTH) +) +data_iddr_inst ( + .clk(clk_io), + .d(input_d), + .q1(output_q1), + .q2(output_q2) +); + +endmodule diff --git a/fpga/lib/eth/rtl/ssio_ddr_in_diff.v b/fpga/lib/eth/rtl/ssio_ddr_in_diff.v new file mode 100644 index 000000000..cab427915 --- /dev/null +++ b/fpga/lib/eth/rtl/ssio_ddr_in_diff.v @@ -0,0 +1,119 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Generic source synchronous DDR input + */ +module ssio_ddr_in_diff # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk_p, + input wire input_clk_n, + + input wire [WIDTH-1:0] input_d_p, + input wire [WIDTH-1:0] input_d_n, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q1, + output wire [WIDTH-1:0] output_q2 +); + +wire input_clk; +wire [WIDTH-1:0] input_d; + +genvar n; + +generate + +if (TARGET == "XILINX") begin + IBUFDS + clk_ibufds_inst ( + .I(input_clk_p), + .IB(input_clk_n), + .O(input_clk) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + IBUFDS + data_ibufds_inst ( + .I(input_d_p[n]), + .IB(input_d_n[n]), + .O(input_d[n]) + ); + end +end else if (TARGET == "ALTERA") begin + ALT_INBUF_DIFF + clk_inbuf_diff_inst ( + .i(input_clk_p), + .ibar(input_clk_n), + .o(input_clk) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + ALT_INBUF_DIFF + data_inbuf_diff_inst ( + .i(input_d_p[n]), + .ibar(input_d_n[n]), + .o(input_d[n]) + ); + end +end else begin + assign input_clk = input_clk_p; + assign input_d = input_d_p; +end + +endgenerate + +ssio_ddr_in #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .WIDTH(WIDTH) +) +ssio_ddr_in_inst( + .input_clk(input_clk), + .input_d(input_d), + .output_clk(output_clk), + .output_q1(output_q1), + .output_q2(output_q2) +); + +endmodule diff --git a/fpga/lib/eth/rtl/ssio_ddr_out.v b/fpga/lib/eth/rtl/ssio_ddr_out.v new file mode 100644 index 000000000..c77aa691e --- /dev/null +++ b/fpga/lib/eth/rtl/ssio_ddr_out.v @@ -0,0 +1,82 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Generic source synchronous DDR output + */ +module ssio_ddr_out # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Use 90 degree clock for transmit ("TRUE", "FALSE") + parameter USE_CLK90 = "TRUE", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + input wire clk90, + + input wire [WIDTH-1:0] input_d1, + input wire [WIDTH-1:0] input_d2, + + output wire output_clk, + output wire [WIDTH-1:0] output_q +); + +wire ref_clk = USE_CLK90 == "TRUE" ? clk90 : clk; + +oddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(1) +) +clk_oddr_inst ( + .clk(ref_clk), + .d1(1'b1), + .d2(1'b0), + .q(output_clk) +); + +oddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(WIDTH) +) +data_oddr_inst ( + .clk(clk), + .d1(input_d1), + .d2(input_d2), + .q(output_q) +); + +endmodule diff --git a/fpga/lib/eth/rtl/ssio_ddr_out_diff.v b/fpga/lib/eth/rtl/ssio_ddr_out_diff.v new file mode 100644 index 000000000..ba1beadc4 --- /dev/null +++ b/fpga/lib/eth/rtl/ssio_ddr_out_diff.v @@ -0,0 +1,119 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Generic source synchronous DDR output + */ +module ssio_ddr_out_diff # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Use 90 degree clock for transmit ("TRUE", "FALSE") + parameter USE_CLK90 = "TRUE", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + input wire clk90, + + input wire [WIDTH-1:0] input_d1, + input wire [WIDTH-1:0] input_d2, + + output wire output_clk_p, + output wire output_clk_n, + output wire [WIDTH-1:0] output_q_p, + output wire [WIDTH-1:0] output_q_n +); + +wire output_clk; +wire [WIDTH-1:0] output_q; + +ssio_ddr_out #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .USE_CLK90(USE_CLK90), + .WIDTH(WIDTH) +) +ssio_ddr_out_inst( + .clk(clk), + .clk90(clk90), + .input_d1(input_d1), + .input_d2(input_d2), + .output_clk(output_clk), + .output_q(output_q) +); + +genvar n; + +generate + +if (TARGET == "XILINX") begin + OBUFDS + clk_obufds_inst ( + .I(output_clk), + .O(output_clk_p), + .OB(output_clk_n) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + OBUFDS + data_obufds_inst ( + .I(output_q[n]), + .O(output_q_p[n]), + .OB(output_q_n[n]) + ); + end +end else if (TARGET == "ALTERA") begin + ALT_OUTBUF_DIFF + clk_outbuf_diff_inst ( + .i(output_clk), + .o(output_clk_p), + .obar(output_clk_n) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + ALT_OUTBUF_DIFF + data_outbuf_diff_inst ( + .i(output_q[n]), + .o(output_q_p[n]), + .obar(output_q_n[n]) + ); + end +end else begin + assign output_clk_p = output_clk; + assign output_clk_n = ~output_clk; + assign output_q_p = output_q; + assign output_q_n = ~output_q; +end + +endgenerate + +endmodule diff --git a/fpga/lib/eth/rtl/ssio_sdr_in.v b/fpga/lib/eth/rtl/ssio_sdr_in.v new file mode 100644 index 000000000..09797f4c8 --- /dev/null +++ b/fpga/lib/eth/rtl/ssio_sdr_in.v @@ -0,0 +1,163 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Generic source synchronous SDR input + */ +module ssio_sdr_in # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q +); + +wire clk_int; +wire clk_io; + +generate + +if (TARGET == "XILINX") begin + + // use Xilinx clocking primitives + + if (CLOCK_INPUT_STYLE == "BUFG") begin + + // buffer RX clock + BUFG + clk_bufg ( + .I(input_clk), + .O(clk_int) + ); + + // pass through RX clock to logic and input buffers + assign clk_io = clk_int; + assign output_clk = clk_int; + + end else if (CLOCK_INPUT_STYLE == "BUFR") begin + + assign clk_int = input_clk; + + // pass through RX clock to input buffers + BUFIO + clk_bufio ( + .I(clk_int), + .O(clk_io) + ); + + // pass through RX clock to logic + BUFR #( + .BUFR_DIVIDE("BYPASS") + ) + clk_bufr ( + .I(clk_int), + .O(output_clk), + .CE(1'b1), + .CLR(1'b0) + ); + + end else if (CLOCK_INPUT_STYLE == "BUFIO") begin + + assign clk_int = input_clk; + + // pass through RX clock to input buffers + BUFIO + clk_bufio ( + .I(clk_int), + .O(clk_io) + ); + + // pass through RX clock to MAC + BUFG + clk_bufg ( + .I(clk_int), + .O(output_clk) + ); + + end else if (CLOCK_INPUT_STYLE == "BUFIO2") begin + + // pass through RX clock to input buffers + BUFIO2 #( + .DIVIDE(1), + .DIVIDE_BYPASS("TRUE"), + .I_INVERT("FALSE"), + .USE_DOUBLER("FALSE") + ) + clk_bufio ( + .I(input_clk), + .DIVCLK(clk_int), + .IOCLK(clk_io), + .SERDESSTROBE() + ); + + // pass through RX clock to MAC + BUFG + clk_bufg ( + .I(clk_int), + .O(output_clk) + ); + + end + +end else begin + + // pass through RX clock to input buffers + assign clk_io = input_clk; + + // pass through RX clock to logic + assign clk_int = input_clk; + assign output_clk = clk_int; + +end + +endgenerate + +(* IOB = "TRUE" *) +reg [WIDTH-1:0] output_q_reg = {WIDTH{1'b0}}; + +assign output_q = output_q_reg; + +always @(posedge clk_io) begin + output_q_reg <= input_d; +end + +endmodule diff --git a/fpga/lib/eth/rtl/ssio_sdr_in_diff.v b/fpga/lib/eth/rtl/ssio_sdr_in_diff.v new file mode 100644 index 000000000..625014389 --- /dev/null +++ b/fpga/lib/eth/rtl/ssio_sdr_in_diff.v @@ -0,0 +1,113 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Generic source synchronous SDR input + */ +module ssio_sdr_in_diff # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk_p, + input wire input_clk_n, + + input wire [WIDTH-1:0] input_d_p, + input wire [WIDTH-1:0] input_d_n, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q +); + +wire input_clk; +wire [WIDTH-1:0] input_d; + +genvar n; + +generate + +if (TARGET == "XILINX") begin + IBUFDS + clk_ibufds_inst ( + .I(input_clk_p), + .IB(input_clk_n), + .O(input_clk) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + IBUFDS + data_ibufds_inst ( + .I(input_d_p[n]), + .IB(input_d_n[n]), + .O(input_d[n]) + ); + end +end else if (TARGET == "ALTERA") begin + ALT_INBUF_DIFF + clk_inbuf_diff_inst ( + .i(input_clk_p), + .ibar(input_clk_n), + .o(input_clk) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + ALT_INBUF_DIFF + data_inbuf_diff_inst ( + .i(input_d_p[n]), + .ibar(input_d_n[n]), + .o(input_d[n]) + ); + end +end else begin + assign input_clk = input_clk_p; + assign input_d = input_d_p; +end + +endgenerate + +ssio_sdr_in #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .WIDTH(WIDTH) +) +ssio_ddr_in_inst( + .input_clk(input_clk), + .input_d(input_d), + .output_clk(output_clk), + .output_q(output_q) +); + +endmodule diff --git a/fpga/lib/eth/rtl/ssio_sdr_out.v b/fpga/lib/eth/rtl/ssio_sdr_out.v new file mode 100644 index 000000000..702f9060a --- /dev/null +++ b/fpga/lib/eth/rtl/ssio_sdr_out.v @@ -0,0 +1,73 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Generic source synchronous SDR output + */ +module ssio_sdr_out # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk, + output wire [WIDTH-1:0] output_q +); + +oddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(1) +) +clk_oddr_inst ( + .clk(clk), + .d1(1'b0), + .d2(1'b1), + .q(output_clk) +); + +(* IOB = "TRUE" *) +reg [WIDTH-1:0] output_q_reg = {WIDTH{1'b0}}; + +assign output_q = output_q_reg; + +always @(posedge clk) begin + output_q_reg <= input_d; +end + +endmodule diff --git a/fpga/lib/eth/rtl/ssio_sdr_out_diff.v b/fpga/lib/eth/rtl/ssio_sdr_out_diff.v new file mode 100644 index 000000000..5fe26076b --- /dev/null +++ b/fpga/lib/eth/rtl/ssio_sdr_out_diff.v @@ -0,0 +1,112 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Generic source synchronous SDR output + */ +module ssio_sdr_out_diff # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk_p, + output wire output_clk_n, + output wire [WIDTH-1:0] output_q_p, + output wire [WIDTH-1:0] output_q_n +); + +wire output_clk; +wire [WIDTH-1:0] output_q; + +ssio_sdr_out #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(WIDTH) +) +ssio_ddr_out_inst( + .clk(clk), + .input_d(input_d), + .output_clk(output_clk), + .output_q(output_q) +); + +genvar n; + +generate + +if (TARGET == "XILINX") begin + OBUFDS + clk_obufds_inst ( + .I(output_clk), + .O(output_clk_p), + .OB(output_clk_n) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + OBUFDS + data_obufds_inst ( + .I(output_q[n]), + .O(output_q_p[n]), + .OB(output_q_n[n]) + ); + end +end else if (TARGET == "ALTERA") begin + ALT_OUTBUF_DIFF + clk_outbuf_diff_inst ( + .i(output_clk), + .o(output_clk_p), + .obar(output_clk_n) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + ALT_OUTBUF_DIFF + data_outbuf_diff_inst ( + .i(output_q[n]), + .o(output_q_p[n]), + .obar(output_q_n[n]) + ); + end +end else begin + assign output_clk_p = output_clk; + assign output_clk_n = ~output_clk; + assign output_q_p = output_q; + assign output_q_n = ~output_q; +end + +endgenerate + +endmodule diff --git a/fpga/lib/eth/rtl/udp.v b/fpga/lib/eth/rtl/udp.v new file mode 100644 index 000000000..94e6ad614 --- /dev/null +++ b/fpga/lib/eth/rtl/udp.v @@ -0,0 +1,413 @@ +/* + +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 1ns / 1ps + +/* + * UDP block, IP interface + */ +module udp # +( + parameter CHECKSUM_GEN_ENABLE = 1, + parameter CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11, + parameter CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3 +) +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_ip_eth_dest_mac, + input wire [47:0] s_ip_eth_src_mac, + input wire [15:0] s_ip_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_udp_eth_dest_mac, + input wire [47:0] s_udp_eth_src_mac, + input wire [15:0] s_udp_eth_type, + input wire [3:0] s_udp_ip_version, + input wire [3:0] s_udp_ip_ihl, + input wire [5:0] s_udp_ip_dscp, + input wire [1:0] s_udp_ip_ecn, + input wire [15:0] s_udp_ip_identification, + input wire [2:0] s_udp_ip_flags, + input wire [12:0] s_udp_ip_fragment_offset, + input wire [7:0] s_udp_ip_ttl, + input wire [15:0] s_udp_ip_header_checksum, + input wire [31:0] s_udp_ip_source_ip, + input wire [31:0] s_udp_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [7:0] s_udp_payload_axis_tdata, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_udp_eth_dest_mac, + output wire [47:0] m_udp_eth_src_mac, + output wire [15:0] m_udp_eth_type, + output wire [3:0] m_udp_ip_version, + output wire [3:0] m_udp_ip_ihl, + output wire [5:0] m_udp_ip_dscp, + output wire [1:0] m_udp_ip_ecn, + output wire [15:0] m_udp_ip_length, + output wire [15:0] m_udp_ip_identification, + output wire [2:0] m_udp_ip_flags, + output wire [12:0] m_udp_ip_fragment_offset, + output wire [7:0] m_udp_ip_ttl, + output wire [7:0] m_udp_ip_protocol, + output wire [15:0] m_udp_ip_header_checksum, + output wire [31:0] m_udp_ip_source_ip, + output wire [31:0] m_udp_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [7:0] m_udp_payload_axis_tdata, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire tx_error_payload_early_termination +); + +wire tx_udp_hdr_valid; +wire tx_udp_hdr_ready; +wire [47:0] tx_udp_eth_dest_mac; +wire [47:0] tx_udp_eth_src_mac; +wire [15:0] tx_udp_eth_type; +wire [3:0] tx_udp_ip_version; +wire [3:0] tx_udp_ip_ihl; +wire [5:0] tx_udp_ip_dscp; +wire [1:0] tx_udp_ip_ecn; +wire [15:0] tx_udp_ip_identification; +wire [2:0] tx_udp_ip_flags; +wire [12:0] tx_udp_ip_fragment_offset; +wire [7:0] tx_udp_ip_ttl; +wire [15:0] tx_udp_ip_header_checksum; +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; + +udp_ip_rx +udp_ip_rx_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_ip_eth_dest_mac), + .s_eth_src_mac(s_ip_eth_src_mac), + .s_eth_type(s_ip_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_udp_eth_dest_mac), + .m_eth_src_mac(m_udp_eth_src_mac), + .m_eth_type(m_udp_eth_type), + .m_ip_version(m_udp_ip_version), + .m_ip_ihl(m_udp_ip_ihl), + .m_ip_dscp(m_udp_ip_dscp), + .m_ip_ecn(m_udp_ip_ecn), + .m_ip_length(m_udp_ip_length), + .m_ip_identification(m_udp_ip_identification), + .m_ip_flags(m_udp_ip_flags), + .m_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_ip_ttl(m_udp_ip_ttl), + .m_ip_protocol(m_udp_ip_protocol), + .m_ip_header_checksum(m_udp_ip_header_checksum), + .m_ip_source_ip(m_udp_ip_source_ip), + .m_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status signals + .busy(rx_busy), + .error_header_early_termination(rx_error_header_early_termination), + .error_payload_early_termination(rx_error_payload_early_termination) +); + +generate + +if (CHECKSUM_GEN_ENABLE) begin + + udp_checksum_gen #( + .PAYLOAD_FIFO_ADDR_WIDTH(CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH), + .HEADER_FIFO_ADDR_WIDTH(CHECKSUM_HEADER_FIFO_ADDR_WIDTH) + ) + udp_checksum_gen_inst ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_udp_eth_dest_mac), + .s_eth_src_mac(s_udp_eth_src_mac), + .s_eth_type(s_udp_eth_type), + .s_ip_version(s_udp_ip_version), + .s_ip_ihl(s_udp_ip_ihl), + .s_ip_dscp(s_udp_ip_dscp), + .s_ip_ecn(s_udp_ip_ecn), + .s_ip_identification(s_udp_ip_identification), + .s_ip_flags(s_udp_ip_flags), + .s_ip_fragment_offset(s_udp_ip_fragment_offset), + .s_ip_ttl(s_udp_ip_ttl), + .s_ip_header_checksum(s_udp_ip_header_checksum), + .s_ip_source_ip(s_udp_ip_source_ip), + .s_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(tx_udp_hdr_valid), + .m_udp_hdr_ready(tx_udp_hdr_ready), + .m_eth_dest_mac(tx_udp_eth_dest_mac), + .m_eth_src_mac(tx_udp_eth_src_mac), + .m_eth_type(tx_udp_eth_type), + .m_ip_version(tx_udp_ip_version), + .m_ip_ihl(tx_udp_ip_ihl), + .m_ip_dscp(tx_udp_ip_dscp), + .m_ip_ecn(tx_udp_ip_ecn), + .m_ip_length(), + .m_ip_identification(tx_udp_ip_identification), + .m_ip_flags(tx_udp_ip_flags), + .m_ip_fragment_offset(tx_udp_ip_fragment_offset), + .m_ip_ttl(tx_udp_ip_ttl), + .m_ip_protocol(), + .m_ip_header_checksum(tx_udp_ip_header_checksum), + .m_ip_source_ip(tx_udp_ip_source_ip), + .m_ip_dest_ip(tx_udp_ip_dest_ip), + .m_udp_source_port(tx_udp_source_port), + .m_udp_dest_port(tx_udp_dest_port), + .m_udp_length(tx_udp_length), + .m_udp_checksum(tx_udp_checksum), + .m_udp_payload_axis_tdata(tx_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(tx_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(tx_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(tx_udp_payload_axis_tuser), + // Status signals + .busy() + ); + +end else begin + + assign tx_udp_hdr_valid = s_udp_hdr_valid; + assign s_udp_hdr_ready = tx_udp_hdr_ready; + assign tx_udp_eth_dest_mac = s_udp_eth_dest_mac; + assign tx_udp_eth_src_mac = s_udp_eth_src_mac; + assign tx_udp_eth_type = s_udp_eth_type; + assign tx_udp_ip_version = s_udp_ip_version; + assign tx_udp_ip_ihl = s_udp_ip_ihl; + assign tx_udp_ip_dscp = s_udp_ip_dscp; + assign tx_udp_ip_ecn = s_udp_ip_ecn; + assign tx_udp_ip_identification = s_udp_ip_identification; + assign tx_udp_ip_flags = s_udp_ip_flags; + assign tx_udp_ip_fragment_offset = s_udp_ip_fragment_offset; + assign tx_udp_ip_ttl = s_udp_ip_ttl; + assign tx_udp_ip_header_checksum = s_udp_ip_header_checksum; + assign tx_udp_ip_source_ip = s_udp_ip_source_ip; + assign tx_udp_ip_dest_ip = s_udp_ip_dest_ip; + assign tx_udp_source_port = s_udp_source_port; + assign tx_udp_dest_port = s_udp_dest_port; + assign tx_udp_length = s_udp_length; + assign tx_udp_checksum = s_udp_checksum; + assign tx_udp_payload_axis_tdata = s_udp_payload_axis_tdata; + assign tx_udp_payload_axis_tvalid = s_udp_payload_axis_tvalid; + assign s_udp_payload_axis_tready = tx_udp_payload_axis_tready; + assign tx_udp_payload_axis_tlast = s_udp_payload_axis_tlast; + assign tx_udp_payload_axis_tuser = s_udp_payload_axis_tuser; + +end + +endgenerate + +udp_ip_tx +udp_ip_tx_inst ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(tx_udp_hdr_valid), + .s_udp_hdr_ready(tx_udp_hdr_ready), + .s_eth_dest_mac(tx_udp_eth_dest_mac), + .s_eth_src_mac(tx_udp_eth_src_mac), + .s_eth_type(tx_udp_eth_type), + .s_ip_version(tx_udp_ip_version), + .s_ip_ihl(tx_udp_ip_ihl), + .s_ip_dscp(tx_udp_ip_dscp), + .s_ip_ecn(tx_udp_ip_ecn), + .s_ip_identification(tx_udp_ip_identification), + .s_ip_flags(tx_udp_ip_flags), + .s_ip_fragment_offset(tx_udp_ip_fragment_offset), + .s_ip_ttl(tx_udp_ip_ttl), + .s_ip_protocol(8'h11), + .s_ip_header_checksum(tx_udp_ip_header_checksum), + .s_ip_source_ip(tx_udp_ip_source_ip), + .s_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), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_ip_eth_dest_mac), + .m_eth_src_mac(m_ip_eth_src_mac), + .m_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(tx_busy), + .error_payload_early_termination(tx_error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/rtl/udp_64.v b/fpga/lib/eth/rtl/udp_64.v new file mode 100644 index 000000000..2feaff945 --- /dev/null +++ b/fpga/lib/eth/rtl/udp_64.v @@ -0,0 +1,424 @@ +/* + +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 1ns / 1ps + +/* + * UDP block, IP interface (64 bit datapath) + */ +module udp_64 # +( + parameter CHECKSUM_GEN_ENABLE = 1, + parameter CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11, + parameter CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3 +) +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_ip_eth_dest_mac, + input wire [47:0] s_ip_eth_src_mac, + input wire [15:0] s_ip_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_udp_eth_dest_mac, + input wire [47:0] s_udp_eth_src_mac, + input wire [15:0] s_udp_eth_type, + input wire [3:0] s_udp_ip_version, + input wire [3:0] s_udp_ip_ihl, + input wire [5:0] s_udp_ip_dscp, + input wire [1:0] s_udp_ip_ecn, + input wire [15:0] s_udp_ip_identification, + input wire [2:0] s_udp_ip_flags, + input wire [12:0] s_udp_ip_fragment_offset, + input wire [7:0] s_udp_ip_ttl, + input wire [15:0] s_udp_ip_header_checksum, + input wire [31:0] s_udp_ip_source_ip, + input wire [31:0] s_udp_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [63:0] s_udp_payload_axis_tdata, + input wire [7:0] s_udp_payload_axis_tkeep, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_udp_eth_dest_mac, + output wire [47:0] m_udp_eth_src_mac, + output wire [15:0] m_udp_eth_type, + output wire [3:0] m_udp_ip_version, + output wire [3:0] m_udp_ip_ihl, + output wire [5:0] m_udp_ip_dscp, + output wire [1:0] m_udp_ip_ecn, + output wire [15:0] m_udp_ip_length, + output wire [15:0] m_udp_ip_identification, + output wire [2:0] m_udp_ip_flags, + output wire [12:0] m_udp_ip_fragment_offset, + output wire [7:0] m_udp_ip_ttl, + output wire [7:0] m_udp_ip_protocol, + output wire [15:0] m_udp_ip_header_checksum, + output wire [31:0] m_udp_ip_source_ip, + output wire [31:0] m_udp_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [63:0] m_udp_payload_axis_tdata, + output wire [7:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire tx_error_payload_early_termination +); + +wire tx_udp_hdr_valid; +wire tx_udp_hdr_ready; +wire [47:0] tx_udp_eth_dest_mac; +wire [47:0] tx_udp_eth_src_mac; +wire [15:0] tx_udp_eth_type; +wire [3:0] tx_udp_ip_version; +wire [3:0] tx_udp_ip_ihl; +wire [5:0] tx_udp_ip_dscp; +wire [1:0] tx_udp_ip_ecn; +wire [15:0] tx_udp_ip_identification; +wire [2:0] tx_udp_ip_flags; +wire [12:0] tx_udp_ip_fragment_offset; +wire [7:0] tx_udp_ip_ttl; +wire [15:0] tx_udp_ip_header_checksum; +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 [63:0] tx_udp_payload_axis_tdata; +wire [7:0] tx_udp_payload_axis_tkeep; +wire tx_udp_payload_axis_tvalid; +wire tx_udp_payload_axis_tready; +wire tx_udp_payload_axis_tlast; +wire tx_udp_payload_axis_tuser; + +udp_ip_rx_64 +udp_ip_rx_64_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_ip_eth_dest_mac), + .s_eth_src_mac(s_ip_eth_src_mac), + .s_eth_type(s_ip_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_udp_eth_dest_mac), + .m_eth_src_mac(m_udp_eth_src_mac), + .m_eth_type(m_udp_eth_type), + .m_ip_version(m_udp_ip_version), + .m_ip_ihl(m_udp_ip_ihl), + .m_ip_dscp(m_udp_ip_dscp), + .m_ip_ecn(m_udp_ip_ecn), + .m_ip_length(m_udp_ip_length), + .m_ip_identification(m_udp_ip_identification), + .m_ip_flags(m_udp_ip_flags), + .m_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_ip_ttl(m_udp_ip_ttl), + .m_ip_protocol(m_udp_ip_protocol), + .m_ip_header_checksum(m_udp_ip_header_checksum), + .m_ip_source_ip(m_udp_ip_source_ip), + .m_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status signals + .busy(rx_busy), + .error_header_early_termination(rx_error_header_early_termination), + .error_payload_early_termination(rx_error_payload_early_termination) +); + +generate + +if (CHECKSUM_GEN_ENABLE) begin + + udp_checksum_gen_64 #( + .PAYLOAD_FIFO_ADDR_WIDTH(CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH), + .HEADER_FIFO_ADDR_WIDTH(CHECKSUM_HEADER_FIFO_ADDR_WIDTH) + ) + udp_checksum_gen_64_inst ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_udp_eth_dest_mac), + .s_eth_src_mac(s_udp_eth_src_mac), + .s_eth_type(s_udp_eth_type), + .s_ip_version(s_udp_ip_version), + .s_ip_ihl(s_udp_ip_ihl), + .s_ip_dscp(s_udp_ip_dscp), + .s_ip_ecn(s_udp_ip_ecn), + .s_ip_identification(s_udp_ip_identification), + .s_ip_flags(s_udp_ip_flags), + .s_ip_fragment_offset(s_udp_ip_fragment_offset), + .s_ip_ttl(s_udp_ip_ttl), + .s_ip_header_checksum(s_udp_ip_header_checksum), + .s_ip_source_ip(s_udp_ip_source_ip), + .s_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(tx_udp_hdr_valid), + .m_udp_hdr_ready(tx_udp_hdr_ready), + .m_eth_dest_mac(tx_udp_eth_dest_mac), + .m_eth_src_mac(tx_udp_eth_src_mac), + .m_eth_type(tx_udp_eth_type), + .m_ip_version(tx_udp_ip_version), + .m_ip_ihl(tx_udp_ip_ihl), + .m_ip_dscp(tx_udp_ip_dscp), + .m_ip_ecn(tx_udp_ip_ecn), + .m_ip_length(), + .m_ip_identification(tx_udp_ip_identification), + .m_ip_flags(tx_udp_ip_flags), + .m_ip_fragment_offset(tx_udp_ip_fragment_offset), + .m_ip_ttl(tx_udp_ip_ttl), + .m_ip_header_checksum(tx_udp_ip_header_checksum), + .m_ip_source_ip(tx_udp_ip_source_ip), + .m_ip_dest_ip(tx_udp_ip_dest_ip), + .m_udp_source_port(tx_udp_source_port), + .m_udp_dest_port(tx_udp_dest_port), + .m_udp_length(tx_udp_length), + .m_udp_checksum(tx_udp_checksum), + .m_udp_payload_axis_tdata(tx_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(tx_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(tx_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(tx_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(tx_udp_payload_axis_tuser), + // Status signals + .busy() + ); + +end else begin + + assign tx_udp_hdr_valid = s_udp_hdr_valid; + assign s_udp_hdr_ready = tx_udp_hdr_ready; + assign tx_udp_eth_dest_mac = s_udp_eth_dest_mac; + assign tx_udp_eth_src_mac = s_udp_eth_src_mac; + assign tx_udp_eth_type = s_udp_eth_type; + assign tx_udp_ip_version = s_udp_ip_version; + assign tx_udp_ip_ihl = s_udp_ip_ihl; + assign tx_udp_ip_dscp = s_udp_ip_dscp; + assign tx_udp_ip_ecn = s_udp_ip_ecn; + assign tx_udp_ip_identification = s_udp_ip_identification; + assign tx_udp_ip_flags = s_udp_ip_flags; + assign tx_udp_ip_fragment_offset = s_udp_ip_fragment_offset; + assign tx_udp_ip_ttl = s_udp_ip_ttl; + assign tx_udp_ip_header_checksum = s_udp_ip_header_checksum; + assign tx_udp_ip_source_ip = s_udp_ip_source_ip; + assign tx_udp_ip_dest_ip = s_udp_ip_dest_ip; + assign tx_udp_source_port = s_udp_source_port; + assign tx_udp_dest_port = s_udp_dest_port; + assign tx_udp_length = s_udp_length; + assign tx_udp_checksum = s_udp_checksum; + assign tx_udp_payload_axis_tdata = s_udp_payload_axis_tdata; + assign tx_udp_payload_axis_tkeep = s_udp_payload_axis_tkeep; + assign tx_udp_payload_axis_tvalid = s_udp_payload_axis_tvalid; + assign s_udp_payload_axis_tready = tx_udp_payload_axis_tready; + assign tx_udp_payload_axis_tlast = s_udp_payload_axis_tlast; + assign tx_udp_payload_axis_tuser = s_udp_payload_axis_tuser; + +end + +endgenerate + +udp_ip_tx_64 +udp_ip_tx_64_inst ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(tx_udp_hdr_valid), + .s_udp_hdr_ready(tx_udp_hdr_ready), + .s_eth_dest_mac(tx_udp_eth_dest_mac), + .s_eth_src_mac(tx_udp_eth_src_mac), + .s_eth_type(tx_udp_eth_type), + .s_ip_version(tx_udp_ip_version), + .s_ip_ihl(tx_udp_ip_ihl), + .s_ip_dscp(tx_udp_ip_dscp), + .s_ip_ecn(tx_udp_ip_ecn), + .s_ip_identification(tx_udp_ip_identification), + .s_ip_flags(tx_udp_ip_flags), + .s_ip_fragment_offset(tx_udp_ip_fragment_offset), + .s_ip_ttl(tx_udp_ip_ttl), + .s_ip_protocol(8'h11), + .s_ip_header_checksum(tx_udp_ip_header_checksum), + .s_ip_source_ip(tx_udp_ip_source_ip), + .s_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_tkeep(tx_udp_payload_axis_tkeep), + .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), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_ip_eth_dest_mac), + .m_eth_src_mac(m_ip_eth_src_mac), + .m_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(tx_busy), + .error_payload_early_termination(tx_error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/rtl/udp_arb_mux.v b/fpga/lib/eth/rtl/udp_arb_mux.v new file mode 100644 index 000000000..abe2a1e10 --- /dev/null +++ b/fpga/lib/eth/rtl/udp_arb_mux.v @@ -0,0 +1,435 @@ +/* + +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 1ns / 1ps + +/* + * UDP arbitrated multiplexer + */ +module udp_arb_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * UDP frame inputs + */ + input wire [S_COUNT-1:0] s_udp_hdr_valid, + output wire [S_COUNT-1:0] s_udp_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*4-1:0] s_ip_version, + input wire [S_COUNT*4-1:0] s_ip_ihl, + input wire [S_COUNT*6-1:0] s_ip_dscp, + input wire [S_COUNT*2-1:0] s_ip_ecn, + input wire [S_COUNT*16-1:0] s_ip_length, + input wire [S_COUNT*16-1:0] s_ip_identification, + input wire [S_COUNT*3-1:0] s_ip_flags, + input wire [S_COUNT*13-1:0] s_ip_fragment_offset, + input wire [S_COUNT*8-1:0] s_ip_ttl, + input wire [S_COUNT*8-1:0] s_ip_protocol, + input wire [S_COUNT*16-1:0] s_ip_header_checksum, + input wire [S_COUNT*32-1:0] s_ip_source_ip, + input wire [S_COUNT*32-1:0] s_ip_dest_ip, + input wire [S_COUNT*16-1:0] s_udp_source_port, + input wire [S_COUNT*16-1:0] s_udp_dest_port, + input wire [S_COUNT*16-1:0] s_udp_length, + input wire [S_COUNT*16-1:0] s_udp_checksum, + input wire [S_COUNT*DATA_WIDTH-1:0] s_udp_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_udp_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_udp_payload_axis_tready, + input wire [S_COUNT-1:0] s_udp_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_udp_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_udp_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [DATA_WIDTH-1:0] m_udp_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_udp_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_udp_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_udp_payload_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg frame_reg = 1'b0, frame_next; + +reg s_udp_hdr_ready_mask_reg = 1'b0, s_udp_hdr_ready_mask_next; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; +reg [15:0] m_udp_source_port_reg = 16'd0, m_udp_source_port_next; +reg [15:0] m_udp_dest_port_reg = 16'd0, m_udp_dest_port_next; +reg [15:0] m_udp_length_reg = 16'd0, m_udp_length_next; +reg [15:0] m_udp_checksum_reg = 16'd0, m_udp_checksum_next; + +wire [S_COUNT-1:0] request; +wire [S_COUNT-1:0] acknowledge; +wire [S_COUNT-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT-1:0] grant_encoded; + +// internal datapath +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_int; +reg m_udp_payload_axis_tvalid_int; +reg m_udp_payload_axis_tready_int_reg = 1'b0; +reg m_udp_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_int; +wire m_udp_payload_axis_tready_int_early; + +assign s_udp_hdr_ready = (!s_udp_hdr_ready_mask_reg && grant_valid) << grant_encoded; + +assign s_udp_payload_axis_tready = (m_udp_payload_axis_tready_int_reg && grant_valid) << grant_encoded; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_udp_payload_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_udp_payload_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_udp_payload_axis_tvalid[grant_encoded]; +wire current_s_tready = s_udp_payload_axis_tready[grant_encoded]; +wire current_s_tlast = s_udp_payload_axis_tlast[grant_encoded]; +wire [ID_WIDTH-1:0] current_s_tid = s_udp_payload_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_udp_payload_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_udp_payload_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + +// arbiter instance +arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +generate + genvar n; + + for (n = 0; n < S_COUNT; n = n + 1) begin + assign request[n] = s_udp_hdr_valid[n] && !grant[n]; + assign acknowledge[n] = grant[n] && s_udp_payload_axis_tvalid[n] && s_udp_payload_axis_tready[n] && s_udp_payload_axis_tlast[n]; + end +endgenerate + +always @* begin + frame_next = frame_reg; + + s_udp_hdr_ready_mask_next = s_udp_hdr_ready_mask_reg; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg && !m_udp_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + m_udp_source_port_next = m_udp_source_port_reg; + m_udp_dest_port_next = m_udp_dest_port_reg; + m_udp_length_next = m_udp_length_reg; + m_udp_checksum_next = m_udp_checksum_reg; + + if (s_udp_payload_axis_tvalid[grant_encoded] && s_udp_payload_axis_tready[grant_encoded]) begin + // end of frame detection + if (s_udp_payload_axis_tlast[grant_encoded]) begin + frame_next = 1'b0; + s_udp_hdr_ready_mask_next = 1'b0; + end + end + + if (!frame_reg && grant_valid) begin + // start of frame + frame_next = 1'b1; + + s_udp_hdr_ready_mask_next = 1'b1; + + m_udp_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[grant_encoded*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[grant_encoded*48 +: 48]; + m_eth_type_next = s_eth_type[grant_encoded*16 +: 16]; + m_ip_version_next = s_ip_version[grant_encoded*4 +: 4]; + m_ip_ihl_next = s_ip_ihl[grant_encoded*4 +: 4]; + m_ip_dscp_next = s_ip_dscp[grant_encoded*6 +: 6]; + m_ip_ecn_next = s_ip_ecn[grant_encoded*2 +: 2]; + m_ip_length_next = s_ip_length[grant_encoded*16 +: 16]; + m_ip_identification_next = s_ip_identification[grant_encoded*16 +: 16]; + m_ip_flags_next = s_ip_flags[grant_encoded*3 +: 3]; + m_ip_fragment_offset_next = s_ip_fragment_offset[grant_encoded*13 +: 13]; + m_ip_ttl_next = s_ip_ttl[grant_encoded*8 +: 8]; + m_ip_protocol_next = s_ip_protocol[grant_encoded*8 +: 8]; + m_ip_header_checksum_next = s_ip_header_checksum[grant_encoded*16 +: 16]; + m_ip_source_ip_next = s_ip_source_ip[grant_encoded*32 +: 32]; + m_ip_dest_ip_next = s_ip_dest_ip[grant_encoded*32 +: 32]; + m_udp_source_port_next = s_udp_source_port[grant_encoded*16 +: 16]; + m_udp_dest_port_next = s_udp_dest_port[grant_encoded*16 +: 16]; + m_udp_length_next = s_udp_length[grant_encoded*16 +: 16]; + m_udp_checksum_next = s_udp_checksum[grant_encoded*16 +: 16]; + end + + // pass through selected packet data + m_udp_payload_axis_tdata_int = current_s_tdata; + m_udp_payload_axis_tkeep_int = current_s_tkeep; + m_udp_payload_axis_tvalid_int = current_s_tvalid && m_udp_payload_axis_tready_int_reg && grant_valid; + m_udp_payload_axis_tlast_int = current_s_tlast; + m_udp_payload_axis_tid_int = current_s_tid; + m_udp_payload_axis_tdest_int = current_s_tdest; + m_udp_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + frame_reg <= 1'b0; + s_udp_hdr_ready_mask_reg <= 1'b0; + m_udp_hdr_valid_reg <= 1'b0; + end else begin + frame_reg <= frame_next; + s_udp_hdr_ready_mask_reg <= s_udp_hdr_ready_mask_next; + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; + m_udp_source_port_reg <= m_udp_source_port_next; + m_udp_dest_port_reg <= m_udp_dest_port_next; + m_udp_length_reg <= m_udp_length_next; + m_udp_checksum_reg <= m_udp_checksum_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_udp_payload_axis_tvalid_reg = 1'b0, m_udp_payload_axis_tvalid_next; +reg m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_udp_payload_axis_tvalid_reg = 1'b0, temp_m_udp_payload_axis_tvalid_next; +reg temp_m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_udp_payload_axis_temp_to_output; + +assign m_udp_payload_axis_tdata = m_udp_payload_axis_tdata_reg; +assign m_udp_payload_axis_tkeep = KEEP_ENABLE ? m_udp_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg; +assign m_udp_payload_axis_tlast = m_udp_payload_axis_tlast_reg; +assign m_udp_payload_axis_tid = ID_ENABLE ? m_udp_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_udp_payload_axis_tdest = DEST_ENABLE ? m_udp_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_udp_payload_axis_tuser = USER_ENABLE ? m_udp_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_udp_payload_axis_tready_int_early = m_udp_payload_axis_tready || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid_reg || !m_udp_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b0; + + if (m_udp_payload_axis_tready_int_reg) begin + // input is ready + if (m_udp_payload_axis_tready || !m_udp_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_udp_payload_axis_tready) begin + // input is not ready, but output is ready + m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_udp_payload_axis_tvalid_reg <= 1'b0; + m_udp_payload_axis_tready_int_reg <= 1'b0; + temp_m_udp_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next; + m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early; + temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end else if (store_udp_payload_axis_temp_to_output) begin + m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg; + m_udp_payload_axis_tkeep_reg <= temp_m_udp_payload_axis_tkeep_reg; + m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg; + m_udp_payload_axis_tid_reg <= temp_m_udp_payload_axis_tid_reg; + m_udp_payload_axis_tdest_reg <= temp_m_udp_payload_axis_tdest_reg; + m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + temp_m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + temp_m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + temp_m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/udp_checksum_gen.v b/fpga/lib/eth/rtl/udp_checksum_gen.v new file mode 100644 index 000000000..41c5f1591 --- /dev/null +++ b/fpga/lib/eth/rtl/udp_checksum_gen.v @@ -0,0 +1,555 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * UDP checksum calculation module + */ +module udp_checksum_gen # +( + parameter PAYLOAD_FIFO_ADDR_WIDTH = 11, + parameter HEADER_FIFO_ADDR_WIDTH = 3 +) +( + input wire clk, + input wire rst, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [7:0] s_udp_payload_axis_tdata, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [7:0] m_udp_payload_axis_tdata, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives a UDP frame with header fields in parallel and payload on +an AXI stream interface, calculates the length and checksum, then produces the +header fields in parallel along with the UDP payload in a separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_SUM_HEADER_1 = 3'd1, + STATE_SUM_HEADER_2 = 3'd2, + STATE_SUM_HEADER_3 = 3'd3, + STATE_SUM_PAYLOAD = 3'd4, + STATE_FINISH_SUM = 3'd5; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_udp_hdr; +reg shift_payload_in; +reg [31:0] checksum_part; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [31:0] checksum_reg = 32'd0, checksum_next; + +reg [47:0] eth_dest_mac_reg = 48'd0; +reg [47:0] eth_src_mac_reg = 48'd0; +reg [15:0] eth_type_reg = 16'd0; +reg [3:0] ip_version_reg = 4'd0; +reg [3:0] ip_ihl_reg = 4'd0; +reg [5:0] ip_dscp_reg = 6'd0; +reg [1:0] ip_ecn_reg = 2'd0; +reg [15:0] ip_identification_reg = 16'd0; +reg [2:0] ip_flags_reg = 3'd0; +reg [12:0] ip_fragment_offset_reg = 13'd0; +reg [7:0] ip_ttl_reg = 8'd0; +reg [15:0] ip_header_checksum_reg = 16'd0; +reg [31:0] ip_source_ip_reg = 32'd0; +reg [31:0] ip_dest_ip_reg = 32'd0; +reg [15:0] udp_source_port_reg = 16'd0; +reg [15:0] udp_dest_port_reg = 16'd0; + +reg hdr_valid_reg = 0, hdr_valid_next; + +reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next; +reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next; + +reg busy_reg = 1'b0; + +/* + * UDP Payload FIFO + */ +wire [7:0] s_udp_payload_fifo_tdata; +wire s_udp_payload_fifo_tvalid; +wire s_udp_payload_fifo_tready; +wire s_udp_payload_fifo_tlast; +wire s_udp_payload_fifo_tuser; + +wire [7:0] m_udp_payload_fifo_tdata; +wire m_udp_payload_fifo_tvalid; +wire m_udp_payload_fifo_tready; +wire m_udp_payload_fifo_tlast; +wire m_udp_payload_fifo_tuser; + +axis_fifo #( + .ADDR_WIDTH(PAYLOAD_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(0) +) +payload_fifo ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_udp_payload_fifo_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(s_udp_payload_fifo_tvalid), + .s_axis_tready(s_udp_payload_fifo_tready), + .s_axis_tlast(s_udp_payload_fifo_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(s_udp_payload_fifo_tuser), + // AXI output + .m_axis_tdata(m_udp_payload_fifo_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(m_udp_payload_fifo_tvalid), + .m_axis_tready(m_udp_payload_fifo_tready), + .m_axis_tlast(m_udp_payload_fifo_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(m_udp_payload_fifo_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +assign s_udp_payload_fifo_tdata = s_udp_payload_axis_tdata; +assign s_udp_payload_fifo_tvalid = s_udp_payload_axis_tvalid && shift_payload_in; +assign s_udp_payload_axis_tready = s_udp_payload_fifo_tready && shift_payload_in; +assign s_udp_payload_fifo_tlast = s_udp_payload_axis_tlast; +assign s_udp_payload_fifo_tuser = s_udp_payload_axis_tuser; + +assign m_udp_payload_axis_tdata = m_udp_payload_fifo_tdata; +assign m_udp_payload_axis_tvalid = m_udp_payload_fifo_tvalid; +assign m_udp_payload_fifo_tready = m_udp_payload_axis_tready; +assign m_udp_payload_axis_tlast = m_udp_payload_fifo_tlast; +assign m_udp_payload_axis_tuser = m_udp_payload_fifo_tuser; + +/* + * UDP Header FIFO + */ +reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_wr_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_wr_ptr_next; +reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_rd_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_rd_ptr_next; + +reg [47:0] eth_dest_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [47:0] eth_src_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] eth_type_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [3:0] ip_version_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [3:0] ip_ihl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [5:0] ip_dscp_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [1:0] ip_ecn_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] ip_identification_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [2:0] ip_flags_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [12:0] ip_fragment_offset_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [7:0] ip_ttl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] ip_header_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [31:0] ip_source_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [31:0] ip_dest_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_source_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_dest_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_length_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; + +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; +reg [15:0] m_udp_source_port_reg = 16'd0; +reg [15:0] m_udp_dest_port_reg = 16'd0; +reg [15:0] m_udp_length_reg = 16'd0; +reg [15:0] m_udp_checksum_reg = 16'd0; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; + +// full when first MSB different but rest same +wire header_fifo_full = ((header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH] != header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH]) && + (header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0] == header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire header_fifo_empty = header_fifo_wr_ptr_reg == header_fifo_rd_ptr_reg; + +// control signals +reg header_fifo_write; +reg header_fifo_read; + +wire header_fifo_ready = !header_fifo_full; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; + +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_udp_length_reg + 16'd20; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = 8'h11; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +// Write logic +always @* begin + header_fifo_write = 1'b0; + + header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg; + + if (hdr_valid_reg) begin + // input data valid + if (~header_fifo_full) begin + // not full, perform write + header_fifo_write = 1'b1; + header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg + 1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + header_fifo_wr_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}; + end else begin + header_fifo_wr_ptr_reg <= header_fifo_wr_ptr_next; + end + + if (header_fifo_write) begin + eth_dest_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_dest_mac_reg; + eth_src_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_src_mac_reg; + eth_type_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_type_reg; + ip_version_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_version_reg; + ip_ihl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ihl_reg; + ip_dscp_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dscp_reg; + ip_ecn_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ecn_reg; + ip_identification_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_identification_reg; + ip_flags_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_flags_reg; + ip_fragment_offset_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_fragment_offset_reg; + ip_ttl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ttl_reg; + ip_header_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_header_checksum_reg; + ip_source_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_source_ip_reg; + ip_dest_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dest_ip_reg; + udp_source_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_source_port_reg; + udp_dest_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_dest_port_reg; + udp_length_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= frame_ptr_reg; + udp_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= checksum_reg[15:0]; + end +end + +// Read logic +always @* begin + header_fifo_read = 1'b0; + + header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg; + + if (m_udp_hdr_ready || !m_udp_hdr_valid) begin + // output data not valid OR currently being transferred + if (!header_fifo_empty) begin + // not empty, perform read + header_fifo_read = 1'b1; + m_udp_hdr_valid_next = 1'b1; + header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg + 1; + end else begin + // empty, invalidate + m_udp_hdr_valid_next = 1'b0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + header_fifo_rd_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}; + m_udp_hdr_valid_reg <= 1'b0; + end else begin + header_fifo_rd_ptr_reg <= header_fifo_rd_ptr_next; + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + end + + if (header_fifo_read) begin + m_eth_dest_mac_reg <= eth_dest_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_eth_src_mac_reg <= eth_src_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_eth_type_reg <= eth_type_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_version_reg <= ip_version_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ihl_reg <= ip_ihl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_dscp_reg <= ip_dscp_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ecn_reg <= ip_ecn_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_identification_reg <= ip_identification_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_flags_reg <= ip_flags_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_fragment_offset_reg <= ip_fragment_offset_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ttl_reg <= ip_ttl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_header_checksum_reg <= ip_header_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_source_ip_reg <= ip_source_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_dest_ip_reg <= ip_dest_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_source_port_reg <= udp_source_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_dest_port_reg <= udp_dest_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_length_reg <= udp_length_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_checksum_reg <= udp_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + end +end + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + s_udp_hdr_ready_next = 1'b0; + s_udp_payload_axis_tready_next = 1'b0; + + store_udp_hdr = 1'b0; + shift_payload_in = 1'b0; + + frame_ptr_next = frame_ptr_reg; + checksum_next = checksum_reg; + + hdr_valid_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state + s_udp_hdr_ready_next = header_fifo_ready; + + if (s_udp_hdr_ready && s_udp_hdr_valid) begin + store_udp_hdr = 1'b1; + frame_ptr_next = 0; + // 16'h0011 = zero padded type field + // 16'h0010 = header length times two + checksum_next = 16'h0011 + 16'h0010; + s_udp_hdr_ready_next = 1'b0; + state_next = STATE_SUM_HEADER_1; + end else begin + state_next = STATE_IDLE; + end + end + STATE_SUM_HEADER_1: begin + // sum pseudo header and header + checksum_next = checksum_reg + ip_source_ip_reg[31:16] + ip_source_ip_reg[15:0]; + state_next = STATE_SUM_HEADER_2; + end + STATE_SUM_HEADER_2: begin + // sum pseudo header and header + checksum_next = checksum_reg + ip_dest_ip_reg[31:16] + ip_dest_ip_reg[15:0]; + state_next = STATE_SUM_HEADER_3; + end + STATE_SUM_HEADER_3: begin + // sum pseudo header and header + checksum_next = checksum_reg + udp_source_port_reg + udp_dest_port_reg; + frame_ptr_next = 8; + state_next = STATE_SUM_PAYLOAD; + end + STATE_SUM_PAYLOAD: begin + // sum payload + shift_payload_in = 1'b1; + + if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin + // checksum computation for payload - alternately store and accumulate + // add 2 for length calculation (two length fields in pseudo header) + if (frame_ptr_reg[0]) begin + checksum_next = checksum_reg + {8'h00, s_udp_payload_axis_tdata} + 2; + end else begin + checksum_next = checksum_reg + {s_udp_payload_axis_tdata, 8'h00} + 2; + end + + frame_ptr_next = frame_ptr_reg + 1; + + if (s_udp_payload_axis_tlast) begin + state_next = STATE_FINISH_SUM; + end else begin + state_next = STATE_SUM_PAYLOAD; + end + end else begin + state_next = STATE_SUM_PAYLOAD; + end + end + STATE_FINISH_SUM: begin + // add MSW (twice!) for proper ones complement sum + checksum_part = checksum_reg[15:0] + checksum_reg[31:16]; + checksum_next = ~(checksum_part[15:0] + checksum_part[16]); + hdr_valid_next = 1; + state_next = STATE_IDLE; + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_udp_hdr_ready_reg <= 1'b0; + s_udp_payload_axis_tready_reg <= 1'b0; + hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + + hdr_valid_reg <= hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + end + + frame_ptr_reg <= frame_ptr_next; + checksum_reg <= checksum_next; + + // datapath + if (store_udp_hdr) begin + eth_dest_mac_reg <= s_eth_dest_mac; + eth_src_mac_reg <= s_eth_src_mac; + eth_type_reg <= s_eth_type; + ip_version_reg <= s_ip_version; + ip_ihl_reg <= s_ip_ihl; + ip_dscp_reg <= s_ip_dscp; + ip_ecn_reg <= s_ip_ecn; + ip_identification_reg <= s_ip_identification; + ip_flags_reg <= s_ip_flags; + ip_fragment_offset_reg <= s_ip_fragment_offset; + ip_ttl_reg <= s_ip_ttl; + ip_header_checksum_reg <= s_ip_header_checksum; + ip_source_ip_reg <= s_ip_source_ip; + ip_dest_ip_reg <= s_ip_dest_ip; + udp_source_port_reg <= s_udp_source_port; + udp_dest_port_reg <= s_udp_dest_port; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/udp_checksum_gen_64.v b/fpga/lib/eth/rtl/udp_checksum_gen_64.v new file mode 100644 index 000000000..756c60309 --- /dev/null +++ b/fpga/lib/eth/rtl/udp_checksum_gen_64.v @@ -0,0 +1,591 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * UDP checksum calculation module (64 bit datapath) + */ +module udp_checksum_gen_64 # +( + parameter PAYLOAD_FIFO_ADDR_WIDTH = 8, + parameter HEADER_FIFO_ADDR_WIDTH = 3 +) +( + input wire clk, + input wire rst, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [63:0] s_udp_payload_axis_tdata, + input wire [7:0] s_udp_payload_axis_tkeep, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [63:0] m_udp_payload_axis_tdata, + output wire [7:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives a UDP frame with header fields in parallel and payload on +an AXI stream interface, calculates the length and checksum, then produces the +header fields in parallel along with the UDP payload in a separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_SUM_HEADER = 3'd1, + STATE_SUM_PAYLOAD = 3'd2, + STATE_FINISH_SUM_1 = 3'd3, + STATE_FINISH_SUM_2 = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_udp_hdr; +reg shift_payload_in; +reg [31:0] checksum_part; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [31:0] checksum_reg = 32'd0, checksum_next; +reg [16:0] checksum_temp1_reg = 17'd0, checksum_temp1_next; +reg [16:0] checksum_temp2_reg = 17'd0, checksum_temp2_next; + +reg [47:0] eth_dest_mac_reg = 48'd0; +reg [47:0] eth_src_mac_reg = 48'd0; +reg [15:0] eth_type_reg = 16'd0; +reg [3:0] ip_version_reg = 4'd0; +reg [3:0] ip_ihl_reg = 4'd0; +reg [5:0] ip_dscp_reg = 6'd0; +reg [1:0] ip_ecn_reg = 2'd0; +reg [15:0] ip_identification_reg = 16'd0; +reg [2:0] ip_flags_reg = 3'd0; +reg [12:0] ip_fragment_offset_reg = 13'd0; +reg [7:0] ip_ttl_reg = 8'd0; +reg [15:0] ip_header_checksum_reg = 16'd0; +reg [31:0] ip_source_ip_reg = 32'd0; +reg [31:0] ip_dest_ip_reg = 32'd0; +reg [15:0] udp_source_port_reg = 16'd0; +reg [15:0] udp_dest_port_reg = 16'd0; + +reg hdr_valid_reg = 0, hdr_valid_next; + +reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next; +reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next; + +reg busy_reg = 1'b0; + +/* + * UDP Payload FIFO + */ +wire [63:0] s_udp_payload_fifo_tdata; +wire [7:0] s_udp_payload_fifo_tkeep; +wire s_udp_payload_fifo_tvalid; +wire s_udp_payload_fifo_tready; +wire s_udp_payload_fifo_tlast; +wire s_udp_payload_fifo_tuser; + +wire [63:0] m_udp_payload_fifo_tdata; +wire [7:0] m_udp_payload_fifo_tkeep; +wire m_udp_payload_fifo_tvalid; +wire m_udp_payload_fifo_tready; +wire m_udp_payload_fifo_tlast; +wire m_udp_payload_fifo_tuser; + +axis_fifo #( + .ADDR_WIDTH(PAYLOAD_FIFO_ADDR_WIDTH), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(0) +) +payload_fifo ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_udp_payload_fifo_tdata), + .s_axis_tkeep(s_udp_payload_fifo_tkeep), + .s_axis_tvalid(s_udp_payload_fifo_tvalid), + .s_axis_tready(s_udp_payload_fifo_tready), + .s_axis_tlast(s_udp_payload_fifo_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(s_udp_payload_fifo_tuser), + // AXI output + .m_axis_tdata(m_udp_payload_fifo_tdata), + .m_axis_tkeep(m_udp_payload_fifo_tkeep), + .m_axis_tvalid(m_udp_payload_fifo_tvalid), + .m_axis_tready(m_udp_payload_fifo_tready), + .m_axis_tlast(m_udp_payload_fifo_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(m_udp_payload_fifo_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +assign s_udp_payload_fifo_tdata = s_udp_payload_axis_tdata; +assign s_udp_payload_fifo_tkeep = s_udp_payload_axis_tkeep; +assign s_udp_payload_fifo_tvalid = s_udp_payload_axis_tvalid && shift_payload_in; +assign s_udp_payload_axis_tready = s_udp_payload_fifo_tready && shift_payload_in; +assign s_udp_payload_fifo_tlast = s_udp_payload_axis_tlast; +assign s_udp_payload_fifo_tuser = s_udp_payload_axis_tuser; + +assign m_udp_payload_axis_tdata = m_udp_payload_fifo_tdata; +assign m_udp_payload_axis_tkeep = m_udp_payload_fifo_tkeep; +assign m_udp_payload_axis_tvalid = m_udp_payload_fifo_tvalid; +assign m_udp_payload_fifo_tready = m_udp_payload_axis_tready; +assign m_udp_payload_axis_tlast = m_udp_payload_fifo_tlast; +assign m_udp_payload_axis_tuser = m_udp_payload_fifo_tuser; + +/* + * UDP Header FIFO + */ +reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_wr_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_wr_ptr_next; +reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_rd_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_rd_ptr_next; + +reg [47:0] eth_dest_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [47:0] eth_src_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] eth_type_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [3:0] ip_version_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [3:0] ip_ihl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [5:0] ip_dscp_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [1:0] ip_ecn_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] ip_identification_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [2:0] ip_flags_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [12:0] ip_fragment_offset_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [7:0] ip_ttl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] ip_header_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [31:0] ip_source_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [31:0] ip_dest_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_source_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_dest_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_length_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; + +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; +reg [15:0] m_udp_source_port_reg = 16'd0; +reg [15:0] m_udp_dest_port_reg = 16'd0; +reg [15:0] m_udp_length_reg = 16'd0; +reg [15:0] m_udp_checksum_reg = 16'd0; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; + +// full when first MSB different but rest same +wire header_fifo_full = ((header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH] != header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH]) && + (header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0] == header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire header_fifo_empty = header_fifo_wr_ptr_reg == header_fifo_rd_ptr_reg; + +// control signals +reg header_fifo_write; +reg header_fifo_read; + +wire header_fifo_ready = !header_fifo_full; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; + +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_udp_length_reg + 16'd20; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = 8'h11; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +// Write logic +always @* begin + header_fifo_write = 1'b0; + + header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg; + + if (hdr_valid_reg) begin + // input data valid + if (~header_fifo_full) begin + // not full, perform write + header_fifo_write = 1'b1; + header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg + 1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + header_fifo_wr_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}; + end else begin + header_fifo_wr_ptr_reg <= header_fifo_wr_ptr_next; + end + + if (header_fifo_write) begin + eth_dest_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_dest_mac_reg; + eth_src_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_src_mac_reg; + eth_type_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_type_reg; + ip_version_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_version_reg; + ip_ihl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ihl_reg; + ip_dscp_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dscp_reg; + ip_ecn_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ecn_reg; + ip_identification_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_identification_reg; + ip_flags_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_flags_reg; + ip_fragment_offset_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_fragment_offset_reg; + ip_ttl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ttl_reg; + ip_header_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_header_checksum_reg; + ip_source_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_source_ip_reg; + ip_dest_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dest_ip_reg; + udp_source_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_source_port_reg; + udp_dest_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_dest_port_reg; + udp_length_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= frame_ptr_reg; + udp_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= checksum_reg[15:0]; + end +end + +// Read logic +always @* begin + header_fifo_read = 1'b0; + + header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg; + + if (m_udp_hdr_ready || !m_udp_hdr_valid) begin + // output data not valid OR currently being transferred + if (!header_fifo_empty) begin + // not empty, perform read + header_fifo_read = 1'b1; + m_udp_hdr_valid_next = 1'b1; + header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg + 1; + end else begin + // empty, invalidate + m_udp_hdr_valid_next = 1'b0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + header_fifo_rd_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}; + m_udp_hdr_valid_reg <= 1'b0; + end else begin + header_fifo_rd_ptr_reg <= header_fifo_rd_ptr_next; + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + end + + if (header_fifo_read) begin + m_eth_dest_mac_reg <= eth_dest_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_eth_src_mac_reg <= eth_src_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_eth_type_reg <= eth_type_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_version_reg <= ip_version_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ihl_reg <= ip_ihl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_dscp_reg <= ip_dscp_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ecn_reg <= ip_ecn_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_identification_reg <= ip_identification_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_flags_reg <= ip_flags_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_fragment_offset_reg <= ip_fragment_offset_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ttl_reg <= ip_ttl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_header_checksum_reg <= ip_header_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_source_ip_reg <= ip_source_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_dest_ip_reg <= ip_dest_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_source_port_reg <= udp_source_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_dest_port_reg <= udp_dest_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_length_reg <= udp_length_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_checksum_reg <= udp_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + end +end + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg; + +assign busy = busy_reg; + +integer i, word_cnt; + +always @* begin + state_next = STATE_IDLE; + + s_udp_hdr_ready_next = 1'b0; + s_udp_payload_axis_tready_next = 1'b0; + + store_udp_hdr = 1'b0; + shift_payload_in = 1'b0; + + frame_ptr_next = frame_ptr_reg; + checksum_next = checksum_reg; + checksum_temp1_next = checksum_temp1_reg; + checksum_temp2_next = checksum_temp2_reg; + + hdr_valid_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state + s_udp_hdr_ready_next = header_fifo_ready; + + if (s_udp_hdr_ready && s_udp_hdr_valid) begin + store_udp_hdr = 1'b1; + frame_ptr_next = 0; + // 16'h0011 = zero padded type field + // 16'h0010 = header length times two + checksum_next = 16'h0011 + 16'h0010; + checksum_temp1_next = s_ip_source_ip[31:16]; + checksum_temp2_next = s_ip_source_ip[15:0]; + s_udp_hdr_ready_next = 1'b0; + state_next = STATE_SUM_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_SUM_HEADER: begin + // sum pseudo header and header + checksum_next = checksum_reg + checksum_temp1_reg + checksum_temp2_reg; + checksum_temp1_next = ip_dest_ip_reg[31:16] + ip_dest_ip_reg[15:0]; + checksum_temp2_next = udp_source_port_reg + udp_dest_port_reg; + frame_ptr_next = 8; + state_next = STATE_SUM_PAYLOAD; + end + STATE_SUM_PAYLOAD: begin + // sum payload + shift_payload_in = 1'b1; + + if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin + word_cnt = 1; + for (i = 1; i <= 8; i = i + 1) begin + if (s_udp_payload_axis_tkeep == 8'hff >> (8-i)) word_cnt = i; + end + + checksum_temp1_next = 0; + checksum_temp2_next = 0; + + for (i = 0; i < 4; i = i + 1) begin + if (s_udp_payload_axis_tkeep[i]) begin + if (i & 1) begin + checksum_temp1_next = checksum_temp1_next + {8'h00, s_udp_payload_axis_tdata[i*8 +: 8]}; + end else begin + checksum_temp1_next = checksum_temp1_next + {s_udp_payload_axis_tdata[i*8 +: 8], 8'h00}; + end + end + end + + for (i = 4; i < 8; i = i + 1) begin + if (s_udp_payload_axis_tkeep[i]) begin + if (i & 1) begin + checksum_temp2_next = checksum_temp2_next + {8'h00, s_udp_payload_axis_tdata[i*8 +: 8]}; + end else begin + checksum_temp2_next = checksum_temp2_next + {s_udp_payload_axis_tdata[i*8 +: 8], 8'h00}; + end + end + end + + // add length * 2 (two copies of length field in pseudo header) + checksum_next = checksum_reg + checksum_temp1_reg + checksum_temp2_reg + (word_cnt << 1); + + frame_ptr_next = frame_ptr_reg + word_cnt; + + if (s_udp_payload_axis_tlast) begin + state_next = STATE_FINISH_SUM_1; + end else begin + state_next = STATE_SUM_PAYLOAD; + end + end else begin + state_next = STATE_SUM_PAYLOAD; + end + end + STATE_FINISH_SUM_1: begin + // empty pipeline + checksum_next = checksum_reg + checksum_temp1_reg + checksum_temp2_reg; + state_next = STATE_FINISH_SUM_2; + end + STATE_FINISH_SUM_2: begin + // add MSW (twice!) for proper ones complement sum + checksum_part = checksum_reg[15:0] + checksum_reg[31:16]; + checksum_next = ~(checksum_part[15:0] + checksum_part[16]); + hdr_valid_next = 1; + state_next = STATE_IDLE; + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_udp_hdr_ready_reg <= 1'b0; + s_udp_payload_axis_tready_reg <= 1'b0; + hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + + hdr_valid_reg <= hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + end + + frame_ptr_reg <= frame_ptr_next; + checksum_reg <= checksum_next; + checksum_temp1_reg <= checksum_temp1_next; + checksum_temp2_reg <= checksum_temp2_next; + + // datapath + if (store_udp_hdr) begin + eth_dest_mac_reg <= s_eth_dest_mac; + eth_src_mac_reg <= s_eth_src_mac; + eth_type_reg <= s_eth_type; + ip_version_reg <= s_ip_version; + ip_ihl_reg <= s_ip_ihl; + ip_dscp_reg <= s_ip_dscp; + ip_ecn_reg <= s_ip_ecn; + ip_identification_reg <= s_ip_identification; + ip_flags_reg <= s_ip_flags; + ip_fragment_offset_reg <= s_ip_fragment_offset; + ip_ttl_reg <= s_ip_ttl; + ip_header_checksum_reg <= s_ip_header_checksum; + ip_source_ip_reg <= s_ip_source_ip; + ip_dest_ip_reg <= s_ip_dest_ip; + udp_source_port_reg <= s_udp_source_port; + udp_dest_port_reg <= s_udp_dest_port; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/udp_complete.v b/fpga/lib/eth/rtl/udp_complete.v new file mode 100644 index 000000000..3ff5816cf --- /dev/null +++ b/fpga/lib/eth/rtl/udp_complete.v @@ -0,0 +1,637 @@ +/* + +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 1ns / 1ps + +/* + * IPv4 and ARP block with UDP support, ethernet frame interface + */ +module udp_complete #( + parameter ARP_CACHE_ADDR_WIDTH = 9, + parameter ARP_REQUEST_RETRY_COUNT = 4, + parameter ARP_REQUEST_RETRY_INTERVAL = 125000000*2, + parameter ARP_REQUEST_TIMEOUT = 125000000*30, + parameter UDP_CHECKSUM_GEN_ENABLE = 1, + parameter UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11, + parameter UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * UDP input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [5:0] s_udp_ip_dscp, + input wire [1:0] s_udp_ip_ecn, + input wire [7:0] s_udp_ip_ttl, + input wire [31:0] s_udp_ip_source_ip, + input wire [31:0] s_udp_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [7:0] s_udp_payload_axis_tdata, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_udp_eth_dest_mac, + output wire [47:0] m_udp_eth_src_mac, + output wire [15:0] m_udp_eth_type, + output wire [3:0] m_udp_ip_version, + output wire [3:0] m_udp_ip_ihl, + output wire [5:0] m_udp_ip_dscp, + output wire [1:0] m_udp_ip_ecn, + output wire [15:0] m_udp_ip_length, + output wire [15:0] m_udp_ip_identification, + output wire [2:0] m_udp_ip_flags, + output wire [12:0] m_udp_ip_fragment_offset, + output wire [7:0] m_udp_ip_ttl, + output wire [7:0] m_udp_ip_protocol, + output wire [15:0] m_udp_ip_header_checksum, + output wire [31:0] m_udp_ip_source_ip, + output wire [31:0] m_udp_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [7:0] m_udp_payload_axis_tdata, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status + */ + output wire ip_rx_busy, + output wire ip_tx_busy, + output wire udp_rx_busy, + output wire udp_tx_busy, + output wire ip_rx_error_header_early_termination, + output wire ip_rx_error_payload_early_termination, + output wire ip_rx_error_invalid_header, + output wire ip_rx_error_invalid_checksum, + output wire ip_tx_error_payload_early_termination, + output wire ip_tx_error_arp_failed, + output wire udp_rx_error_header_early_termination, + output wire udp_rx_error_payload_early_termination, + output wire udp_tx_error_payload_early_termination, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_arp_cache +); + +wire ip_rx_ip_hdr_valid; +wire ip_rx_ip_hdr_ready; +wire [47:0] ip_rx_ip_eth_dest_mac; +wire [47:0] ip_rx_ip_eth_src_mac; +wire [15:0] ip_rx_ip_eth_type; +wire [3:0] ip_rx_ip_version; +wire [3:0] ip_rx_ip_ihl; +wire [5:0] ip_rx_ip_dscp; +wire [1:0] ip_rx_ip_ecn; +wire [15:0] ip_rx_ip_length; +wire [15:0] ip_rx_ip_identification; +wire [2:0] ip_rx_ip_flags; +wire [12:0] ip_rx_ip_fragment_offset; +wire [7:0] ip_rx_ip_ttl; +wire [7:0] ip_rx_ip_protocol; +wire [15:0] ip_rx_ip_header_checksum; +wire [31:0] ip_rx_ip_source_ip; +wire [31:0] ip_rx_ip_dest_ip; +wire [7:0] ip_rx_ip_payload_axis_tdata; +wire ip_rx_ip_payload_axis_tvalid; +wire ip_rx_ip_payload_axis_tlast; +wire ip_rx_ip_payload_axis_tuser; +wire ip_rx_ip_payload_axis_tready; + +wire ip_tx_ip_hdr_valid; +wire ip_tx_ip_hdr_ready; +wire [5:0] ip_tx_ip_dscp; +wire [1:0] ip_tx_ip_ecn; +wire [15:0] ip_tx_ip_length; +wire [7:0] ip_tx_ip_ttl; +wire [7:0] ip_tx_ip_protocol; +wire [31:0] ip_tx_ip_source_ip; +wire [31:0] ip_tx_ip_dest_ip; +wire [7:0] ip_tx_ip_payload_axis_tdata; +wire ip_tx_ip_payload_axis_tvalid; +wire ip_tx_ip_payload_axis_tlast; +wire ip_tx_ip_payload_axis_tuser; +wire ip_tx_ip_payload_axis_tready; + +wire udp_rx_ip_hdr_valid; +wire udp_rx_ip_hdr_ready; +wire [47:0] udp_rx_ip_eth_dest_mac; +wire [47:0] udp_rx_ip_eth_src_mac; +wire [15:0] udp_rx_ip_eth_type; +wire [3:0] udp_rx_ip_version; +wire [3:0] udp_rx_ip_ihl; +wire [5:0] udp_rx_ip_dscp; +wire [1:0] udp_rx_ip_ecn; +wire [15:0] udp_rx_ip_length; +wire [15:0] udp_rx_ip_identification; +wire [2:0] udp_rx_ip_flags; +wire [12:0] udp_rx_ip_fragment_offset; +wire [7:0] udp_rx_ip_ttl; +wire [7:0] udp_rx_ip_protocol; +wire [15:0] udp_rx_ip_header_checksum; +wire [31:0] udp_rx_ip_source_ip; +wire [31:0] udp_rx_ip_dest_ip; +wire [7:0] udp_rx_ip_payload_axis_tdata; +wire udp_rx_ip_payload_axis_tvalid; +wire udp_rx_ip_payload_axis_tlast; +wire udp_rx_ip_payload_axis_tuser; +wire udp_rx_ip_payload_axis_tready; + +wire udp_tx_ip_hdr_valid; +wire udp_tx_ip_hdr_ready; +wire [5:0] udp_tx_ip_dscp; +wire [1:0] udp_tx_ip_ecn; +wire [15:0] udp_tx_ip_length; +wire [7:0] udp_tx_ip_ttl; +wire [7:0] udp_tx_ip_protocol; +wire [31:0] udp_tx_ip_source_ip; +wire [31:0] udp_tx_ip_dest_ip; +wire [7:0] udp_tx_ip_payload_axis_tdata; +wire udp_tx_ip_payload_axis_tvalid; +wire udp_tx_ip_payload_axis_tlast; +wire udp_tx_ip_payload_axis_tuser; +wire udp_tx_ip_payload_axis_tready; + +/* + * Input classifier (ip_protocol) + */ +wire s_select_udp = (ip_rx_ip_protocol == 8'h11); +wire s_select_ip = !s_select_udp; + +reg s_select_udp_reg = 1'b0; +reg s_select_ip_reg = 1'b0; + +always @(posedge clk) begin + if (rst) begin + s_select_udp_reg <= 1'b0; + s_select_ip_reg <= 1'b0; + end else begin + if (ip_rx_ip_payload_axis_tvalid) begin + if ((!s_select_udp_reg && !s_select_ip_reg) || + (ip_rx_ip_payload_axis_tvalid && ip_rx_ip_payload_axis_tready && ip_rx_ip_payload_axis_tlast)) begin + s_select_udp_reg <= s_select_udp; + s_select_ip_reg <= s_select_ip; + end + end else begin + s_select_udp_reg <= 1'b0; + s_select_ip_reg <= 1'b0; + end + end +end + +// IP frame to UDP module +assign udp_rx_ip_hdr_valid = s_select_udp && ip_rx_ip_hdr_valid; +assign udp_rx_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac; +assign udp_rx_ip_eth_src_mac = ip_rx_ip_eth_src_mac; +assign udp_rx_ip_eth_type = ip_rx_ip_eth_type; +assign udp_rx_ip_version = ip_rx_ip_version; +assign udp_rx_ip_ihl = ip_rx_ip_ihl; +assign udp_rx_ip_dscp = ip_rx_ip_dscp; +assign udp_rx_ip_ecn = ip_rx_ip_ecn; +assign udp_rx_ip_length = ip_rx_ip_length; +assign udp_rx_ip_identification = ip_rx_ip_identification; +assign udp_rx_ip_flags = ip_rx_ip_flags; +assign udp_rx_ip_fragment_offset = ip_rx_ip_fragment_offset; +assign udp_rx_ip_ttl = ip_rx_ip_ttl; +assign udp_rx_ip_protocol = 8'h11; +assign udp_rx_ip_header_checksum = ip_rx_ip_header_checksum; +assign udp_rx_ip_source_ip = ip_rx_ip_source_ip; +assign udp_rx_ip_dest_ip = ip_rx_ip_dest_ip; +assign udp_rx_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata; +assign udp_rx_ip_payload_axis_tvalid = s_select_udp_reg && ip_rx_ip_payload_axis_tvalid; +assign udp_rx_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast; +assign udp_rx_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser; + +// External IP frame output +assign m_ip_hdr_valid = s_select_ip && ip_rx_ip_hdr_valid; +assign m_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac; +assign m_ip_eth_src_mac = ip_rx_ip_eth_src_mac; +assign m_ip_eth_type = ip_rx_ip_eth_type; +assign m_ip_version = ip_rx_ip_version; +assign m_ip_ihl = ip_rx_ip_ihl; +assign m_ip_dscp = ip_rx_ip_dscp; +assign m_ip_ecn = ip_rx_ip_ecn; +assign m_ip_length = ip_rx_ip_length; +assign m_ip_identification = ip_rx_ip_identification; +assign m_ip_flags = ip_rx_ip_flags; +assign m_ip_fragment_offset = ip_rx_ip_fragment_offset; +assign m_ip_ttl = ip_rx_ip_ttl; +assign m_ip_protocol = ip_rx_ip_protocol; +assign m_ip_header_checksum = ip_rx_ip_header_checksum; +assign m_ip_source_ip = ip_rx_ip_source_ip; +assign m_ip_dest_ip = ip_rx_ip_dest_ip; +assign m_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata; +assign m_ip_payload_axis_tvalid = s_select_ip_reg && ip_rx_ip_payload_axis_tvalid; +assign m_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast; +assign m_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser; + +assign ip_rx_ip_hdr_ready = (s_select_udp && udp_rx_ip_hdr_ready) || + (s_select_ip && m_ip_hdr_ready); + +assign ip_rx_ip_payload_axis_tready = (s_select_udp_reg && udp_rx_ip_payload_axis_tready) || + (s_select_ip_reg && m_ip_payload_axis_tready); + +/* + * Output arbiter + */ +ip_arb_mux #( + .S_COUNT(2), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +ip_arb_mux_inst ( + .clk(clk), + .rst(rst), + // IP frame inputs + .s_ip_hdr_valid({s_ip_hdr_valid, udp_tx_ip_hdr_valid}), + .s_ip_hdr_ready({s_ip_hdr_ready, udp_tx_ip_hdr_ready}), + .s_eth_dest_mac(0), + .s_eth_src_mac(0), + .s_eth_type(0), + .s_ip_version(0), + .s_ip_ihl(0), + .s_ip_dscp({s_ip_dscp, udp_tx_ip_dscp}), + .s_ip_ecn({s_ip_ecn, udp_tx_ip_ecn}), + .s_ip_length({s_ip_length, udp_tx_ip_length}), + .s_ip_identification(0), + .s_ip_flags(0), + .s_ip_fragment_offset(0), + .s_ip_ttl({s_ip_ttl, udp_tx_ip_ttl}), + .s_ip_protocol({s_ip_protocol, udp_tx_ip_protocol}), + .s_ip_header_checksum(0), + .s_ip_source_ip({s_ip_source_ip, udp_tx_ip_source_ip}), + .s_ip_dest_ip({s_ip_dest_ip, udp_tx_ip_dest_ip}), + .s_ip_payload_axis_tdata({s_ip_payload_axis_tdata, udp_tx_ip_payload_axis_tdata}), + .s_ip_payload_axis_tkeep(0), + .s_ip_payload_axis_tvalid({s_ip_payload_axis_tvalid, udp_tx_ip_payload_axis_tvalid}), + .s_ip_payload_axis_tready({s_ip_payload_axis_tready, udp_tx_ip_payload_axis_tready}), + .s_ip_payload_axis_tlast({s_ip_payload_axis_tlast, udp_tx_ip_payload_axis_tlast}), + .s_ip_payload_axis_tid(0), + .s_ip_payload_axis_tdest(0), + .s_ip_payload_axis_tuser({s_ip_payload_axis_tuser, udp_tx_ip_payload_axis_tuser}), + // IP frame output + .m_ip_hdr_valid(ip_tx_ip_hdr_valid), + .m_ip_hdr_ready(ip_tx_ip_hdr_ready), + .m_eth_dest_mac(), + .m_eth_src_mac(), + .m_eth_type(), + .m_ip_version(), + .m_ip_ihl(), + .m_ip_dscp(ip_tx_ip_dscp), + .m_ip_ecn(ip_tx_ip_ecn), + .m_ip_length(ip_tx_ip_length), + .m_ip_identification(), + .m_ip_flags(), + .m_ip_fragment_offset(), + .m_ip_ttl(ip_tx_ip_ttl), + .m_ip_protocol(ip_tx_ip_protocol), + .m_ip_header_checksum(), + .m_ip_source_ip(ip_tx_ip_source_ip), + .m_ip_dest_ip(ip_tx_ip_dest_ip), + .m_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(), + .m_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast), + .m_ip_payload_axis_tid(), + .m_ip_payload_axis_tdest(), + .m_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser) +); + +/* + * IP stack + */ +ip_complete #( + .ARP_CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .ARP_REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .ARP_REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .ARP_REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT) +) +ip_complete_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(ip_tx_ip_hdr_valid), + .s_ip_hdr_ready(ip_tx_ip_hdr_ready), + .s_ip_dscp(ip_tx_ip_dscp), + .s_ip_ecn(ip_tx_ip_ecn), + .s_ip_length(ip_tx_ip_length), + .s_ip_ttl(ip_tx_ip_ttl), + .s_ip_protocol(ip_tx_ip_protocol), + .s_ip_source_ip(ip_tx_ip_source_ip), + .s_ip_dest_ip(ip_tx_ip_dest_ip), + .s_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(ip_rx_ip_hdr_valid), + .m_ip_hdr_ready(ip_rx_ip_hdr_ready), + .m_ip_eth_dest_mac(ip_rx_ip_eth_dest_mac), + .m_ip_eth_src_mac(ip_rx_ip_eth_src_mac), + .m_ip_eth_type(ip_rx_ip_eth_type), + .m_ip_version(ip_rx_ip_version), + .m_ip_ihl(ip_rx_ip_ihl), + .m_ip_dscp(ip_rx_ip_dscp), + .m_ip_ecn(ip_rx_ip_ecn), + .m_ip_length(ip_rx_ip_length), + .m_ip_identification(ip_rx_ip_identification), + .m_ip_flags(ip_rx_ip_flags), + .m_ip_fragment_offset(ip_rx_ip_fragment_offset), + .m_ip_ttl(ip_rx_ip_ttl), + .m_ip_protocol(ip_rx_ip_protocol), + .m_ip_header_checksum(ip_rx_ip_header_checksum), + .m_ip_source_ip(ip_rx_ip_source_ip), + .m_ip_dest_ip(ip_rx_ip_dest_ip), + .m_ip_payload_axis_tdata(ip_rx_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(ip_rx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(ip_rx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(ip_rx_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(ip_rx_ip_payload_axis_tuser), + // Status + .rx_busy(ip_rx_busy), + .tx_busy(ip_tx_busy), + .rx_error_header_early_termination(ip_rx_error_header_early_termination), + .rx_error_payload_early_termination(ip_rx_error_payload_early_termination), + .rx_error_invalid_header(ip_rx_error_invalid_header), + .rx_error_invalid_checksum(ip_rx_error_invalid_checksum), + .tx_error_payload_early_termination(ip_tx_error_payload_early_termination), + .tx_error_arp_failed(ip_tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_arp_cache(clear_arp_cache) +); + +/* + * UDP interface + */ +udp #( + .CHECKSUM_GEN_ENABLE(UDP_CHECKSUM_GEN_ENABLE), + .CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH(UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH), + .CHECKSUM_HEADER_FIFO_ADDR_WIDTH(UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH) +) +udp_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(udp_rx_ip_hdr_valid), + .s_ip_hdr_ready(udp_rx_ip_hdr_ready), + .s_ip_eth_dest_mac(udp_rx_ip_eth_dest_mac), + .s_ip_eth_src_mac(udp_rx_ip_eth_src_mac), + .s_ip_eth_type(udp_rx_ip_eth_type), + .s_ip_version(udp_rx_ip_version), + .s_ip_ihl(udp_rx_ip_ihl), + .s_ip_dscp(udp_rx_ip_dscp), + .s_ip_ecn(udp_rx_ip_ecn), + .s_ip_length(udp_rx_ip_length), + .s_ip_identification(udp_rx_ip_identification), + .s_ip_flags(udp_rx_ip_flags), + .s_ip_fragment_offset(udp_rx_ip_fragment_offset), + .s_ip_ttl(udp_rx_ip_ttl), + .s_ip_protocol(udp_rx_ip_protocol), + .s_ip_header_checksum(udp_rx_ip_header_checksum), + .s_ip_source_ip(udp_rx_ip_source_ip), + .s_ip_dest_ip(udp_rx_ip_dest_ip), + .s_ip_payload_axis_tdata(udp_rx_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(udp_rx_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(udp_rx_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(udp_rx_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(udp_rx_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(udp_tx_ip_hdr_valid), + .m_ip_hdr_ready(udp_tx_ip_hdr_ready), + .m_ip_eth_dest_mac(), + .m_ip_eth_src_mac(), + .m_ip_eth_type(), + .m_ip_version(), + .m_ip_ihl(), + .m_ip_dscp(udp_tx_ip_dscp), + .m_ip_ecn(udp_tx_ip_ecn), + .m_ip_length(udp_tx_ip_length), + .m_ip_identification(), + .m_ip_flags(), + .m_ip_fragment_offset(), + .m_ip_ttl(udp_tx_ip_ttl), + .m_ip_protocol(udp_tx_ip_protocol), + .m_ip_header_checksum(), + .m_ip_source_ip(udp_tx_ip_source_ip), + .m_ip_dest_ip(udp_tx_ip_dest_ip), + .m_ip_payload_axis_tdata(udp_tx_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(udp_tx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(udp_tx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(udp_tx_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(udp_tx_ip_payload_axis_tuser), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_udp_eth_dest_mac(48'd0), + .s_udp_eth_src_mac(48'd0), + .s_udp_eth_type(16'd0), + .s_udp_ip_version(4'd0), + .s_udp_ip_ihl(4'd0), + .s_udp_ip_dscp(s_udp_ip_dscp), + .s_udp_ip_ecn(s_udp_ip_ecn), + .s_udp_ip_identification(16'd0), + .s_udp_ip_flags(3'd0), + .s_udp_ip_fragment_offset(13'd0), + .s_udp_ip_ttl(s_udp_ip_ttl), + .s_udp_ip_header_checksum(16'd0), + .s_udp_ip_source_ip(s_udp_ip_source_ip), + .s_udp_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_udp_eth_dest_mac(m_udp_eth_dest_mac), + .m_udp_eth_src_mac(m_udp_eth_src_mac), + .m_udp_eth_type(m_udp_eth_type), + .m_udp_ip_version(m_udp_ip_version), + .m_udp_ip_ihl(m_udp_ip_ihl), + .m_udp_ip_dscp(m_udp_ip_dscp), + .m_udp_ip_ecn(m_udp_ip_ecn), + .m_udp_ip_length(m_udp_ip_length), + .m_udp_ip_identification(m_udp_ip_identification), + .m_udp_ip_flags(m_udp_ip_flags), + .m_udp_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_udp_ip_ttl(m_udp_ip_ttl), + .m_udp_ip_protocol(m_udp_ip_protocol), + .m_udp_ip_header_checksum(m_udp_ip_header_checksum), + .m_udp_ip_source_ip(m_udp_ip_source_ip), + .m_udp_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status + .rx_busy(udp_rx_busy), + .tx_busy(udp_tx_busy), + .rx_error_header_early_termination(udp_rx_error_header_early_termination), + .rx_error_payload_early_termination(udp_rx_error_payload_early_termination), + .tx_error_payload_early_termination(udp_tx_error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/rtl/udp_complete_64.v b/fpga/lib/eth/rtl/udp_complete_64.v new file mode 100644 index 000000000..c31e225a1 --- /dev/null +++ b/fpga/lib/eth/rtl/udp_complete_64.v @@ -0,0 +1,657 @@ +/* + +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 1ns / 1ps + +/* + * IPv4 and ARP block with UDP support, ethernet frame interface (64 bit datapath) + */ +module udp_complete_64 #( + parameter ARP_CACHE_ADDR_WIDTH = 9, + parameter ARP_REQUEST_RETRY_COUNT = 4, + parameter ARP_REQUEST_RETRY_INTERVAL = 125000000*2, + parameter ARP_REQUEST_TIMEOUT = 125000000*30, + parameter UDP_CHECKSUM_GEN_ENABLE = 1, + parameter UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11, + parameter UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * UDP input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [5:0] s_udp_ip_dscp, + input wire [1:0] s_udp_ip_ecn, + input wire [7:0] s_udp_ip_ttl, + input wire [31:0] s_udp_ip_source_ip, + input wire [31:0] s_udp_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [63:0] s_udp_payload_axis_tdata, + input wire [7:0] s_udp_payload_axis_tkeep, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_udp_eth_dest_mac, + output wire [47:0] m_udp_eth_src_mac, + output wire [15:0] m_udp_eth_type, + output wire [3:0] m_udp_ip_version, + output wire [3:0] m_udp_ip_ihl, + output wire [5:0] m_udp_ip_dscp, + output wire [1:0] m_udp_ip_ecn, + output wire [15:0] m_udp_ip_length, + output wire [15:0] m_udp_ip_identification, + output wire [2:0] m_udp_ip_flags, + output wire [12:0] m_udp_ip_fragment_offset, + output wire [7:0] m_udp_ip_ttl, + output wire [7:0] m_udp_ip_protocol, + output wire [15:0] m_udp_ip_header_checksum, + output wire [31:0] m_udp_ip_source_ip, + output wire [31:0] m_udp_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [63:0] m_udp_payload_axis_tdata, + output wire [7:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status + */ + output wire ip_rx_busy, + output wire ip_tx_busy, + output wire udp_rx_busy, + output wire udp_tx_busy, + output wire ip_rx_error_header_early_termination, + output wire ip_rx_error_payload_early_termination, + output wire ip_rx_error_invalid_header, + output wire ip_rx_error_invalid_checksum, + output wire ip_tx_error_payload_early_termination, + output wire ip_tx_error_arp_failed, + output wire udp_rx_error_header_early_termination, + output wire udp_rx_error_payload_early_termination, + output wire udp_tx_error_payload_early_termination, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_arp_cache +); + +wire ip_rx_ip_hdr_valid; +wire ip_rx_ip_hdr_ready; +wire [47:0] ip_rx_ip_eth_dest_mac; +wire [47:0] ip_rx_ip_eth_src_mac; +wire [15:0] ip_rx_ip_eth_type; +wire [3:0] ip_rx_ip_version; +wire [3:0] ip_rx_ip_ihl; +wire [5:0] ip_rx_ip_dscp; +wire [1:0] ip_rx_ip_ecn; +wire [15:0] ip_rx_ip_length; +wire [15:0] ip_rx_ip_identification; +wire [2:0] ip_rx_ip_flags; +wire [12:0] ip_rx_ip_fragment_offset; +wire [7:0] ip_rx_ip_ttl; +wire [7:0] ip_rx_ip_protocol; +wire [15:0] ip_rx_ip_header_checksum; +wire [31:0] ip_rx_ip_source_ip; +wire [31:0] ip_rx_ip_dest_ip; +wire [63:0] ip_rx_ip_payload_axis_tdata; +wire [7:0] ip_rx_ip_payload_axis_tkeep; +wire ip_rx_ip_payload_axis_tvalid; +wire ip_rx_ip_payload_axis_tlast; +wire ip_rx_ip_payload_axis_tuser; +wire ip_rx_ip_payload_axis_tready; + +wire ip_tx_ip_hdr_valid; +wire ip_tx_ip_hdr_ready; +wire [5:0] ip_tx_ip_dscp; +wire [1:0] ip_tx_ip_ecn; +wire [15:0] ip_tx_ip_length; +wire [7:0] ip_tx_ip_ttl; +wire [7:0] ip_tx_ip_protocol; +wire [31:0] ip_tx_ip_source_ip; +wire [31:0] ip_tx_ip_dest_ip; +wire [63:0] ip_tx_ip_payload_axis_tdata; +wire [7:0] ip_tx_ip_payload_axis_tkeep; +wire ip_tx_ip_payload_axis_tvalid; +wire ip_tx_ip_payload_axis_tlast; +wire ip_tx_ip_payload_axis_tuser; +wire ip_tx_ip_payload_axis_tready; + +wire udp_rx_ip_hdr_valid; +wire udp_rx_ip_hdr_ready; +wire [47:0] udp_rx_ip_eth_dest_mac; +wire [47:0] udp_rx_ip_eth_src_mac; +wire [15:0] udp_rx_ip_eth_type; +wire [3:0] udp_rx_ip_version; +wire [3:0] udp_rx_ip_ihl; +wire [5:0] udp_rx_ip_dscp; +wire [1:0] udp_rx_ip_ecn; +wire [15:0] udp_rx_ip_length; +wire [15:0] udp_rx_ip_identification; +wire [2:0] udp_rx_ip_flags; +wire [12:0] udp_rx_ip_fragment_offset; +wire [7:0] udp_rx_ip_ttl; +wire [7:0] udp_rx_ip_protocol; +wire [15:0] udp_rx_ip_header_checksum; +wire [31:0] udp_rx_ip_source_ip; +wire [31:0] udp_rx_ip_dest_ip; +wire [63:0] udp_rx_ip_payload_axis_tdata; +wire [7:0] udp_rx_ip_payload_axis_tkeep; +wire udp_rx_ip_payload_axis_tvalid; +wire udp_rx_ip_payload_axis_tlast; +wire udp_rx_ip_payload_axis_tuser; +wire udp_rx_ip_payload_axis_tready; + +wire udp_tx_ip_hdr_valid; +wire udp_tx_ip_hdr_ready; +wire [5:0] udp_tx_ip_dscp; +wire [1:0] udp_tx_ip_ecn; +wire [15:0] udp_tx_ip_length; +wire [7:0] udp_tx_ip_ttl; +wire [7:0] udp_tx_ip_protocol; +wire [31:0] udp_tx_ip_source_ip; +wire [31:0] udp_tx_ip_dest_ip; +wire [63:0] udp_tx_ip_payload_axis_tdata; +wire [7:0] udp_tx_ip_payload_axis_tkeep; +wire udp_tx_ip_payload_axis_tvalid; +wire udp_tx_ip_payload_axis_tlast; +wire udp_tx_ip_payload_axis_tuser; +wire udp_tx_ip_payload_axis_tready; + +/* + * Input classifier (ip_protocol) + */ +wire s_select_udp = (ip_rx_ip_protocol == 8'h11); +wire s_select_ip = !s_select_udp; + +reg s_select_udp_reg = 1'b0; +reg s_select_ip_reg = 1'b0; + +always @(posedge clk) begin + if (rst) begin + s_select_udp_reg <= 1'b0; + s_select_ip_reg <= 1'b0; + end else begin + if (ip_rx_ip_payload_axis_tvalid) begin + if ((!s_select_udp_reg && !s_select_ip_reg) || + (ip_rx_ip_payload_axis_tvalid && ip_rx_ip_payload_axis_tready && ip_rx_ip_payload_axis_tlast)) begin + s_select_udp_reg <= s_select_udp; + s_select_ip_reg <= s_select_ip; + end + end else begin + s_select_udp_reg <= 1'b0; + s_select_ip_reg <= 1'b0; + end + end +end + +// IP frame to UDP module +assign udp_rx_ip_hdr_valid = s_select_udp && ip_rx_ip_hdr_valid; +assign udp_rx_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac; +assign udp_rx_ip_eth_src_mac = ip_rx_ip_eth_src_mac; +assign udp_rx_ip_eth_type = ip_rx_ip_eth_type; +assign udp_rx_ip_version = ip_rx_ip_version; +assign udp_rx_ip_ihl = ip_rx_ip_ihl; +assign udp_rx_ip_dscp = ip_rx_ip_dscp; +assign udp_rx_ip_ecn = ip_rx_ip_ecn; +assign udp_rx_ip_length = ip_rx_ip_length; +assign udp_rx_ip_identification = ip_rx_ip_identification; +assign udp_rx_ip_flags = ip_rx_ip_flags; +assign udp_rx_ip_fragment_offset = ip_rx_ip_fragment_offset; +assign udp_rx_ip_ttl = ip_rx_ip_ttl; +assign udp_rx_ip_protocol = 8'h11; +assign udp_rx_ip_header_checksum = ip_rx_ip_header_checksum; +assign udp_rx_ip_source_ip = ip_rx_ip_source_ip; +assign udp_rx_ip_dest_ip = ip_rx_ip_dest_ip; +assign udp_rx_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata; +assign udp_rx_ip_payload_axis_tkeep = ip_rx_ip_payload_axis_tkeep; +assign udp_rx_ip_payload_axis_tvalid = s_select_udp_reg && ip_rx_ip_payload_axis_tvalid; +assign udp_rx_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast; +assign udp_rx_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser; + +// External IP frame output +assign m_ip_hdr_valid = s_select_ip && ip_rx_ip_hdr_valid; +assign m_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac; +assign m_ip_eth_src_mac = ip_rx_ip_eth_src_mac; +assign m_ip_eth_type = ip_rx_ip_eth_type; +assign m_ip_version = ip_rx_ip_version; +assign m_ip_ihl = ip_rx_ip_ihl; +assign m_ip_dscp = ip_rx_ip_dscp; +assign m_ip_ecn = ip_rx_ip_ecn; +assign m_ip_length = ip_rx_ip_length; +assign m_ip_identification = ip_rx_ip_identification; +assign m_ip_flags = ip_rx_ip_flags; +assign m_ip_fragment_offset = ip_rx_ip_fragment_offset; +assign m_ip_ttl = ip_rx_ip_ttl; +assign m_ip_protocol = ip_rx_ip_protocol; +assign m_ip_header_checksum = ip_rx_ip_header_checksum; +assign m_ip_source_ip = ip_rx_ip_source_ip; +assign m_ip_dest_ip = ip_rx_ip_dest_ip; +assign m_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata; +assign m_ip_payload_axis_tkeep = ip_rx_ip_payload_axis_tkeep; +assign m_ip_payload_axis_tvalid = s_select_ip_reg && ip_rx_ip_payload_axis_tvalid; +assign m_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast; +assign m_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser; + +assign ip_rx_ip_hdr_ready = (s_select_udp && udp_rx_ip_hdr_ready) || + (s_select_ip && m_ip_hdr_ready); + +assign ip_rx_ip_payload_axis_tready = (s_select_udp_reg && udp_rx_ip_payload_axis_tready) || + (s_select_ip_reg && m_ip_payload_axis_tready); + +/* + * Output arbiter + */ +ip_arb_mux #( + .S_COUNT(2), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +ip_arb_mux_inst ( + .clk(clk), + .rst(rst), + // IP frame inputs + .s_ip_hdr_valid({s_ip_hdr_valid, udp_tx_ip_hdr_valid}), + .s_ip_hdr_ready({s_ip_hdr_ready, udp_tx_ip_hdr_ready}), + .s_eth_dest_mac(0), + .s_eth_src_mac(0), + .s_eth_type(0), + .s_ip_version(0), + .s_ip_ihl(0), + .s_ip_dscp({s_ip_dscp, udp_tx_ip_dscp}), + .s_ip_ecn({s_ip_ecn, udp_tx_ip_ecn}), + .s_ip_length({s_ip_length, udp_tx_ip_length}), + .s_ip_identification(0), + .s_ip_flags(0), + .s_ip_fragment_offset(0), + .s_ip_ttl({s_ip_ttl, udp_tx_ip_ttl}), + .s_ip_protocol({s_ip_protocol, udp_tx_ip_protocol}), + .s_ip_header_checksum(0), + .s_ip_source_ip({s_ip_source_ip, udp_tx_ip_source_ip}), + .s_ip_dest_ip({s_ip_dest_ip, udp_tx_ip_dest_ip}), + .s_ip_payload_axis_tdata({s_ip_payload_axis_tdata, udp_tx_ip_payload_axis_tdata}), + .s_ip_payload_axis_tkeep({s_ip_payload_axis_tkeep, udp_tx_ip_payload_axis_tkeep}), + .s_ip_payload_axis_tvalid({s_ip_payload_axis_tvalid, udp_tx_ip_payload_axis_tvalid}), + .s_ip_payload_axis_tready({s_ip_payload_axis_tready, udp_tx_ip_payload_axis_tready}), + .s_ip_payload_axis_tlast({s_ip_payload_axis_tlast, udp_tx_ip_payload_axis_tlast}), + .s_ip_payload_axis_tid(0), + .s_ip_payload_axis_tdest(0), + .s_ip_payload_axis_tuser({s_ip_payload_axis_tuser, udp_tx_ip_payload_axis_tuser}), + // IP frame output + .m_ip_hdr_valid(ip_tx_ip_hdr_valid), + .m_ip_hdr_ready(ip_tx_ip_hdr_ready), + .m_eth_dest_mac(), + .m_eth_src_mac(), + .m_eth_type(), + .m_ip_version(), + .m_ip_ihl(), + .m_ip_dscp(ip_tx_ip_dscp), + .m_ip_ecn(ip_tx_ip_ecn), + .m_ip_length(ip_tx_ip_length), + .m_ip_identification(), + .m_ip_flags(), + .m_ip_fragment_offset(), + .m_ip_ttl(ip_tx_ip_ttl), + .m_ip_protocol(ip_tx_ip_protocol), + .m_ip_header_checksum(), + .m_ip_source_ip(ip_tx_ip_source_ip), + .m_ip_dest_ip(ip_tx_ip_dest_ip), + .m_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(ip_tx_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast), + .m_ip_payload_axis_tid(), + .m_ip_payload_axis_tdest(), + .m_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser) +); + +/* + * IP stack + */ +ip_complete_64 #( + .ARP_CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .ARP_REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .ARP_REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .ARP_REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT) +) +ip_complete_64_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(ip_tx_ip_hdr_valid), + .s_ip_hdr_ready(ip_tx_ip_hdr_ready), + .s_ip_dscp(ip_tx_ip_dscp), + .s_ip_ecn(ip_tx_ip_ecn), + .s_ip_length(ip_tx_ip_length), + .s_ip_ttl(ip_tx_ip_ttl), + .s_ip_protocol(ip_tx_ip_protocol), + .s_ip_source_ip(ip_tx_ip_source_ip), + .s_ip_dest_ip(ip_tx_ip_dest_ip), + .s_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(ip_tx_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(ip_rx_ip_hdr_valid), + .m_ip_hdr_ready(ip_rx_ip_hdr_ready), + .m_ip_eth_dest_mac(ip_rx_ip_eth_dest_mac), + .m_ip_eth_src_mac(ip_rx_ip_eth_src_mac), + .m_ip_eth_type(ip_rx_ip_eth_type), + .m_ip_version(ip_rx_ip_version), + .m_ip_ihl(ip_rx_ip_ihl), + .m_ip_dscp(ip_rx_ip_dscp), + .m_ip_ecn(ip_rx_ip_ecn), + .m_ip_length(ip_rx_ip_length), + .m_ip_identification(ip_rx_ip_identification), + .m_ip_flags(ip_rx_ip_flags), + .m_ip_fragment_offset(ip_rx_ip_fragment_offset), + .m_ip_ttl(ip_rx_ip_ttl), + .m_ip_protocol(ip_rx_ip_protocol), + .m_ip_header_checksum(ip_rx_ip_header_checksum), + .m_ip_source_ip(ip_rx_ip_source_ip), + .m_ip_dest_ip(ip_rx_ip_dest_ip), + .m_ip_payload_axis_tdata(ip_rx_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(ip_rx_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(ip_rx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(ip_rx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(ip_rx_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(ip_rx_ip_payload_axis_tuser), + // Status + .rx_busy(ip_rx_busy), + .tx_busy(ip_tx_busy), + .rx_error_header_early_termination(ip_rx_error_header_early_termination), + .rx_error_payload_early_termination(ip_rx_error_payload_early_termination), + .rx_error_invalid_header(ip_rx_error_invalid_header), + .rx_error_invalid_checksum(ip_rx_error_invalid_checksum), + .tx_error_payload_early_termination(ip_tx_error_payload_early_termination), + .tx_error_arp_failed(ip_tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_arp_cache(clear_arp_cache) +); + +/* + * UDP interface + */ +udp_64 #( + .CHECKSUM_GEN_ENABLE(UDP_CHECKSUM_GEN_ENABLE), + .CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH(UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH), + .CHECKSUM_HEADER_FIFO_ADDR_WIDTH(UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH) +) +udp_64_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(udp_rx_ip_hdr_valid), + .s_ip_hdr_ready(udp_rx_ip_hdr_ready), + .s_ip_eth_dest_mac(udp_rx_ip_eth_dest_mac), + .s_ip_eth_src_mac(udp_rx_ip_eth_src_mac), + .s_ip_eth_type(udp_rx_ip_eth_type), + .s_ip_version(udp_rx_ip_version), + .s_ip_ihl(udp_rx_ip_ihl), + .s_ip_dscp(udp_rx_ip_dscp), + .s_ip_ecn(udp_rx_ip_ecn), + .s_ip_length(udp_rx_ip_length), + .s_ip_identification(udp_rx_ip_identification), + .s_ip_flags(udp_rx_ip_flags), + .s_ip_fragment_offset(udp_rx_ip_fragment_offset), + .s_ip_ttl(udp_rx_ip_ttl), + .s_ip_protocol(udp_rx_ip_protocol), + .s_ip_header_checksum(udp_rx_ip_header_checksum), + .s_ip_source_ip(udp_rx_ip_source_ip), + .s_ip_dest_ip(udp_rx_ip_dest_ip), + .s_ip_payload_axis_tdata(udp_rx_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(udp_rx_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(udp_rx_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(udp_rx_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(udp_rx_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(udp_rx_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(udp_tx_ip_hdr_valid), + .m_ip_hdr_ready(udp_tx_ip_hdr_ready), + .m_ip_eth_dest_mac(), + .m_ip_eth_src_mac(), + .m_ip_eth_type(), + .m_ip_version(), + .m_ip_ihl(), + .m_ip_dscp(udp_tx_ip_dscp), + .m_ip_ecn(udp_tx_ip_ecn), + .m_ip_length(udp_tx_ip_length), + .m_ip_identification(), + .m_ip_flags(), + .m_ip_fragment_offset(), + .m_ip_ttl(udp_tx_ip_ttl), + .m_ip_protocol(udp_tx_ip_protocol), + .m_ip_header_checksum(), + .m_ip_source_ip(udp_tx_ip_source_ip), + .m_ip_dest_ip(udp_tx_ip_dest_ip), + .m_ip_payload_axis_tdata(udp_tx_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(udp_tx_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(udp_tx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(udp_tx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(udp_tx_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(udp_tx_ip_payload_axis_tuser), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_udp_eth_dest_mac(48'd0), + .s_udp_eth_src_mac(48'd0), + .s_udp_eth_type(16'd0), + .s_udp_ip_version(4'd0), + .s_udp_ip_ihl(4'd0), + .s_udp_ip_dscp(s_udp_ip_dscp), + .s_udp_ip_ecn(s_udp_ip_ecn), + .s_udp_ip_identification(16'd0), + .s_udp_ip_flags(3'd0), + .s_udp_ip_fragment_offset(13'd0), + .s_udp_ip_ttl(s_udp_ip_ttl), + .s_udp_ip_header_checksum(16'd0), + .s_udp_ip_source_ip(s_udp_ip_source_ip), + .s_udp_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_udp_eth_dest_mac(m_udp_eth_dest_mac), + .m_udp_eth_src_mac(m_udp_eth_src_mac), + .m_udp_eth_type(m_udp_eth_type), + .m_udp_ip_version(m_udp_ip_version), + .m_udp_ip_ihl(m_udp_ip_ihl), + .m_udp_ip_dscp(m_udp_ip_dscp), + .m_udp_ip_ecn(m_udp_ip_ecn), + .m_udp_ip_length(m_udp_ip_length), + .m_udp_ip_identification(m_udp_ip_identification), + .m_udp_ip_flags(m_udp_ip_flags), + .m_udp_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_udp_ip_ttl(m_udp_ip_ttl), + .m_udp_ip_protocol(m_udp_ip_protocol), + .m_udp_ip_header_checksum(m_udp_ip_header_checksum), + .m_udp_ip_source_ip(m_udp_ip_source_ip), + .m_udp_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status + .rx_busy(udp_rx_busy), + .tx_busy(udp_tx_busy), + .rx_error_header_early_termination(udp_rx_error_header_early_termination), + .rx_error_payload_early_termination(udp_rx_error_payload_early_termination), + .tx_error_payload_early_termination(udp_tx_error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/rtl/udp_demux.v b/fpga/lib/eth/rtl/udp_demux.v new file mode 100644 index 000000000..f4dfb8cc9 --- /dev/null +++ b/fpga/lib/eth/rtl/udp_demux.v @@ -0,0 +1,424 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * UDP demultiplexer + */ +module udp_demux # +( + parameter M_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [DATA_WIDTH-1:0] s_udp_payload_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire [ID_WIDTH-1:0] s_udp_payload_axis_tid, + input wire [DEST_WIDTH-1:0] s_udp_payload_axis_tdest, + input wire [USER_WIDTH-1:0] s_udp_payload_axis_tuser, + + /* + * UDP frame outputs + */ + output wire [M_COUNT-1:0] m_udp_hdr_valid, + input wire [M_COUNT-1:0] m_udp_hdr_ready, + output wire [M_COUNT*48-1:0] m_eth_dest_mac, + output wire [M_COUNT*48-1:0] m_eth_src_mac, + output wire [M_COUNT*16-1:0] m_eth_type, + output wire [M_COUNT*4-1:0] m_ip_version, + output wire [M_COUNT*4-1:0] m_ip_ihl, + output wire [M_COUNT*6-1:0] m_ip_dscp, + output wire [M_COUNT*2-1:0] m_ip_ecn, + output wire [M_COUNT*16-1:0] m_ip_length, + output wire [M_COUNT*16-1:0] m_ip_identification, + output wire [M_COUNT*3-1:0] m_ip_flags, + output wire [M_COUNT*13-1:0] m_ip_fragment_offset, + output wire [M_COUNT*8-1:0] m_ip_ttl, + output wire [M_COUNT*8-1:0] m_ip_protocol, + output wire [M_COUNT*16-1:0] m_ip_header_checksum, + output wire [M_COUNT*32-1:0] m_ip_source_ip, + output wire [M_COUNT*32-1:0] m_ip_dest_ip, + output wire [M_COUNT*16-1:0] m_udp_source_port, + output wire [M_COUNT*16-1:0] m_udp_dest_port, + output wire [M_COUNT*16-1:0] m_udp_length, + output wire [M_COUNT*16-1:0] m_udp_checksum, + output wire [M_COUNT*DATA_WIDTH-1:0] m_udp_payload_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep, + output wire [M_COUNT-1:0] m_udp_payload_axis_tvalid, + input wire [M_COUNT-1:0] m_udp_payload_axis_tready, + output wire [M_COUNT-1:0] m_udp_payload_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_udp_payload_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_udp_payload_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_udp_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [$clog2(M_COUNT)-1:0] select +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +reg [CL_M_COUNT-1:0] select_reg = {CL_M_COUNT{1'b0}}, select_ctl, select_next; +reg drop_reg = 1'b0, drop_ctl, drop_next; +reg frame_reg = 1'b0, frame_ctl, frame_next; + +reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next; + +reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next; + +reg [M_COUNT-1:0] m_udp_hdr_valid_reg = 0, m_udp_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; +reg [15:0] m_udp_source_port_reg = 16'd0, m_udp_source_port_next; +reg [15:0] m_udp_dest_port_reg = 16'd0, m_udp_dest_port_next; +reg [15:0] m_udp_length_reg = 16'd0, m_udp_length_next; +reg [15:0] m_udp_checksum_reg = 16'd0, m_udp_checksum_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_int; +reg [M_COUNT-1:0] m_udp_payload_axis_tvalid_int; +reg m_udp_payload_axis_tready_int_reg = 1'b0; +reg m_udp_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_int; +wire m_udp_payload_axis_tready_int_early; + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg && enable; + +assign s_udp_payload_axis_tready = s_udp_payload_axis_tready_reg && enable; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; +assign m_eth_dest_mac = {M_COUNT{m_eth_dest_mac_reg}}; +assign m_eth_src_mac = {M_COUNT{m_eth_src_mac_reg}}; +assign m_eth_type = {M_COUNT{m_eth_type_reg}}; +assign m_ip_version = {M_COUNT{m_ip_version_reg}}; +assign m_ip_ihl = {M_COUNT{m_ip_ihl_reg}}; +assign m_ip_dscp = {M_COUNT{m_ip_dscp_reg}}; +assign m_ip_ecn = {M_COUNT{m_ip_ecn_reg}}; +assign m_ip_length = {M_COUNT{m_ip_length_reg}}; +assign m_ip_identification = {M_COUNT{m_ip_identification_reg}}; +assign m_ip_flags = {M_COUNT{m_ip_flags_reg}}; +assign m_ip_fragment_offset = {M_COUNT{m_ip_fragment_offset_reg}}; +assign m_ip_ttl = {M_COUNT{m_ip_ttl_reg}}; +assign m_ip_protocol = {M_COUNT{m_ip_protocol_reg}}; +assign m_ip_header_checksum = {M_COUNT{m_ip_header_checksum_reg}}; +assign m_ip_source_ip = {M_COUNT{m_ip_source_ip_reg}}; +assign m_ip_dest_ip = {M_COUNT{m_ip_dest_ip_reg}}; +assign m_udp_source_port = {M_COUNT{m_udp_source_port_reg}}; +assign m_udp_dest_port = {M_COUNT{m_udp_dest_port_reg}}; +assign m_udp_length = {M_COUNT{m_udp_length_reg}}; +assign m_udp_checksum = {M_COUNT{m_udp_checksum_reg}}; + +integer i; + +always @* begin + select_next = select_reg; + select_ctl = select_reg; + drop_next = drop_reg; + drop_ctl = drop_reg; + frame_next = frame_reg; + frame_ctl = frame_reg; + + s_udp_hdr_ready_next = 1'b0; + + s_udp_payload_axis_tready_next = 1'b0; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg & ~m_udp_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + m_udp_source_port_next = m_udp_source_port_reg; + m_udp_dest_port_next = m_udp_dest_port_reg; + m_udp_length_next = m_udp_length_reg; + m_udp_checksum_next = m_udp_checksum_reg; + + if (s_udp_payload_axis_tvalid && s_udp_payload_axis_tready) begin + // end of frame detection + if (s_udp_payload_axis_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + + if (!frame_reg && s_udp_hdr_valid && s_udp_hdr_ready) begin + // start of frame, grab select value + select_ctl = select; + drop_ctl = drop; + frame_ctl = 1'b1; + + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = frame_ctl; + + s_udp_hdr_ready_next = 1'b0; + + m_udp_hdr_valid_next = (!drop_ctl) << select_ctl; + m_eth_dest_mac_next = s_eth_dest_mac; + m_eth_src_mac_next = s_eth_src_mac; + m_eth_type_next = s_eth_type; + m_ip_version_next = s_ip_version; + m_ip_ihl_next = s_ip_ihl; + m_ip_dscp_next = s_ip_dscp; + m_ip_ecn_next = s_ip_ecn; + m_ip_length_next = s_ip_length; + m_ip_identification_next = s_ip_identification; + m_ip_flags_next = s_ip_flags; + m_ip_fragment_offset_next = s_ip_fragment_offset; + m_ip_ttl_next = s_ip_ttl; + m_ip_protocol_next = s_ip_protocol; + m_ip_header_checksum_next = s_ip_header_checksum; + m_ip_source_ip_next = s_ip_source_ip; + m_ip_dest_ip_next = s_ip_dest_ip; + m_udp_source_port_next = s_udp_source_port; + m_udp_dest_port_next = s_udp_dest_port; + m_udp_length_next = s_udp_length; + m_udp_checksum_next = s_udp_checksum; + end + + s_udp_hdr_ready_next = !frame_next && !m_udp_hdr_valid_next; + + s_udp_payload_axis_tready_next = (m_udp_payload_axis_tready_int_early || drop_ctl) && frame_ctl; + + m_udp_payload_axis_tdata_int = s_udp_payload_axis_tdata; + m_udp_payload_axis_tkeep_int = s_udp_payload_axis_tkeep; + m_udp_payload_axis_tvalid_int = (s_udp_payload_axis_tvalid && s_udp_payload_axis_tready && !drop_ctl) << select_ctl; + m_udp_payload_axis_tlast_int = s_udp_payload_axis_tlast; + m_udp_payload_axis_tid_int = s_udp_payload_axis_tid; + m_udp_payload_axis_tdest_int = s_udp_payload_axis_tdest; + m_udp_payload_axis_tuser_int = s_udp_payload_axis_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 2'd0; + drop_reg <= 1'b0; + frame_reg <= 1'b0; + s_udp_hdr_ready_reg <= 1'b0; + s_udp_payload_axis_tready_reg <= 1'b0; + m_udp_hdr_valid_reg <= 0; + end else begin + select_reg <= select_next; + drop_reg <= drop_next; + frame_reg <= frame_next; + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; + m_udp_source_port_reg <= m_udp_source_port_next; + m_udp_dest_port_reg <= m_udp_dest_port_next; + m_udp_length_reg <= m_udp_length_next; + m_udp_checksum_reg <= m_udp_checksum_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_udp_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, m_udp_payload_axis_tvalid_next; +reg m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] temp_m_udp_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, temp_m_udp_payload_axis_tvalid_next; +reg temp_m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_udp_payload_axis_temp_to_output; + +assign m_udp_payload_axis_tdata = {M_COUNT{m_udp_payload_axis_tdata_reg}}; +assign m_udp_payload_axis_tkeep = KEEP_ENABLE ? {M_COUNT{m_udp_payload_axis_tkeep_reg}} : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg; +assign m_udp_payload_axis_tlast = {M_COUNT{m_udp_payload_axis_tlast_reg}}; +assign m_udp_payload_axis_tid = ID_ENABLE ? {M_COUNT{m_udp_payload_axis_tid_reg}} : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_udp_payload_axis_tdest = DEST_ENABLE ? {M_COUNT{m_udp_payload_axis_tdest_reg}} : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_udp_payload_axis_tuser = USER_ENABLE ? {M_COUNT{m_udp_payload_axis_tuser_reg}} : {M_COUNT*USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_udp_payload_axis_tready_int_early = (m_udp_payload_axis_tready & m_udp_payload_axis_tvalid) || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid || !m_udp_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b0; + + if (m_udp_payload_axis_tready_int_reg) begin + // input is ready + if ((m_udp_payload_axis_tready & m_udp_payload_axis_tvalid) || !m_udp_payload_axis_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_udp_payload_axis_tready & m_udp_payload_axis_tvalid) begin + // input is not ready, but output is ready + m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_udp_payload_axis_tvalid_reg <= {M_COUNT{1'b0}}; + m_udp_payload_axis_tready_int_reg <= 1'b0; + temp_m_udp_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next; + m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early; + temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end else if (store_udp_payload_axis_temp_to_output) begin + m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg; + m_udp_payload_axis_tkeep_reg <= temp_m_udp_payload_axis_tkeep_reg; + m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg; + m_udp_payload_axis_tid_reg <= temp_m_udp_payload_axis_tid_reg; + m_udp_payload_axis_tdest_reg <= temp_m_udp_payload_axis_tdest_reg; + m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + temp_m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + temp_m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + temp_m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/udp_ip_rx.v b/fpga/lib/eth/rtl/udp_ip_rx.v new file mode 100644 index 000000000..609ff09a2 --- /dev/null +++ b/fpga/lib/eth/rtl/udp_ip_rx.v @@ -0,0 +1,532 @@ +/* + +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 1ns / 1ps + +/* + * UDP ethernet frame receiver (IP frame in, UDP frame out) + */ +module udp_ip_rx +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [7:0] m_udp_payload_axis_tdata, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_payload_early_termination +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives an IP frame with header fields in parallel and payload on +an AXI stream interface, decodes and strips the UDP header fields, then +produces the header fields in parallel along with the UDP payload in a +separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_READ_PAYLOAD = 3'd2, + STATE_READ_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_ip_hdr; +reg store_udp_source_port_0; +reg store_udp_source_port_1; +reg store_udp_dest_port_0; +reg store_udp_dest_port_1; +reg store_udp_length_0; +reg store_udp_length_1; +reg store_udp_checksum_0; +reg store_udp_checksum_1; +reg store_last_word; + +reg [2:0] hdr_ptr_reg = 3'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [7:0] last_word_data_reg = 8'd0; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; +reg [15:0] m_udp_source_port_reg = 16'd0; +reg [15:0] m_udp_dest_port_reg = 16'd0; +reg [15:0] m_udp_length_reg = 16'd0; +reg [15:0] m_udp_checksum_reg = 16'd0; + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; +reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +// internal datapath +reg [7:0] m_udp_payload_axis_tdata_int; +reg m_udp_payload_axis_tvalid_int; +reg m_udp_payload_axis_tready_int_reg = 1'b0; +reg m_udp_payload_axis_tlast_int; +reg m_udp_payload_axis_tuser_int; +wire m_udp_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +always @* begin + state_next = STATE_IDLE; + + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b0; + + store_ip_hdr = 1'b0; + store_udp_source_port_0 = 1'b0; + store_udp_source_port_1 = 1'b0; + store_udp_dest_port_0 = 1'b0; + store_udp_dest_port_1 = 1'b0; + store_udp_length_0 = 1'b0; + store_udp_length_1 = 1'b0; + store_udp_checksum_0 = 1'b0; + store_udp_checksum_1 = 1'b0; + + store_last_word = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg && !m_udp_hdr_ready; + + error_header_early_termination_next = 1'b0; + error_payload_early_termination_next = 1'b0; + + m_udp_payload_axis_tdata_int = 8'd0; + m_udp_payload_axis_tvalid_int = 1'b0; + m_udp_payload_axis_tlast_int = 1'b0; + m_udp_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for header + hdr_ptr_next = 3'd0; + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + + if (s_ip_hdr_ready && s_ip_hdr_valid) begin + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b1; + store_ip_hdr = 1'b1; + state_next = STATE_READ_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header state + s_ip_payload_axis_tready_next = 1'b1; + word_count_next = m_udp_length_reg - 16'd8; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + // word transfer in - store it + hdr_ptr_next = hdr_ptr_reg + 3'd1; + state_next = STATE_READ_HEADER; + + case (hdr_ptr_reg) + 3'h0: store_udp_source_port_1 = 1'b1; + 3'h1: store_udp_source_port_0 = 1'b1; + 3'h2: store_udp_dest_port_1 = 1'b1; + 3'h3: store_udp_dest_port_0 = 1'b1; + 3'h4: store_udp_length_1 = 1'b1; + 3'h5: store_udp_length_0 = 1'b1; + 3'h6: store_udp_checksum_1 = 1'b1; + 3'h7: begin + store_udp_checksum_0 = 1'b1; + m_udp_hdr_valid_next = 1'b1; + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + state_next = STATE_READ_PAYLOAD; + end + endcase + + if (s_ip_payload_axis_tlast) begin + error_header_early_termination_next = 1'b1; + m_udp_hdr_valid_next = 1'b0; + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end + + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_READ_PAYLOAD: begin + // read payload + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + + m_udp_payload_axis_tdata_int = s_ip_payload_axis_tdata; + m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid; + m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd1; + if (s_ip_payload_axis_tlast) begin + if (word_count_reg != 16'd1) begin + // end of frame, but length does not match + m_udp_payload_axis_tuser_int = 1'b1; + error_payload_early_termination_next = 1'b1; + end + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + if (word_count_reg == 16'd1) begin + store_last_word = 1'b1; + m_udp_payload_axis_tvalid_int = 1'b0; + state_next = STATE_READ_PAYLOAD_LAST; + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + STATE_READ_PAYLOAD_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + + m_udp_payload_axis_tdata_int = last_word_data_reg; + m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid && s_ip_payload_axis_tlast; + m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // wait for end of frame; read and discard + s_ip_payload_axis_tready_next = 1'b1; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_ip_hdr_ready_reg <= 1'b0; + s_ip_payload_axis_tready_reg <= 1'b0; + m_udp_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_payload_early_termination_reg <= error_payload_early_termination_next; + + busy_reg <= state_next != STATE_IDLE; + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + // datapath + if (store_ip_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + m_ip_version_reg <= s_ip_version; + m_ip_ihl_reg <= s_ip_ihl; + m_ip_dscp_reg <= s_ip_dscp; + m_ip_ecn_reg <= s_ip_ecn; + m_ip_length_reg <= s_ip_length; + m_ip_identification_reg <= s_ip_identification; + m_ip_flags_reg <= s_ip_flags; + m_ip_fragment_offset_reg <= s_ip_fragment_offset; + m_ip_ttl_reg <= s_ip_ttl; + m_ip_protocol_reg <= s_ip_protocol; + m_ip_header_checksum_reg <= s_ip_header_checksum; + m_ip_source_ip_reg <= s_ip_source_ip; + m_ip_dest_ip_reg <= s_ip_dest_ip; + end + + if (store_last_word) begin + last_word_data_reg <= m_udp_payload_axis_tdata_int; + end + + if (store_udp_source_port_0) m_udp_source_port_reg[ 7: 0] <= s_ip_payload_axis_tdata; + if (store_udp_source_port_1) m_udp_source_port_reg[15: 8] <= s_ip_payload_axis_tdata; + if (store_udp_dest_port_0) m_udp_dest_port_reg[ 7: 0] <= s_ip_payload_axis_tdata; + if (store_udp_dest_port_1) m_udp_dest_port_reg[15: 8] <= s_ip_payload_axis_tdata; + if (store_udp_length_0) m_udp_length_reg[ 7: 0] <= s_ip_payload_axis_tdata; + if (store_udp_length_1) m_udp_length_reg[15: 8] <= s_ip_payload_axis_tdata; + if (store_udp_checksum_0) m_udp_checksum_reg[ 7: 0] <= s_ip_payload_axis_tdata; + if (store_udp_checksum_1) m_udp_checksum_reg[15: 8] <= s_ip_payload_axis_tdata; +end + +// output datapath logic +reg [7:0] m_udp_payload_axis_tdata_reg = 8'd0; +reg m_udp_payload_axis_tvalid_reg = 1'b0, m_udp_payload_axis_tvalid_next; +reg m_udp_payload_axis_tlast_reg = 1'b0; +reg m_udp_payload_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_udp_payload_axis_tdata_reg = 8'd0; +reg temp_m_udp_payload_axis_tvalid_reg = 1'b0, temp_m_udp_payload_axis_tvalid_next; +reg temp_m_udp_payload_axis_tlast_reg = 1'b0; +reg temp_m_udp_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_udp_payload_int_to_output; +reg store_udp_payload_int_to_temp; +reg store_udp_payload_axis_temp_to_output; + +assign m_udp_payload_axis_tdata = m_udp_payload_axis_tdata_reg; +assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg; +assign m_udp_payload_axis_tlast = m_udp_payload_axis_tlast_reg; +assign m_udp_payload_axis_tuser = m_udp_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_udp_payload_axis_tready_int_early = m_udp_payload_axis_tready || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid_reg || !m_udp_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + + store_udp_payload_int_to_output = 1'b0; + store_udp_payload_int_to_temp = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b0; + + if (m_udp_payload_axis_tready_int_reg) begin + // input is ready + if (m_udp_payload_axis_tready || !m_udp_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_udp_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_udp_payload_int_to_temp = 1'b1; + end + end else if (m_udp_payload_axis_tready) begin + // input is not ready, but output is ready + m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_udp_payload_axis_tvalid_reg <= 1'b0; + m_udp_payload_axis_tready_int_reg <= 1'b0; + temp_m_udp_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next; + m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early; + temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next; + end + + // datapath + if (store_udp_payload_int_to_output) begin + m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end else if (store_udp_payload_axis_temp_to_output) begin + m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg; + m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg; + m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg; + end + + if (store_udp_payload_int_to_temp) begin + temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/udp_ip_rx_64.v b/fpga/lib/eth/rtl/udp_ip_rx_64.v new file mode 100644 index 000000000..e3244ce28 --- /dev/null +++ b/fpga/lib/eth/rtl/udp_ip_rx_64.v @@ -0,0 +1,560 @@ +/* + +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 1ns / 1ps + +/* + * UDP ethernet frame receiver (IP frame in, UDP frame out, 64 bit datapath) + */ +module udp_ip_rx_64 +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [63:0] m_udp_payload_axis_tdata, + output wire [7:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_payload_early_termination +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives an IP frame with header fields in parallel and payload on +an AXI stream interface, decodes and strips the UDP header fields, then +produces the header fields in parallel along with the UDP payload in a +separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_READ_PAYLOAD = 3'd2, + STATE_READ_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_ip_hdr; +reg store_hdr_word_0; +reg store_last_word; + +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [63:0] last_word_data_reg = 64'd0; +reg [7:0] last_word_keep_reg = 8'd0; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; +reg [15:0] m_udp_source_port_reg = 16'd0; +reg [15:0] m_udp_dest_port_reg = 16'd0; +reg [15:0] m_udp_length_reg = 16'd0; +reg [15:0] m_udp_checksum_reg = 16'd0; + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; +reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +// internal datapath +reg [63:0] m_udp_payload_axis_tdata_int; +reg [7:0] m_udp_payload_axis_tkeep_int; +reg m_udp_payload_axis_tvalid_int; +reg m_udp_payload_axis_tready_int_reg = 1'b0; +reg m_udp_payload_axis_tlast_int; +reg m_udp_payload_axis_tuser_int; +wire m_udp_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +always @* begin + state_next = STATE_IDLE; + + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b0; + + store_ip_hdr = 1'b0; + store_hdr_word_0 = 1'b0; + + store_last_word = 1'b0; + + word_count_next = word_count_reg; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg && !m_udp_hdr_ready; + + error_header_early_termination_next = 1'b0; + error_payload_early_termination_next = 1'b0; + + m_udp_payload_axis_tdata_int = 64'd0; + m_udp_payload_axis_tkeep_int = 8'd0; + m_udp_payload_axis_tvalid_int = 1'b0; + m_udp_payload_axis_tlast_int = 1'b0; + m_udp_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for header + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + + if (s_ip_hdr_ready && s_ip_hdr_valid) begin + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b1; + store_ip_hdr = 1'b1; + state_next = STATE_READ_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header state + s_ip_payload_axis_tready_next = 1'b1; + + word_count_next = {s_ip_payload_axis_tdata[39:32], s_ip_payload_axis_tdata[47:40]} - 16'd8; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + // word transfer in - store it + state_next = STATE_READ_HEADER; + + store_hdr_word_0 = 1'b1; + m_udp_hdr_valid_next = 1'b1; + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + state_next = STATE_READ_PAYLOAD; + + if (s_ip_payload_axis_tlast) begin + error_header_early_termination_next = 1'b1; + m_udp_hdr_valid_next = 1'b0; + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end + + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_READ_PAYLOAD: begin + // read payload + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + + m_udp_payload_axis_tdata_int = s_ip_payload_axis_tdata; + m_udp_payload_axis_tkeep_int = s_ip_payload_axis_tkeep; + m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid; + m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + store_last_word = 1'b1; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd8; + if (word_count_reg <= 8) begin + // have entire payload + m_udp_payload_axis_tkeep_int = s_ip_payload_axis_tkeep & count2keep(word_count_reg); + if (s_ip_payload_axis_tlast) begin + if (keep2count(s_ip_payload_axis_tkeep) < word_count_reg[4:0]) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_udp_payload_axis_tuser_int = 1'b1; + end + s_ip_payload_axis_tready_next = 1'b0; + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + m_udp_payload_axis_tvalid_int = 1'b0; + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + if (s_ip_payload_axis_tlast) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_udp_payload_axis_tuser_int = 1'b1; + s_ip_payload_axis_tready_next = 1'b0; + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + STATE_READ_PAYLOAD_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + + m_udp_payload_axis_tdata_int = last_word_data_reg; + m_udp_payload_axis_tkeep_int = last_word_keep_reg; + m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid && s_ip_payload_axis_tlast; + m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // wait for end of frame; read and discard + s_ip_payload_axis_tready_next = 1'b1; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_ip_hdr_ready_reg <= 1'b0; + s_ip_payload_axis_tready_reg <= 1'b0; + m_udp_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_payload_early_termination_reg <= error_payload_early_termination_next; + + busy_reg <= state_next != STATE_IDLE; + end + + word_count_reg <= word_count_next; + + // datapath + if (store_ip_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + m_ip_version_reg <= s_ip_version; + m_ip_ihl_reg <= s_ip_ihl; + m_ip_dscp_reg <= s_ip_dscp; + m_ip_ecn_reg <= s_ip_ecn; + m_ip_length_reg <= s_ip_length; + m_ip_identification_reg <= s_ip_identification; + m_ip_flags_reg <= s_ip_flags; + m_ip_fragment_offset_reg <= s_ip_fragment_offset; + m_ip_ttl_reg <= s_ip_ttl; + m_ip_protocol_reg <= s_ip_protocol; + m_ip_header_checksum_reg <= s_ip_header_checksum; + m_ip_source_ip_reg <= s_ip_source_ip; + m_ip_dest_ip_reg <= s_ip_dest_ip; + end + + if (store_last_word) begin + last_word_data_reg <= m_udp_payload_axis_tdata_int; + last_word_keep_reg <= m_udp_payload_axis_tkeep_int; + end + + if (store_hdr_word_0) begin + m_udp_source_port_reg[15: 8] <= s_ip_payload_axis_tdata[ 7: 0]; + m_udp_source_port_reg[ 7: 0] <= s_ip_payload_axis_tdata[15: 8]; + m_udp_dest_port_reg[15: 8] <= s_ip_payload_axis_tdata[23:16]; + m_udp_dest_port_reg[ 7: 0] <= s_ip_payload_axis_tdata[31:24]; + m_udp_length_reg[15: 8] <= s_ip_payload_axis_tdata[39:32]; + m_udp_length_reg[ 7: 0] <= s_ip_payload_axis_tdata[47:40]; + m_udp_checksum_reg[15: 8] <= s_ip_payload_axis_tdata[55:48]; + m_udp_checksum_reg[ 7: 0] <= s_ip_payload_axis_tdata[63:56]; + end +end + +// output datapath logic +reg [63:0] m_udp_payload_axis_tdata_reg = 64'd0; +reg [7:0] m_udp_payload_axis_tkeep_reg = 8'd0; +reg m_udp_payload_axis_tvalid_reg = 1'b0, m_udp_payload_axis_tvalid_next; +reg m_udp_payload_axis_tlast_reg = 1'b0; +reg m_udp_payload_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_udp_payload_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_udp_payload_axis_tkeep_reg = 8'd0; +reg temp_m_udp_payload_axis_tvalid_reg = 1'b0, temp_m_udp_payload_axis_tvalid_next; +reg temp_m_udp_payload_axis_tlast_reg = 1'b0; +reg temp_m_udp_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_udp_payload_int_to_output; +reg store_udp_payload_int_to_temp; +reg store_udp_payload_axis_temp_to_output; + +assign m_udp_payload_axis_tdata = m_udp_payload_axis_tdata_reg; +assign m_udp_payload_axis_tkeep = m_udp_payload_axis_tkeep_reg; +assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg; +assign m_udp_payload_axis_tlast = m_udp_payload_axis_tlast_reg; +assign m_udp_payload_axis_tuser = m_udp_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_udp_payload_axis_tready_int_early = m_udp_payload_axis_tready || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid_reg || !m_udp_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + + store_udp_payload_int_to_output = 1'b0; + store_udp_payload_int_to_temp = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b0; + + if (m_udp_payload_axis_tready_int_reg) begin + // input is ready + if (m_udp_payload_axis_tready || !m_udp_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_udp_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_udp_payload_int_to_temp = 1'b1; + end + end else if (m_udp_payload_axis_tready) begin + // input is not ready, but output is ready + m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_udp_payload_axis_tvalid_reg <= 1'b0; + m_udp_payload_axis_tready_int_reg <= 1'b0; + temp_m_udp_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next; + m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early; + temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next; + end + + // datapath + if (store_udp_payload_int_to_output) begin + m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end else if (store_udp_payload_axis_temp_to_output) begin + m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg; + m_udp_payload_axis_tkeep_reg <= temp_m_udp_payload_axis_tkeep_reg; + m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg; + m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg; + end + + if (store_udp_payload_int_to_temp) begin + temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + temp_m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/udp_ip_tx.v b/fpga/lib/eth/rtl/udp_ip_tx.v new file mode 100644 index 000000000..23ae0ee47 --- /dev/null +++ b/fpga/lib/eth/rtl/udp_ip_tx.v @@ -0,0 +1,493 @@ +/* + +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 1ns / 1ps + +/* + * UDP ethernet frame transmitter (UDP frame in, IP frame out) + */ +module udp_ip_tx +( + input wire clk, + input wire rst, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [7:0] s_udp_payload_axis_tdata, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_payload_early_termination +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives a UDP frame with header fields in parallel along with the +payload in an AXI stream, combines the header with the payload, passes through +the IP headers, and transmits the complete IP payload on an AXI interface. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_WRITE_HEADER = 3'd1, + STATE_WRITE_PAYLOAD = 3'd2, + STATE_WRITE_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_udp_hdr; +reg store_last_word; + +reg [2:0] hdr_ptr_reg = 3'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [7:0] last_word_data_reg = 8'd0; + +reg [15:0] udp_source_port_reg = 16'd0; +reg [15:0] udp_dest_port_reg = 16'd0; +reg [15:0] udp_length_reg = 16'd0; +reg [15:0] udp_checksum_reg = 16'd0; + +reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next; +reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; + +reg busy_reg = 1'b0; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +// internal datapath +reg [7:0] m_ip_payload_axis_tdata_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg; +assign s_udp_payload_axis_tready = s_udp_payload_axis_tready_reg; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +assign busy = busy_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +always @* begin + state_next = STATE_IDLE; + + s_udp_hdr_ready_next = 1'b0; + s_udp_payload_axis_tready_next = 1'b0; + + store_udp_hdr = 1'b0; + + store_last_word = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + + error_payload_early_termination_next = 1'b0; + + m_ip_payload_axis_tdata_int = 8'd0; + m_ip_payload_axis_tvalid_int = 1'b0; + m_ip_payload_axis_tlast_int = 1'b0; + m_ip_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + hdr_ptr_next = 3'd0; + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + + if (s_udp_hdr_ready && s_udp_hdr_valid) begin + store_udp_hdr = 1'b1; + s_udp_hdr_ready_next = 1'b0; + m_ip_hdr_valid_next = 1'b1; + if (m_ip_payload_axis_tready_int_reg) begin + m_ip_payload_axis_tvalid_int = 1'b1; + m_ip_payload_axis_tdata_int = s_udp_source_port[15: 8]; + hdr_ptr_next = 3'd1; + end + state_next = STATE_WRITE_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // write header state + word_count_next = udp_length_reg - 16'd8; + + if (m_ip_payload_axis_tready_int_reg) begin + // word transfer out + hdr_ptr_next = hdr_ptr_reg + 3'd1; + m_ip_payload_axis_tvalid_int = 1'b1; + state_next = STATE_WRITE_HEADER; + case (hdr_ptr_reg) + 3'h0: m_ip_payload_axis_tdata_int = udp_source_port_reg[15: 8]; + 3'h1: m_ip_payload_axis_tdata_int = udp_source_port_reg[ 7: 0]; + 3'h2: m_ip_payload_axis_tdata_int = udp_dest_port_reg[15: 8]; + 3'h3: m_ip_payload_axis_tdata_int = udp_dest_port_reg[ 7: 0]; + 3'h4: m_ip_payload_axis_tdata_int = udp_length_reg[15: 8]; + 3'h5: m_ip_payload_axis_tdata_int = udp_length_reg[ 7: 0]; + 3'h6: m_ip_payload_axis_tdata_int = udp_checksum_reg[15: 8]; + 3'h7: begin + m_ip_payload_axis_tdata_int = udp_checksum_reg[ 7: 0]; + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + state_next = STATE_WRITE_PAYLOAD; + end + endcase + end else begin + state_next = STATE_WRITE_HEADER; + end + end + STATE_WRITE_PAYLOAD: begin + // write payload + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = s_udp_payload_axis_tdata; + m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid; + m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser; + + if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd1; + if (s_udp_payload_axis_tlast) begin + if (word_count_reg != 16'd1) begin + // end of frame, but length does not match + m_ip_payload_axis_tuser_int = 1'b1; + error_payload_early_termination_next = 1'b1; + end + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + s_udp_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + if (word_count_reg == 16'd1) begin + store_last_word = 1'b1; + m_ip_payload_axis_tvalid_int = 1'b0; + state_next = STATE_WRITE_PAYLOAD_LAST; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + STATE_WRITE_PAYLOAD_LAST: begin + // read and discard until end of frame + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = last_word_data_reg; + m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid && s_udp_payload_axis_tlast; + m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser; + + if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin + if (s_udp_payload_axis_tlast) begin + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + s_udp_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // wait for end of frame; read and discard + s_udp_payload_axis_tready_next = 1'b1; + + if (s_udp_payload_axis_tvalid) begin + if (s_udp_payload_axis_tlast) begin + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + s_udp_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_udp_hdr_ready_reg <= 1'b0; + s_udp_payload_axis_tready_reg <= 1'b0; + m_ip_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + + error_payload_early_termination_reg <= error_payload_early_termination_next; + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + // datapath + if (store_udp_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + m_ip_version_reg <= s_ip_version; + m_ip_ihl_reg <= s_ip_ihl; + m_ip_dscp_reg <= s_ip_dscp; + m_ip_ecn_reg <= s_ip_ecn; + m_ip_length_reg <= s_udp_length + 20; + m_ip_identification_reg <= s_ip_identification; + m_ip_flags_reg <= s_ip_flags; + m_ip_fragment_offset_reg <= s_ip_fragment_offset; + m_ip_ttl_reg <= s_ip_ttl; + m_ip_protocol_reg <= s_ip_protocol; + m_ip_header_checksum_reg <= s_ip_header_checksum; + m_ip_source_ip_reg <= s_ip_source_ip; + m_ip_dest_ip_reg <= s_ip_dest_ip; + udp_source_port_reg <= s_udp_source_port; + udp_dest_port_reg <= s_udp_dest_port; + udp_length_reg <= s_udp_length; + udp_checksum_reg <= s_udp_checksum; + end + + if (store_last_word) begin + last_word_data_reg <= m_ip_payload_axis_tdata_int; + end +end + +// output datapath logic +reg [7:0] m_ip_payload_axis_tdata_reg = 8'd0; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg m_ip_payload_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_ip_payload_axis_tdata_reg = 8'd0; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg temp_m_ip_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_ip_payload_int_to_output; +reg store_ip_payload_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_ip_payload_int_to_output = 1'b0; + store_ip_payload_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_ip_payload_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_ip_payload_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/udp_ip_tx_64.v b/fpga/lib/eth/rtl/udp_ip_tx_64.v new file mode 100644 index 000000000..fe899b900 --- /dev/null +++ b/fpga/lib/eth/rtl/udp_ip_tx_64.v @@ -0,0 +1,549 @@ +/* + +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 1ns / 1ps + +/* + * UDP ethernet frame transmitter (UDP frame in, IP frame out, 64-bit datapath) + */ +module udp_ip_tx_64 +( + input wire clk, + input wire rst, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [63:0] s_udp_payload_axis_tdata, + input wire [7:0] s_udp_payload_axis_tkeep, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_payload_early_termination +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives a UDP frame with header fields in parallel along with the +payload in an AXI stream, combines the header with the payload, passes through +the IP headers, and transmits the complete IP payload on an AXI interface. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_WRITE_HEADER = 3'd1, + STATE_WRITE_PAYLOAD = 3'd2, + STATE_WRITE_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_udp_hdr; +reg store_last_word; + +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [63:0] last_word_data_reg = 64'd0; +reg [7:0] last_word_keep_reg = 8'd0; + +reg [15:0] udp_source_port_reg = 16'd0; +reg [15:0] udp_dest_port_reg = 16'd0; +reg [15:0] udp_length_reg = 16'd0; +reg [15:0] udp_checksum_reg = 16'd0; + +reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next; +reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; + +reg busy_reg = 1'b0; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +// internal datapath +reg [63:0] m_ip_payload_axis_tdata_int; +reg [7:0] m_ip_payload_axis_tkeep_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg; +assign s_udp_payload_axis_tready = s_udp_payload_axis_tready_reg; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +assign busy = busy_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +always @* begin + state_next = STATE_IDLE; + + s_udp_hdr_ready_next = 1'b0; + s_udp_payload_axis_tready_next = 1'b0; + + store_udp_hdr = 1'b0; + + store_last_word = 1'b0; + + word_count_next = word_count_reg; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + + error_payload_early_termination_next = 1'b0; + + m_ip_payload_axis_tdata_int = 64'd0; + m_ip_payload_axis_tkeep_int = 8'd0; + m_ip_payload_axis_tvalid_int = 1'b0; + m_ip_payload_axis_tlast_int = 1'b0; + m_ip_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + word_count_next = s_udp_length - 16'd8; + + if (s_udp_hdr_ready && s_udp_hdr_valid) begin + store_udp_hdr = 1'b1; + s_udp_hdr_ready_next = 1'b0; + m_ip_hdr_valid_next = 1'b1; + state_next = STATE_WRITE_HEADER; + if (m_ip_payload_axis_tready_int_reg) begin + m_ip_payload_axis_tvalid_int = 1'b1; + m_ip_payload_axis_tdata_int[ 7: 0] = s_udp_source_port[15: 8]; + m_ip_payload_axis_tdata_int[15: 8] = s_udp_source_port[ 7: 0]; + m_ip_payload_axis_tdata_int[23:16] = s_udp_dest_port[15: 8]; + m_ip_payload_axis_tdata_int[31:24] = s_udp_dest_port[ 7: 0]; + m_ip_payload_axis_tdata_int[39:32] = s_udp_length[15: 8]; + m_ip_payload_axis_tdata_int[47:40] = s_udp_length[ 7: 0]; + m_ip_payload_axis_tdata_int[55:48] = s_udp_checksum[15: 8]; + m_ip_payload_axis_tdata_int[63:56] = s_udp_checksum[ 7: 0]; + m_ip_payload_axis_tkeep_int = 8'hff; + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + state_next = STATE_WRITE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // write header state + if (m_ip_payload_axis_tready_int_reg) begin + // word transfer out + m_ip_payload_axis_tvalid_int = 1'b1; + m_ip_payload_axis_tdata_int[ 7: 0] = udp_source_port_reg[15: 8]; + m_ip_payload_axis_tdata_int[15: 8] = udp_source_port_reg[ 7: 0]; + m_ip_payload_axis_tdata_int[23:16] = udp_dest_port_reg[15: 8]; + m_ip_payload_axis_tdata_int[31:24] = udp_dest_port_reg[ 7: 0]; + m_ip_payload_axis_tdata_int[39:32] = udp_length_reg[15: 8]; + m_ip_payload_axis_tdata_int[47:40] = udp_length_reg[ 7: 0]; + m_ip_payload_axis_tdata_int[55:48] = udp_checksum_reg[15: 8]; + m_ip_payload_axis_tdata_int[63:56] = udp_checksum_reg[ 7: 0]; + m_ip_payload_axis_tkeep_int = 8'hff; + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + state_next = STATE_WRITE_PAYLOAD; + end else begin + state_next = STATE_WRITE_HEADER; + end + end + STATE_WRITE_PAYLOAD: begin + // write payload + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = s_udp_payload_axis_tdata; + m_ip_payload_axis_tkeep_int = s_udp_payload_axis_tkeep; + m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid; + m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser; + + store_last_word = 1'b1; + + if (m_ip_payload_axis_tready_int_reg && s_udp_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd8; + if (word_count_reg <= 8) begin + // have entire payload + m_ip_payload_axis_tkeep_int = count2keep(word_count_reg); + if (s_udp_payload_axis_tlast) begin + if (keep2count(s_udp_payload_axis_tkeep) < word_count_reg[4:0]) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_ip_payload_axis_tuser_int = 1'b1; + end + s_udp_payload_axis_tready_next = 1'b0; + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + m_ip_payload_axis_tvalid_int = 1'b0; + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + if (s_udp_payload_axis_tlast) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_ip_payload_axis_tuser_int = 1'b1; + s_udp_payload_axis_tready_next = 1'b0; + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + STATE_WRITE_PAYLOAD_LAST: begin + // read and discard until end of frame + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = last_word_data_reg; + m_ip_payload_axis_tkeep_int = last_word_keep_reg; + m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid && s_udp_payload_axis_tlast; + m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser; + + if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin + if (s_udp_payload_axis_tlast) begin + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + s_udp_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // wait for end of frame; read and discard + s_udp_payload_axis_tready_next = 1'b1; + + if (s_udp_payload_axis_tvalid) begin + if (s_udp_payload_axis_tlast) begin + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + s_udp_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_udp_hdr_ready_reg <= 1'b0; + s_udp_payload_axis_tready_reg <= 1'b0; + m_ip_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + + error_payload_early_termination_reg <= error_payload_early_termination_next; + end + + word_count_reg <= word_count_next; + + // datapath + if (store_udp_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + m_ip_version_reg <= s_ip_version; + m_ip_ihl_reg <= s_ip_ihl; + m_ip_dscp_reg <= s_ip_dscp; + m_ip_ecn_reg <= s_ip_ecn; + m_ip_length_reg <= s_udp_length + 20; + m_ip_identification_reg <= s_ip_identification; + m_ip_flags_reg <= s_ip_flags; + m_ip_fragment_offset_reg <= s_ip_fragment_offset; + m_ip_ttl_reg <= s_ip_ttl; + m_ip_protocol_reg <= s_ip_protocol; + m_ip_header_checksum_reg <= s_ip_header_checksum; + m_ip_source_ip_reg <= s_ip_source_ip; + m_ip_dest_ip_reg <= s_ip_dest_ip; + udp_source_port_reg <= s_udp_source_port; + udp_dest_port_reg <= s_udp_dest_port; + udp_length_reg <= s_udp_length; + udp_checksum_reg <= s_udp_checksum; + end + + if (store_last_word) begin + last_word_data_reg <= m_ip_payload_axis_tdata_int; + last_word_keep_reg <= m_ip_payload_axis_tkeep_int; + end +end + +// output datapath logic +reg [63:0] m_ip_payload_axis_tdata_reg = 64'd0; +reg [7:0] m_ip_payload_axis_tkeep_reg = 8'd0; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg m_ip_payload_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_ip_payload_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_ip_payload_axis_tkeep_reg = 8'd0; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg temp_m_ip_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_ip_payload_int_to_output; +reg store_ip_payload_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tkeep = m_ip_payload_axis_tkeep_reg; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_ip_payload_int_to_output = 1'b0; + store_ip_payload_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_ip_payload_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_ip_payload_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/udp_mux.v b/fpga/lib/eth/rtl/udp_mux.v new file mode 100644 index 000000000..7d33cedbb --- /dev/null +++ b/fpga/lib/eth/rtl/udp_mux.v @@ -0,0 +1,418 @@ +/* + +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 1ns / 1ps + +/* + * UDP multiplexer + */ +module udp_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * UDP frame inputs + */ + input wire [S_COUNT-1:0] s_udp_hdr_valid, + output wire [S_COUNT-1:0] s_udp_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*4-1:0] s_ip_version, + input wire [S_COUNT*4-1:0] s_ip_ihl, + input wire [S_COUNT*6-1:0] s_ip_dscp, + input wire [S_COUNT*2-1:0] s_ip_ecn, + input wire [S_COUNT*16-1:0] s_ip_length, + input wire [S_COUNT*16-1:0] s_ip_identification, + input wire [S_COUNT*3-1:0] s_ip_flags, + input wire [S_COUNT*13-1:0] s_ip_fragment_offset, + input wire [S_COUNT*8-1:0] s_ip_ttl, + input wire [S_COUNT*8-1:0] s_ip_protocol, + input wire [S_COUNT*16-1:0] s_ip_header_checksum, + input wire [S_COUNT*32-1:0] s_ip_source_ip, + input wire [S_COUNT*32-1:0] s_ip_dest_ip, + input wire [S_COUNT*16-1:0] s_udp_source_port, + input wire [S_COUNT*16-1:0] s_udp_dest_port, + input wire [S_COUNT*16-1:0] s_udp_length, + input wire [S_COUNT*16-1:0] s_udp_checksum, + input wire [S_COUNT*DATA_WIDTH-1:0] s_udp_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_udp_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_udp_payload_axis_tready, + input wire [S_COUNT-1:0] s_udp_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_udp_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_udp_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [DATA_WIDTH-1:0] m_udp_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_udp_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_udp_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_udp_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire [$clog2(S_COUNT)-1:0] select +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg [CL_S_COUNT-1:0] select_reg = 2'd0, select_next; +reg frame_reg = 1'b0, frame_next; + +reg [S_COUNT-1:0] s_udp_hdr_ready_reg = 0, s_udp_hdr_ready_next; + +reg [S_COUNT-1:0] s_udp_payload_axis_tready_reg = 0, s_udp_payload_axis_tready_next; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; +reg [15:0] m_udp_source_port_reg = 16'd0, m_udp_source_port_next; +reg [15:0] m_udp_dest_port_reg = 16'd0, m_udp_dest_port_next; +reg [15:0] m_udp_length_reg = 16'd0, m_udp_length_next; +reg [15:0] m_udp_checksum_reg = 16'd0, m_udp_checksum_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_int; +reg m_udp_payload_axis_tvalid_int; +reg m_udp_payload_axis_tready_int_reg = 1'b0; +reg m_udp_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_int; +wire m_udp_payload_axis_tready_int_early; + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg; + +assign s_udp_payload_axis_tready = s_udp_payload_axis_tready_reg; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_udp_payload_axis_tdata[select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_udp_payload_axis_tkeep[select_reg*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_udp_payload_axis_tvalid[select_reg]; +wire current_s_tready = s_udp_payload_axis_tready[select_reg]; +wire current_s_tlast = s_udp_payload_axis_tlast[select_reg]; +wire [ID_WIDTH-1:0] current_s_tid = s_udp_payload_axis_tid[select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_udp_payload_axis_tdest[select_reg*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_udp_payload_axis_tuser[select_reg*USER_WIDTH +: USER_WIDTH]; + +always @* begin + select_next = select_reg; + frame_next = frame_reg; + + s_udp_hdr_ready_next = 0; + + s_udp_payload_axis_tready_next = 0; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg && !m_udp_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + m_udp_source_port_next = m_udp_source_port_reg; + m_udp_dest_port_next = m_udp_dest_port_reg; + m_udp_length_next = m_udp_length_reg; + m_udp_checksum_next = m_udp_checksum_reg; + + if (current_s_tvalid & current_s_tready) begin + // end of frame detection + if (current_s_tlast) begin + frame_next = 1'b0; + end + end + + if (!frame_reg && enable && !m_udp_hdr_valid && (s_udp_hdr_valid & (1 << select))) begin + // start of frame, grab select value + frame_next = 1'b1; + select_next = select; + + s_udp_hdr_ready_next = (1 << select); + + m_udp_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[select*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[select*48 +: 48]; + m_eth_type_next = s_eth_type[select*16 +: 16]; + m_ip_version_next = s_ip_version[select*4 +: 4]; + m_ip_ihl_next = s_ip_ihl[select*4 +: 4]; + m_ip_dscp_next = s_ip_dscp[select*6 +: 6]; + m_ip_ecn_next = s_ip_ecn[select*2 +: 2]; + m_ip_length_next = s_ip_length[select*16 +: 16]; + m_ip_identification_next = s_ip_identification[select*16 +: 16]; + m_ip_flags_next = s_ip_flags[select*3 +: 3]; + m_ip_fragment_offset_next = s_ip_fragment_offset[select*13 +: 13]; + m_ip_ttl_next = s_ip_ttl[select*8 +: 8]; + m_ip_protocol_next = s_ip_protocol[select*8 +: 8]; + m_ip_header_checksum_next = s_ip_header_checksum[select*16 +: 16]; + m_ip_source_ip_next = s_ip_source_ip[select*32 +: 32]; + m_ip_dest_ip_next = s_ip_dest_ip[select*32 +: 32]; + m_udp_source_port_next = s_udp_source_port[select*16 +: 16]; + m_udp_dest_port_next = s_udp_dest_port[select*16 +: 16]; + m_udp_length_next = s_udp_length[select*16 +: 16]; + m_udp_checksum_next = s_udp_checksum[select*16 +: 16]; + end + + // generate ready signal on selected port + s_udp_payload_axis_tready_next = (m_udp_payload_axis_tready_int_early && frame_next) << select_next; + + // pass through selected packet data + m_udp_payload_axis_tdata_int = current_s_tdata; + m_udp_payload_axis_tkeep_int = current_s_tkeep; + m_udp_payload_axis_tvalid_int = current_s_tvalid && current_s_tready && frame_reg; + m_udp_payload_axis_tlast_int = current_s_tlast; + m_udp_payload_axis_tid_int = current_s_tid; + m_udp_payload_axis_tdest_int = current_s_tdest; + m_udp_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 0; + frame_reg <= 1'b0; + s_udp_hdr_ready_reg <= 0; + s_udp_payload_axis_tready_reg <= 0; + m_udp_hdr_valid_reg <= 1'b0; + end else begin + select_reg <= select_next; + frame_reg <= frame_next; + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; + m_udp_source_port_reg <= m_udp_source_port_next; + m_udp_dest_port_reg <= m_udp_dest_port_next; + m_udp_length_reg <= m_udp_length_next; + m_udp_checksum_reg <= m_udp_checksum_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_udp_payload_axis_tvalid_reg = 1'b0, m_udp_payload_axis_tvalid_next; +reg m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_udp_payload_axis_tvalid_reg = 1'b0, temp_m_udp_payload_axis_tvalid_next; +reg temp_m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_udp_payload_axis_tdata = m_udp_payload_axis_tdata_reg; +assign m_udp_payload_axis_tkeep = KEEP_ENABLE ? m_udp_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg; +assign m_udp_payload_axis_tlast = m_udp_payload_axis_tlast_reg; +assign m_udp_payload_axis_tid = ID_ENABLE ? m_udp_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_udp_payload_axis_tdest = DEST_ENABLE ? m_udp_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_udp_payload_axis_tuser = USER_ENABLE ? m_udp_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_udp_payload_axis_tready_int_early = m_udp_payload_axis_tready || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid_reg || !m_udp_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_udp_payload_axis_tready_int_reg) begin + // input is ready + if (m_udp_payload_axis_tready || !m_udp_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_udp_payload_axis_tready) begin + // input is not ready, but output is ready + m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_udp_payload_axis_tvalid_reg <= 1'b0; + m_udp_payload_axis_tready_int_reg <= 1'b0; + temp_m_udp_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next; + m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early; + temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg; + m_udp_payload_axis_tkeep_reg <= temp_m_udp_payload_axis_tkeep_reg; + m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg; + m_udp_payload_axis_tid_reg <= temp_m_udp_payload_axis_tid_reg; + m_udp_payload_axis_tdest_reg <= temp_m_udp_payload_axis_tdest_reg; + m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + temp_m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + temp_m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + temp_m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end +end + +endmodule diff --git a/fpga/lib/eth/rtl/xgmii_baser_dec_64.v b/fpga/lib/eth/rtl/xgmii_baser_dec_64.v new file mode 100644 index 000000000..f10c88b78 --- /dev/null +++ b/fpga/lib/eth/rtl/xgmii_baser_dec_64.v @@ -0,0 +1,325 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * XGMII 10GBASE-R decoder + */ +module xgmii_baser_dec_64 # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2 +) +( + input wire clk, + input wire rst, + + /* + * 10GBASE-R encoded input + */ + input wire [DATA_WIDTH-1:0] encoded_rx_data, + input wire [HDR_WIDTH-1:0] encoded_rx_hdr, + + /* + * XGMII interface + */ + output wire [DATA_WIDTH-1:0] xgmii_rxd, + output wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * Status + */ + output wire rx_bad_block +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_LPI = 8'h06, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe, + XGMII_SEQ_OS = 8'h9c, + XGMII_RES_0 = 8'h1c, + XGMII_RES_1 = 8'h3c, + XGMII_RES_2 = 8'h7c, + XGMII_RES_3 = 8'hbc, + XGMII_RES_4 = 8'hdc, + XGMII_RES_5 = 8'hf7, + XGMII_SIG_OS = 8'h5c; + +localparam [6:0] + CTRL_IDLE = 7'h00, + CTRL_LPI = 7'h06, + CTRL_ERROR = 7'h1e, + CTRL_RES_0 = 7'h2d, + CTRL_RES_1 = 7'h33, + CTRL_RES_2 = 7'h4b, + CTRL_RES_3 = 7'h55, + CTRL_RES_4 = 7'h66, + CTRL_RES_5 = 7'h78; + +localparam [3:0] + O_SEQ_OS = 4'h0, + O_SIG_OS = 4'hf; + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +localparam [7:0] + BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT + BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT + BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT + BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT + BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT + BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT + BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT + BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT + BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT + BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT + +reg [DATA_WIDTH-1:0] decoded_ctrl; +reg [CTRL_WIDTH-1:0] decode_err; + +reg [DATA_WIDTH-1:0] xgmii_rxd_reg = {DATA_WIDTH{1'b0}}, xgmii_rxd_next; +reg [CTRL_WIDTH-1:0] xgmii_rxc_reg = {CTRL_WIDTH{1'b0}}, xgmii_rxc_next; + +reg rx_bad_block_reg = 1'b0, rx_bad_block_next; + +assign xgmii_rxd = xgmii_rxd_reg; +assign xgmii_rxc = xgmii_rxc_reg; + +assign rx_bad_block = rx_bad_block_reg; + +integer i; + +always @* begin + xgmii_rxd_next = {8{XGMII_ERROR}}; + xgmii_rxc_next = 8'hff; + rx_bad_block_next = 1'b0; + + for (i = 0; i < CTRL_WIDTH; i = i + 1) begin + case (encoded_rx_data[7*i+8 +: 7]) + CTRL_IDLE: begin + decoded_ctrl[8*i +: 8] = XGMII_IDLE; + decode_err[i] = 1'b0; + end + CTRL_ERROR: begin + decoded_ctrl[8*i +: 8] = XGMII_ERROR; + decode_err[i] = 1'b0; + end + CTRL_RES_0: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_0; + decode_err[i] = 1'b0; + end + CTRL_RES_1: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_1; + decode_err[i] = 1'b0; + end + CTRL_RES_2: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_2; + decode_err[i] = 1'b0; + end + CTRL_RES_3: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_3; + decode_err[i] = 1'b0; + end + CTRL_RES_4: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_4; + decode_err[i] = 1'b0; + end + CTRL_RES_5: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_5; + decode_err[i] = 1'b0; + end + default: begin + decoded_ctrl[8*i +: 8] = XGMII_ERROR; + decode_err[i] = 1'b1; + end + endcase + end + + if (encoded_rx_hdr == SYNC_DATA) begin + xgmii_rxd_next = encoded_rx_data; + xgmii_rxc_next = 8'h00; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_CTRL: begin + // C7 C6 C5 C4 C3 C2 C1 C0 BT + xgmii_rxd_next = decoded_ctrl; + xgmii_rxc_next = 8'hff; + end + BLOCK_TYPE_OS_4: begin + // D7 D6 D5 O4 C3 C2 C1 C0 BT + xgmii_rxd_next[31:0] = decoded_ctrl[31:0]; + xgmii_rxc_next[3:0] = 4'hf; + if (encoded_rx_data[39:36] == O_SEQ_OS) begin + xgmii_rxd_next[63:32] = {encoded_rx_data[63:40], XGMII_SEQ_OS}; + xgmii_rxc_next[7:4] = 4'h1; + end else begin + xgmii_rxd_next[63:32] = {4{XGMII_ERROR}}; + xgmii_rxc_next[7:4] = 4'hf; + end + end + BLOCK_TYPE_START_4: begin + // D7 D6 D5 C3 C2 C1 C0 BT + xgmii_rxd_next = {encoded_rx_data[63:40], XGMII_START, decoded_ctrl[31:0]}; + xgmii_rxc_next = 8'h1f; + end + BLOCK_TYPE_OS_START: begin + // D7 D6 D5 O0 D3 D2 D1 BT + if (encoded_rx_data[35:32] == O_SEQ_OS) begin + xgmii_rxd_next[31:0] = {encoded_rx_data[31:8], XGMII_SEQ_OS}; + xgmii_rxc_next[3:0] = 4'h1; + end else begin + xgmii_rxd_next[31:0] = {4{XGMII_ERROR}}; + xgmii_rxc_next[3:0] = 4'hf; + end + xgmii_rxd_next[63:32] = {encoded_rx_data[63:40], XGMII_START}; + xgmii_rxc_next[7:4] = 4'h1; + end + BLOCK_TYPE_OS_04: begin + // D7 D6 D5 O4 O0 D3 D2 D1 BT + if (encoded_rx_data[35:32] == O_SEQ_OS) begin + xgmii_rxd_next[31:0] = {encoded_rx_data[31:8], XGMII_SEQ_OS}; + xgmii_rxc_next[3:0] = 4'h1; + end else begin + xgmii_rxd_next[31:0] = {4{XGMII_ERROR}}; + xgmii_rxc_next[3:0] = 4'hf; + end + if (encoded_rx_data[39:36] == O_SEQ_OS) begin + xgmii_rxd_next[63:32] = {encoded_rx_data[63:40], XGMII_SEQ_OS}; + xgmii_rxc_next[7:4] = 4'h1; + end else begin + xgmii_rxd_next[63:32] = {4{XGMII_ERROR}}; + xgmii_rxc_next[7:4] = 4'hf; + end + end + BLOCK_TYPE_START_0: begin + // D7 D6 D5 D4 D3 D2 D1 BT + xgmii_rxd_next = {encoded_rx_data[63:8], XGMII_START}; + xgmii_rxc_next = 8'h01; + end + BLOCK_TYPE_OS_0: begin + // C7 C6 C5 C4 O0 D3 D2 D1 BT + if (encoded_rx_data[35:32] == O_SEQ_OS) begin + xgmii_rxd_next[31:0] = {encoded_rx_data[31:8], XGMII_SEQ_OS}; + xgmii_rxc_next[3:0] = 4'h1; + end else begin + xgmii_rxd_next[31:0] = {4{XGMII_ERROR}}; + xgmii_rxc_next[3:0] = 4'hf; + end + xgmii_rxd_next[63:32] = decoded_ctrl[63:32]; + xgmii_rxc_next[7:4] = 4'hf; + end + BLOCK_TYPE_TERM_0: begin + // C7 C6 C5 C4 C3 C2 C1 BT + xgmii_rxd_next = {decoded_ctrl[63:8], XGMII_TERM}; + xgmii_rxc_next = 8'hff; + end + BLOCK_TYPE_TERM_1: begin + // C7 C6 C5 C4 C3 C2 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:16], XGMII_TERM, encoded_rx_data[15:8]}; + xgmii_rxc_next = 8'hfe; + end + BLOCK_TYPE_TERM_2: begin + // C7 C6 C5 C4 C3 D1 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:24], XGMII_TERM, encoded_rx_data[23:8]}; + xgmii_rxc_next = 8'hfc; + end + BLOCK_TYPE_TERM_3: begin + // C7 C6 C5 C4 D2 D1 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:32], XGMII_TERM, encoded_rx_data[31:8]}; + xgmii_rxc_next = 8'hf8; + end + BLOCK_TYPE_TERM_4: begin + // C7 C6 C5 D3 D2 D1 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:40], XGMII_TERM, encoded_rx_data[39:8]}; + xgmii_rxc_next = 8'hf0; + end + BLOCK_TYPE_TERM_5: begin + // C7 C6 D4 D3 D2 D1 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:48], XGMII_TERM, encoded_rx_data[47:8]}; + xgmii_rxc_next = 8'he0; + end + BLOCK_TYPE_TERM_6: begin + // C7 D5 D4 D3 D2 D1 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:56], XGMII_TERM, encoded_rx_data[55:8]}; + xgmii_rxc_next = 8'hc0; + end + BLOCK_TYPE_TERM_7: begin + // D6 D5 D4 D3 D2 D1 D0 BT + xgmii_rxd_next = {XGMII_TERM, encoded_rx_data[63:8]}; + xgmii_rxc_next = 8'h80; + end + default: begin + // invalid block type + xgmii_rxd_next = {8{XGMII_ERROR}}; + xgmii_rxc_next = 8'hff; + rx_bad_block_next = 1'b1; + end + endcase + end else begin + // invalid header + xgmii_rxd_next = {8{XGMII_ERROR}}; + xgmii_rxc_next = 8'hff; + rx_bad_block_next = 1'b1; + end +end + +always @(posedge clk) begin + xgmii_rxd_reg <= xgmii_rxd_next; + xgmii_rxc_reg <= xgmii_rxc_next; + + rx_bad_block_reg <= rx_bad_block_next; +end + +endmodule diff --git a/fpga/lib/eth/rtl/xgmii_baser_enc_64.v b/fpga/lib/eth/rtl/xgmii_baser_enc_64.v new file mode 100644 index 000000000..9cb38d7c0 --- /dev/null +++ b/fpga/lib/eth/rtl/xgmii_baser_enc_64.v @@ -0,0 +1,253 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * XGMII 10GBASE-R encoder + */ +module xgmii_baser_enc_64 # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2 +) +( + input wire clk, + input wire rst, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_txd, + input wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * 10GBASE-R encoded interface + */ + output wire [DATA_WIDTH-1:0] encoded_tx_data, + output wire [HDR_WIDTH-1:0] encoded_tx_hdr +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_LPI = 8'h06, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe, + XGMII_SEQ_OS = 8'h9c, + XGMII_RES_0 = 8'h1c, + XGMII_RES_1 = 8'h3c, + XGMII_RES_2 = 8'h7c, + XGMII_RES_3 = 8'hbc, + XGMII_RES_4 = 8'hdc, + XGMII_RES_5 = 8'hf7, + XGMII_SIG_OS = 8'h5c; + +localparam [6:0] + CTRL_IDLE = 7'h00, + CTRL_LPI = 7'h06, + CTRL_ERROR = 7'h1e, + CTRL_RES_0 = 7'h2d, + CTRL_RES_1 = 7'h33, + CTRL_RES_2 = 7'h4b, + CTRL_RES_3 = 7'h55, + CTRL_RES_4 = 7'h66, + CTRL_RES_5 = 7'h78; + +localparam [3:0] + O_SEQ_OS = 4'h0, + O_SIG_OS = 4'hf; + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +localparam [7:0] + BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT + BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT + BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT + BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT + BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT + BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT + BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT + BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT + BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT + BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT + +reg [DATA_WIDTH*7/8-1:0] encoded_ctrl; +reg [CTRL_WIDTH-1:0] encode_err; + +reg [DATA_WIDTH-1:0] encoded_tx_data_reg = {DATA_WIDTH{1'b0}}, encoded_tx_data_next; +reg [HDR_WIDTH-1:0] encoded_tx_hdr_reg = {HDR_WIDTH{1'b0}}, encoded_tx_hdr_next; + +assign encoded_tx_data = encoded_tx_data_reg; +assign encoded_tx_hdr = encoded_tx_hdr_reg; + +integer i; + +always @* begin + + + for (i = 0; i < CTRL_WIDTH; i = i + 1) begin + if (xgmii_txc[i]) begin + // control + case (xgmii_txd[8*i +: 8]) + XGMII_IDLE: begin + encoded_ctrl[7*i +: 7] = CTRL_IDLE; + encode_err[i] = 1'b0; + end + XGMII_ERROR: begin + encoded_ctrl[7*i +: 7] = CTRL_ERROR; + encode_err[i] = 1'b0; + end + XGMII_RES_0: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_0; + encode_err[i] = 1'b0; + end + XGMII_RES_1: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_1; + encode_err[i] = 1'b0; + end + XGMII_RES_2: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_2; + encode_err[i] = 1'b0; + end + XGMII_RES_3: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_3; + encode_err[i] = 1'b0; + end + XGMII_RES_4: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_4; + encode_err[i] = 1'b0; + end + XGMII_RES_5: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_5; + encode_err[i] = 1'b0; + end + default: begin + encoded_ctrl[7*i +: 7] = CTRL_ERROR; + encode_err[i] = 1'b1; + end + endcase + end else begin + // data (always invalid as control) + encoded_ctrl[7*i +: 7] = CTRL_ERROR; + encode_err[i] = 1'b1; + end + end + + if (xgmii_txc == 8'h00) begin + encoded_tx_data_next = xgmii_txd; + encoded_tx_hdr_next = SYNC_DATA; + end else begin + if (xgmii_txc[0] && xgmii_txd[7:0] == XGMII_START && !xgmii_txc[7:1]) begin + // start in lane 0 + encoded_tx_data_next = {xgmii_txd[63:8], BLOCK_TYPE_START_0}; + end else if (xgmii_txc[4] && xgmii_txd[39:32] == XGMII_START && !xgmii_txc[7:5]) begin + // start in lane 4 + if (xgmii_txc[0] && xgmii_txd[7:0] == XGMII_SEQ_OS && !xgmii_txc[3:1]) begin + // ordered set in lane 0 + encoded_tx_data_next[35:0] = {O_SEQ_OS, xgmii_txd[31:8], BLOCK_TYPE_START_4}; + end else begin + encoded_tx_data_next[35:0] = {encoded_ctrl[27:0], BLOCK_TYPE_START_4}; + end + encoded_tx_data_next[63:36] = {xgmii_txd[63:40], 4'd0}; + end else if (xgmii_txc[0] && xgmii_txd[7:0] == XGMII_SEQ_OS && !xgmii_txc[3:1]) begin + // ordered set in lane 0 + encoded_tx_data_next[35:8] = {O_SEQ_OS, xgmii_txd[31:8]}; + if (xgmii_txc[4] && xgmii_txd[39:32] == XGMII_SEQ_OS && !xgmii_txc[7:5]) begin + // ordered set in lane 4 + encoded_tx_data_next[63:36] = {xgmii_txd[63:40], O_SEQ_OS}; + encoded_tx_data_next[7:0] = BLOCK_TYPE_OS_04; + end else begin + encoded_tx_data_next[63:36] = encoded_ctrl[55:28]; + encoded_tx_data_next[7:0] = BLOCK_TYPE_OS_0; + end + end else if (xgmii_txc[4] && xgmii_txd[39:32] == XGMII_SEQ_OS && !xgmii_txc[7:5]) begin + // ordered set in lane 4 + encoded_tx_data_next = {xgmii_txd[63:40], O_SEQ_OS, 4'd0, encoded_ctrl[27:0], BLOCK_TYPE_OS_4}; + end else if (xgmii_txc[0] && xgmii_txd[7:0] == XGMII_TERM) begin + // terminate in lane 0 + encoded_tx_data_next = {encoded_ctrl[55:7], 7'd0, BLOCK_TYPE_TERM_0}; + end else if (xgmii_txc[1] && xgmii_txd[15:8] == XGMII_TERM && !xgmii_txc[0]) begin + // terminate in lane 1 + encoded_tx_data_next = {encoded_ctrl[55:14], 6'd0, xgmii_txd[7:0], BLOCK_TYPE_TERM_1}; + end else if (xgmii_txc[2] && xgmii_txd[23:16] == XGMII_TERM && !xgmii_txc[1:0]) begin + // terminate in lane 2 + encoded_tx_data_next = {encoded_ctrl[55:21], 5'd0, xgmii_txd[15:0], BLOCK_TYPE_TERM_2}; + end else if (xgmii_txc[3] && xgmii_txd[31:24] == XGMII_TERM && !xgmii_txc[2:0]) begin + // terminate in lane 3 + encoded_tx_data_next = {encoded_ctrl[55:28], 4'd0, xgmii_txd[23:0], BLOCK_TYPE_TERM_3}; + end else if (xgmii_txc[4] && xgmii_txd[39:32] == XGMII_TERM && !xgmii_txc[3:0]) begin + // terminate in lane 4 + encoded_tx_data_next = {encoded_ctrl[55:35], 3'd0, xgmii_txd[31:0], BLOCK_TYPE_TERM_4}; + end else if (xgmii_txc[5] && xgmii_txd[47:40] == XGMII_TERM && !xgmii_txc[4:0]) begin + // terminate in lane 5 + encoded_tx_data_next = {encoded_ctrl[55:42], 2'd0, xgmii_txd[39:0], BLOCK_TYPE_TERM_5}; + end else if (xgmii_txc[6] && xgmii_txd[55:48] == XGMII_TERM && !xgmii_txc[5:0]) begin + // terminate in lane 6 + encoded_tx_data_next = {encoded_ctrl[55:49], 1'd0, xgmii_txd[47:0], BLOCK_TYPE_TERM_6}; + end else if (xgmii_txc[7] && xgmii_txd[63:56] == XGMII_TERM && !xgmii_txc[6:0]) begin + // terminate in lane 7 + encoded_tx_data_next = {xgmii_txd[55:0], BLOCK_TYPE_TERM_7}; + end else begin + // all control + encoded_tx_data_next = {encoded_ctrl, BLOCK_TYPE_CTRL}; + end + encoded_tx_hdr_next = SYNC_CTRL; + end +end + +always @(posedge clk) begin + encoded_tx_data_reg <= encoded_tx_data_next; + encoded_tx_hdr_reg <= encoded_tx_hdr_next; +end + +endmodule diff --git a/fpga/lib/eth/rtl/xgmii_deinterleave.v b/fpga/lib/eth/rtl/xgmii_deinterleave.v new file mode 100644 index 000000000..5410bc955 --- /dev/null +++ b/fpga/lib/eth/rtl/xgmii_deinterleave.v @@ -0,0 +1,57 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * XGMII control/data deinterleave + */ +module xgmii_deinterleave +( + input wire [72:0] input_xgmii_dc, + + output wire [63:0] output_xgmii_d, + output wire [7:0] output_xgmii_c +); + +assign output_xgmii_d[7:0] = input_xgmii_dc[7:0]; +assign output_xgmii_c[0] = input_xgmii_dc[8]; +assign output_xgmii_d[15:8] = input_xgmii_dc[16:9]; +assign output_xgmii_c[1] = input_xgmii_dc[17]; +assign output_xgmii_d[23:16] = input_xgmii_dc[25:18]; +assign output_xgmii_c[2] = input_xgmii_dc[26]; +assign output_xgmii_d[31:24] = input_xgmii_dc[34:27]; +assign output_xgmii_c[3] = input_xgmii_dc[35]; +assign output_xgmii_d[39:32] = input_xgmii_dc[43:36]; +assign output_xgmii_c[4] = input_xgmii_dc[44]; +assign output_xgmii_d[47:40] = input_xgmii_dc[52:45]; +assign output_xgmii_c[5] = input_xgmii_dc[53]; +assign output_xgmii_d[55:48] = input_xgmii_dc[61:54]; +assign output_xgmii_c[6] = input_xgmii_dc[62]; +assign output_xgmii_d[63:56] = input_xgmii_dc[70:63]; +assign output_xgmii_c[7] = input_xgmii_dc[71]; + +endmodule diff --git a/fpga/lib/eth/rtl/xgmii_interleave.v b/fpga/lib/eth/rtl/xgmii_interleave.v new file mode 100644 index 000000000..cf365e2f1 --- /dev/null +++ b/fpga/lib/eth/rtl/xgmii_interleave.v @@ -0,0 +1,57 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * XGMII control/data interleave + */ +module xgmii_interleave +( + input wire [63:0] input_xgmii_d, + input wire [7:0] input_xgmii_c, + + output wire [72:0] output_xgmii_dc +); + +assign output_xgmii_dc[7:0] = input_xgmii_d[7:0]; +assign output_xgmii_dc[8] = input_xgmii_c[0]; +assign output_xgmii_dc[16:9] = input_xgmii_d[15:8]; +assign output_xgmii_dc[17] = input_xgmii_c[1]; +assign output_xgmii_dc[25:18] = input_xgmii_d[23:16]; +assign output_xgmii_dc[26] = input_xgmii_c[2]; +assign output_xgmii_dc[34:27] = input_xgmii_d[31:24]; +assign output_xgmii_dc[35] = input_xgmii_c[3]; +assign output_xgmii_dc[43:36] = input_xgmii_d[39:32]; +assign output_xgmii_dc[44] = input_xgmii_c[4]; +assign output_xgmii_dc[52:45] = input_xgmii_d[47:40]; +assign output_xgmii_dc[53] = input_xgmii_c[5]; +assign output_xgmii_dc[61:54] = input_xgmii_d[55:48]; +assign output_xgmii_dc[62] = input_xgmii_c[6]; +assign output_xgmii_dc[70:63] = input_xgmii_d[63:56]; +assign output_xgmii_dc[71] = input_xgmii_c[7]; + +endmodule diff --git a/fpga/lib/eth/syn/eth_mac_1g_gmii.tcl b/fpga/lib/eth/syn/eth_mac_1g_gmii.tcl new file mode 100644 index 000000000..65c637c78 --- /dev/null +++ b/fpga/lib/eth/syn/eth_mac_1g_gmii.tcl @@ -0,0 +1,55 @@ +# Copyright (c) 2019 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. + +# GMII Gigabit Ethernet MAC timing constraints + +foreach mac_inst [get_cells -hier -filter {(ORIG_REF_NAME == eth_mac_1g_gmii || REF_NAME == eth_mac_1g_gmii)}] { + puts "Inserting timing constraints for eth_mac_1g_gmii instance $mac_inst" + + set select_ffs [get_cells -hier -regexp ".*/tx_mii_select_sync_reg\\\[\\d\\\]" -filter "PARENT == $mac_inst"] + + if {[llength $select_ffs]} { + set_property ASYNC_REG TRUE $select_ffs + + set src_clk [get_clocks -of_objects [get_pins $mac_inst/mii_select_reg_reg/C]] + + set_max_delay -from [get_cells $mac_inst/mii_select_reg_reg] -to [get_cells $mac_inst/tx_mii_select_sync_reg[0]] -datapath_only [get_property -min PERIOD $src_clk] + } + + set select_ffs [get_cells -hier -regexp ".*/rx_mii_select_sync_reg\\\[\\d\\\]" -filter "PARENT == $mac_inst"] + + if {[llength $select_ffs]} { + set_property ASYNC_REG TRUE $select_ffs + + set src_clk [get_clocks -of_objects [get_pins $mac_inst/mii_select_reg_reg/C]] + + set_max_delay -from [get_cells $mac_inst/mii_select_reg_reg] -to [get_cells $mac_inst/rx_mii_select_sync_reg[0]] -datapath_only [get_property -min PERIOD $src_clk] + } + + set prescale_ffs [get_cells -hier -regexp ".*/rx_prescale_sync_reg\\\[\\d\\\]" -filter "PARENT == $mac_inst"] + + if {[llength $prescale_ffs]} { + set_property ASYNC_REG TRUE $prescale_ffs + + set src_clk [get_clocks -of_objects [get_pins $mac_inst/rx_prescale_reg[2]/C]] + + set_max_delay -from [get_cells $mac_inst/rx_prescale_reg[2]] -to [get_cells $mac_inst/rx_prescale_sync_reg[0]] -datapath_only [get_property -min PERIOD $src_clk] + } +} diff --git a/fpga/lib/eth/syn/eth_mac_1g_rgmii.tcl b/fpga/lib/eth/syn/eth_mac_1g_rgmii.tcl new file mode 100644 index 000000000..7f8d0e273 --- /dev/null +++ b/fpga/lib/eth/syn/eth_mac_1g_rgmii.tcl @@ -0,0 +1,55 @@ +# Copyright (c) 2019 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. + +# RGMII Gigabit Ethernet MAC timing constraints + +foreach mac_inst [get_cells -hier -filter {(ORIG_REF_NAME == eth_mac_1g_rgmii || REF_NAME == eth_mac_1g_rgmii)}] { + puts "Inserting timing constraints for eth_mac_1g_rgmii instance $mac_inst" + + set select_ffs [get_cells -hier -regexp ".*/tx_mii_select_sync_reg\\\[\\d\\\]" -filter "PARENT == $mac_inst"] + + if {[llength $select_ffs]} { + set_property ASYNC_REG TRUE $select_ffs + + set src_clk [get_clocks -of_objects [get_pins $mac_inst/mii_select_reg_reg/C]] + + set_max_delay -from [get_cells $mac_inst/mii_select_reg_reg] -to [get_cells $mac_inst/tx_mii_select_sync_reg[0]] -datapath_only [get_property -min PERIOD $src_clk] + } + + set select_ffs [get_cells -hier -regexp ".*/rx_mii_select_sync_reg\\\[\\d\\\]" -filter "PARENT == $mac_inst"] + + if {[llength $select_ffs]} { + set_property ASYNC_REG TRUE $select_ffs + + set src_clk [get_clocks -of_objects [get_pins $mac_inst/mii_select_reg_reg/C]] + + set_max_delay -from [get_cells $mac_inst/mii_select_reg_reg] -to [get_cells $mac_inst/rx_mii_select_sync_reg[0]] -datapath_only [get_property -min PERIOD $src_clk] + } + + set prescale_ffs [get_cells -hier -regexp ".*/rx_prescale_sync_reg\\\[\\d\\\]" -filter "PARENT == $mac_inst"] + + if {[llength $prescale_ffs]} { + set_property ASYNC_REG TRUE $prescale_ffs + + set src_clk [get_clocks -of_objects [get_pins $mac_inst/rx_prescale_reg[2]/C]] + + set_max_delay -from [get_cells $mac_inst/rx_prescale_reg[2]] -to [get_cells $mac_inst/rx_prescale_sync_reg[0]] -datapath_only [get_property -min PERIOD $src_clk] + } +} diff --git a/fpga/lib/eth/syn/eth_mac_fifo.tcl b/fpga/lib/eth/syn/eth_mac_fifo.tcl new file mode 100644 index 000000000..69a06318e --- /dev/null +++ b/fpga/lib/eth/syn/eth_mac_fifo.tcl @@ -0,0 +1,49 @@ +# Copyright (c) 2019 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. + +# Ethernet MAC with FIFO timing constraints + +foreach mac_inst [get_cells -hier -filter {(ORIG_REF_NAME == eth_mac_1g_fifo || REF_NAME == eth_mac_1g_fifo || \ + ORIG_REF_NAME == eth_mac_10g_fifo || REF_NAME == eth_mac_10g_fifo || \ + ORIG_REF_NAME == eth_mac_1g_gmii_fifo || REF_NAME == eth_mac_1g_gmii_fifo || \ + ORIG_REF_NAME == eth_mac_1g_rgmii_fifo || REF_NAME == eth_mac_1g_rgmii_fifo || \ + ORIG_REF_NAME == eth_mac_mii_fifo || REF_NAME == eth_mac_mii_fifo)}] { + puts "Inserting timing constraints for ethernet MAC with FIFO instance $mac_inst" + + set sync_ffs [get_cells -hier -regexp ".*/rx_sync_reg_\[1234\]_reg\\\[\\d+\\\]" -filter "PARENT == $mac_inst"] + + if {[llength $sync_ffs]} { + set_property ASYNC_REG TRUE $sync_ffs + + set src_clk [get_clocks -of_objects [get_pins $mac_inst/rx_sync_reg_1_reg[*]/C]] + + set_max_delay -from [get_cells $mac_inst/rx_sync_reg_1_reg[*]] -to [get_cells $mac_inst/rx_sync_reg_2_reg[*]] -datapath_only [get_property -min PERIOD $src_clk] + } + + set sync_ffs [get_cells -hier -regexp ".*/tx_sync_reg_\[1234\]_reg\\\[\\d+\\\]" -filter "PARENT == $mac_inst"] + + if {[llength $sync_ffs]} { + set_property ASYNC_REG TRUE $sync_ffs + + set src_clk [get_clocks -of_objects [get_pins $mac_inst/tx_sync_reg_1_reg[*]/C]] + + set_max_delay -from [get_cells $mac_inst/tx_sync_reg_1_reg[*]] -to [get_cells $mac_inst/tx_sync_reg_2_reg[*]] -datapath_only [get_property -min PERIOD $src_clk] + } +} diff --git a/fpga/lib/eth/syn/gmii_phy_if.tcl b/fpga/lib/eth/syn/gmii_phy_if.tcl new file mode 100644 index 000000000..f20ccc1fa --- /dev/null +++ b/fpga/lib/eth/syn/gmii_phy_if.tcl @@ -0,0 +1,31 @@ +# Copyright (c) 2019 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. + +# GMII PHY IF timing constraints + +foreach if_inst [get_cells -hier -filter {(ORIG_REF_NAME == gmii_phy_if || REF_NAME == gmii_phy_if)}] { + puts "Inserting timing constraints for gmii_phy_if instance $if_inst" + + # reset synchronization + set reset_ffs [get_cells -hier -regexp ".*/(rx|tx)_rst_reg_reg\\\[\\d\\\]" -filter "PARENT == $if_inst"] + + set_property ASYNC_REG TRUE $reset_ffs + set_false_path -to [get_pins -of_objects $reset_ffs -filter {IS_PRESET || IS_RESET}] +} diff --git a/fpga/lib/eth/syn/mii_phy_if.tcl b/fpga/lib/eth/syn/mii_phy_if.tcl new file mode 100644 index 000000000..64f518200 --- /dev/null +++ b/fpga/lib/eth/syn/mii_phy_if.tcl @@ -0,0 +1,31 @@ +# Copyright (c) 2019 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. + +# MII PHY IF timing constraints + +foreach if_inst [get_cells -hier -filter {(ORIG_REF_NAME == mii_phy_if || REF_NAME == mii_phy_if)}] { + puts "Inserting timing constraints for mii_phy_if instance $if_inst" + + # reset synchronization + set reset_ffs [get_cells -hier -regexp ".*/(rx|tx)_rst_reg_reg\\\[\\d\\\]" -filter "PARENT == $if_inst"] + + set_property ASYNC_REG TRUE $reset_ffs + set_false_path -to [get_pins -of_objects $reset_ffs -filter {IS_PRESET || IS_RESET}] +} diff --git a/fpga/lib/eth/syn/rgmii_phy_if.tcl b/fpga/lib/eth/syn/rgmii_phy_if.tcl new file mode 100644 index 000000000..57105081d --- /dev/null +++ b/fpga/lib/eth/syn/rgmii_phy_if.tcl @@ -0,0 +1,39 @@ +# Copyright (c) 2019 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. + +# RGMII PHY IF timing constraints + +foreach if_inst [get_cells -hier -filter {(ORIG_REF_NAME == rgmii_phy_if || REF_NAME == rgmii_phy_if)}] { + puts "Inserting timing constraints for rgmii_phy_if instance $if_inst" + + # reset synchronization + set reset_ffs [get_cells -hier -regexp ".*/(rx|tx)_rst_reg_reg\\\[\\d\\\]" -filter "PARENT == $if_inst"] + + set_property ASYNC_REG TRUE $reset_ffs + set_false_path -to [get_pins -of_objects $reset_ffs -filter {IS_PRESET || IS_RESET}] + + # clock output + set_property ASYNC_REG TRUE [get_cells $if_inst/clk_oddr_inst/oddr[0].oddr_inst] + + set src_clk [get_clocks -of_objects [get_pins $if_inst/rgmii_tx_clk_1_reg/C]] + + set_max_delay -from [get_cells $if_inst/rgmii_tx_clk_1_reg] -to [get_cells $if_inst/clk_oddr_inst/oddr[0].oddr_inst] -datapath_only [expr [get_property -min PERIOD $src_clk]/4] + set_max_delay -from [get_cells $if_inst/rgmii_tx_clk_2_reg] -to [get_cells $if_inst/clk_oddr_inst/oddr[0].oddr_inst] -datapath_only [expr [get_property -min PERIOD $src_clk]/4] +} diff --git a/fpga/lib/eth/tb/arp_ep.py b/fpga/lib/eth/tb/arp_ep.py new file mode 100644 index 000000000..977cf1411 --- /dev/null +++ b/fpga/lib/eth/tb/arp_ep.py @@ -0,0 +1,337 @@ +""" + +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. + +""" + +from myhdl import * +import axis_ep +import eth_ep +import struct + +class ARPFrame(object): + def __init__(self, + eth_dest_mac=0, + eth_src_mac=0, + eth_type=0, + arp_htype=1, + arp_ptype=0x0800, + arp_hlen=6, + arp_plen=4, + arp_oper=2, + arp_sha=0x5A5152535455, + arp_spa=0xc0a80164, + arp_tha=0xDAD1D2D3D4D5, + arp_tpa=0xc0a80164 + ): + + self.eth_dest_mac = eth_dest_mac + self.eth_src_mac = eth_src_mac + self.eth_type = eth_type + self.arp_htype = arp_htype + self.arp_ptype = arp_ptype + self.arp_hlen = arp_hlen + self.arp_plen = arp_plen + self.arp_oper = arp_oper + self.arp_sha = arp_sha + self.arp_spa = arp_spa + self.arp_tha = arp_tha + self.arp_tpa = arp_tpa + + if type(eth_dest_mac) is dict: + self.eth_dest_mac = eth_dest_mac['eth_dest_mac'] + self.eth_src_mac = eth_dest_mac['eth_src_mac'] + self.eth_type = eth_dest_mac['eth_type'] + self.arp_htype = eth_dest_mac['arp_htype'] + self.arp_ptype = eth_dest_mac['arp_ptype'] + self.arp_hlen = eth_dest_mac['arp_hlen'] + self.arp_plen = eth_dest_mac['arp_plen'] + self.arp_oper = eth_dest_mac['arp_oper'] + self.arp_sha = eth_dest_mac['arp_sha'] + self.arp_spa = eth_dest_mac['arp_spa'] + self.arp_tha = eth_dest_mac['arp_tha'] + self.arp_tpa = eth_dest_mac['arp_tpa'] + if type(eth_dest_mac) is ARPFrame: + self.eth_dest_mac = eth_dest_mac.eth_dest_mac + self.eth_src_mac = eth_dest_mac.eth_src_mac + self.eth_type = eth_dest_mac.eth_type + self.arp_htype = eth_dest_mac.arp_htype + self.arp_ptype = eth_dest_mac.arp_ptype + self.arp_hlen = eth_dest_mac.arp_hlen + self.arp_plen = eth_dest_mac.arp_plen + self.arp_oper = eth_dest_mac.arp_oper + self.arp_sha = eth_dest_mac.arp_sha + self.arp_spa = eth_dest_mac.arp_spa + self.arp_tha = eth_dest_mac.arp_tha + self.arp_tpa = eth_dest_mac.arp_tpa + + def build_axis(self): + return self.build_eth().build_axis() + + def build_eth(self): + data = b'' + + data += struct.pack('>H', self.arp_htype) + data += struct.pack('>H', self.arp_ptype) + data += struct.pack('B', self.arp_hlen) + data += struct.pack('B', self.arp_plen) + data += struct.pack('>H', self.arp_oper) + data += struct.pack('>Q', self.arp_sha)[2:] + data += struct.pack('>L', self.arp_spa) + data += struct.pack('>Q', self.arp_tha)[2:] + data += struct.pack('>L', self.arp_tpa) + + return eth_ep.EthFrame(data, self.eth_dest_mac, self.eth_src_mac, self.eth_type) + + def parse_axis(self, data): + frame = eth_ep.EthFrame() + frame.parse_axis(data) + self.parse_eth(frame) + + def parse_eth(self, data): + self.eth_src_mac = data.eth_src_mac + self.eth_dest_mac = data.eth_dest_mac + self.eth_type = data.eth_type + + self.arp_htype = struct.unpack('>H', data.payload.data[0:2])[0] + self.arp_ptype = struct.unpack('>H', data.payload.data[2:4])[0] + self.arp_hlen = struct.unpack('B', data.payload.data[4:5])[0] + self.arp_plen = struct.unpack('B', data.payload.data[5:6])[0] + self.arp_oper = struct.unpack('>H', data.payload.data[6:8])[0] + self.arp_sha = struct.unpack('>Q', b'\x00\x00'+data.payload.data[8:14])[0] + self.arp_spa = struct.unpack('>L', data.payload.data[14:18])[0] + self.arp_tha = struct.unpack('>Q', b'\x00\x00'+data.payload.data[18:24])[0] + self.arp_tpa = struct.unpack('>L', data.payload.data[24:28])[0] + + def __eq__(self, other): + if type(other) is ARPFrame: + return ( + self.eth_src_mac == other.eth_src_mac and + self.eth_dest_mac == other.eth_dest_mac and + self.eth_type == other.eth_type and + self.arp_htype == other.arp_htype and + self.arp_ptype == other.arp_ptype and + self.arp_hlen == other.arp_hlen and + self.arp_plen == other.arp_plen and + self.arp_oper == other.arp_oper and + self.arp_sha == other.arp_sha and + self.arp_spa == other.arp_spa and + self.arp_tha == other.arp_tha and + self.arp_tpa == other.arp_tpa + ) + return False + + def __repr__(self): + return ( + ('ArpFrame(eth_dest_mac=0x%012x, ' % self.eth_dest_mac) + + ('eth_src_mac=0x%012x, ' % self.eth_src_mac) + + ('eth_type=0x%04x, ' % self.eth_type) + + ('arp_htype=0x%04x, ' % self.arp_htype) + + ('arp_ptype=0x%04x, ' % self.arp_ptype) + + ('arp_hlen=%d, ' % self.arp_hlen) + + ('arp_plen=%d, ' % self.arp_plen) + + ('arp_oper=0x%04x, ' % self.arp_oper) + + ('arp_sha=0x%012x, ' % self.arp_sha) + + ('arp_spa=0x%08x, ' % self.arp_spa) + + ('arp_tha=0x%012x, ' % self.arp_tha) + + ('arp_tpa=0x%08x)' % self.arp_tpa) + ) + + +class ARPFrameSource(): + def __init__(self): + self.active = False + self.has_logic = False + self.queue = [] + self.clk = Signal(bool(0)) + + def send(self, frame): + self.queue.append(ARPFrame(frame)) + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def idle(self): + return not self.queue and not self.active + + def wait(self): + while not self.idle(): + yield self.clk.posedge + + def create_logic(self, + clk, + rst, + frame_valid=None, + frame_ready=None, + eth_dest_mac=Signal(intbv(0)[48:]), + eth_src_mac=Signal(intbv(0)[48:]), + eth_type=Signal(intbv(0)[16:]), + arp_htype=Signal(intbv(0)[16:]), + arp_ptype=Signal(intbv(0)[16:]), + arp_hlen=Signal(intbv(6)[8:]), + arp_plen=Signal(intbv(4)[8:]), + arp_oper=Signal(intbv(0)[16:]), + arp_sha=Signal(intbv(0)[48:]), + arp_spa=Signal(intbv(0)[32:]), + arp_tha=Signal(intbv(0)[48:]), + arp_tpa=Signal(intbv(0)[32:]), + pause=0, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + self.clk = clk + + @instance + def logic(): + frame = dict() + + while True: + yield clk.posedge, rst.posedge + + if rst: + frame_valid.next = False + self.active = False + else: + frame_valid.next = self.active and (frame_valid or not pause) + if frame_ready and frame_valid: + frame_valid.next = False + self.active = False + if not self.active and self.queue: + frame = self.queue.pop(0) + eth_dest_mac.next = frame.eth_dest_mac + eth_src_mac.next = frame.eth_src_mac + eth_type.next = frame.eth_type + arp_htype.next = frame.arp_htype + arp_ptype.next = frame.arp_ptype + arp_hlen.next = frame.arp_hlen + arp_plen.next = frame.arp_plen + arp_oper.next = frame.arp_oper + arp_sha.next = frame.arp_sha + arp_spa.next = frame.arp_spa + arp_tha.next = frame.arp_tha + arp_tpa.next = frame.arp_tpa + + if name is not None: + print("[%s] Sending frame %s" % (name, repr(frame))) + + frame_valid.next = not pause + self.active = True + + return instances() + + +class ARPFrameSink(): + def __init__(self): + self.has_logic = False + self.queue = [] + self.sync = Signal(intbv(0)) + + def recv(self): + if self.queue: + return self.queue.pop(0) + return None + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def wait(self, timeout=0): + if self.queue: + return + if timeout: + yield self.sync, delay(timeout) + else: + yield self.sync + + def create_logic(self, + clk, + rst, + frame_valid=None, + frame_ready=None, + eth_dest_mac=Signal(intbv(0)[48:]), + eth_src_mac=Signal(intbv(0)[48:]), + eth_type=Signal(intbv(0)[16:]), + arp_htype=Signal(intbv(0)[16:]), + arp_ptype=Signal(intbv(0)[16:]), + arp_hlen=Signal(intbv(6)[8:]), + arp_plen=Signal(intbv(4)[8:]), + arp_oper=Signal(intbv(0)[16:]), + arp_sha=Signal(intbv(0)[48:]), + arp_spa=Signal(intbv(0)[32:]), + arp_tha=Signal(intbv(0)[48:]), + arp_tpa=Signal(intbv(0)[32:]), + pause=0, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + frame_ready_int = Signal(bool(False)) + frame_valid_int = Signal(bool(False)) + + @always_comb + def pause_logic(): + frame_ready.next = frame_ready_int and not pause + frame_valid_int.next = frame_valid and not pause + + @instance + def logic(): + while True: + yield clk.posedge, rst.posedge + + if rst: + frame_ready_int.next = False + else: + frame_ready_int.next = True + + if frame_ready_int and frame_valid_int: + frame = ARPFrame() + frame.eth_dest_mac = int(eth_dest_mac) + frame.eth_src_mac = int(eth_src_mac) + frame.eth_type = int(eth_type) + frame.arp_htype = int(arp_htype) + frame.arp_ptype = int(arp_ptype) + frame.arp_hlen = int(arp_hlen) + frame.arp_plen = int(arp_plen) + frame.arp_oper = int(arp_oper) + frame.arp_sha = int(arp_sha) + frame.arp_spa = int(arp_spa) + frame.arp_tha = int(arp_tha) + frame.arp_tpa = int(arp_tpa) + self.queue.append(frame) + self.sync.next = not self.sync + + if name is not None: + print("[%s] Got frame %s" % (name, repr(frame))) + + return instances() + diff --git a/fpga/lib/eth/tb/axis_ep.py b/fpga/lib/eth/tb/axis_ep.py new file mode 120000 index 000000000..f4ce98383 --- /dev/null +++ b/fpga/lib/eth/tb/axis_ep.py @@ -0,0 +1 @@ +../lib/axis/tb/axis_ep.py \ No newline at end of file diff --git a/fpga/lib/eth/tb/baser_serdes_ep.py b/fpga/lib/eth/tb/baser_serdes_ep.py new file mode 100644 index 000000000..0c085bb05 --- /dev/null +++ b/fpga/lib/eth/tb/baser_serdes_ep.py @@ -0,0 +1,677 @@ +""" + +Copyright (c) 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. + +""" + +from myhdl import * + +import xgmii_ep + +ETH_PRE = 0x55 +ETH_SFD = 0xD5 + +XGMII_IDLE = 0x07 +XGMII_LPI = 0x06 +XGMII_START = 0xfb +XGMII_TERM = 0xfd +XGMII_ERROR = 0xfe +XGMII_SEQ_OS = 0x9c +XGMII_RES_0 = 0x1c +XGMII_RES_1 = 0x3c +XGMII_RES_2 = 0x7c +XGMII_RES_3 = 0xbc +XGMII_RES_4 = 0xdc +XGMII_RES_5 = 0xf7 +XGMII_SIG_OS = 0x5c + +CTRL_IDLE = 0x00 +CTRL_LPI = 0x06 +CTRL_ERROR = 0x1e +CTRL_RES_0 = 0x2d +CTRL_RES_1 = 0x33 +CTRL_RES_2 = 0x4b +CTRL_RES_3 = 0x55 +CTRL_RES_4 = 0x66 +CTRL_RES_5 = 0x78 + +O_SEQ_OS = 0x0 +O_SIG_OS = 0xf + +SYNC_DATA = 0b10 +SYNC_CTRL = 0b01 + +BLOCK_TYPE_CTRL = 0x1e # C7 C6 C5 C4 C3 C2 C1 C0 BT +BLOCK_TYPE_OS_4 = 0x2d # D7 D6 D5 O4 C3 C2 C1 C0 BT +BLOCK_TYPE_START_4 = 0x33 # D7 D6 D5 C3 C2 C1 C0 BT +BLOCK_TYPE_OS_START = 0x66 # D7 D6 D5 O0 D3 D2 D1 BT +BLOCK_TYPE_OS_04 = 0x55 # D7 D6 D5 O4 O0 D3 D2 D1 BT +BLOCK_TYPE_START_0 = 0x78 # D7 D6 D5 D4 D3 D2 D1 BT +BLOCK_TYPE_OS_0 = 0x4b # C7 C6 C5 C4 O0 D3 D2 D1 BT +BLOCK_TYPE_TERM_0 = 0x87 # C7 C6 C5 C4 C3 C2 C1 BT +BLOCK_TYPE_TERM_1 = 0x99 # C7 C6 C5 C4 C3 C2 D0 BT +BLOCK_TYPE_TERM_2 = 0xaa # C7 C6 C5 C4 C3 D1 D0 BT +BLOCK_TYPE_TERM_3 = 0xb4 # C7 C6 C5 C4 D2 D1 D0 BT +BLOCK_TYPE_TERM_4 = 0xcc # C7 C6 C5 D3 D2 D1 D0 BT +BLOCK_TYPE_TERM_5 = 0xd2 # C7 C6 D4 D3 D2 D1 D0 BT +BLOCK_TYPE_TERM_6 = 0xe1 # C7 D5 D4 D3 D2 D1 D0 BT +BLOCK_TYPE_TERM_7 = 0xff # D6 D5 D4 D3 D2 D1 D0 BT + +def block_type_term_lane(bt): + if bt == BLOCK_TYPE_TERM_0: + return 0 + elif bt == BLOCK_TYPE_TERM_1: + return 1 + elif bt == BLOCK_TYPE_TERM_2: + return 2 + elif bt == BLOCK_TYPE_TERM_3: + return 3 + elif bt == BLOCK_TYPE_TERM_4: + return 4 + elif bt == BLOCK_TYPE_TERM_5: + return 5 + elif bt == BLOCK_TYPE_TERM_6: + return 6 + elif bt == BLOCK_TYPE_TERM_7: + return 7 + else: + return None + +class BaseRSerdesSource(object): + def __init__(self, ifg=12, enable_dic=True): + self.has_logic = False + self.queue = [] + self.ifg = ifg + self.enable_dic = enable_dic + self.force_offset_start = False + + def send(self, frame): + self.queue.append(xgmii_ep.XGMIIFrame(frame)) + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def create_logic(self, + clk, + tx_data, + tx_header, + enable=True, + scramble=True, + reverse=False, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + assert len(tx_data) in [64] + assert len(tx_data) == len(tx_header)*32 + + bw = int(len(tx_data)/8) + + @instance + def logic(): + frame = None + ccl = [] + ifg_cnt = 0 + deficit_idle_cnt = 0 + scrambler_state = 0 + + while True: + yield clk.posedge + + if enable: + data = 0x000000000000001e + header = 0b01 + + if ifg_cnt > bw-1 or (not self.enable_dic and ifg_cnt > 0): + ifg_cnt = max(ifg_cnt - bw, 0) + elif ccl: + header, data = ccl.pop(0) + if not ccl: + l = block_type_term_lane(data & 0xff) + if l is not None: + ifg_cnt = self.ifg - (bw-l) + deficit_idle_cnt + else: + ifg_cnt = self.ifg + deficit_idle_cnt + elif self.queue: + frame = self.queue.pop(0) + dl, cl = frame.build() + if name is not None: + print("[%s] Sending frame %s" % (name, repr(frame))) + + assert len(dl) > 0 + assert dl[0] == ETH_PRE + dl[0] = XGMII_START + cl[0] = 1 + dl.append(XGMII_TERM) + cl.append(1) + + if (bw == 8 and ifg_cnt >= 4) or self.force_offset_start: + ifg_cnt = max(ifg_cnt-4, 0) + dl = [XGMII_IDLE]*4+dl + cl = [1]*4+cl + + deficit_idle_cnt = max(ifg_cnt, 0) + ifg_cnt = 0 + + # pad length to multiple of 8 by adding idles + if len(dl)%8: + for k in range(8-(len(dl)%8)): + dl.append(XGMII_IDLE) + cl.append(1) + + # 10GBASE-R encoding + for k in range(0, len(dl), 8): + di = dl[k:k+8] + ci = cl[k:k+8] + + # remap control characters + ctrl = 0 + ctrl_decode_error = [0]*8 + for i in range(8): + if not ci[i]: + # data + ctrl |= CTRL_ERROR << i*7 + elif di[i] == XGMII_IDLE: + # idle + ctrl |= CTRL_IDLE << i*7 + elif di[i] == XGMII_LPI: + # LPI + ctrl |= CTRL_LPI << i*7 + elif di[i] == XGMII_ERROR: + # error + ctrl |= CTRL_ERROR << i*7 + elif di[i] == XGMII_RES_0: + # reserved0 + ctrl |= CTRL_RES_0 << i*7 + elif di[i] == XGMII_RES_1: + # reserved1 + ctrl |= CTRL_RES_1 << i*7 + elif di[i] == XGMII_RES_2: + # reserved2 + ctrl |= CTRL_RES_2 << i*7 + elif di[i] == XGMII_RES_3: + # reserved3 + ctrl |= CTRL_RES_3 << i*7 + elif di[i] == XGMII_RES_4: + # reserved4 + ctrl |= CTRL_RES_4 << i*7 + elif di[i] == XGMII_RES_5: + # reserved5 + ctrl |= CTRL_RES_5 << i*7 + else: + # invalid + ctrl |= CTRL_ERROR << i*7 + ctrl_decode_error[i] = ci[i] + + if not any(ci): + # data + h = SYNC_DATA + d = 0 + for i in range(8): + d |= di[i] << i*8 + else: + # control + h = SYNC_CTRL + if ci[0] and di[0] == XGMII_START and not any(ci[1:]): + # start in lane 0 + d = BLOCK_TYPE_START_0 + for i in range(1,8): + d |= di[i] << i*8 + elif ci[4] and di[4] == XGMII_START and not any(ci[5:]): + # start in lane 4 + if ci[0] and (di[0] == XGMII_SEQ_OS or di[0] == XGMII_SIG_OS) and not any(ci[1:4]): + # ordered set in lane 0 + d = BLOCK_TYPE_OS_START + for i in range(1,4): + d |= di[i] << i*8 + if di[0] == XGMII_SIG_OS: + # signal ordered set + d |= O_SIG_OS << 32 + else: + # other control + d = BLOCK_TYPE_START_4 | (ctrl & 0xfffffff) << 8 + + for i in range(5,8): + d |= di[i] << i*8 + elif ci[0] and (di[0] == XGMII_SEQ_OS or di[0] == XGMII_SIG_OS) and not any(ci[1:4]): + # ordered set in lane 0 + if ci[4] and (di[4] == XGMII_SEQ_OS or di[4] == XGMII_SIG_OS) and not any(ci[5:8]): + # ordered set in lane 4 + d = BLOCK_TYPE_OS_04 + for i in range(5,8): + d |= di[i] << i*8 + if di[4] == XGMII_SIG_OS: + # signal ordered set + d |= O_SIG_OS << 36 + else: + d = BLOCK_TYPE_OS_0 | (ctrl & 0xfffffff) << 40 + for i in range(1,4): + d |= di[i] << i*8 + if di[0] == XGMII_SIG_OS: + # signal ordered set + d |= O_SIG_OS << 32 + elif ci[4] and (di[4] == XGMII_SEQ_OS or di[4] == XGMII_SIG_OS) and not any(ci[5:8]): + # ordered set in lane 4 + d = BLOCK_TYPE_OS_4 | (ctrl & 0xfffffff) << 8 + for i in range(5,8): + d |= di[i] << i*8 + if di[4] == XGMII_SIG_OS: + # signal ordered set + d |= O_SIG_OS << 36 + elif ci[0] and di[0] == XGMII_TERM: + # terminate in lane 0 + d = BLOCK_TYPE_TERM_0 | (ctrl & 0xffffffffffff80) << 8 + elif ci[1] and di[1] == XGMII_TERM and not ci[0]: + # terminate in lane 1 + d = BLOCK_TYPE_TERM_1 | (ctrl & 0xffffffffffc000) << 8 | di[0] << 8 + elif ci[2] and di[2] == XGMII_TERM and not any(ci[0:2]): + # terminate in lane 2 + d = BLOCK_TYPE_TERM_2 | (ctrl & 0xffffffffe00000) << 8 + for i in range(2): + d |= di[i] << ((i+1)*8) + elif ci[3] and di[3] == XGMII_TERM and not any(ci[0:3]): + # terminate in lane 3 + d = BLOCK_TYPE_TERM_3 | (ctrl & 0xfffffff0000000) << 8 + for i in range(3): + d |= di[i] << ((i+1)*8) + elif ci[4] and di[4] == XGMII_TERM and not any(ci[0:4]): + # terminate in lane 4 + d = BLOCK_TYPE_TERM_4 | (ctrl & 0xfffff800000000) << 8 + for i in range(4): + d |= di[i] << ((i+1)*8) + elif ci[5] and di[5] == XGMII_TERM and not any(ci[0:5]): + # terminate in lane 5 + d = BLOCK_TYPE_TERM_5 | (ctrl & 0xfffc0000000000) << 8 + for i in range(5): + d |= di[i] << ((i+1)*8) + elif ci[6] and di[6] == XGMII_TERM and not any(ci[0:6]): + # terminate in lane 6 + d = BLOCK_TYPE_TERM_6 | (ctrl & 0xfe000000000000) << 8 + for i in range(6): + d |= di[i] << ((i+1)*8) + elif ci[7] and di[7] == XGMII_TERM and not any(ci[0:7]): + # terminate in lane 7 + d = BLOCK_TYPE_TERM_7 + for i in range(7): + d |= di[i] << ((i+1)*8) + else: + # all control + d = BLOCK_TYPE_CTRL | ctrl << 8 + + ccl.append((h, d)) + + header, data = ccl.pop(0) + if not ccl: + l = block_type_term_lane(data & 0xff) + if l is not None: + ifg_cnt = self.ifg - (bw-l) + deficit_idle_cnt + else: + ifg_cnt = self.ifg + deficit_idle_cnt + else: + ifg_cnt = 0 + deficit_idle_cnt = 0 + + if scramble: + # 64b66b scrambler + b = 0 + for i in range(len(tx_data)): + if bool(scrambler_state & (1<<38)) ^ bool(scrambler_state & (1<<57)) ^ bool(data & (1 << i)): + scrambler_state = ((scrambler_state & 0x1ffffffffffffff) << 1) | 1 + b = b | (1 << i) + else: + scrambler_state = (scrambler_state & 0x1ffffffffffffff) << 1 + data = b + + if reverse: + # bit reverse + data = sum(1 << (63-i) for i in range(64) if (data >> i) & 1) + header = sum(1 << (1-i) for i in range(2) if (header >> i) & 1) + + tx_data.next = data + tx_header.next = header + + return instances() + + +class BaseRSerdesSink(object): + def __init__(self): + self.has_logic = False + self.queue = [] + self.sync = Signal(intbv(0)) + + def recv(self): + if self.queue: + return self.queue.pop(0) + return None + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def wait(self, timeout=0): + if self.queue: + return + if timeout: + yield self.sync, delay(timeout) + else: + yield self.sync + + def create_logic(self, + clk, + rx_data, + rx_header, + enable=True, + scramble=True, + reverse=False, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + assert len(rx_data) in [64] + assert len(rx_data) == len(rx_header)*32 + + bw = int(len(rx_data)/8) + + @instance + def logic(): + frame = None + d = [] + c = [] + scrambler_state = 0 + + while True: + yield clk.posedge + + if enable: + data = int(rx_data) + header = int(rx_header) + + if reverse: + # bit reverse + data = sum(1 << (63-i) for i in range(64) if (data >> i) & 1) + header = sum(1 << (1-i) for i in range(2) if (header >> i) & 1) + + if scramble: + # 64b66b descrambler + b = 0 + for i in range(len(rx_data)): + if bool(scrambler_state & (1<<38)) ^ bool(scrambler_state & (1<<57)) ^ bool(data & (1 << i)): + b = b | (1 << i) + scrambler_state = (scrambler_state & 0x1ffffffffffffff) << 1 | bool(data & (1 << i)) + data = b + + # 10GBASE-R decoding + + # remap control characters + ctrl = [0]*8 + for i in range(8): + if (data >> i*7+8) & 0x7f == CTRL_IDLE: + # idle + ctrl[i] = XGMII_IDLE; + elif (data >> i*7+8) & 0x7f == CTRL_LPI: + # LPI + ctrl[i] = XGMII_LPI + elif (data >> i*7+8) & 0x7f == CTRL_ERROR: + # error + ctrl[i] = XGMII_ERROR + elif (data >> i*7+8) & 0x7f == CTRL_RES_0: + # reserved0 + ctrl[i] = XGMII_RES_0 + elif (data >> i*7+8) & 0x7f == CTRL_RES_1: + # reserved1 + ctrl[i] = XGMII_RES_1 + elif (data >> i*7+8) & 0x7f == CTRL_RES_2: + # reserved2 + ctrl[i] = XGMII_RES_2 + elif (data >> i*7+8) & 0x7f == CTRL_RES_3: + # reserved3 + ctrl[i] = XGMII_RES_3 + elif (data >> i*7+8) & 0x7f == CTRL_RES_4: + # reserved4 + ctrl[i] = XGMII_RES_4 + elif (data >> i*7+8) & 0x7f == CTRL_RES_5: + # reserved5 + ctrl[i] = XGMII_RES_5 + else: + # invalid + ctrl[i] = XGMII_ERROR + + dl = [] + cl = [] + if header == SYNC_DATA: + # data + for k in range(8): + dl.append((data >> k*8) & 0xff) + cl.append(0) + elif header == SYNC_CTRL: + if data & 0xff == BLOCK_TYPE_CTRL: + # C7 C6 C5 C4 C3 C2 C1 C0 BT + dl = ctrl + cl = [1]*8 + elif data & 0xff == BLOCK_TYPE_OS_4: + # D7 D6 D5 O4 C3 C2 C1 C0 BT + dl = ctrl[0:4] + cl = [1]*4 + if (data >> 36) & 0xf == O_SEQ_OS: + dl.append(XGMII_SEQ_OS) + elif (data >> 36) & 0xf == O_SIG_OS: + dl.append(XGMII_SIG_OS) + else: + dl.append(XGMII_ERROR) + cl.append(1) + for k in range(4,7): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + elif data & 0xff == BLOCK_TYPE_START_4: + # D7 D6 D5 C3 C2 C1 C0 BT + dl = ctrl[0:4] + cl = [1]*4 + dl.append(XGMII_START) + cl.append(1) + for k in range(4,7): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + elif data & 0xff == BLOCK_TYPE_OS_START: + # D7 D6 D5 O0 D3 D2 D1 BT + if (data >> 32) & 0xf == O_SEQ_OS: + dl.append(XGMII_SEQ_OS) + elif (data >> 32) & 0xf == O_SIG_OS: + dl.append(XGMII_SIG_OS) + else: + dl.append(XGMII_ERROR) + cl.append(1) + for k in range(0,3): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + dl.append(XGMII_START) + cl.append(1) + for k in range(4,7): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + elif data & 0xff == BLOCK_TYPE_OS_04: + # D7 D6 D5 O4 O0 D3 D2 D1 BT + if (data >> 32) & 0xf == O_SEQ_OS: + dl.append(XGMII_SEQ_OS) + elif (data >> 32) & 0xf == O_SIG_OS: + dl.append(XGMII_SIG_OS) + else: + dl.append(XGMII_ERROR) + cl.append(1) + for k in range(0,3): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + if (data >> 36) & 0xf == O_SEQ_OS: + dl.append(XGMII_SEQ_OS) + elif (data >> 36) & 0xf == O_SIG_OS: + dl.append(XGMII_SIG_OS) + else: + dl.append(XGMII_ERROR) + cl.append(1) + for k in range(4,7): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + elif data & 0xff == BLOCK_TYPE_START_0: + # D7 D6 D5 D4 D3 D2 D1 BT + dl.append(XGMII_START) + cl.append(1) + for k in range(7): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + elif data & 0xff == BLOCK_TYPE_OS_0: + # C7 C6 C5 C4 O0 D3 D2 D1 BT + if (data >> 32) & 0xf == O_SEQ_OS: + dl.append(XGMII_SEQ_OS) + elif (data >> 32) & 0xf == O_SIG_OS: + dl.append(XGMII_SEQ_OS) + else: + dl.append(XGMII_ERROR) + cl.append(1) + for k in range(0,3): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + dl.extend(ctrl[4:8]) + cl.extend([1]*4) + elif data & 0xff == BLOCK_TYPE_TERM_0: + # C7 C6 C5 C4 C3 C2 C1 BT + dl.append(XGMII_TERM) + cl.append(1) + dl.extend(ctrl[1:]) + cl.extend([1]*7) + elif data & 0xff == BLOCK_TYPE_TERM_1: + # C7 C6 C5 C4 C3 C2 D0 BT + dl.append((data >> 8) & 0xff) + cl.append(0) + dl.append(XGMII_TERM) + cl.append(1) + dl.extend(ctrl[2:]) + cl.extend([1]*6) + elif data & 0xff == BLOCK_TYPE_TERM_2: + # C7 C6 C5 C4 C3 D1 D0 BT + for k in range(2): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + dl.append(XGMII_TERM) + cl.append(1) + dl.extend(ctrl[3:]) + cl.extend([1]*5) + elif data & 0xff == BLOCK_TYPE_TERM_3: + # C7 C6 C5 C4 D2 D1 D0 BT + for k in range(3): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + dl.append(XGMII_TERM) + cl.append(1) + dl.extend(ctrl[4:]) + cl.extend([1]*4) + elif data & 0xff == BLOCK_TYPE_TERM_4: + # C7 C6 C5 D3 D2 D1 D0 BT + for k in range(4): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + dl.append(XGMII_TERM) + cl.append(1) + dl.extend(ctrl[5:]) + cl.extend([1]*3) + elif data & 0xff == BLOCK_TYPE_TERM_5: + # C7 C6 D4 D3 D2 D1 D0 BT + for k in range(5): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + dl.append(XGMII_TERM) + cl.append(1) + dl.extend(ctrl[6:]) + cl.extend([1]*2) + elif data & 0xff == BLOCK_TYPE_TERM_6: + # C7 D5 D4 D3 D2 D1 D0 BT + for k in range(6): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + dl.append(XGMII_TERM) + cl.append(1) + dl.extend(ctrl[7:]) + cl.extend([1]*1) + elif data & 0xff == BLOCK_TYPE_TERM_7: + # D6 D5 D4 D3 D2 D1 D0 BT + for k in range(7): + dl.append((data >> (k+1)*8) & 0xff) + cl.append(0) + dl.append(XGMII_TERM) + cl.append(1) + else: + # invalid block type + dl = [XGMII_ERROR]*8 + cl = [1]*8 + else: + # invalid sync header + dl = [XGMII_ERROR]*8 + cl = [1]*8 + + if frame is None: + if cl[0] and dl[0] == XGMII_START: + # start in lane 0 + frame = xgmii_ep.XGMIIFrame() + d = [ETH_PRE] + c = [0] + for i in range(1,bw): + d.append(dl[i]) + c.append(cl[i]) + elif bw == 8 and cl[4] and dl[4] == XGMII_START: + # start in lane 4 + frame = xgmii_ep.XGMIIFrame() + d = [ETH_PRE] + c = [0] + for i in range(5,bw): + d.append(dl[i]) + c.append(cl[i]) + else: + for i in range(bw): + if cl[i]: + # got a control character; terminate frame reception + if dl[i] != XGMII_TERM: + # store control character if it's not a termination + d.append(dl[i]) + c.append(cl[i]) + frame.parse(d, c) + self.queue.append(frame) + self.sync.next = not self.sync + if name is not None: + print("[%s] Got frame %s" % (name, repr(frame))) + frame = None + d = [] + c = [] + break + else: + d.append(dl[i]) + c.append(cl[i]) + + return instances() + diff --git a/fpga/lib/eth/tb/eth_ep.py b/fpga/lib/eth/tb/eth_ep.py new file mode 100644 index 000000000..f7dd3f1a3 --- /dev/null +++ b/fpga/lib/eth/tb/eth_ep.py @@ -0,0 +1,322 @@ +""" + +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. + +""" + +from myhdl import * +import axis_ep +import struct +import zlib + +class EthFrame(object): + def __init__(self, payload=b'', eth_dest_mac=0, eth_src_mac=0, eth_type=0, eth_fcs=None): + self._payload = axis_ep.AXIStreamFrame() + self.eth_dest_mac = eth_dest_mac + self.eth_src_mac = eth_src_mac + self.eth_type = eth_type + self.eth_fcs = eth_fcs + + if type(payload) is dict: + self.payload = axis_ep.AXIStreamFrame(payload['eth_payload']) + self.eth_dest_mac = payload['eth_dest_mac'] + self.eth_src_mac = payload['eth_src_mac'] + self.eth_type = payload['eth_type'] + self.eth_fcs = payload['eth_fcs'] + if type(payload) is bytes: + payload = bytearray(payload) + if type(payload) is bytearray or type(payload) is axis_ep.AXIStreamFrame: + self.payload = axis_ep.AXIStreamFrame(payload) + if type(payload) is EthFrame: + self.payload = axis_ep.AXIStreamFrame(payload.payload) + self.eth_dest_mac = payload.eth_dest_mac + self.eth_src_mac = payload.eth_src_mac + self.eth_type = payload.eth_type + self.eth_fcs = payload.eth_fcs + + @property + def payload(self): + return self._payload + + @payload.setter + def payload(self, value): + self._payload = axis_ep.AXIStreamFrame(value) + + def calc_fcs(self): + frame = self.build_axis().data + + return zlib.crc32(bytes(frame)) & 0xffffffff + + def update_fcs(self): + self.eth_fcs = self.calc_fcs() + + def build_axis(self): + data = b'' + + data += struct.pack('>Q', self.eth_dest_mac)[2:] + data += struct.pack('>Q', self.eth_src_mac)[2:] + data += struct.pack('>H', self.eth_type) + + data += self.payload.data + + return axis_ep.AXIStreamFrame(data) + + def build_axis_fcs(self): + if self.eth_fcs is None: + self.update_fcs() + + data = self.build_axis().data + + data += struct.pack('Q', b'\x00\x00'+data[0:6])[0] + self.eth_src_mac = struct.unpack('>Q', b'\x00\x00'+data[6:12])[0] + self.eth_type = struct.unpack('>H', data[12:14])[0] + data = data[14:] + self.payload = axis_ep.AXIStreamFrame(data) + + def parse_axis_fcs(self, data): + self.parse_axis(data) + data = self.payload.data + self.payload = axis_ep.AXIStreamFrame(data[:-4]) + self.eth_fcs = struct.unpack(' 0: + d.append(f.pop(0)) + if self.error is None: + er.append(0) + else: + er.append(self.error[i]) + i += 1 + + if assert_er: + er[-1] = 1 + self.error = 1 + + return d, er + + def parse(self, d, er): + if d is None or er is None: + return + + self.data = bytearray(d) + self.error = er + + def __eq__(self, other): + if type(other) is GMIIFrame: + return self.data == other.data + return False + + def __repr__(self): + return 'GMIIFrame(data=%s, error=%s)' % (repr(self.data), repr(self.error)) + + def __iter__(self): + return self.data.__iter__() + + +class GMIISource(object): + def __init__(self): + self.has_logic = False + self.queue = [] + + def send(self, frame): + self.queue.append(GMIIFrame(frame)) + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def create_logic(self, + clk, + rst, + txd, + tx_en, + tx_er, + clk_enable=True, + mii_select=False, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + assert len(txd) == 8 + + @instance + def logic(): + frame = None + d = [] + er = [] + ifg_cnt = 0 + + while True: + yield clk.posedge, rst.posedge + + if rst: + frame = None + txd.next = 0 + tx_en.next = 0 + tx_er.next = 0 + d = [] + er = [] + ifg_cnt = 0 + else: + if not clk_enable: + pass + elif ifg_cnt > 0: + ifg_cnt -= 1 + txd.next = 0 + tx_er.next = 0 + tx_en.next = 0 + elif len(d) > 0: + txd.next = d.pop(0) + tx_er.next = er.pop(0) + tx_en.next = 1 + if len(d) == 0: + if mii_select: + ifg_cnt = 12*2 + else: + ifg_cnt = 12 + elif self.queue: + frame = GMIIFrame(self.queue.pop(0)) + d, er = frame.build() + if name is not None: + print("[%s] Sending frame %s" % (name, repr(frame))) + if mii_select: + d2 = [] + for b in d: + d2.append(b & 0x0F) + d2.append(b >> 4) + d = d2 + er2 = [] + for b in er: + er2.append(b) + er2.append(b) + er = er2 + txd.next = d.pop(0) + tx_er.next = er.pop(0) + tx_en.next = 1 + else: + txd.next = 0 + tx_er.next = 0 + tx_en.next = 0 + + return instances() + + +class GMIISink(object): + def __init__(self): + self.has_logic = False + self.queue = [] + self.sync = Signal(intbv(0)) + + def recv(self): + if self.queue: + return self.queue.pop(0) + return None + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def wait(self, timeout=0): + if self.queue: + return + if timeout: + yield self.sync, delay(timeout) + else: + yield self.sync + + def create_logic(self, + clk, + rst, + rxd, + rx_dv, + rx_er, + clk_enable=True, + mii_select=False, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + assert len(rxd) == 8 + + @instance + def logic(): + frame = None + d = [] + er = [] + + while True: + yield clk.posedge, rst.posedge + + if rst: + frame = None + d = [] + er = [] + else: + if not clk_enable: + pass + elif rx_dv: + if frame is None: + frame = GMIIFrame() + d = [] + er = [] + d.append(int(rxd)) + er.append(int(rx_er)) + elif frame is not None: + if len(d) > 0: + if mii_select: + odd = True + sync = False + b = 0 + be = 0 + d2 = [] + er2 = [] + for n, e in zip(d, er): + odd = not odd + b = (n & 0x0F) << 4 | b >> 4 + be |= e + if not sync and b == 0xD5: + odd = True + sync = True + if odd: + d2.append(b) + er2.append(be) + be = False + d = d2 + er = er2 + frame.parse(d, er) + self.queue.append(frame) + self.sync.next = not self.sync + if name is not None: + print("[%s] Got frame %s" % (name, repr(frame))) + frame = None + d = [] + er = [] + + return instances() + diff --git a/fpga/lib/eth/tb/ip_ep.py b/fpga/lib/eth/tb/ip_ep.py new file mode 100644 index 000000000..4afcaea26 --- /dev/null +++ b/fpga/lib/eth/tb/ip_ep.py @@ -0,0 +1,494 @@ +""" + +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. + +""" + +from myhdl import * +import axis_ep +import eth_ep +import struct + +class IPFrame(object): + def __init__(self, + payload=b'', + eth_dest_mac=0, + eth_src_mac=0, + eth_type=0, + ip_version=4, + ip_ihl=5, + ip_dscp=0, + ip_ecn=0, + ip_length=None, + ip_identification=0, + ip_flags=2, + ip_fragment_offset=0, + ip_ttl=64, + ip_protocol=0x11, + ip_header_checksum=None, + ip_source_ip=0xc0a80164, + ip_dest_ip=0xc0a80165 + ): + + self._payload = axis_ep.AXIStreamFrame() + self.eth_dest_mac = eth_dest_mac + self.eth_src_mac = eth_src_mac + self.eth_type = eth_type + self.ip_version = ip_version + self.ip_ihl = ip_ihl + self.ip_dscp = ip_dscp + self.ip_ecn = ip_ecn + self.ip_length = ip_length + self.ip_identification = ip_identification + self.ip_flags = ip_flags + self.ip_fragment_offset = ip_fragment_offset + self.ip_ttl = ip_ttl + self.ip_protocol = ip_protocol + self.ip_header_checksum = ip_header_checksum + self.ip_source_ip = ip_source_ip + self.ip_dest_ip = ip_dest_ip + + if type(payload) is dict: + self.payload = axis_ep.AXIStreamFrame(payload['ip_payload']) + self.eth_dest_mac = payload['eth_dest_mac'] + self.eth_src_mac = payload['eth_src_mac'] + self.eth_type = payload['eth_type'] + self.ip_version = payload['ip_version'] + self.ip_ihl = payload['ip_ihl'] + self.ip_dscp = payload['ip_dscp'] + self.ip_ecn = payload['ip_ecn'] + self.ip_length = payload['ip_length'] + self.ip_identification = payload['ip_identification'] + self.ip_flags = payload['ip_flags'] + self.ip_fragment_offset = payload['ip_fragment_offset'] + self.ip_ttl = payload['ip_ttl'] + self.ip_protocol = payload['ip_protocol'] + self.ip_header_checksum = payload['ip_header_checksum'] + self.ip_source_ip = payload['ip_source_ip'] + self.ip_dest_ip = payload['ip_dest_ip'] + if type(payload) is bytes: + payload = bytearray(payload) + if type(payload) is bytearray or type(payload) is axis_ep.AXIStreamFrame: + self.payload = axis_ep.AXIStreamFrame(payload) + if type(payload) is IPFrame: + self.payload = axis_ep.AXIStreamFrame(payload.payload) + self.eth_dest_mac = payload.eth_dest_mac + self.eth_src_mac = payload.eth_src_mac + self.eth_type = payload.eth_type + self.ip_version = payload.ip_version + self.ip_ihl = payload.ip_ihl + self.ip_dscp = payload.ip_dscp + self.ip_ecn = payload.ip_ecn + self.ip_length = payload.ip_length + self.ip_identification = payload.ip_identification + self.ip_flags = payload.ip_flags + self.ip_fragment_offset = payload.ip_fragment_offset + self.ip_ttl = payload.ip_ttl + self.ip_protocol = payload.ip_protocol + self.ip_header_checksum = payload.ip_header_checksum + self.ip_source_ip = payload.ip_source_ip + self.ip_dest_ip = payload.ip_dest_ip + + @property + def payload(self): + return self._payload + + @payload.setter + def payload(self, value): + self._payload = axis_ep.AXIStreamFrame(value) + + def update_length(self): + self.ip_length = len(self.payload.data) + 20 + + def calc_checksum(self): + cksum = self.ip_version << 12 | self.ip_ihl << 8 | self.ip_dscp << 2 | self.ip_ecn + cksum += self.ip_length + cksum += self.ip_identification + cksum += self.ip_flags << 13 | self.ip_fragment_offset + cksum += self.ip_ttl << 8 | self.ip_protocol + cksum += self.ip_source_ip & 0xffff + cksum += (self.ip_source_ip >> 16) & 0xffff + cksum += self.ip_dest_ip & 0xffff + cksum += (self.ip_dest_ip >> 16) & 0xffff + cksum = (cksum & 0xffff) + (cksum >> 16) + cksum = (cksum & 0xffff) + (cksum >> 16) + return ~cksum & 0xffff + + def update_checksum(self): + self.ip_header_checksum = self.calc_checksum() + + def build(self): + if self.ip_length is None: + self.update_length() + if self.ip_header_checksum is None: + self.update_checksum() + + def build_axis(self): + return self.build_eth().build_axis() + + def build_eth(self): + self.build() + data = b'' + + data += struct.pack('B', self.ip_version << 4 | self.ip_ihl) + data += struct.pack('B', self.ip_dscp << 2 | self.ip_ecn) + data += struct.pack('>H', self.ip_length) + data += struct.pack('>H', self.ip_identification) + data += struct.pack('>H', self.ip_flags << 13 | self.ip_fragment_offset) + data += struct.pack('B', self.ip_ttl) + data += struct.pack('B', self.ip_protocol) + data += struct.pack('>H', self.ip_header_checksum) + data += struct.pack('>L', self.ip_source_ip) + data += struct.pack('>L', self.ip_dest_ip) + + data += self.payload.data + + return eth_ep.EthFrame(data, self.eth_dest_mac, self.eth_src_mac, self.eth_type) + + def parse_axis(self, data): + frame = eth_ep.EthFrame() + frame.parse_axis(data) + self.parse_eth(frame) + + def parse_eth(self, data): + self.eth_src_mac = data.eth_src_mac + self.eth_dest_mac = data.eth_dest_mac + self.eth_type = data.eth_type + + v = struct.unpack('B', data.payload.data[0:1])[0] + self.ip_version = (v >> 4) & 0xF + self.ip_ihl = v & 0xF + v = struct.unpack('B', data.payload.data[1:2])[0] + self.ip_dscp = (v >> 2) & 0x3F + self.ip_ecn = v & 0x3 + self.ip_length = struct.unpack('>H', data.payload.data[2:4])[0] + self.ip_identification = struct.unpack('>H', data.payload.data[4:6])[0] + v = struct.unpack('>H', data.payload.data[6:8])[0] + self.ip_flags = (v >> 13) & 0x7 + self.ip_fragment_offset = v & 0x1FFF + self.ip_ttl = struct.unpack('B', data.payload.data[8:9])[0] + self.ip_protocol = struct.unpack('B', data.payload.data[9:10])[0] + self.ip_header_checksum = struct.unpack('>H', data.payload.data[10:12])[0] + self.ip_source_ip = struct.unpack('>L', data.payload.data[12:16])[0] + self.ip_dest_ip = struct.unpack('>L', data.payload.data[16:20])[0] + + self.payload = axis_ep.AXIStreamFrame(data.payload.data[20:self.ip_length]) + + def __eq__(self, other): + if type(other) is IPFrame: + return ( + self.eth_src_mac == other.eth_src_mac and + self.eth_dest_mac == other.eth_dest_mac and + self.eth_type == other.eth_type and + self.ip_version == other.ip_version and + self.ip_ihl == other.ip_ihl and + self.ip_dscp == other.ip_dscp and + self.ip_ecn == other.ip_ecn and + self.ip_length == other.ip_length and + self.ip_identification == other.ip_identification and + self.ip_flags == other.ip_flags and + self.ip_fragment_offset == other.ip_fragment_offset and + self.ip_ttl == other.ip_ttl and + self.ip_protocol == other.ip_protocol and + self.ip_header_checksum == other.ip_header_checksum and + self.ip_source_ip == other.ip_source_ip and + self.ip_dest_ip == other.ip_dest_ip and + self.payload == other.payload + ) + return False + + def __repr__(self): + return ( + ('IPFrame(payload=%s, ' % repr(self.payload)) + + ('eth_dest_mac=0x%012x, ' % self.eth_dest_mac) + + ('eth_src_mac=0x%012x, ' % self.eth_src_mac) + + ('eth_type=0x%04x, ' % self.eth_type) + + ('ip_version=%d, ' % self.ip_version) + + ('ip_ihl=%d, ' % self.ip_ihl) + + ('ip_dscp=%d, ' % self.ip_dscp) + + ('ip_ecn=%d, ' % self.ip_ecn) + + ('ip_length=%d, ' % self.ip_length) + + ('ip_identification=%d, ' % self.ip_identification) + + ('ip_flags=%d, ' % self.ip_flags) + + ('ip_fragment_offset=%d, ' % self.ip_fragment_offset) + + ('ip_ttl=%d, ' % self.ip_ttl) + + ('ip_protocol=0x%02x, ' % self.ip_protocol) + + ('ip_header_checksum=0x%x, ' % self.ip_header_checksum) + + ('ip_source_ip=0x%08x, ' % self.ip_source_ip) + + ('ip_dest_ip=0x%08x)' % self.ip_dest_ip) + ) + + +class IPFrameSource(): + def __init__(self): + self.active = False + self.has_logic = False + self.queue = [] + self.payload_source = axis_ep.AXIStreamSource() + self.header_queue = [] + self.clk = Signal(bool(0)) + + def send(self, frame): + frame = IPFrame(frame) + if not self.header_queue: + self.header_queue.append(frame) + self.payload_source.send(frame.payload) + else: + self.queue.append(frame) + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def idle(self): + return not self.queue and not self.active and self.payload_source.idle() + + def wait(self): + while not self.idle(): + yield self.clk.posedge + + def create_logic(self, + clk, + rst, + ip_hdr_valid=None, + ip_hdr_ready=None, + eth_dest_mac=Signal(intbv(0)[48:]), + eth_src_mac=Signal(intbv(0)[48:]), + eth_type=Signal(intbv(0)[16:]), + ip_version=Signal(intbv(4)[4:]), + ip_ihl=Signal(intbv(5)[4:]), + ip_dscp=Signal(intbv(0)[6:]), + ip_ecn=Signal(intbv(0)[2:]), + ip_length=Signal(intbv(0)[16:]), + ip_identification=Signal(intbv(0)[16:]), + ip_flags=Signal(intbv(0)[3:]), + ip_fragment_offset=Signal(intbv(0)[13:]), + ip_ttl=Signal(intbv(0)[8:]), + ip_protocol=Signal(intbv(0)[8:]), + ip_header_checksum=Signal(intbv(0)[16:]), + ip_source_ip=Signal(intbv(0)[32:]), + ip_dest_ip=Signal(intbv(0)[32:]), + ip_payload_tdata=None, + ip_payload_tkeep=Signal(bool(True)), + ip_payload_tvalid=Signal(bool(False)), + ip_payload_tready=Signal(bool(True)), + ip_payload_tlast=Signal(bool(False)), + ip_payload_tuser=Signal(bool(False)), + pause=0, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + self.clk = clk + + ip_payload_source = self.payload_source.create_logic( + clk=clk, + rst=rst, + tdata=ip_payload_tdata, + tkeep=ip_payload_tkeep, + tvalid=ip_payload_tvalid, + tready=ip_payload_tready, + tlast=ip_payload_tlast, + tuser=ip_payload_tuser, + pause=pause, + ) + + @instance + def logic(): + while True: + yield clk.posedge, rst.posedge + + if rst: + ip_hdr_valid.next = False + self.active = False + else: + ip_hdr_valid.next = self.active and (ip_hdr_valid or not pause) + if ip_hdr_ready and ip_hdr_valid: + ip_hdr_valid.next = False + self.active = False + if not self.active and self.header_queue: + frame = self.header_queue.pop(0) + frame.build() + eth_dest_mac.next = frame.eth_dest_mac + eth_src_mac.next = frame.eth_src_mac + eth_type.next = frame.eth_type + ip_version.next = frame.ip_version + ip_ihl.next = frame.ip_ihl + ip_dscp.next = frame.ip_dscp + ip_ecn.next = frame.ip_ecn + ip_length.next = frame.ip_length + ip_identification.next = frame.ip_identification + ip_flags.next = frame.ip_flags + ip_fragment_offset.next = frame.ip_fragment_offset + ip_ttl.next = frame.ip_ttl + ip_protocol.next = frame.ip_protocol + ip_header_checksum.next = frame.ip_header_checksum + ip_source_ip.next = frame.ip_source_ip + ip_dest_ip.next = frame.ip_dest_ip + + if name is not None: + print("[%s] Sending frame %s" % (name, repr(frame))) + + ip_hdr_valid.next = not pause + self.active = True + + if self.queue and not self.header_queue: + frame = self.queue.pop(0) + self.header_queue.append(frame) + self.payload_source.send(frame.payload) + + return instances() + + +class IPFrameSink(): + def __init__(self): + self.has_logic = False + self.queue = [] + self.payload_sink = axis_ep.AXIStreamSink() + self.header_queue = [] + self.sync = Signal(intbv(0)) + + def recv(self): + if self.queue: + return self.queue.pop(0) + return None + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def wait(self, timeout=0): + if self.queue: + return + if timeout: + yield self.sync, delay(timeout) + else: + yield self.sync + + def create_logic(self, + clk, + rst, + ip_hdr_valid=None, + ip_hdr_ready=None, + eth_dest_mac=Signal(intbv(0)[48:]), + eth_src_mac=Signal(intbv(0)[48:]), + eth_type=Signal(intbv(0)[16:]), + ip_version=Signal(intbv(4)[4:]), + ip_ihl=Signal(intbv(5)[4:]), + ip_dscp=Signal(intbv(0)[6:]), + ip_ecn=Signal(intbv(0)[2:]), + ip_length=Signal(intbv(0)[16:]), + ip_identification=Signal(intbv(0)[16:]), + ip_flags=Signal(intbv(0)[3:]), + ip_fragment_offset=Signal(intbv(0)[13:]), + ip_ttl=Signal(intbv(0)[8:]), + ip_protocol=Signal(intbv(0)[8:]), + ip_header_checksum=Signal(intbv(0)[16:]), + ip_source_ip=Signal(intbv(0)[32:]), + ip_dest_ip=Signal(intbv(0)[32:]), + ip_payload_tdata=None, + ip_payload_tkeep=Signal(bool(True)), + ip_payload_tvalid=Signal(bool(True)), + ip_payload_tready=Signal(bool(True)), + ip_payload_tlast=Signal(bool(True)), + ip_payload_tuser=Signal(bool(False)), + pause=0, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + ip_hdr_ready_int = Signal(bool(False)) + ip_hdr_valid_int = Signal(bool(False)) + ip_payload_pause = Signal(bool(False)) + + ip_payload_sink = self.payload_sink.create_logic( + clk=clk, + rst=rst, + tdata=ip_payload_tdata, + tkeep=ip_payload_tkeep, + tvalid=ip_payload_tvalid, + tready=ip_payload_tready, + tlast=ip_payload_tlast, + tuser=ip_payload_tuser, + pause=ip_payload_pause + ) + + @always_comb + def pause_logic(): + ip_hdr_ready.next = ip_hdr_ready_int and not pause + ip_hdr_valid_int.next = ip_hdr_valid and not pause + ip_payload_pause.next = pause # or ip_hdr_valid_int + + @instance + def logic(): + while True: + yield clk.posedge, rst.posedge + + if rst: + ip_hdr_ready_int.next = False + else: + ip_hdr_ready_int.next = True + + if ip_hdr_ready_int and ip_hdr_valid_int: + frame = IPFrame() + frame.eth_dest_mac = int(eth_dest_mac) + frame.eth_src_mac = int(eth_src_mac) + frame.eth_type = int(eth_type) + frame.ip_version = int(ip_version) + frame.ip_ihl = int(ip_ihl) + frame.ip_dscp = int(ip_dscp) + frame.ip_ecn = int(ip_ecn) + frame.ip_length = int(ip_length) + frame.ip_identification = int(ip_identification) + frame.ip_flags = int(ip_flags) + frame.ip_fragment_offset = int(ip_fragment_offset) + frame.ip_ttl = int(ip_ttl) + frame.ip_protocol = int(ip_protocol) + frame.ip_header_checksum = int(ip_header_checksum) + frame.ip_source_ip = int(ip_source_ip) + frame.ip_dest_ip = int(ip_dest_ip) + self.header_queue.append(frame) + + if not self.payload_sink.empty() and self.header_queue: + frame = self.header_queue.pop(0) + frame.payload = self.payload_sink.recv() + self.queue.append(frame) + self.sync.next = not self.sync + + if name is not None: + print("[%s] Got frame %s" % (name, repr(frame))) + + # ensure all payloads have been matched to headers + if len(self.header_queue) == 0: + assert self.payload_sink.empty() + + return instances() + diff --git a/fpga/lib/eth/tb/mii_ep.py b/fpga/lib/eth/tb/mii_ep.py new file mode 100644 index 000000000..64c4a09b9 --- /dev/null +++ b/fpga/lib/eth/tb/mii_ep.py @@ -0,0 +1,262 @@ +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * + +class MIIFrame(object): + def __init__(self, data=b'', error=None): + self.data = b'' + self.error = None + + if type(data) is MIIFrame: + self.data = data.data + self.error = data.error + else: + self.data = bytearray(data) + + def build(self): + if self.data is None: + return + + f = list(self.data) + d = [] + er = [] + i = 0 + + assert_er = False + if (type(self.error) is int or type(self.error) is bool) and self.error: + assert_er = True + self.error = None + + while len(f) > 0: + db = f.pop(0) + d.append(db & 0x0f) + d.append(db >> 4) + if self.error is None: + er.append(0) + er.append(0) + else: + er.append(self.error[i]) + er.append(self.error[i]) + i += 1 + + if assert_er: + er[-1] = 1 + self.error = 1 + + return d, er + + def parse(self, d, er): + if d is None or er is None: + return + + # sync and repack data + odd = True + sync = False + b = 0 + be = 0 + d2 = [] + er2 = [] + for n, e in zip(d, er): + odd = not odd + b = (n & 0x0F) << 4 | b >> 4 + be |= e + if not sync and b == 0xD5: + odd = True + sync = True + if odd: + d2.append(b) + er2.append(be) + be = False + + self.data = bytearray(d2) + self.error = er2 + + def __eq__(self, other): + if type(other) is MIIFrame: + return self.data == other.data + return False + + def __repr__(self): + return 'MIIFrame(data=%s, error=%s)' % (repr(self.data), repr(self.error)) + + def __iter__(self): + return self.data.__iter__() + + +class MIISource(object): + def __init__(self): + self.has_logic = False + self.queue = [] + + def send(self, frame): + self.queue.append(MIIFrame(frame)) + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def create_logic(self, + clk, + rst, + txd, + tx_en, + tx_er, + clk_enable=True, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + assert len(txd) == 4 + + @instance + def logic(): + frame = None + d = [] + er = [] + ifg_cnt = 0 + + while True: + yield clk.posedge, rst.posedge + + if rst: + frame = None + txd.next = 0 + tx_en.next = 0 + tx_er.next = 0 + d = [] + er = [] + ifg_cnt = 0 + else: + if not clk_enable: + pass + elif ifg_cnt > 0: + ifg_cnt -= 1 + txd.next = 0 + tx_er.next = 0 + tx_en.next = 0 + elif len(d) > 0: + txd.next = d.pop(0) + tx_er.next = er.pop(0) + tx_en.next = 1 + if len(d) == 0: + ifg_cnt = 12*2 + elif self.queue: + frame = MIIFrame(self.queue.pop(0)) + d, er = frame.build() + if name is not None: + print("[%s] Sending frame %s" % (name, repr(frame))) + txd.next = d.pop(0) + tx_er.next = er.pop(0) + tx_en.next = 1 + else: + txd.next = 0 + tx_er.next = 0 + tx_en.next = 0 + + return logic + + +class MIISink(object): + def __init__(self): + self.has_logic = False + self.queue = [] + self.sync = Signal(intbv(0)) + + def recv(self): + if self.queue: + return self.queue.pop(0) + return None + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def wait(self, timeout=0): + if self.queue: + return + if timeout: + yield self.sync, delay(timeout) + else: + yield self.sync + + def create_logic(self, + clk, + rst, + rxd, + rx_dv, + rx_er, + clk_enable=True, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + assert len(rxd) == 4 + + @instance + def logic(): + frame = None + d = [] + er = [] + + while True: + yield clk.posedge, rst.posedge + + if rst: + frame = None + d = [] + er = [] + else: + if not clk_enable: + pass + elif rx_dv: + if frame is None: + frame = MIIFrame() + d = [] + er = [] + d.append(int(rxd)) + er.append(int(rx_er)) + elif frame is not None: + if len(d) > 0: + frame.parse(d, er) + self.queue.append(frame) + self.sync.next = not self.sync + if name is not None: + print("[%s] Got frame %s" % (name, repr(frame))) + frame = None + d = [] + er = [] + + return logic + diff --git a/fpga/lib/eth/tb/ptp.py b/fpga/lib/eth/tb/ptp.py new file mode 100644 index 000000000..5379e789e --- /dev/null +++ b/fpga/lib/eth/tb/ptp.py @@ -0,0 +1,127 @@ +""" + +Copyright (c) 2015-2019 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. + +""" + +from myhdl import * + +class PtpClock(object): + def __init__(self): + self.period_ns = 0x6 + self.period_fns = 0x6666 + self.drift_ns = 0x0 + self.drift_fns = 0x0002 + self.drift_rate = 5 + + self.set_96_l = [] + self.set_64_l = [] + + def set_96(self, ts): + self.set_96_l.append(ts) + + def set_64(self, ts): + self.set_64_l.append(ts) + + def create_logic(self, + clk, + rst, + ts_96=Signal(intbv(0)[96:]), + ts_64=Signal(intbv(0)[64:]), + ts_step=Signal(bool(0)) + ): + + @instance + def logic(): + + ts_96_s = 0 + ts_96_ns = 0 + ts_96_fns = 0 + + ts_64_ns = 0 + ts_64_fns = 0 + + drift_cnt = 0 + + while True: + yield clk.posedge, rst.posedge + + if rst: + ts_96_s = 0 + ts_96_ns = 0 + ts_96_fns = 0 + ts_64_ns = 0 + ts_64_fns = 0 + drift_cnt = 0 + ts_96.next = 0 + ts_64.next = 0 + else: + ts_step.next = 0 + + t = ((ts_96_ns << 16) + ts_96_fns) + ((self.period_ns << 16) + self.period_fns) + + if drift_cnt > 0: + t += (self.drift_ns << 16) + self.drift_fns + ts_step.next = 1 + + if t > (1000000000<<16): + ts_96_s += 1 + t -= (1000000000<<16) + + ts_96_fns = t & 0xffff + ts_96_ns = t >> 16 + + if self.set_96_l: + ts = self.set_96_l.pop(0) + + ts_96_s = ts >> 48 + ts_96_ns = (ts >> 16) & 0x3fffffff + ts_96_fns = ts & 0xffff + + ts_step.next = 1 + + ts_96.next = (ts_96_s << 48) | (ts_96_ns << 16) | (ts_96_fns) + + t = ((ts_64_ns << 16) + ts_64_fns) + ((self.period_ns << 16) + self.period_fns) + + if drift_cnt > 0: + t += ((self.drift_ns << 16) + self.drift_fns) + ts_step.next = 1 + + ts_64_fns = t & 0xffff + ts_64_ns = t >> 16 + + if self.set_64_l: + ts = self.set_64_l.pop(0) + + ts_64_ns = ts >> 16 + ts_64_fns = ts & 0xffff + + ts_step.next = 1 + + ts_64.next = (ts_64_ns << 16) | ts_64_fns + + if drift_cnt > 0: + drift_cnt -= 1 + else: + drift_cnt = self.drift_rate-1 + + return instances() diff --git a/fpga/lib/eth/tb/rgmii_ep.py b/fpga/lib/eth/tb/rgmii_ep.py new file mode 100644 index 000000000..705b098c0 --- /dev/null +++ b/fpga/lib/eth/tb/rgmii_ep.py @@ -0,0 +1,106 @@ +""" + +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. + +""" + +from myhdl import * + +import gmii_ep + +class RGMIISource(gmii_ep.GMIISource): + def create_logic(self, + clk, + rst, + txd, + tx_ctl, + clk_enable=True, + mii_select=False, + name=None + ): + + assert not self.has_logic + + gmii_txd = Signal(intbv(0)[8:]) + gmii_tx_en = Signal(bool(0)) + gmii_tx_er = Signal(bool(0)) + + gmii_txd_reg = Signal(intbv(0)[8:]) + gmii_tx_en_reg = Signal(bool(0)) + gmii_tx_er_reg = Signal(bool(0)) + + gmii_source = super(RGMIISource, self).create_logic(clk, rst, gmii_txd, gmii_tx_en, gmii_tx_er, clk_enable, mii_select, name) + + @instance + def logic(): + while True: + yield clk.negedge + txd.next = gmii_txd_reg[4:0] + tx_ctl.next = gmii_tx_en_reg + yield clk.posedge + if not mii_select: + txd.next = gmii_txd_reg[8:4] + tx_ctl.next = gmii_tx_en_reg ^ gmii_tx_er_reg + gmii_txd_reg.next = gmii_txd + gmii_tx_en_reg.next = gmii_tx_en + gmii_tx_er_reg.next = gmii_tx_er + + return instances() + + +class RGMIISink(gmii_ep.GMIISink): + def create_logic(self, + clk, + rst, + rxd, + rx_ctl, + clk_enable=True, + mii_select=False, + name=None + ): + + assert not self.has_logic + + gmii_rxd = Signal(intbv(0)[8:]) + gmii_rx_dv = Signal(bool(0)) + gmii_rx_er = Signal(bool(0)) + + gmii_sink = super(RGMIISink, self).create_logic(clk, rst, gmii_rxd, gmii_rx_dv, gmii_rx_er, clk_enable, mii_select, name) + + @instance + def logic(): + dat = 0 + ctl1 = 0 + ctl2 = 0 + + while True: + yield clk.posedge + gmii_rxd.next = dat + gmii_rx_dv.next = ctl1 + gmii_rx_er.next = ctl1 ^ ctl2 + dat = int(rxd.val) + ctl1 = int(rx_ctl.val) + yield clk.negedge + dat |= int(rxd.val) << 4 + ctl2 = int(rx_ctl.val) + + return instances() + diff --git a/fpga/lib/eth/tb/test_arp.py b/fpga/lib/eth/tb/test_arp.py new file mode 100755 index 000000000..ff1cde6ac --- /dev/null +++ b/fpga/lib/eth/tb/test_arp.py @@ -0,0 +1,451 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import arp_ep + +module = 'arp' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/arp_cache.v") +srcs.append("../rtl/arp_eth_rx.v") +srcs.append("../rtl/arp_eth_tx.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + + arp_request_valid = Signal(bool(0)) + arp_request_ip = Signal(intbv(0)[32:]) + arp_response_ready = Signal(bool(0)) + + local_mac = Signal(intbv(0)[48:]) + local_ip = Signal(intbv(0)[32:]) + gateway_ip = Signal(intbv(0)[32:]) + subnet_mask = Signal(intbv(0)[32:]) + clear_cache = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + + arp_request_ready = Signal(bool(0)) + arp_response_valid = Signal(bool(0)) + arp_response_error = Signal(bool(0)) + arp_response_mac = Signal(intbv(0)[48:]) + + # sources and sinks + eth_source_pause = Signal(bool(0)) + eth_sink_pause = Signal(bool(0)) + + eth_source = eth_ep.EthFrameSource() + + eth_source_logic = eth_source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=eth_source_pause, + name='eth_source' + ) + + eth_sink = eth_ep.EthFrameSink() + + eth_sink_logic = eth_sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=eth_sink_pause, + name='eth_sink' + ) + + arp_request_source = axis_ep.AXIStreamSource() + + arp_request_source_logic = arp_request_source.create_logic( + clk, + rst, + tdata=(arp_request_ip,), + tvalid=arp_request_valid, + tready=arp_request_ready, + name='arp_request_source' + ) + + arp_response_sink = axis_ep.AXIStreamSink() + + arp_response_sink_logic = arp_response_sink.create_logic( + clk, + rst, + tdata=(arp_response_error, arp_response_mac), + tvalid=arp_response_valid, + tready=arp_response_ready, + name='arp_response_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + arp_request_valid=arp_request_valid, + arp_request_ready=arp_request_ready, + arp_request_ip=arp_request_ip, + arp_response_valid=arp_response_valid, + arp_response_ready=arp_response_ready, + arp_response_error=arp_response_error, + arp_response_mac=arp_response_mac, + + local_mac=local_mac, + local_ip=local_ip, + gateway_ip=gateway_ip, + subnet_mask=subnet_mask, + clear_cache=clear_cache + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + local_mac.next = 0xDAD1D2D3D4D5 + local_ip.next = 0xc0a80165 + gateway_ip.next = 0xc0a80101 + subnet_mask.next = 0xFFFFFF00 + + yield clk.posedge + print("test 1: ARP request") + current_test.next = 1 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0x000000000000 + test_frame.arp_tpa = 0xc0a80165 + eth_source.send(test_frame.build_eth()) + + yield eth_sink.wait() + rx_frame = eth_sink.recv() + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0x5A5152535455 + assert check_frame.eth_src_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 2 + assert check_frame.arp_sha == 0xDAD1D2D3D4D5 + assert check_frame.arp_spa == 0xc0a80165 + assert check_frame.arp_tha == 0x5A5152535455 + assert check_frame.arp_tpa == 0xc0a80164 + + yield delay(100) + + yield clk.posedge + print("test 2: Cached read") + current_test.next = 2 + + arp_request_source.send([(0xc0a80164,)]) + + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert not err + assert mac == 0x5A5152535455 + + yield delay(100) + + yield clk.posedge + print("test 3: Unached read") + current_test.next = 3 + + arp_request_source.send([(0xc0a80166,)]) + + # wait for ARP request packet + yield eth_sink.wait() + rx_frame = eth_sink.recv() + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0xDAD1D2D3D4D5 + assert check_frame.arp_spa == 0xc0a80165 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80166 + + # generate response + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x6A6162636465 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 2 + test_frame.arp_sha = 0x6A6162636465 + test_frame.arp_spa = 0xc0a80166 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + eth_source.send(test_frame.build_eth()) + + # wait for lookup + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert not err + assert mac == 0x6A6162636465 + + yield delay(100) + + yield clk.posedge + print("test 4: Unached read, outside of subnet") + current_test.next = 4 + + arp_request_source.send([(0x08080808,)]) + + # wait for ARP request packet + yield eth_sink.wait() + rx_frame = eth_sink.recv() + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0xDAD1D2D3D4D5 + assert check_frame.arp_spa == 0xc0a80165 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80101 + + # generate response + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0xAABBCCDDEEFF + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 2 + test_frame.arp_sha = 0xAABBCCDDEEFF + test_frame.arp_spa = 0xc0a80101 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + eth_source.send(test_frame.build_eth()) + + # wait for lookup + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert not err + assert mac == 0xAABBCCDDEEFF + + yield delay(100) + + yield clk.posedge + print("test 5: Unached read, timeout") + current_test.next = 5 + + arp_request_source.send([(0xc0a80167,)]) + + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert err + + # check for 4 ARP requests + assert eth_sink.count() == 4 + + while not eth_sink.empty(): + rx_frame = eth_sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0xDAD1D2D3D4D5 + assert check_frame.arp_spa == 0xc0a80165 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80167 + + yield delay(100) + + yield clk.posedge + print("test 6: Broadcast") + current_test.next = 6 + + # subnet broadcast + arp_request_source.send([(0xc0a801ff,)]) + + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert not err + assert mac == 0xffffffffffff + + # general broadcast + arp_request_source.send([(0xffffffff,)]) + + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert not err + assert mac == 0xffffffffffff + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_arp.v b/fpga/lib/eth/tb/test_arp.v new file mode 100644 index 000000000..463fb53ae --- /dev/null +++ b/fpga/lib/eth/tb/test_arp.v @@ -0,0 +1,173 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for arp + */ +module test_arp; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [7:0] s_eth_payload_axis_tdata = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; + +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +reg arp_request_valid = 0; +reg [31:0] arp_request_ip = 0; +reg arp_response_ready = 0; + +reg [47:0] local_mac = 0; +reg [31:0] local_ip = 0; +reg [31:0] gateway_ip = 0; +reg [31:0] subnet_mask = 0; +reg clear_cache = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; + +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [7:0] m_eth_payload_axis_tdata; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; + +wire arp_request_ready; +wire arp_response_valid; +wire arp_response_error; +wire [47:0] arp_response_mac; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + arp_request_valid, + arp_request_ip, + arp_response_ready, + local_mac, + local_ip, + gateway_ip, + subnet_mask, + clear_cache + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + arp_request_ready, + arp_response_valid, + arp_response_error, + arp_response_mac + ); + + // dump file + $dumpfile("test_arp.lxt"); + $dumpvars(0, test_arp); +end + +arp #( + .CACHE_ADDR_WIDTH(2), + .REQUEST_RETRY_COUNT(4), + .REQUEST_RETRY_INTERVAL(150), + .REQUEST_TIMEOUT(400) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_cache(clear_cache) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_arp_64.py b/fpga/lib/eth/tb/test_arp_64.py new file mode 100755 index 000000000..4a43a63b0 --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_64.py @@ -0,0 +1,457 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import arp_ep + +module = 'arp_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/arp_cache.v") +srcs.append("../rtl/arp_eth_rx_64.v") +srcs.append("../rtl/arp_eth_tx_64.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + s_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + + arp_request_valid = Signal(bool(0)) + arp_request_ip = Signal(intbv(0)[32:]) + arp_response_ready = Signal(bool(0)) + + local_mac = Signal(intbv(0)[48:]) + local_ip = Signal(intbv(0)[32:]) + gateway_ip = Signal(intbv(0)[32:]) + subnet_mask = Signal(intbv(0)[32:]) + clear_cache = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + m_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + + arp_request_ready = Signal(bool(0)) + arp_response_valid = Signal(bool(0)) + arp_response_error = Signal(bool(0)) + arp_response_mac = Signal(intbv(0)[48:]) + + # sources and sinks + eth_source_pause = Signal(bool(0)) + eth_sink_pause = Signal(bool(0)) + + eth_source = eth_ep.EthFrameSource() + + eth_source_logic = eth_source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tkeep=s_eth_payload_axis_tkeep, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=eth_source_pause, + name='eth_source' + ) + + eth_sink = eth_ep.EthFrameSink() + + eth_sink_logic = eth_sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=eth_sink_pause, + name='eth_sink' + ) + + arp_request_source = axis_ep.AXIStreamSource() + + arp_request_source_logic = arp_request_source.create_logic( + clk, + rst, + tdata=(arp_request_ip,), + tvalid=arp_request_valid, + tready=arp_request_ready, + name='arp_request_source' + ) + + arp_response_sink = axis_ep.AXIStreamSink() + + arp_response_sink_logic = arp_response_sink.create_logic( + clk, + rst, + tdata=(arp_response_error, arp_response_mac), + tvalid=arp_response_valid, + tready=arp_response_ready, + name='arp_response_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + arp_request_valid=arp_request_valid, + arp_request_ready=arp_request_ready, + arp_request_ip=arp_request_ip, + arp_response_valid=arp_response_valid, + arp_response_ready=arp_response_ready, + arp_response_error=arp_response_error, + arp_response_mac=arp_response_mac, + + local_mac=local_mac, + local_ip=local_ip, + gateway_ip=gateway_ip, + subnet_mask=subnet_mask, + clear_cache=clear_cache + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + local_mac.next = 0xDAD1D2D3D4D5 + local_ip.next = 0xc0a80165 + gateway_ip.next = 0xc0a80101 + subnet_mask.next = 0xFFFFFF00 + + yield clk.posedge + print("test 1: ARP request") + current_test.next = 1 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0x000000000000 + test_frame.arp_tpa = 0xc0a80165 + eth_source.send(test_frame.build_eth()) + + yield eth_sink.wait() + rx_frame = eth_sink.recv() + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0x5A5152535455 + assert check_frame.eth_src_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 2 + assert check_frame.arp_sha == 0xDAD1D2D3D4D5 + assert check_frame.arp_spa == 0xc0a80165 + assert check_frame.arp_tha == 0x5A5152535455 + assert check_frame.arp_tpa == 0xc0a80164 + + yield delay(100) + + yield clk.posedge + print("test 2: Cached read") + current_test.next = 2 + + arp_request_source.send([(0xc0a80164,)]) + + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert not err + assert mac == 0x5A5152535455 + + yield delay(100) + + yield clk.posedge + print("test 3: Unached read") + current_test.next = 3 + + arp_request_source.send([(0xc0a80166,)]) + + # wait for ARP request packet + yield eth_sink.wait() + rx_frame = eth_sink.recv() + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0xDAD1D2D3D4D5 + assert check_frame.arp_spa == 0xc0a80165 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80166 + + # generate response + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x6A6162636465 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 2 + test_frame.arp_sha = 0x6A6162636465 + test_frame.arp_spa = 0xc0a80166 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + eth_source.send(test_frame.build_eth()) + + # wait for lookup + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert not err + assert mac == 0x6A6162636465 + + yield delay(100) + + yield clk.posedge + print("test 4: Unached read, outside of subnet") + current_test.next = 4 + + arp_request_source.send([(0x08080808,)]) + + # wait for ARP request packet + yield eth_sink.wait() + rx_frame = eth_sink.recv() + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0xDAD1D2D3D4D5 + assert check_frame.arp_spa == 0xc0a80165 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80101 + + # generate response + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0xAABBCCDDEEFF + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 2 + test_frame.arp_sha = 0xAABBCCDDEEFF + test_frame.arp_spa = 0xc0a80101 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + eth_source.send(test_frame.build_eth()) + + # wait for lookup + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert not err + assert mac == 0xAABBCCDDEEFF + + yield delay(100) + + yield clk.posedge + print("test 5: Unached read, timeout") + current_test.next = 5 + + arp_request_source.send([(0xc0a80167,)]) + + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert err + + # check for 4 ARP requests + assert eth_sink.count() == 4 + + while not eth_sink.empty(): + rx_frame = eth_sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0xDAD1D2D3D4D5 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0xDAD1D2D3D4D5 + assert check_frame.arp_spa == 0xc0a80165 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80167 + + yield delay(100) + + yield clk.posedge + print("test 6: Broadcast") + current_test.next = 6 + + # subnet broadcast + arp_request_source.send([(0xc0a801ff,)]) + + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert not err + assert mac == 0xffffffffffff + + # general broadcast + arp_request_source.send([(0xffffffff,)]) + + yield arp_response_sink.wait() + err, mac = arp_response_sink.recv().data[0] + + assert not err + assert mac == 0xffffffffffff + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_arp_64.v b/fpga/lib/eth/tb/test_arp_64.v new file mode 100644 index 000000000..1565185f0 --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_64.v @@ -0,0 +1,179 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for arp_64 + */ +module test_arp_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [63:0] s_eth_payload_axis_tdata = 0; +reg [7:0] s_eth_payload_axis_tkeep = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; + +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +reg arp_request_valid = 0; +reg [31:0] arp_request_ip = 0; +reg arp_response_ready = 0; + +reg [47:0] local_mac = 0; +reg [31:0] local_ip = 0; +reg [31:0] gateway_ip = 0; +reg [31:0] subnet_mask = 0; +reg clear_cache = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; + +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [63:0] m_eth_payload_axis_tdata; +wire [7:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; + +wire arp_request_ready; +wire arp_response_valid; +wire arp_response_error; +wire [47:0] arp_response_mac; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + arp_request_valid, + arp_request_ip, + arp_response_ready, + local_mac, + local_ip, + gateway_ip, + subnet_mask, + clear_cache + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + arp_request_ready, + arp_response_valid, + arp_response_error, + arp_response_mac + ); + + // dump file + $dumpfile("test_arp_64.lxt"); + $dumpvars(0, test_arp_64); +end + +arp_64 #( + .CACHE_ADDR_WIDTH(2), + .REQUEST_RETRY_COUNT(4), + .REQUEST_RETRY_INTERVAL(150), + .REQUEST_TIMEOUT(400) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_cache(clear_cache) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_arp_cache.py b/fpga/lib/eth/tb/test_arp_cache.py new file mode 100755 index 000000000..9237b47de --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_cache.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'arp_cache' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + CACHE_ADDR_WIDTH = 2 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + query_request_valid = Signal(bool(0)) + query_request_ip = Signal(intbv(0)[32:]) + + query_response_ready = Signal(bool(0)) + + write_request_valid = Signal(bool(0)) + write_request_ip = Signal(intbv(0)[32:]) + write_request_mac = Signal(intbv(0)[48:]) + + clear_cache = Signal(bool(0)) + + # Outputs + query_request_ready = Signal(bool(0)) + + query_response_valid = Signal(bool(0)) + query_response_error = Signal(bool(0)) + query_response_mac = Signal(intbv(0)[48:]) + + write_request_ready = Signal(bool(0)) + + # sources and sinks + query_request_source_pause = Signal(bool(0)) + query_response_sink_pause = Signal(bool(0)) + write_request_source_pause = Signal(bool(0)) + + query_request_source = axis_ep.AXIStreamSource() + + query_request_source_logic = query_request_source.create_logic( + clk, + rst, + tdata=(query_request_ip,), + tvalid=query_request_valid, + tready=query_request_ready, + pause=query_request_source_pause, + name='query_request_source' + ) + + query_response_sink = axis_ep.AXIStreamSink() + + query_response_sink_logic = query_response_sink.create_logic( + clk, + rst, + tdata=(query_response_mac,), + tvalid=query_response_valid, + tready=query_response_ready, + tuser=query_response_error, + pause=query_response_sink_pause, + name='query_response_sink' + ) + + write_request_source = axis_ep.AXIStreamSource() + + write_request_source_logic = write_request_source.create_logic( + clk, + rst, + tdata=(write_request_ip, write_request_mac), + tvalid=write_request_valid, + tready=write_request_ready, + pause=write_request_source_pause, + name='write_request_source' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + query_request_valid=query_request_valid, + query_request_ready=query_request_ready, + query_request_ip=query_request_ip, + + query_response_valid=query_response_valid, + query_response_ready=query_response_ready, + query_response_error=query_response_error, + query_response_mac=query_response_mac, + + write_request_valid=write_request_valid, + write_request_ready=write_request_ready, + write_request_ip=write_request_ip, + write_request_mac=write_request_mac, + + clear_cache=clear_cache + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + print("test 1: write") + current_test.next = 1 + + yield clk.posedge + write_request_source.send([(0xc0a80111, 0x0000c0a80111)]) + write_request_source.send([(0xc0a80112, 0x0000c0a80112)]) + + yield delay(100) + + while not write_request_source.empty(): + yield clk.posedge + + yield delay(100) + + yield clk.posedge + print("test 2: read") + current_test.next = 2 + + yield clk.posedge + query_request_source.send([(0xc0a80111, )]) + query_request_source.send([(0xc0a80112, )]) + query_request_source.send([(0xc0a80113, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.data[0][0] == 0x0000c0a80111 + assert not resp.user[0] + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.data[0][0] == 0x0000c0a80112 + assert not resp.user[0] + + # not in cache; was not written + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.user[0] + + yield delay(100) + + yield clk.posedge + print("test 3: write more") + current_test.next = 3 + + yield clk.posedge + write_request_source.send([(0xc0a80121, 0x0000c0a80121)]) + write_request_source.send([(0xc0a80122, 0x0000c0a80122)]) + # overwrites 0xc0a80112 + write_request_source.send([(0xc0a80123, 0x0000c0a80123)]) + + while not write_request_source.empty(): + yield clk.posedge + + yield delay(100) + + yield clk.posedge + print("test 4: read more") + current_test.next = 4 + + yield clk.posedge + query_request_source.send([(0xc0a80111, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.data[0][0] == 0x0000c0a80111 + assert not resp.user[0] + + # not in cache; was overwritten + yield clk.posedge + query_request_source.send([(0xc0a80112, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.user[0] + + yield clk.posedge + query_request_source.send([(0xc0a80121, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.data[0][0] == 0x0000c0a80121 + assert not resp.user[0] + + yield clk.posedge + query_request_source.send([(0xc0a80122, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.data[0][0] == 0x0000c0a80122 + assert not resp.user[0] + + yield clk.posedge + query_request_source.send([(0xc0a80123, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.data[0][0] == 0x0000c0a80123 + assert not resp.user[0] + + yield delay(100) + + yield clk.posedge + print("test 5: Test overwrite") + current_test.next = 5 + + yield clk.posedge + write_request_source.send([(0xc0a80123, 0x0000c0a80164)]) + + while not write_request_source.empty(): + yield clk.posedge + + # read values + yield clk.posedge + query_request_source.send([(0xc0a80111, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.data[0][0] == 0x0000c0a80111 + assert not resp.user[0] + + # not in cache; was overwritten + yield clk.posedge + query_request_source.send([(0xc0a80112, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.user[0] + + yield clk.posedge + query_request_source.send([(0xc0a80121, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.data[0][0] == 0x0000c0a80121 + assert not resp.user[0] + + yield clk.posedge + query_request_source.send([(0xc0a80122, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.data[0][0] == 0x0000c0a80122 + assert not resp.user[0] + + yield clk.posedge + query_request_source.send([(0xc0a80123, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.data[0][0] == 0x0000c0a80164 + assert not resp.user[0] + + yield delay(100) + + yield clk.posedge + print("test 6: clear cache") + current_test.next = 6 + + yield clk.posedge + clear_cache.next = True + yield clk.posedge + clear_cache.next = False + + yield delay(100) + + yield clk.posedge + query_request_source.send([(0xc0a80111, )]) + + yield query_response_sink.wait() + resp = query_response_sink.recv() + assert resp.user[0] + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_arp_cache.v b/fpga/lib/eth/tb/test_arp_cache.v new file mode 100755 index 000000000..d91625d97 --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_cache.v @@ -0,0 +1,112 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for arp_cache + */ +module test_arp_cache; + +// Parameters +parameter CACHE_ADDR_WIDTH = 2; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg query_request_valid = 0; +reg [31:0] query_request_ip = 0; + +reg query_response_ready = 0; + +reg write_request_valid = 0; +reg [31:0] write_request_ip = 0; +reg [47:0] write_request_mac = 0; + +reg clear_cache = 0; + +// Outputs +wire query_request_ready; + +wire query_response_valid; +wire query_response_error; +wire [47:0] query_response_mac; + +wire write_request_ready; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + query_request_valid, + query_request_ip, + query_response_ready, + write_request_valid, + write_request_ip, + write_request_mac, + clear_cache + ); + $to_myhdl( + query_request_ready, + query_response_valid, + query_response_error, + query_response_mac, + write_request_ready + ); + + // dump file + $dumpfile("test_arp_cache.lxt"); + $dumpvars(0, test_arp_cache); +end + +arp_cache #( + .CACHE_ADDR_WIDTH(CACHE_ADDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // Query cache + .query_request_valid(query_request_valid), + .query_request_ready(query_request_ready), + .query_request_ip(query_request_ip), + .query_response_valid(query_response_valid), + .query_response_ready(query_response_ready), + .query_response_error(query_response_error), + .query_response_mac(query_response_mac), + // Write cache + .write_request_valid(write_request_valid), + .write_request_ready(write_request_ready), + .write_request_ip(write_request_ip), + .write_request_mac(write_request_mac), + // Configuration + .clear_cache(clear_cache) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_arp_eth_rx.py b/fpga/lib/eth/tb/test_arp_eth_rx.py new file mode 100755 index 000000000..19303fac3 --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_eth_rx.py @@ -0,0 +1,533 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import arp_ep +import eth_ep + +module = 'arp_eth_rx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + m_frame_ready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + m_frame_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_arp_htype = Signal(intbv(0)[16:]) + m_arp_ptype = Signal(intbv(0)[16:]) + m_arp_hlen = Signal(intbv(0)[8:]) + m_arp_plen = Signal(intbv(0)[8:]) + m_arp_oper = Signal(intbv(0)[16:]) + m_arp_sha = Signal(intbv(0)[48:]) + m_arp_spa = Signal(intbv(0)[32:]) + m_arp_tha = Signal(intbv(0)[48:]) + m_arp_tpa = Signal(intbv(0)[32:]) + busy = Signal(bool(0)) + error_header_early_termination = Signal(bool(0)) + error_invalid_header = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = eth_ep.EthFrameSource() + + source_logic = source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = arp_ep.ARPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + frame_ready=m_frame_ready, + frame_valid=m_frame_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + arp_htype=m_arp_htype, + arp_ptype=m_arp_ptype, + arp_hlen=m_arp_hlen, + arp_plen=m_arp_plen, + arp_oper=m_arp_oper, + arp_sha=m_arp_sha, + arp_spa=m_arp_spa, + arp_tha=m_arp_tha, + arp_tpa=m_arp_tpa, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_frame_valid=m_frame_valid, + m_frame_ready=m_frame_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_arp_htype=m_arp_htype, + m_arp_ptype=m_arp_ptype, + m_arp_hlen=m_arp_hlen, + m_arp_plen=m_arp_plen, + m_arp_oper=m_arp_oper, + m_arp_sha=m_arp_sha, + m_arp_spa=m_arp_spa, + m_arp_tha=m_arp_tha, + m_arp_tpa=m_arp_tpa, + + busy=busy, + error_header_early_termination=error_header_early_termination, + error_invalid_header=error_invalid_header + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + source.send(test_frame.build_eth()) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: packet with trailing bytes") + current_test.next = 2 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + eth_frame = test_frame.build_eth() + eth_frame.payload.data += bytearray(range(10)) + source.send(eth_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + source.send(test_frame.build_eth()) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = arp_ep.ARPFrame() + test_frame1.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0806 + test_frame1.arp_htype = 0x0001 + test_frame1.arp_ptype = 0x0800 + test_frame1.arp_hlen = 6 + test_frame1.arp_plen = 4 + test_frame1.arp_oper = 1 + test_frame1.arp_sha = 0x5A5152535455 + test_frame1.arp_spa = 0xc0a80164 + test_frame1.arp_tha = 0xDAD1D2D3D4D5 + test_frame1.arp_tpa = 0xc0a80165 + test_frame2 = arp_ep.ARPFrame() + test_frame2.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0806 + test_frame2.arp_htype = 0x0001 + test_frame2.arp_ptype = 0x0800 + test_frame2.arp_hlen = 6 + test_frame2.arp_plen = 4 + test_frame2.arp_oper = 1 + test_frame2.arp_sha = 0x5A5152535455 + test_frame2.arp_spa = 0xc0a80164 + test_frame2.arp_tha = 0xDAD1D2D3D4D5 + test_frame2.arp_tpa = 0xc0a80165 + source.send(test_frame1.build_eth()) + source.send(test_frame2.build_eth()) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = arp_ep.ARPFrame() + test_frame1.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0806 + test_frame1.arp_htype = 0x0001 + test_frame1.arp_ptype = 0x0800 + test_frame1.arp_hlen = 6 + test_frame1.arp_plen = 4 + test_frame1.arp_oper = 1 + test_frame1.arp_sha = 0x5A5152535455 + test_frame1.arp_spa = 0xc0a80164 + test_frame1.arp_tha = 0xDAD1D2D3D4D5 + test_frame1.arp_tpa = 0xc0a80165 + test_frame2 = arp_ep.ARPFrame() + test_frame2.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0806 + test_frame2.arp_htype = 0x0001 + test_frame2.arp_ptype = 0x0800 + test_frame2.arp_hlen = 6 + test_frame2.arp_plen = 4 + test_frame2.arp_oper = 1 + test_frame2.arp_sha = 0x5A5152535455 + test_frame2.arp_spa = 0xc0a80164 + test_frame2.arp_tha = 0xDAD1D2D3D4D5 + test_frame2.arp_tpa = 0xc0a80165 + source.send(test_frame1.build_eth()) + source.send(test_frame2.build_eth()) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = arp_ep.ARPFrame() + test_frame1.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0806 + test_frame1.arp_htype = 0x0001 + test_frame1.arp_ptype = 0x0800 + test_frame1.arp_hlen = 6 + test_frame1.arp_plen = 4 + test_frame1.arp_oper = 1 + test_frame1.arp_sha = 0x5A5152535455 + test_frame1.arp_spa = 0xc0a80164 + test_frame1.arp_tha = 0xDAD1D2D3D4D5 + test_frame1.arp_tpa = 0xc0a80165 + test_frame2 = arp_ep.ARPFrame() + test_frame2.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0806 + test_frame2.arp_htype = 0x0001 + test_frame2.arp_ptype = 0x0800 + test_frame2.arp_hlen = 6 + test_frame2.arp_plen = 4 + test_frame2.arp_oper = 1 + test_frame2.arp_sha = 0x5A5152535455 + test_frame2.arp_spa = 0xc0a80164 + test_frame2.arp_tha = 0xDAD1D2D3D4D5 + test_frame2.arp_tpa = 0xc0a80165 + source.send(test_frame1.build_eth()) + source.send(test_frame2.build_eth()) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: truncated packet") + current_test.next = 7 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + eth_frame = test_frame.build_eth() + eth_frame.payload.data = eth_frame.payload.data[:-2] + source.send(eth_frame) + yield clk.posedge + + yield s_eth_payload_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + assert error_header_early_termination + + yield delay(100) + + yield clk.posedge + print("test 8: bad header") + current_test.next = 8 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 0 + test_frame.arp_plen = 0 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + source.send(test_frame.build_eth()) + yield clk.posedge + + yield s_eth_payload_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + assert error_invalid_header + + yield delay(100) + + yield clk.posedge + print("test 9: assert tuser") + current_test.next = 9 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + eth_frame = test_frame.build_eth() + eth_frame.payload.user = 1 + source.send(eth_frame) + yield clk.posedge + + yield s_eth_payload_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_arp_eth_rx.v b/fpga/lib/eth/tb/test_arp_eth_rx.v new file mode 100644 index 000000000..dea6e2ff9 --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_eth_rx.v @@ -0,0 +1,147 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for arp_eth_rx + */ +module test_arp_eth_rx; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [7:0] s_eth_payload_axis_tdata = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg m_frame_ready = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; +wire m_frame_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [15:0] m_arp_htype; +wire [15:0] m_arp_ptype; +wire [7:0] m_arp_hlen; +wire [7:0] m_arp_plen; +wire [15:0] m_arp_oper; +wire [47:0] m_arp_sha; +wire [31:0] m_arp_spa; +wire [47:0] m_arp_tha; +wire [31:0] m_arp_tpa; +wire busy; +wire error_header_early_termination; +wire error_invalid_header; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + m_frame_ready + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_frame_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_arp_htype, + m_arp_ptype, + m_arp_hlen, + m_arp_plen, + m_arp_oper, + m_arp_sha, + m_arp_spa, + m_arp_tha, + m_arp_tpa, + busy, + error_header_early_termination, + error_invalid_header + ); + + // dump file + $dumpfile("test_arp_eth_rx.lxt"); + $dumpvars(0, test_arp_eth_rx); +end + +arp_eth_rx +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // ARP frame output + .m_frame_valid(m_frame_valid), + .m_frame_ready(m_frame_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_arp_htype(m_arp_htype), + .m_arp_ptype(m_arp_ptype), + .m_arp_hlen(m_arp_hlen), + .m_arp_plen(m_arp_plen), + .m_arp_oper(m_arp_oper), + .m_arp_sha(m_arp_sha), + .m_arp_spa(m_arp_spa), + .m_arp_tha(m_arp_tha), + .m_arp_tpa(m_arp_tpa), + // Status signals + .busy(busy), + .error_header_early_termination(error_header_early_termination), + .error_invalid_header(error_invalid_header) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_arp_eth_rx_64.py b/fpga/lib/eth/tb/test_arp_eth_rx_64.py new file mode 100755 index 000000000..0191157b8 --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_eth_rx_64.py @@ -0,0 +1,536 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import arp_ep +import eth_ep + +module = 'arp_eth_rx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + s_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + m_frame_ready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + m_frame_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_arp_htype = Signal(intbv(0)[16:]) + m_arp_ptype = Signal(intbv(0)[16:]) + m_arp_hlen = Signal(intbv(0)[8:]) + m_arp_plen = Signal(intbv(0)[8:]) + m_arp_oper = Signal(intbv(0)[16:]) + m_arp_sha = Signal(intbv(0)[48:]) + m_arp_spa = Signal(intbv(0)[32:]) + m_arp_tha = Signal(intbv(0)[48:]) + m_arp_tpa = Signal(intbv(0)[32:]) + busy = Signal(bool(0)) + error_header_early_termination = Signal(bool(0)) + error_invalid_header = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = eth_ep.EthFrameSource() + + source_logic = source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tkeep=s_eth_payload_axis_tkeep, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = arp_ep.ARPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + frame_ready=m_frame_ready, + frame_valid=m_frame_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + arp_htype=m_arp_htype, + arp_ptype=m_arp_ptype, + arp_hlen=m_arp_hlen, + arp_plen=m_arp_plen, + arp_oper=m_arp_oper, + arp_sha=m_arp_sha, + arp_spa=m_arp_spa, + arp_tha=m_arp_tha, + arp_tpa=m_arp_tpa, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_frame_valid=m_frame_valid, + m_frame_ready=m_frame_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_arp_htype=m_arp_htype, + m_arp_ptype=m_arp_ptype, + m_arp_hlen=m_arp_hlen, + m_arp_plen=m_arp_plen, + m_arp_oper=m_arp_oper, + m_arp_sha=m_arp_sha, + m_arp_spa=m_arp_spa, + m_arp_tha=m_arp_tha, + m_arp_tpa=m_arp_tpa, + + busy=busy, + error_header_early_termination=error_header_early_termination, + error_invalid_header=error_invalid_header + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + source.send(test_frame.build_eth()) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: packet with trailing bytes") + current_test.next = 2 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + eth_frame = test_frame.build_eth() + eth_frame.payload.data += bytearray(range(10)) + source.send(eth_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + source.send(test_frame.build_eth()) + yield clk.posedge + + yield delay(16) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(16) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = arp_ep.ARPFrame() + test_frame1.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0806 + test_frame1.arp_htype = 0x0001 + test_frame1.arp_ptype = 0x0800 + test_frame1.arp_hlen = 6 + test_frame1.arp_plen = 4 + test_frame1.arp_oper = 1 + test_frame1.arp_sha = 0x5A5152535455 + test_frame1.arp_spa = 0xc0a80164 + test_frame1.arp_tha = 0xDAD1D2D3D4D5 + test_frame1.arp_tpa = 0xc0a80165 + test_frame2 = arp_ep.ARPFrame() + test_frame2.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0806 + test_frame2.arp_htype = 0x0001 + test_frame2.arp_ptype = 0x0800 + test_frame2.arp_hlen = 6 + test_frame2.arp_plen = 4 + test_frame2.arp_oper = 1 + test_frame2.arp_sha = 0x5A5152535455 + test_frame2.arp_spa = 0xc0a80164 + test_frame2.arp_tha = 0xDAD1D2D3D4D5 + test_frame2.arp_tpa = 0xc0a80165 + source.send(test_frame1.build_eth()) + source.send(test_frame2.build_eth()) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = arp_ep.ARPFrame() + test_frame1.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0806 + test_frame1.arp_htype = 0x0001 + test_frame1.arp_ptype = 0x0800 + test_frame1.arp_hlen = 6 + test_frame1.arp_plen = 4 + test_frame1.arp_oper = 1 + test_frame1.arp_sha = 0x5A5152535455 + test_frame1.arp_spa = 0xc0a80164 + test_frame1.arp_tha = 0xDAD1D2D3D4D5 + test_frame1.arp_tpa = 0xc0a80165 + test_frame2 = arp_ep.ARPFrame() + test_frame2.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0806 + test_frame2.arp_htype = 0x0001 + test_frame2.arp_ptype = 0x0800 + test_frame2.arp_hlen = 6 + test_frame2.arp_plen = 4 + test_frame2.arp_oper = 1 + test_frame2.arp_sha = 0x5A5152535455 + test_frame2.arp_spa = 0xc0a80164 + test_frame2.arp_tha = 0xDAD1D2D3D4D5 + test_frame2.arp_tpa = 0xc0a80165 + source.send(test_frame1.build_eth()) + source.send(test_frame2.build_eth()) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = arp_ep.ARPFrame() + test_frame1.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0806 + test_frame1.arp_htype = 0x0001 + test_frame1.arp_ptype = 0x0800 + test_frame1.arp_hlen = 6 + test_frame1.arp_plen = 4 + test_frame1.arp_oper = 1 + test_frame1.arp_sha = 0x5A5152535455 + test_frame1.arp_spa = 0xc0a80164 + test_frame1.arp_tha = 0xDAD1D2D3D4D5 + test_frame1.arp_tpa = 0xc0a80165 + test_frame2 = arp_ep.ARPFrame() + test_frame2.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0806 + test_frame2.arp_htype = 0x0001 + test_frame2.arp_ptype = 0x0800 + test_frame2.arp_hlen = 6 + test_frame2.arp_plen = 4 + test_frame2.arp_oper = 1 + test_frame2.arp_sha = 0x5A5152535455 + test_frame2.arp_spa = 0xc0a80164 + test_frame2.arp_tha = 0xDAD1D2D3D4D5 + test_frame2.arp_tpa = 0xc0a80165 + source.send(test_frame1.build_eth()) + source.send(test_frame2.build_eth()) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: truncated packet") + current_test.next = 7 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + eth_frame = test_frame.build_eth() + eth_frame.payload.data = eth_frame.payload.data[:-2] + source.send(eth_frame) + yield clk.posedge + + yield s_eth_payload_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + assert error_header_early_termination + + yield delay(100) + + yield clk.posedge + print("test 8: bad header") + current_test.next = 8 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 0 + test_frame.arp_plen = 0 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + source.send(test_frame.build_eth()) + yield clk.posedge + + yield s_eth_payload_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + assert error_invalid_header + + yield delay(100) + + yield clk.posedge + print("test 9: assert tuser") + current_test.next = 9 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + eth_frame = test_frame.build_eth() + eth_frame.payload.user = 1 + source.send(eth_frame) + yield clk.posedge + + yield s_eth_payload_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_arp_eth_rx_64.v b/fpga/lib/eth/tb/test_arp_eth_rx_64.v new file mode 100644 index 000000000..00b4b6a79 --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_eth_rx_64.v @@ -0,0 +1,150 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for arp_eth_rx_64 + */ +module test_arp_eth_rx_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [63:0] s_eth_payload_axis_tdata = 0; +reg [7:0] s_eth_payload_axis_tkeep = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg m_frame_ready = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; +wire m_frame_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [15:0] m_arp_htype; +wire [15:0] m_arp_ptype; +wire [7:0] m_arp_hlen; +wire [7:0] m_arp_plen; +wire [15:0] m_arp_oper; +wire [47:0] m_arp_sha; +wire [31:0] m_arp_spa; +wire [47:0] m_arp_tha; +wire [31:0] m_arp_tpa; +wire busy; +wire error_header_early_termination; +wire error_invalid_header; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + m_frame_ready + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_frame_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_arp_htype, + m_arp_ptype, + m_arp_hlen, + m_arp_plen, + m_arp_oper, + m_arp_sha, + m_arp_spa, + m_arp_tha, + m_arp_tpa, + busy, + error_header_early_termination, + error_invalid_header + ); + + // dump file + $dumpfile("test_arp_eth_rx_64.lxt"); + $dumpvars(0, test_arp_eth_rx_64); +end + +arp_eth_rx_64 +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // ARP frame output + .m_frame_valid(m_frame_valid), + .m_frame_ready(m_frame_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_arp_htype(m_arp_htype), + .m_arp_ptype(m_arp_ptype), + .m_arp_hlen(m_arp_hlen), + .m_arp_plen(m_arp_plen), + .m_arp_oper(m_arp_oper), + .m_arp_sha(m_arp_sha), + .m_arp_spa(m_arp_spa), + .m_arp_tha(m_arp_tha), + .m_arp_tpa(m_arp_tpa), + // Status signals + .busy(busy), + .error_header_early_termination(error_header_early_termination), + .error_invalid_header(error_invalid_header) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_arp_eth_tx.py b/fpga/lib/eth/tb/test_arp_eth_tx.py new file mode 100755 index 000000000..02ac9cf17 --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_eth_tx.py @@ -0,0 +1,356 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep + +module = 'arp_eth_tx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_frame_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_arp_htype = Signal(intbv(0)[16:]) + s_arp_ptype = Signal(intbv(0)[16:]) + s_arp_oper = Signal(intbv(0)[16:]) + s_arp_sha = Signal(intbv(0)[48:]) + s_arp_spa = Signal(intbv(0)[32:]) + s_arp_tha = Signal(intbv(0)[48:]) + s_arp_tpa = Signal(intbv(0)[32:]) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + + # Outputs + s_frame_ready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = arp_ep.ARPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + frame_ready=s_frame_ready, + frame_valid=s_frame_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + arp_htype=s_arp_htype, + arp_ptype=s_arp_ptype, + arp_oper=s_arp_oper, + arp_sha=s_arp_sha, + arp_spa=s_arp_spa, + arp_tha=s_arp_tha, + arp_tpa=s_arp_tpa, + pause=source_pause, + name='source' + ) + + sink = eth_ep.EthFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_frame_valid=s_frame_valid, + s_frame_ready=s_frame_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_arp_htype=s_arp_htype, + s_arp_ptype=s_arp_ptype, + s_arp_oper=s_arp_oper, + s_arp_sha=s_arp_sha, + s_arp_spa=s_arp_spa, + s_arp_tha=s_arp_tha, + s_arp_tpa=s_arp_tpa, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + busy=busy + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test packet with pauses") + current_test.next = 2 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + source.send(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets") + current_test.next = 3 + + test_frame1 = arp_ep.ARPFrame() + test_frame1.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0806 + test_frame1.arp_htype = 0x0001 + test_frame1.arp_ptype = 0x0800 + test_frame1.arp_hlen = 6 + test_frame1.arp_plen = 4 + test_frame1.arp_oper = 1 + test_frame1.arp_sha = 0x5A5152535455 + test_frame1.arp_spa = 0xc0a80164 + test_frame1.arp_tha = 0xDAD1D2D3D4D5 + test_frame1.arp_tpa = 0xc0a80165 + test_frame2 = arp_ep.ARPFrame() + test_frame2.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0806 + test_frame2.arp_htype = 0x0001 + test_frame2.arp_ptype = 0x0800 + test_frame2.arp_hlen = 6 + test_frame2.arp_plen = 4 + test_frame2.arp_oper = 1 + test_frame2.arp_sha = 0x5A5152535455 + test_frame2.arp_spa = 0xc0a80164 + test_frame2.arp_tha = 0xDAD1D2D3D4D5 + test_frame2.arp_tpa = 0xc0a80165 + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: alternate pause sink") + current_test.next = 4 + + test_frame1 = arp_ep.ARPFrame() + test_frame1.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0806 + test_frame1.arp_htype = 0x0001 + test_frame1.arp_ptype = 0x0800 + test_frame1.arp_hlen = 6 + test_frame1.arp_plen = 4 + test_frame1.arp_oper = 1 + test_frame1.arp_sha = 0x5A5152535455 + test_frame1.arp_spa = 0xc0a80164 + test_frame1.arp_tha = 0xDAD1D2D3D4D5 + test_frame1.arp_tpa = 0xc0a80165 + test_frame2 = arp_ep.ARPFrame() + test_frame2.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0806 + test_frame2.arp_htype = 0x0001 + test_frame2.arp_ptype = 0x0800 + test_frame2.arp_hlen = 6 + test_frame2.arp_plen = 4 + test_frame2.arp_oper = 1 + test_frame2.arp_sha = 0x5A5152535455 + test_frame2.arp_spa = 0xc0a80164 + test_frame2.arp_tha = 0xDAD1D2D3D4D5 + test_frame2.arp_tpa = 0xc0a80165 + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + while m_eth_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_arp_eth_tx.v b/fpga/lib/eth/tb/test_arp_eth_tx.v new file mode 100644 index 000000000..c112d25eb --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_eth_tx.v @@ -0,0 +1,135 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for arp_eth_tx + */ +module test_arp_eth_tx; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_frame_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [15:0] s_arp_htype = 0; +reg [15:0] s_arp_ptype = 0; +reg [15:0] s_arp_oper = 0; +reg [47:0] s_arp_sha = 0; +reg [31:0] s_arp_spa = 0; +reg [47:0] s_arp_tha = 0; +reg [31:0] s_arp_tpa = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +// Outputs +wire s_frame_ready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [7:0] m_eth_payload_axis_tdata; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire busy; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_frame_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_arp_htype, + s_arp_ptype, + s_arp_oper, + s_arp_sha, + s_arp_spa, + s_arp_tha, + s_arp_tpa, + m_eth_hdr_ready, + m_eth_payload_axis_tready + ); + $to_myhdl( + s_frame_ready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + busy + ); + + // dump file + $dumpfile("test_arp_eth_tx.lxt"); + $dumpvars(0, test_arp_eth_tx); +end + +arp_eth_tx +UUT ( + .clk(clk), + .rst(rst), + // ARP frame input + .s_frame_valid(s_frame_valid), + .s_frame_ready(s_frame_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_arp_htype(s_arp_htype), + .s_arp_ptype(s_arp_ptype), + .s_arp_oper(s_arp_oper), + .s_arp_sha(s_arp_sha), + .s_arp_spa(s_arp_spa), + .s_arp_tha(s_arp_tha), + .s_arp_tpa(s_arp_tpa), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy(busy) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_arp_eth_tx_64.py b/fpga/lib/eth/tb/test_arp_eth_tx_64.py new file mode 100755 index 000000000..d6120f343 --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_eth_tx_64.py @@ -0,0 +1,359 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep + +module = 'arp_eth_tx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_frame_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_arp_htype = Signal(intbv(0)[16:]) + s_arp_ptype = Signal(intbv(0)[16:]) + s_arp_oper = Signal(intbv(0)[16:]) + s_arp_sha = Signal(intbv(0)[48:]) + s_arp_spa = Signal(intbv(0)[32:]) + s_arp_tha = Signal(intbv(0)[48:]) + s_arp_tpa = Signal(intbv(0)[32:]) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + + # Outputs + s_frame_ready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + m_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = arp_ep.ARPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + frame_ready=s_frame_ready, + frame_valid=s_frame_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + arp_htype=s_arp_htype, + arp_ptype=s_arp_ptype, + arp_oper=s_arp_oper, + arp_sha=s_arp_sha, + arp_spa=s_arp_spa, + arp_tha=s_arp_tha, + arp_tpa=s_arp_tpa, + pause=source_pause, + name='source' + ) + + sink = eth_ep.EthFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_frame_valid=s_frame_valid, + s_frame_ready=s_frame_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_arp_htype=s_arp_htype, + s_arp_ptype=s_arp_ptype, + s_arp_oper=s_arp_oper, + s_arp_sha=s_arp_sha, + s_arp_spa=s_arp_spa, + s_arp_tha=s_arp_tha, + s_arp_tpa=s_arp_tpa, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + busy=busy + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + source.send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test packet with pauses") + current_test.next = 2 + + test_frame = arp_ep.ARPFrame() + test_frame.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0806 + test_frame.arp_htype = 0x0001 + test_frame.arp_ptype = 0x0800 + test_frame.arp_hlen = 6 + test_frame.arp_plen = 4 + test_frame.arp_oper = 1 + test_frame.arp_sha = 0x5A5152535455 + test_frame.arp_spa = 0xc0a80164 + test_frame.arp_tha = 0xDAD1D2D3D4D5 + test_frame.arp_tpa = 0xc0a80165 + source.send(test_frame) + yield clk.posedge + + yield delay(16) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets") + current_test.next = 3 + + test_frame1 = arp_ep.ARPFrame() + test_frame1.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0806 + test_frame1.arp_htype = 0x0001 + test_frame1.arp_ptype = 0x0800 + test_frame1.arp_hlen = 6 + test_frame1.arp_plen = 4 + test_frame1.arp_oper = 1 + test_frame1.arp_sha = 0x5A5152535455 + test_frame1.arp_spa = 0xc0a80164 + test_frame1.arp_tha = 0xDAD1D2D3D4D5 + test_frame1.arp_tpa = 0xc0a80165 + test_frame2 = arp_ep.ARPFrame() + test_frame2.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0806 + test_frame2.arp_htype = 0x0001 + test_frame2.arp_ptype = 0x0800 + test_frame2.arp_hlen = 6 + test_frame2.arp_plen = 4 + test_frame2.arp_oper = 1 + test_frame2.arp_sha = 0x5A5152535455 + test_frame2.arp_spa = 0xc0a80164 + test_frame2.arp_tha = 0xDAD1D2D3D4D5 + test_frame2.arp_tpa = 0xc0a80165 + source.send(test_frame1) + source.send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: alternate pause sink") + current_test.next = 4 + + test_frame1 = arp_ep.ARPFrame() + test_frame1.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0806 + test_frame1.arp_htype = 0x0001 + test_frame1.arp_ptype = 0x0800 + test_frame1.arp_hlen = 6 + test_frame1.arp_plen = 4 + test_frame1.arp_oper = 1 + test_frame1.arp_sha = 0x5A5152535455 + test_frame1.arp_spa = 0xc0a80164 + test_frame1.arp_tha = 0xDAD1D2D3D4D5 + test_frame1.arp_tpa = 0xc0a80165 + test_frame2 = arp_ep.ARPFrame() + test_frame2.eth_dest_mac = 0xFFFFFFFFFFFF + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0806 + test_frame2.arp_htype = 0x0001 + test_frame2.arp_ptype = 0x0800 + test_frame2.arp_hlen = 6 + test_frame2.arp_plen = 4 + test_frame2.arp_oper = 1 + test_frame2.arp_sha = 0x5A5152535455 + test_frame2.arp_spa = 0xc0a80164 + test_frame2.arp_tha = 0xDAD1D2D3D4D5 + test_frame2.arp_tpa = 0xc0a80165 + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + while m_eth_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + assert check_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_arp_eth_tx_64.v b/fpga/lib/eth/tb/test_arp_eth_tx_64.v new file mode 100644 index 000000000..4b639585c --- /dev/null +++ b/fpga/lib/eth/tb/test_arp_eth_tx_64.v @@ -0,0 +1,138 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for arp_eth_tx_64 + */ +module test_arp_eth_tx_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_frame_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [15:0] s_arp_htype = 0; +reg [15:0] s_arp_ptype = 0; +reg [15:0] s_arp_oper = 0; +reg [47:0] s_arp_sha = 0; +reg [31:0] s_arp_spa = 0; +reg [47:0] s_arp_tha = 0; +reg [31:0] s_arp_tpa = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +// Outputs +wire s_frame_ready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [63:0] m_eth_payload_axis_tdata; +wire [7:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire busy; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_frame_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_arp_htype, + s_arp_ptype, + s_arp_oper, + s_arp_sha, + s_arp_spa, + s_arp_tha, + s_arp_tpa, + m_eth_hdr_ready, + m_eth_payload_axis_tready + ); + $to_myhdl( + s_frame_ready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + busy + ); + + // dump file + $dumpfile("test_arp_eth_tx_64.lxt"); + $dumpvars(0, test_arp_eth_tx_64); +end + +arp_eth_tx_64 +UUT ( + .clk(clk), + .rst(rst), + // ARP frame input + .s_frame_valid(s_frame_valid), + .s_frame_ready(s_frame_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_arp_htype(s_arp_htype), + .s_arp_ptype(s_arp_ptype), + .s_arp_oper(s_arp_oper), + .s_arp_sha(s_arp_sha), + .s_arp_spa(s_arp_spa), + .s_arp_tha(s_arp_tha), + .s_arp_tpa(s_arp_tpa), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy(busy) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_axis_baser_rx_64.py b/fpga/lib/eth/tb/test_axis_baser_rx_64.py new file mode 100755 index 000000000..d13e4993f --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_baser_rx_64.py @@ -0,0 +1,445 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import baser_serdes_ep +import xgmii_ep + +module = 'axis_baser_rx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + PTP_PERIOD_NS = 0x6 + PTP_PERIOD_FNS = 0x6666 + PTP_TS_ENABLE = 0 + PTP_TS_WIDTH = 96 + USER_WIDTH = (PTP_TS_WIDTH if PTP_TS_ENABLE else 0) + 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + encoded_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + encoded_rx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + + # Outputs + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + start_packet = Signal(intbv(0)[2:]) + error_bad_frame = Signal(bool(0)) + error_bad_fcs = Signal(bool(0)) + rx_bad_block = Signal(bool(0)) + + # sources and sinks + source = baser_serdes_ep.BaseRSerdesSource() + + source_logic = source.create_logic( + clk, + tx_data=encoded_rx_data, + tx_header=encoded_rx_hdr, + scramble=False, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + encoded_rx_data=encoded_rx_data, + encoded_rx_hdr=encoded_rx_hdr, + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser, + ptp_ts=ptp_ts, + start_packet=start_packet, + error_bad_frame=error_bad_frame, + error_bad_fcs=error_bad_fcs, + rx_bad_block=rx_bad_block + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_bad_frame_asserted = Signal(bool(0)) + error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_bad_frame): + error_bad_frame_asserted.next = 1 + if (error_bad_fcs): + error_bad_fcs_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(1,18))+list(range(64,82)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: truncated frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + axis_frame1.data = axis_frame1.data[:-1] + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert error_bad_fcs_asserted + + assert rx_frame.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: errored frame, length %d" % payload_len) + current_test.next = 4 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert not error_bad_fcs_asserted + + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 5: test stream, length %d" % payload_len) + current_test.next = 5 + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 6: test stream with zero IFG, length %d" % payload_len) + current_test.next = 6 + + source.ifg = 0 + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + source.ifg = 12 + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 6: test stream with zero IFG and offset start, length %d" % payload_len) + current_test.next = 6 + + source.ifg = 0 + source.force_offset_start = True + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + source.ifg = 12 + source.force_offset_start = False + + yield delay(100) + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_axis_baser_rx_64.v b/fpga/lib/eth/tb/test_axis_baser_rx_64.v new file mode 100644 index 000000000..95eb08067 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_baser_rx_64.v @@ -0,0 +1,118 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for axis_baser_rx_64 + */ +module test_axis_baser_rx_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; +parameter PTP_PERIOD_NS = 4'h6; +parameter PTP_PERIOD_FNS = 16'h6666; +parameter PTP_TS_ENABLE = 0; +parameter PTP_TS_WIDTH = 96; +parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] encoded_rx_data = 0; +reg [HDR_WIDTH-1:0] encoded_rx_hdr = 1; +reg [PTP_TS_WIDTH-1:0] ptp_ts = 0; + +// Outputs +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire [1:0] start_packet; +wire error_bad_frame; +wire error_bad_fcs; +wire rx_bad_block; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + encoded_rx_data, + encoded_rx_hdr, + ptp_ts + ); + $to_myhdl( + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser, + start_packet, + error_bad_frame, + error_bad_fcs, + rx_bad_block + ); + + // dump file + $dumpfile("test_axis_baser_rx_64.lxt"); + $dumpvars(0, test_axis_baser_rx_64); +end + +axis_baser_rx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + .ptp_ts(ptp_ts), + .start_packet(start_packet), + .error_bad_frame(error_bad_frame), + .error_bad_fcs(error_bad_fcs), + .rx_bad_block(rx_bad_block) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_axis_baser_tx_64.py b/fpga/lib/eth/tb/test_axis_baser_tx_64.py new file mode 100755 index 000000000..62269ecb6 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_baser_tx_64.py @@ -0,0 +1,360 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import baser_serdes_ep + +module = 'axis_baser_tx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + PTP_PERIOD_NS = 0x6 + PTP_PERIOD_FNS = 0x6666 + PTP_TS_ENABLE = 0 + PTP_TS_WIDTH = 96 + PTP_TAG_ENABLE = PTP_TS_ENABLE + PTP_TAG_WIDTH = 16 + USER_WIDTH = (PTP_TAG_WIDTH if PTP_TAG_ENABLE else 0) + 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + s_axis_tready = Signal(bool(0)) + encoded_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + encoded_tx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + m_axis_ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + m_axis_ptp_ts_tag = Signal(intbv(0)[PTP_TAG_WIDTH:]) + m_axis_ptp_ts_valid = Signal(bool(0)) + start_packet = Signal(intbv(0)[2:]) + error_underflow = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = baser_serdes_ep.BaseRSerdesSink() + + sink_logic = sink.create_logic( + clk, + rx_data=encoded_tx_data, + rx_header=encoded_tx_hdr, + scramble=False, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + encoded_tx_data=encoded_tx_data, + encoded_tx_hdr=encoded_tx_hdr, + ptp_ts=ptp_ts, + m_axis_ptp_ts=m_axis_ptp_ts, + m_axis_ptp_ts_tag=m_axis_ptp_ts_tag, + m_axis_ptp_ts_valid=m_axis_ptp_ts_valid, + ifg_delay=ifg_delay, + start_packet=start_packet, + error_underflow=error_underflow + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for payload_len in list(range(1,18))+list(range(40,58)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + source.send(axis_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + source.send(axis_frame1) + source.send(axis_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame1.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame1.eth_src_mac + assert eth_frame.eth_type == test_frame1.eth_type + assert eth_frame.payload.data.index(test_frame1.payload.data) == 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame2.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame2.eth_src_mac + assert eth_frame.eth_type == test_frame2.eth_type + assert eth_frame.payload.data.index(test_frame2.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + axis_frame1.last_cycle_user = 1 + + source.send(axis_frame1) + source.send(axis_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + assert rx_frame.error[-1] + + # bad packet + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame2.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame2.eth_src_mac + assert eth_frame.eth_type == test_frame2.eth_type + assert eth_frame.payload.data.index(test_frame2.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 4: test stream, length %d" % payload_len) + current_test.next = 4 + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + source.send(axis_frame) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_axis_baser_tx_64.v b/fpga/lib/eth/tb/test_axis_baser_tx_64.v new file mode 100644 index 000000000..1c62d6bdd --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_baser_tx_64.v @@ -0,0 +1,137 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for axis_baser_tx_64 + */ +module test_axis_baser_tx_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter PTP_PERIOD_NS = 4'h6; +parameter PTP_PERIOD_FNS = 16'h6666; +parameter PTP_TS_ENABLE = 0; +parameter PTP_TS_WIDTH = 96; +parameter PTP_TAG_ENABLE = PTP_TS_ENABLE; +parameter PTP_TAG_WIDTH = 16; +parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg [PTP_TS_WIDTH-1:0] ptp_ts = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] encoded_tx_data; +wire [HDR_WIDTH-1:0] encoded_tx_hdr; +wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts; +wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag; +wire m_axis_ptp_ts_valid; +wire [1:0] start_packet; +wire error_underflow; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser, + ptp_ts, + ifg_delay + ); + $to_myhdl( + s_axis_tready, + encoded_tx_data, + encoded_tx_hdr, + m_axis_ptp_ts, + m_axis_ptp_ts_tag, + m_axis_ptp_ts_valid, + start_packet, + error_underflow + ); + + // dump file + $dumpfile("test_axis_baser_tx_64.lxt"); + $dumpvars(0, test_axis_baser_tx_64); +end + +axis_baser_tx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .PTP_TAG_ENABLE(PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(PTP_TAG_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr), + .ptp_ts(ptp_ts), + .m_axis_ptp_ts(m_axis_ptp_ts), + .m_axis_ptp_ts_tag(m_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(m_axis_ptp_ts_valid), + .ifg_delay(ifg_delay), + .start_packet(start_packet), + .error_underflow(error_underflow) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_axis_eth_fcs.py b/fpga/lib/eth/tb/test_axis_eth_fcs.py new file mode 100755 index 000000000..15fe19ba8 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_eth_fcs.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep + +module = 'axis_eth_fcs' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[8:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(1)) + output_fcs = Signal(intbv(0)[32:]) + output_fcs_valid = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + output_fcs=output_fcs, + output_fcs_valid=output_fcs_valid + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(1,18))+list(range(64,82)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + source.send(axis_frame) + yield clk.posedge + yield clk.posedge + + yield output_fcs_valid.posedge + + print(hex(int(output_fcs))) + print(hex(test_frame.eth_fcs)) + + assert output_fcs == test_frame.eth_fcs + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_axis_eth_fcs.v b/fpga/lib/eth/tb/test_axis_eth_fcs.v new file mode 100644 index 000000000..785ab4e3f --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_eth_fcs.v @@ -0,0 +1,86 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for axis_eth_fcs + */ +module test_axis_eth_fcs; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] s_axis_tdata = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg s_axis_tuser = 0; + +// Outputs +wire s_axis_tready; +wire [31:0] output_fcs; +wire output_fcs_valid; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser + ); + $to_myhdl( + s_axis_tready, + output_fcs, + output_fcs_valid + ); + + // dump file + $dumpfile("test_axis_eth_fcs.lxt"); + $dumpvars(0, test_axis_eth_fcs); +end + +axis_eth_fcs +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .output_fcs(output_fcs), + .output_fcs_valid(output_fcs_valid) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_axis_eth_fcs_64.py b/fpga/lib/eth/tb/test_axis_eth_fcs_64.py new file mode 100755 index 000000000..c592dab04 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_eth_fcs_64.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep + +module = 'axis_eth_fcs_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[64:]) + s_axis_tkeep = Signal(intbv(0)[8:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(1)) + output_fcs = Signal(intbv(0)[32:]) + output_fcs_valid = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + output_fcs=output_fcs, + output_fcs_valid=output_fcs_valid + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(1,18))+list(range(64,82)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + source.send(axis_frame) + yield clk.posedge + yield clk.posedge + + yield output_fcs_valid.posedge + + print(hex(int(output_fcs))) + print(hex(test_frame.eth_fcs)) + + assert output_fcs == test_frame.eth_fcs + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_axis_eth_fcs_64.v b/fpga/lib/eth/tb/test_axis_eth_fcs_64.v new file mode 100644 index 000000000..1d137b1fe --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_eth_fcs_64.v @@ -0,0 +1,89 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for axis_eth_fcs_64 + */ +module test_axis_eth_fcs_64; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [63:0] s_axis_tdata = 0; +reg [7:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg s_axis_tuser = 0; + +// Outputs +wire s_axis_tready; +wire [31:0] output_fcs; +wire output_fcs_valid; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser + ); + $to_myhdl( + s_axis_tready, + output_fcs, + output_fcs_valid + ); + + // dump file + $dumpfile("test_axis_eth_fcs_64.lxt"); + $dumpvars(0, test_axis_eth_fcs_64); +end + +axis_eth_fcs_64 +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .output_fcs(output_fcs), + .output_fcs_valid(output_fcs_valid) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_axis_eth_fcs_check.py b/fpga/lib/eth/tb/test_axis_eth_fcs_check.py new file mode 100755 index 000000000..620ecdcae --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_eth_fcs_check.py @@ -0,0 +1,402 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os +import struct +import zlib + +import axis_ep +import eth_ep + +module = 'axis_eth_fcs_check' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[8:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(bool(0)) + m_axis_tready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_axis_tdata = Signal(intbv(0)[8:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_bad_fcs = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser, + + busy=busy, + error_bad_fcs=error_bad_fcs + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_bad_fcs): + error_bad_fcs_asserted.next = 1 + + def wait_normal(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(1,18))+list(range(64,82)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(axis_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + assert not rx_frame.user[-1] + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame1 + assert not rx_frame.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + assert not rx_frame.user[-1] + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + axis_frame1.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + assert not rx_frame.user[-1] + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: bad FCS, length %d" % payload_len) + current_test.next = 4 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + axis_frame1.data[-1] ^= 0xff + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_bad_fcs_asserted.next = 0 + + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_fcs_asserted + + assert rx_frame.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + assert not rx_frame.user[-1] + + assert sink.empty() + + yield delay(100) + + for payload_len in list(range(1,18)): + yield clk.posedge + print("test 5: test short packet, length %d" % payload_len) + current_test.next = 5 + + test_frame = bytearray(range(payload_len)) + fcs = zlib.crc32(bytes(test_frame)) & 0xffffffff + test_frame_fcs = test_frame + struct.pack(' 0: + clk_enable.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + clk_enable.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for rate, mii in [(1, 0), (10, 0), (5, 1)]: + clk_enable_rate.next = rate + mii_select.next = mii + + yield delay(100) + + for payload_len in list(range(1,18))+list(range(64,82)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + gmii_frame = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + source.send(gmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + gmii_frame1 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + gmii_frame2 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(gmii_frame1) + source.send(gmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: truncated frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + axis_frame1.data = axis_frame1.data[:-1] + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + gmii_frame1 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + gmii_frame2 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(gmii_frame1) + source.send(gmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert error_bad_fcs_asserted + + assert rx_frame.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: errored frame, length %d" % payload_len) + current_test.next = 4 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + gmii_frame1 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + gmii_frame2 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + gmii_frame1.error = 1 + + source.send(gmii_frame1) + source.send(gmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert not error_bad_fcs_asserted + + assert rx_frame.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_axis_gmii_rx.v b/fpga/lib/eth/tb/test_axis_gmii_rx.v new file mode 100644 index 000000000..452fdcbe1 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_gmii_rx.v @@ -0,0 +1,113 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for axis_gmii_rx + */ +module test_axis_gmii_rx; + +// Parameters +parameter DATA_WIDTH = 8; +parameter PTP_TS_ENABLE = 0; +parameter PTP_TS_WIDTH = 96; +parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] gmii_rxd = 0; +reg gmii_rx_dv = 0; +reg gmii_rx_er = 0; +reg [PTP_TS_WIDTH-1:0] ptp_ts = 0; +reg clk_enable = 1; +reg mii_select = 0; + +// Outputs +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire start_packet; +wire error_bad_frame; +wire error_bad_fcs; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + gmii_rxd, + gmii_rx_dv, + gmii_rx_er, + ptp_ts, + clk_enable, + mii_select + ); + $to_myhdl( + m_axis_tdata, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser, + start_packet, + error_bad_frame, + error_bad_fcs + ); + + // dump file + $dumpfile("test_axis_gmii_rx.lxt"); + $dumpvars(0, test_axis_gmii_rx); +end + +axis_gmii_rx #( + .DATA_WIDTH(DATA_WIDTH), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + .ptp_ts(ptp_ts), + .clk_enable(clk_enable), + .mii_select(mii_select), + .start_packet(start_packet), + .error_bad_frame(error_bad_frame), + .error_bad_fcs(error_bad_fcs) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_axis_gmii_tx.py b/fpga/lib/eth/tb/test_axis_gmii_tx.py new file mode 100755 index 000000000..1c47c6a57 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_gmii_tx.py @@ -0,0 +1,350 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import gmii_ep + +module = 'axis_gmii_tx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + PTP_TS_ENABLE = 0 + PTP_TS_WIDTH = 96 + PTP_TAG_ENABLE = PTP_TS_ENABLE + PTP_TAG_WIDTH = 16 + USER_WIDTH = (PTP_TAG_WIDTH if PTP_TAG_ENABLE else 0) + 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + clk_enable = Signal(bool(1)) + mii_select = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + s_axis_tready = Signal(bool(0)) + gmii_txd = Signal(intbv(0)[DATA_WIDTH:]) + gmii_tx_en = Signal(bool(0)) + gmii_tx_er = Signal(bool(0)) + m_axis_ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + m_axis_ptp_ts_tag = Signal(intbv(0)[PTP_TAG_WIDTH:]) + m_axis_ptp_ts_valid = Signal(bool(0)) + start_packet = Signal(bool(0)) + error_underflow = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = gmii_ep.GMIISink() + + sink_logic = sink.create_logic( + clk, + rst, + rxd=gmii_txd, + rx_dv=gmii_tx_en, + rx_er=gmii_tx_er, + clk_enable=clk_enable, + mii_select=mii_select, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + gmii_txd=gmii_txd, + gmii_tx_en=gmii_tx_en, + gmii_tx_er=gmii_tx_er, + + ptp_ts=ptp_ts, + m_axis_ptp_ts=m_axis_ptp_ts, + m_axis_ptp_ts_tag=m_axis_ptp_ts_tag, + m_axis_ptp_ts_valid=m_axis_ptp_ts_valid, + + clk_enable=clk_enable, + mii_select=mii_select, + + ifg_delay=ifg_delay, + + start_packet=start_packet, + error_underflow=error_underflow + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + clk_enable_rate = Signal(int(1)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + clk_enable.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + clk_enable.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate, mii in [(1, 0), (10, 0), (5, 1)]: + clk_enable_rate.next = rate + mii_select.next = mii + + yield delay(100) + + for payload_len in list(range(1,18))+list(range(64,82)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + source.send(axis_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + source.send(axis_frame1) + source.send(axis_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame1.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame1.eth_src_mac + assert eth_frame.eth_type == test_frame1.eth_type + assert eth_frame.payload.data.index(test_frame1.payload.data) == 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame2.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame2.eth_src_mac + assert eth_frame.eth_type == test_frame2.eth_type + assert eth_frame.payload.data.index(test_frame2.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + axis_frame1.user = 1 + + source.send(axis_frame1) + source.send(axis_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + assert rx_frame.error[-1] + + # bad packet + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame2.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame2.eth_src_mac + assert eth_frame.eth_type == test_frame2.eth_type + assert eth_frame.payload.data.index(test_frame2.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_axis_gmii_tx.v b/fpga/lib/eth/tb/test_axis_gmii_tx.v new file mode 100644 index 000000000..f24b03804 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_gmii_tx.v @@ -0,0 +1,133 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for axis_gmii_tx + */ +module test_axis_gmii_tx; + +// Parameters +parameter DATA_WIDTH = 8; +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter PTP_TS_ENABLE = 0; +parameter PTP_TS_WIDTH = 96; +parameter PTP_TAG_ENABLE = PTP_TS_ENABLE; +parameter PTP_TAG_WIDTH = 16; +parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg [PTP_TS_WIDTH-1:0] ptp_ts = 0; +reg clk_enable = 1; +reg mii_select = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] gmii_txd; +wire gmii_tx_en; +wire gmii_tx_er; +wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts; +wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag; +wire m_axis_ptp_ts_valid; +wire start_packet; +wire error_underflow; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser, + ptp_ts, + clk_enable, + mii_select, + ifg_delay + ); + $to_myhdl( + s_axis_tready, + gmii_txd, + gmii_tx_en, + gmii_tx_er, + m_axis_ptp_ts, + m_axis_ptp_ts_tag, + m_axis_ptp_ts_valid, + start_packet, + error_underflow + ); + + // dump file + $dumpfile("test_axis_gmii_tx.lxt"); + $dumpvars(0, test_axis_gmii_tx); +end + +axis_gmii_tx #( + .DATA_WIDTH(DATA_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .PTP_TAG_ENABLE(PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(PTP_TAG_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .ptp_ts(ptp_ts), + .m_axis_ptp_ts(m_axis_ptp_ts), + .m_axis_ptp_ts_tag(m_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(m_axis_ptp_ts_valid), + .clk_enable(clk_enable), + .mii_select(mii_select), + .ifg_delay(ifg_delay), + .start_packet(start_packet), + .error_underflow(error_underflow) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_axis_xgmii_rx_32.py b/fpga/lib/eth/tb/test_axis_xgmii_rx_32.py new file mode 100755 index 000000000..7ec9c7e49 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_xgmii_rx_32.py @@ -0,0 +1,407 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-2017 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep + +module = 'axis_xgmii_rx_32' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 32 + KEEP_WIDTH = (DATA_WIDTH/8) + CTRL_WIDTH = (DATA_WIDTH/8) + PTP_TS_ENABLE = 0 + PTP_TS_WIDTH = 96 + USER_WIDTH = (PTP_TS_WIDTH if PTP_TS_ENABLE else 0) + 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + xgmii_rxd = Signal(intbv(0x07070707)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0xf)[CTRL_WIDTH:]) + ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + + # Outputs + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + start_packet = Signal(bool(0)) + error_bad_frame = Signal(bool(0)) + error_bad_fcs = Signal(bool(0)) + + # sources and sinks + source = xgmii_ep.XGMIISource() + + source_logic = source.create_logic( + clk, + rst, + txd=xgmii_rxd, + txc=xgmii_rxc, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser, + + ptp_ts=ptp_ts, + + start_packet=start_packet, + error_bad_frame=error_bad_frame, + error_bad_fcs=error_bad_fcs + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_bad_frame_asserted = Signal(bool(0)) + error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_bad_frame): + error_bad_frame_asserted.next = 1 + if (error_bad_fcs): + error_bad_fcs_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(1,18))+list(range(64,82)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: truncated frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + axis_frame1.data = axis_frame1.data[:-1] + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert error_bad_fcs_asserted + + assert rx_frame.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: errored frame, length %d" % payload_len) + current_test.next = 4 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert not error_bad_fcs_asserted + + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 5: test stream, length %d" % payload_len) + current_test.next = 5 + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 6: test stream with zero IFG, length %d" % payload_len) + current_test.next = 6 + + source.ifg = 0 + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + source.ifg = 12 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_axis_xgmii_rx_32.v b/fpga/lib/eth/tb/test_axis_xgmii_rx_32.v new file mode 100644 index 000000000..837519434 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_xgmii_rx_32.v @@ -0,0 +1,111 @@ +/* + +Copyright (c) 2015-2017 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 1ns / 1ps + +/* + * Testbench for axis_xgmii_rx_32 + */ +module test_axis_xgmii_rx_32; + +// Parameters +parameter DATA_WIDTH = 32; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter PTP_TS_ENABLE = 0; +parameter PTP_TS_WIDTH = 96; +parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] xgmii_rxd = 32'h07070707; +reg [CTRL_WIDTH-1:0] xgmii_rxc = 4'hf; +reg [PTP_TS_WIDTH-1:0] ptp_ts = 0; + +// Outputs +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire start_packet; +wire error_bad_frame; +wire error_bad_fcs; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + xgmii_rxd, + xgmii_rxc, + ptp_ts + ); + $to_myhdl( + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser, + start_packet, + error_bad_frame, + error_bad_fcs + ); + + // dump file + $dumpfile("test_axis_xgmii_rx_32.lxt"); + $dumpvars(0, test_axis_xgmii_rx_32); +end + +axis_xgmii_rx_32 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + .ptp_ts(ptp_ts), + .start_packet(start_packet), + .error_bad_frame(error_bad_frame), + .error_bad_fcs(error_bad_fcs) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_axis_xgmii_rx_64.py b/fpga/lib/eth/tb/test_axis_xgmii_rx_64.py new file mode 100755 index 000000000..14d0a9318 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_xgmii_rx_64.py @@ -0,0 +1,482 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-2017 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep + +module = 'axis_xgmii_rx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + CTRL_WIDTH = (DATA_WIDTH/8) + PTP_PERIOD_NS = 0x6 + PTP_PERIOD_FNS = 0x6666 + PTP_TS_ENABLE = 0 + PTP_TS_WIDTH = 96 + USER_WIDTH = (PTP_TS_WIDTH if PTP_TS_ENABLE else 0) + 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + xgmii_rxd = Signal(intbv(0x0707070707070707)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0xff)[CTRL_WIDTH:]) + ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + + # Outputs + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + start_packet = Signal(intbv(0)[2:]) + error_bad_frame = Signal(bool(0)) + error_bad_fcs = Signal(bool(0)) + + # sources and sinks + source = xgmii_ep.XGMIISource() + + source_logic = source.create_logic( + clk, + rst, + txd=xgmii_rxd, + txc=xgmii_rxc, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser, + + ptp_ts=ptp_ts, + + start_packet=start_packet, + error_bad_frame=error_bad_frame, + error_bad_fcs=error_bad_fcs + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_bad_frame_asserted = Signal(bool(0)) + error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_bad_frame): + error_bad_frame_asserted.next = 1 + if (error_bad_fcs): + error_bad_fcs_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(1,18))+list(range(64,82)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: truncated frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + axis_frame1.data = axis_frame1.data[:-1] + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert error_bad_fcs_asserted + + assert rx_frame.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: errored frame, length %d" % payload_len) + current_test.next = 4 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert not error_bad_fcs_asserted + + assert rx_frame.last_cycle_user + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 5: test stream, length %d" % payload_len) + current_test.next = 5 + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 6: test stream with zero IFG, length %d" % payload_len) + current_test.next = 6 + + source.ifg = 0 + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + source.ifg = 12 + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 6: test stream with zero IFG and offset start, length %d" % payload_len) + current_test.next = 6 + + source.ifg = 0 + source.force_offset_start = True + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + source.ifg = 12 + source.force_offset_start = False + + yield delay(100) + + yield clk.posedge + print("test 7: Ensure 0xfb in FCS in lane 4 is not detected as start code in lane 0") + current_test.next = 7 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x806f + test_frame.payload = bytearray(range(60)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert not error_bad_frame_asserted + assert not error_bad_fcs_asserted + + assert not rx_frame.last_cycle_user + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_axis_xgmii_rx_64.v b/fpga/lib/eth/tb/test_axis_xgmii_rx_64.v new file mode 100644 index 000000000..f8d7e8b06 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_xgmii_rx_64.v @@ -0,0 +1,115 @@ +/* + +Copyright (c) 2015-2017 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 1ns / 1ps + +/* + * Testbench for axis_xgmii_rx_64 + */ +module test_axis_xgmii_rx_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter PTP_PERIOD_NS = 4'h6; +parameter PTP_PERIOD_FNS = 16'h6666; +parameter PTP_TS_ENABLE = 0; +parameter PTP_TS_WIDTH = 96; +parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] xgmii_rxd = 64'h0707070707070707; +reg [CTRL_WIDTH-1:0] xgmii_rxc = 8'hff; +reg [PTP_TS_WIDTH-1:0] ptp_ts = 0; + +// Outputs +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire [USER_WIDTH-1:0] m_axis_tuser; +wire [1:0] start_packet; +wire error_bad_frame; +wire error_bad_fcs; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + xgmii_rxd, + xgmii_rxc, + ptp_ts + ); + $to_myhdl( + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser, + start_packet, + error_bad_frame, + error_bad_fcs + ); + + // dump file + $dumpfile("test_axis_xgmii_rx_64.lxt"); + $dumpvars(0, test_axis_xgmii_rx_64); +end + +axis_xgmii_rx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + .ptp_ts(ptp_ts), + .start_packet(start_packet), + .error_bad_frame(error_bad_frame), + .error_bad_fcs(error_bad_fcs) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_axis_xgmii_tx_32.py b/fpga/lib/eth/tb/test_axis_xgmii_tx_32.py new file mode 100755 index 000000000..6f3f48edd --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_xgmii_tx_32.py @@ -0,0 +1,363 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-2017 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep + +module = 'axis_xgmii_tx_32' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 32 + KEEP_WIDTH = (DATA_WIDTH/8) + CTRL_WIDTH = (DATA_WIDTH/8) + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + PTP_TS_ENABLE = 0 + PTP_TS_WIDTH = 96 + PTP_TAG_ENABLE = PTP_TS_ENABLE + PTP_TAG_WIDTH = 16 + USER_WIDTH = (PTP_TAG_WIDTH if PTP_TAG_ENABLE else 0) + 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + s_axis_tready = Signal(bool(0)) + xgmii_txd = Signal(intbv(0x07070707)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0xf)[CTRL_WIDTH:]) + m_axis_ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + m_axis_ptp_ts_tag = Signal(intbv(0)[PTP_TAG_WIDTH:]) + m_axis_ptp_ts_valid = Signal(bool(0)) + start_packet = Signal(bool(0)) + error_underflow = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = xgmii_ep.XGMIISink() + + sink_logic = sink.create_logic( + clk, + rst, + rxd=xgmii_txd, + rxc=xgmii_txc, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + + ptp_ts=ptp_ts, + m_axis_ptp_ts=m_axis_ptp_ts, + m_axis_ptp_ts_tag=m_axis_ptp_ts_tag, + m_axis_ptp_ts_valid=m_axis_ptp_ts_valid, + + ifg_delay=ifg_delay, + + start_packet=start_packet, + error_underflow=error_underflow + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for payload_len in list(range(1,18))+list(range(40,58)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + source.send(axis_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + source.send(axis_frame1) + source.send(axis_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame1.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame1.eth_src_mac + assert eth_frame.eth_type == test_frame1.eth_type + assert eth_frame.payload.data.index(test_frame1.payload.data) == 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame2.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame2.eth_src_mac + assert eth_frame.eth_type == test_frame2.eth_type + assert eth_frame.payload.data.index(test_frame2.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + axis_frame1.last_cycle_user = 1 + + source.send(axis_frame1) + source.send(axis_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + assert rx_frame.error[-1] + + # bad packet + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame2.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame2.eth_src_mac + assert eth_frame.eth_type == test_frame2.eth_type + assert eth_frame.payload.data.index(test_frame2.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 4: test stream, length %d" % payload_len) + current_test.next = 4 + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + source.send(axis_frame) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_axis_xgmii_tx_32.v b/fpga/lib/eth/tb/test_axis_xgmii_tx_32.v new file mode 100644 index 000000000..9f2615378 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_xgmii_tx_32.v @@ -0,0 +1,133 @@ +/* + +Copyright (c) 2015-2017 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 1ns / 1ps + +/* + * Testbench for axis_xgmii_tx_32 + */ +module test_axis_xgmii_tx_32; + +// Parameters +parameter DATA_WIDTH = 32; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter PTP_TS_ENABLE = 0; +parameter PTP_TS_WIDTH = 96; +parameter PTP_TAG_ENABLE = PTP_TS_ENABLE; +parameter PTP_TAG_WIDTH = 16; +parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg [PTP_TS_WIDTH-1:0] ptp_ts = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] xgmii_txd; +wire [CTRL_WIDTH-1:0] xgmii_txc; +wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts; +wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag; +wire m_axis_ptp_ts_valid; +wire start_packet; +wire error_underflow; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser, + ptp_ts, + ifg_delay + ); + $to_myhdl( + s_axis_tready, + xgmii_txd, + xgmii_txc, + m_axis_ptp_ts, + m_axis_ptp_ts_tag, + m_axis_ptp_ts_valid, + start_packet, + error_underflow + ); + + // dump file + $dumpfile("test_axis_xgmii_tx_32.lxt"); + $dumpvars(0, test_axis_xgmii_tx_32); +end + +axis_xgmii_tx_32 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .PTP_TAG_ENABLE(PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(PTP_TAG_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .ptp_ts(ptp_ts), + .m_axis_ptp_ts(m_axis_ptp_ts), + .m_axis_ptp_ts_tag(m_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(m_axis_ptp_ts_valid), + .ifg_delay(ifg_delay), + .start_packet(start_packet), + .error_underflow(error_underflow) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_axis_xgmii_tx_64.py b/fpga/lib/eth/tb/test_axis_xgmii_tx_64.py new file mode 100755 index 000000000..aa547938c --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_xgmii_tx_64.py @@ -0,0 +1,365 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-2017 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep + +module = 'axis_xgmii_tx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + CTRL_WIDTH = (DATA_WIDTH/8) + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + PTP_PERIOD_NS = 0x6 + PTP_PERIOD_FNS = 0x6666 + PTP_TS_ENABLE = 0 + PTP_TS_WIDTH = 96 + PTP_TAG_ENABLE = PTP_TS_ENABLE + PTP_TAG_WIDTH = 16 + USER_WIDTH = (PTP_TAG_WIDTH if PTP_TAG_ENABLE else 0) + 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + s_axis_tready = Signal(bool(0)) + xgmii_txd = Signal(intbv(0x0707070707070707)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0xff)[CTRL_WIDTH:]) + m_axis_ptp_ts = Signal(intbv(0)[PTP_TS_WIDTH:]) + m_axis_ptp_ts_tag = Signal(intbv(0)[PTP_TAG_WIDTH:]) + m_axis_ptp_ts_valid = Signal(bool(0)) + start_packet = Signal(intbv(0)[2:]) + error_underflow = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = xgmii_ep.XGMIISink() + + sink_logic = sink.create_logic( + clk, + rst, + rxd=xgmii_txd, + rxc=xgmii_txc, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + + ptp_ts=ptp_ts, + m_axis_ptp_ts=m_axis_ptp_ts, + m_axis_ptp_ts_tag=m_axis_ptp_ts_tag, + m_axis_ptp_ts_valid=m_axis_ptp_ts_valid, + + ifg_delay=ifg_delay, + + start_packet=start_packet, + error_underflow=error_underflow + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for payload_len in list(range(1,18))+list(range(40,58)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + source.send(axis_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + source.send(axis_frame1) + source.send(axis_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame1.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame1.eth_src_mac + assert eth_frame.eth_type == test_frame1.eth_type + assert eth_frame.payload.data.index(test_frame1.payload.data) == 0 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame2.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame2.eth_src_mac + assert eth_frame.eth_type == test_frame2.eth_type + assert eth_frame.payload.data.index(test_frame2.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + axis_frame1.last_cycle_user = 1 + + source.send(axis_frame1) + source.send(axis_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + assert rx_frame.error[-1] + + # bad packet + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame2.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame2.eth_src_mac + assert eth_frame.eth_type == test_frame2.eth_type + assert eth_frame.payload.data.index(test_frame2.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 4: test stream, length %d" % payload_len) + current_test.next = 4 + + for i in range(10): + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + source.send(axis_frame) + + for i in range(10): + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + assert len(eth_frame.payload.data) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_axis_xgmii_tx_64.v b/fpga/lib/eth/tb/test_axis_xgmii_tx_64.v new file mode 100644 index 000000000..e840edd11 --- /dev/null +++ b/fpga/lib/eth/tb/test_axis_xgmii_tx_64.v @@ -0,0 +1,137 @@ +/* + +Copyright (c) 2015-2017 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 1ns / 1ps + +/* + * Testbench for axis_xgmii_tx_64 + */ +module test_axis_xgmii_tx_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter PTP_PERIOD_NS = 4'h6; +parameter PTP_PERIOD_FNS = 16'h6666; +parameter PTP_TS_ENABLE = 0; +parameter PTP_TS_WIDTH = 96; +parameter PTP_TAG_ENABLE = PTP_TS_ENABLE; +parameter PTP_TAG_WIDTH = 16; +parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg [USER_WIDTH-1:0] s_axis_tuser = 0; +reg [PTP_TS_WIDTH-1:0] ptp_ts = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] xgmii_txd; +wire [CTRL_WIDTH-1:0] xgmii_txc; +wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts; +wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag; +wire m_axis_ptp_ts_valid; +wire [1:0] start_packet; +wire error_underflow; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser, + ptp_ts, + ifg_delay + ); + $to_myhdl( + s_axis_tready, + xgmii_txd, + xgmii_txc, + m_axis_ptp_ts, + m_axis_ptp_ts_tag, + m_axis_ptp_ts_valid, + start_packet, + error_underflow + ); + + // dump file + $dumpfile("test_axis_xgmii_tx_64.lxt"); + $dumpvars(0, test_axis_xgmii_tx_64); +end + +axis_xgmii_tx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .PTP_TAG_ENABLE(PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(PTP_TAG_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .ptp_ts(ptp_ts), + .m_axis_ptp_ts(m_axis_ptp_ts), + .m_axis_ptp_ts_tag(m_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(m_axis_ptp_ts_valid), + .ifg_delay(ifg_delay), + .start_packet(start_packet), + .error_underflow(error_underflow) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_arb_mux_4.py b/fpga/lib/eth/tb/test_eth_arb_mux_4.py new file mode 100755 index 000000000..f96e9b710 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_arb_mux_4.py @@ -0,0 +1,473 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep + +module = 'eth_arb_mux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/axis/rtl/arbiter.v") +srcs.append("../lib/axis/rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + ARB_TYPE = "PRIORITY" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_eth_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_eth_hdr_valid = ConcatSignal(*reversed(s_eth_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_eth_payload_axis_tdata = ConcatSignal(*reversed(s_eth_payload_axis_tdata_list)) + s_eth_payload_axis_tkeep = ConcatSignal(*reversed(s_eth_payload_axis_tkeep_list)) + s_eth_payload_axis_tvalid = ConcatSignal(*reversed(s_eth_payload_axis_tvalid_list)) + s_eth_payload_axis_tlast = ConcatSignal(*reversed(s_eth_payload_axis_tlast_list)) + s_eth_payload_axis_tid = ConcatSignal(*reversed(s_eth_payload_axis_tid_list)) + s_eth_payload_axis_tdest = ConcatSignal(*reversed(s_eth_payload_axis_tdest_list)) + s_eth_payload_axis_tuser = ConcatSignal(*reversed(s_eth_payload_axis_tuser_list)) + + m_eth_hdr_ready = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_eth_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_eth_hdr_ready_list = [s_eth_hdr_ready(i) for i in range(S_COUNT)] + s_eth_payload_axis_tready_list = [s_eth_payload_axis_tready(i) for i in range(S_COUNT)] + + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_eth_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_eth_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_eth_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = eth_ep.EthFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready_list[k], + eth_hdr_valid=s_eth_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + eth_payload_tdata=s_eth_payload_axis_tdata_list[k], + eth_payload_tkeep=s_eth_payload_axis_tkeep_list[k], + eth_payload_tvalid=s_eth_payload_axis_tvalid_list[k], + eth_payload_tready=s_eth_payload_axis_tready_list[k], + eth_payload_tlast=s_eth_payload_axis_tlast_list[k], + eth_payload_tuser=s_eth_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = eth_ep.EthFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tid=s_eth_payload_axis_tid, + s_eth_payload_axis_tdest=s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tid=m_eth_payload_axis_tid, + m_eth_payload_axis_tdest=m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: port 0") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A0052535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: port 1") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A0152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0052535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0052535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: back-to-back packets, different ports, arbitration test") + current_test.next = 7 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + yield clk.posedge + + yield delay(800) + yield clk.posedge + source_list[1].send(test_frame1) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_eth_arb_mux_4.v b/fpga/lib/eth/tb/test_eth_arb_mux_4.v new file mode 100644 index 000000000..6c98fb762 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_arb_mux_4.v @@ -0,0 +1,172 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for eth_arb_mux + */ +module test_eth_arb_mux_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter ARB_TYPE = "PRIORITY"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_eth_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_eth_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_eth_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_eth_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_eth_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_eth_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_eth_payload_axis_tuser = 0; + +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_eth_hdr_ready; +wire [S_COUNT-1:0] s_eth_payload_axis_tready; + +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_eth_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_eth_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tid, + s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tid, + m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser + ); + + // dump file + $dumpfile("test_eth_arb_mux_4.lxt"); + $dumpvars(0, test_eth_arb_mux_4); +end + +eth_arb_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tid(s_eth_payload_axis_tid), + .s_eth_payload_axis_tdest(s_eth_payload_axis_tdest), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tid(m_eth_payload_axis_tid), + .m_eth_payload_axis_tdest(m_eth_payload_axis_tdest), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_arb_mux_64_4.py b/fpga/lib/eth/tb/test_eth_arb_mux_64_4.py new file mode 100755 index 000000000..75d321252 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_arb_mux_64_4.py @@ -0,0 +1,473 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep + +module = 'eth_arb_mux' +testbench = 'test_%s_64_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/axis/rtl/arbiter.v") +srcs.append("../lib/axis/rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + ARB_TYPE = "PRIORITY" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_eth_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_eth_hdr_valid = ConcatSignal(*reversed(s_eth_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_eth_payload_axis_tdata = ConcatSignal(*reversed(s_eth_payload_axis_tdata_list)) + s_eth_payload_axis_tkeep = ConcatSignal(*reversed(s_eth_payload_axis_tkeep_list)) + s_eth_payload_axis_tvalid = ConcatSignal(*reversed(s_eth_payload_axis_tvalid_list)) + s_eth_payload_axis_tlast = ConcatSignal(*reversed(s_eth_payload_axis_tlast_list)) + s_eth_payload_axis_tid = ConcatSignal(*reversed(s_eth_payload_axis_tid_list)) + s_eth_payload_axis_tdest = ConcatSignal(*reversed(s_eth_payload_axis_tdest_list)) + s_eth_payload_axis_tuser = ConcatSignal(*reversed(s_eth_payload_axis_tuser_list)) + + m_eth_hdr_ready = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_eth_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_eth_hdr_ready_list = [s_eth_hdr_ready(i) for i in range(S_COUNT)] + s_eth_payload_axis_tready_list = [s_eth_payload_axis_tready(i) for i in range(S_COUNT)] + + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_eth_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_eth_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_eth_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = eth_ep.EthFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready_list[k], + eth_hdr_valid=s_eth_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + eth_payload_tdata=s_eth_payload_axis_tdata_list[k], + eth_payload_tkeep=s_eth_payload_axis_tkeep_list[k], + eth_payload_tvalid=s_eth_payload_axis_tvalid_list[k], + eth_payload_tready=s_eth_payload_axis_tready_list[k], + eth_payload_tlast=s_eth_payload_axis_tlast_list[k], + eth_payload_tuser=s_eth_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = eth_ep.EthFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tid=s_eth_payload_axis_tid, + s_eth_payload_axis_tdest=s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tid=m_eth_payload_axis_tid, + m_eth_payload_axis_tdest=m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: port 0") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A0052535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: port 1") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A0152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0052535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0052535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: back-to-back packets, different ports, arbitration test") + current_test.next = 7 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + yield clk.posedge + + yield delay(120) + yield clk.posedge + source_list[1].send(test_frame1) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_eth_arb_mux_64_4.v b/fpga/lib/eth/tb/test_eth_arb_mux_64_4.v new file mode 100644 index 000000000..ed74367ab --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_arb_mux_64_4.v @@ -0,0 +1,172 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for eth_arb_mux + */ +module test_eth_arb_mux_64_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter ARB_TYPE = "PRIORITY"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_eth_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_eth_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_eth_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_eth_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_eth_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_eth_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_eth_payload_axis_tuser = 0; + +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_eth_hdr_ready; +wire [S_COUNT-1:0] s_eth_payload_axis_tready; + +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_eth_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_eth_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tid, + s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tid, + m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser + ); + + // dump file + $dumpfile("test_eth_arb_mux_64_4.lxt"); + $dumpvars(0, test_eth_arb_mux_64_4); +end + +eth_arb_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tid(s_eth_payload_axis_tid), + .s_eth_payload_axis_tdest(s_eth_payload_axis_tdest), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tid(m_eth_payload_axis_tid), + .m_eth_payload_axis_tdest(m_eth_payload_axis_tdest), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_axis_rx.py b/fpga/lib/eth/tb/test_eth_axis_rx.py new file mode 100755 index 000000000..bb6ad1e32 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_axis_rx.py @@ -0,0 +1,350 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep + +module = 'eth_axis_rx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[8:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_header_early_termination = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = eth_ep.EthFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + busy=busy, + error_header_early_termination=error_header_early_termination + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_header_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_header_early_termination): + error_header_early_termination_asserted.next = 1 + + def wait_normal(): + while s_axis_tvalid or m_eth_payload_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_eth_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_eth_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + + axis_frame = test_frame.build_axis() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(axis_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + axis_frame1.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + for length in range(1,15): + yield clk.posedge + print("test 4: truncated packet, length %d" % length) + current_test.next = 4 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(16)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(16)) + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + axis_frame1.data = axis_frame1.data[:length] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_header_early_termination_asserted.next = 0 + + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert error_header_early_termination_asserted + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_eth_axis_rx.v b/fpga/lib/eth/tb/test_eth_axis_rx.v new file mode 100644 index 000000000..bacd0ddf5 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_axis_rx.v @@ -0,0 +1,117 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for eth_axis_rx + */ +module test_eth_axis_rx; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] s_axis_tdata = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg s_axis_tuser = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [7:0] m_eth_payload_axis_tdata; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire busy; +wire error_header_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + busy, + error_header_early_termination + ); + + // dump file + $dumpfile("test_eth_axis_rx.lxt"); + $dumpvars(0, test_eth_axis_rx); +end + +eth_axis_rx +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy(busy), + .error_header_early_termination(error_header_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_axis_rx_64.py b/fpga/lib/eth/tb/test_eth_axis_rx_64.py new file mode 100755 index 000000000..c7733a363 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_axis_rx_64.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep + +module = 'eth_axis_rx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[64:]) + s_axis_tkeep = Signal(intbv(0)[8:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + + # Outputs + s_axis_tready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + m_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_header_early_termination = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = eth_ep.EthFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + busy=busy, + error_header_early_termination=error_header_early_termination + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_header_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_header_early_termination): + error_header_early_termination_asserted.next = 1 + + def wait_normal(): + while s_axis_tvalid or m_eth_payload_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid or m_eth_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_axis_tvalid or m_eth_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + + axis_frame = test_frame.build_axis() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(axis_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + axis_frame1.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + for length in range(1,15): + yield clk.posedge + print("test 4: truncated packet, length %d" % length) + current_test.next = 4 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(16)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(16)) + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + axis_frame1.data = axis_frame1.data[:length] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_header_early_termination_asserted.next = 0 + + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert error_header_early_termination_asserted + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_eth_axis_rx_64.v b/fpga/lib/eth/tb/test_eth_axis_rx_64.v new file mode 100644 index 000000000..985e2e561 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_axis_rx_64.v @@ -0,0 +1,123 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for eth_axis_rx_64 + */ +module test_eth_axis_rx_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [63:0] s_axis_tdata = 0; +reg [7:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg s_axis_tuser = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +// Outputs +wire s_axis_tready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [63:0] m_eth_payload_axis_tdata; +wire [7:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire busy; +wire error_header_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + busy, + error_header_early_termination + ); + + // dump file + $dumpfile("test_eth_axis_rx_64.lxt"); + $dumpvars(0, test_eth_axis_rx_64); +end + +eth_axis_rx_64 +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy(busy), + .error_header_early_termination(error_header_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_axis_tx.py b/fpga/lib/eth/tb/test_eth_axis_tx.py new file mode 100755 index 000000000..229712fe7 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_axis_tx.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep + +module = 'eth_axis_tx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:0]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + m_axis_tready = Signal(bool(0)) + + # Outputs + m_axis_tdata = Signal(intbv(0)[8:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(bool(0)) + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + busy = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = eth_ep.EthFrameSource() + + source_logic = source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser, + + busy=busy + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_eth_payload_axis_tvalid or m_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + + def wait_pause_source(): + while s_eth_payload_axis_tvalid or m_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_eth_payload_axis_tvalid or m_axis_tvalid or s_eth_hdr_valid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = eth_ep.EthFrame() + check_frame.parse_axis(rx_frame) + + assert check_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = eth_ep.EthFrame() + check_frame.parse_axis(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = eth_ep.EthFrame() + check_frame.parse_axis(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + + test_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = eth_ep.EthFrame() + check_frame.parse_axis(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = eth_ep.EthFrame() + check_frame.parse_axis(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_eth_axis_tx.v b/fpga/lib/eth/tb/test_eth_axis_tx.v new file mode 100644 index 000000000..f72a57235 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_axis_tx.v @@ -0,0 +1,114 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for eth_axis_tx + */ +module test_eth_axis_tx; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [7:0] s_eth_payload_axis_tdata = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_eth_payload_axis_tready; +wire s_eth_hdr_ready; +wire [7:0] m_axis_tdata; +wire m_axis_tvalid; +wire m_axis_tlast; +wire m_axis_tuser; +wire busy; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_axis_tdata, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser, + busy + ); + + // dump file + $dumpfile("test_eth_axis_tx.lxt"); + $dumpvars(0, test_eth_axis_tx); +end + +eth_axis_tx +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + // Status signals + .busy(busy) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_axis_tx_64.py b/fpga/lib/eth/tb/test_eth_axis_tx_64.py new file mode 100755 index 000000000..f529c49f7 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_axis_tx_64.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep + +module = 'eth_axis_tx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:0]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + s_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + m_axis_tready = Signal(bool(0)) + + # Outputs + m_axis_tdata = Signal(intbv(0)[64:]) + m_axis_tkeep = Signal(intbv(0)[8:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(bool(0)) + s_eth_hdr_ready = Signal(bool(1)) + s_eth_payload_axis_tready = Signal(bool(0)) + busy = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = eth_ep.EthFrameSource() + + source_logic = source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tkeep=s_eth_payload_axis_tkeep, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tready=m_axis_tready, + tlast=m_axis_tlast, + tuser=m_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser, + + busy=busy + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_eth_payload_axis_tvalid or m_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + + def wait_pause_source(): + while s_eth_payload_axis_tvalid or m_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_eth_payload_axis_tvalid or m_axis_tvalid or s_eth_hdr_valid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(payload_len)) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = eth_ep.EthFrame() + check_frame.parse_axis(rx_frame) + + assert check_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = eth_ep.EthFrame() + check_frame.parse_axis(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = eth_ep.EthFrame() + check_frame.parse_axis(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + + test_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = eth_ep.EthFrame() + check_frame.parse_axis(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = eth_ep.EthFrame() + check_frame.parse_axis(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_eth_axis_tx_64.v b/fpga/lib/eth/tb/test_eth_axis_tx_64.v new file mode 100644 index 000000000..3f16b14e6 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_axis_tx_64.v @@ -0,0 +1,120 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for eth_axis_tx_64 + */ +module test_eth_axis_tx_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [63:0] s_eth_payload_axis_tdata = 0; +reg [7:0] s_eth_payload_axis_tkeep = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg m_axis_tready = 0; + +// Outputs +wire s_eth_payload_axis_tready; +wire s_eth_hdr_ready; +wire [63:0] m_axis_tdata; +wire [7:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire m_axis_tuser; +wire busy; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser, + busy + ); + + // dump file + $dumpfile("test_eth_axis_tx_64.lxt"); + $dumpvars(0, test_eth_axis_tx_64); +end + +eth_axis_tx_64 +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + // Status signals + .busy(busy) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_demux_4.py b/fpga/lib/eth/tb/test_eth_demux_4.py new file mode 100755 index 000000000..5b9e488ed --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_demux_4.py @@ -0,0 +1,486 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep + +module = 'eth_demux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + M_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_eth_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_eth_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_eth_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + m_eth_hdr_ready_list = [Signal(bool(0)) for i in range(M_COUNT)] + m_eth_payload_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_eth_hdr_ready = ConcatSignal(*reversed(m_eth_hdr_ready_list)) + m_eth_payload_axis_tready = ConcatSignal(*reversed(m_eth_payload_axis_tready_list)) + + enable = Signal(bool(0)) + drop = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + + m_eth_hdr_valid = Signal(intbv(0)[M_COUNT:]) + m_eth_dest_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_src_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_type = Signal(intbv(0)[M_COUNT*16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_eth_payload_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_eth_payload_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_eth_payload_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_eth_payload_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_eth_payload_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_eth_payload_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_eth_hdr_valid_list = [m_eth_hdr_valid(i) for i in range(M_COUNT)] + m_eth_dest_mac_list = [m_eth_dest_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_src_mac_list = [m_eth_src_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_type_list = [m_eth_type((i+1)*16, i*16) for i in range(M_COUNT)] + m_eth_payload_axis_tdata_list = [m_eth_payload_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_eth_payload_axis_tkeep_list = [m_eth_payload_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_eth_payload_axis_tvalid_list = [m_eth_payload_axis_tvalid(i) for i in range(M_COUNT)] + m_eth_payload_axis_tlast_list = [m_eth_payload_axis_tlast(i) for i in range(M_COUNT)] + m_eth_payload_axis_tid_list = [m_eth_payload_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_eth_payload_axis_tdest_list = [m_eth_payload_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_eth_payload_axis_tuser_list = [m_eth_payload_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + source = eth_ep.EthFrameSource() + + source_logic = source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tkeep=s_eth_payload_axis_tkeep, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + for k in range(M_COUNT): + s = eth_ep.EthFrameSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready_list[k], + eth_hdr_valid=m_eth_hdr_valid_list[k], + eth_dest_mac=m_eth_dest_mac_list[k], + eth_src_mac=m_eth_src_mac_list[k], + eth_type=m_eth_type_list[k], + eth_payload_tdata=m_eth_payload_axis_tdata_list[k], + eth_payload_tkeep=m_eth_payload_axis_tkeep_list[k], + eth_payload_tvalid=m_eth_payload_axis_tvalid_list[k], + eth_payload_tready=m_eth_payload_axis_tready_list[k], + eth_payload_tlast=m_eth_payload_axis_tlast_list[k], + eth_payload_tuser=m_eth_payload_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tid=s_eth_payload_axis_tid, + s_eth_payload_axis_tdest=s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tid=m_eth_payload_axis_tid, + m_eth_payload_axis_tdest=m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + enable=enable, + drop=drop, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source.send(test_frame) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source.send(test_frame) + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame1 + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_eth_payload_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_eth_payload_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + select.next = 2 + + source_pause.next = False + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_eth_payload_axis_tvalid or s_eth_hdr_valid: + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: enable") + current_test.next = 7 + + enable.next = False + select.next = 0 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + enable.next = True + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 8: drop") + current_test.next = 8 + + drop.next = True + select.next = 0 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + drop.next = False + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_eth_demux_4.v b/fpga/lib/eth/tb/test_eth_demux_4.v new file mode 100644 index 000000000..82d901a6a --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_demux_4.v @@ -0,0 +1,179 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for eth_demux + */ +module test_eth_demux_4; + +// Parameters +parameter M_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [DATA_WIDTH-1:0] s_eth_payload_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_eth_payload_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_eth_payload_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_eth_payload_axis_tuser = 0; + +reg [M_COUNT-1:0] m_eth_hdr_ready = 0; +reg [M_COUNT-1:0] m_eth_payload_axis_tready = 0; + +reg enable = 0; +reg drop = 0; +reg [1:0] select = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; + +wire [M_COUNT-1:0] m_eth_hdr_valid; +wire [M_COUNT*48-1:0] m_eth_dest_mac; +wire [M_COUNT*48-1:0] m_eth_src_mac; +wire [M_COUNT*16-1:0] m_eth_type; +wire [M_COUNT*DATA_WIDTH-1:0] m_eth_payload_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep; +wire [M_COUNT-1:0] m_eth_payload_axis_tvalid; +wire [M_COUNT-1:0] m_eth_payload_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_eth_payload_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_eth_payload_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_eth_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tid, + s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + enable, + drop, + select + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tid, + m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser + ); + + // dump file + $dumpfile("test_eth_demux_4.lxt"); + $dumpvars(0, test_eth_demux_4); +end + +eth_demux #( + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tid(s_eth_payload_axis_tid), + .s_eth_payload_axis_tdest(s_eth_payload_axis_tdest), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame outputs + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tid(m_eth_payload_axis_tid), + .m_eth_payload_axis_tdest(m_eth_payload_axis_tdest), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Control + .enable(enable), + .drop(drop), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_demux_64_4.py b/fpga/lib/eth/tb/test_eth_demux_64_4.py new file mode 100755 index 000000000..6b5afe2a1 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_demux_64_4.py @@ -0,0 +1,484 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep + +module = 'eth_demux' +testbench = 'test_%s_64_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + M_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_eth_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_eth_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_eth_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + m_eth_hdr_ready_list = [Signal(bool(0)) for i in range(M_COUNT)] + m_eth_payload_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_eth_hdr_ready = ConcatSignal(*reversed(m_eth_hdr_ready_list)) + m_eth_payload_axis_tready = ConcatSignal(*reversed(m_eth_payload_axis_tready_list)) + + enable = Signal(bool(0)) + drop = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + + m_eth_hdr_valid = Signal(intbv(0)[M_COUNT:]) + m_eth_dest_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_src_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_type = Signal(intbv(0)[M_COUNT*16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_eth_payload_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_eth_payload_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_eth_payload_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_eth_payload_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_eth_payload_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_eth_payload_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_eth_hdr_valid_list = [m_eth_hdr_valid(i) for i in range(M_COUNT)] + m_eth_dest_mac_list = [m_eth_dest_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_src_mac_list = [m_eth_src_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_type_list = [m_eth_type((i+1)*16, i*16) for i in range(M_COUNT)] + m_eth_payload_axis_tdata_list = [m_eth_payload_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_eth_payload_axis_tkeep_list = [m_eth_payload_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_eth_payload_axis_tvalid_list = [m_eth_payload_axis_tvalid(i) for i in range(M_COUNT)] + m_eth_payload_axis_tlast_list = [m_eth_payload_axis_tlast(i) for i in range(M_COUNT)] + m_eth_payload_axis_tid_list = [m_eth_payload_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_eth_payload_axis_tdest_list = [m_eth_payload_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_eth_payload_axis_tuser_list = [m_eth_payload_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + source = eth_ep.EthFrameSource() + + source_logic = source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tkeep=s_eth_payload_axis_tkeep, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + for k in range(M_COUNT): + s = eth_ep.EthFrameSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready_list[k], + eth_hdr_valid=m_eth_hdr_valid_list[k], + eth_dest_mac=m_eth_dest_mac_list[k], + eth_src_mac=m_eth_src_mac_list[k], + eth_type=m_eth_type_list[k], + eth_payload_tdata=m_eth_payload_axis_tdata_list[k], + eth_payload_tkeep=m_eth_payload_axis_tkeep_list[k], + eth_payload_tvalid=m_eth_payload_axis_tvalid_list[k], + eth_payload_tready=m_eth_payload_axis_tready_list[k], + eth_payload_tlast=m_eth_payload_axis_tlast_list[k], + eth_payload_tuser=m_eth_payload_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tid=s_eth_payload_axis_tid, + s_eth_payload_axis_tdest=s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tid=m_eth_payload_axis_tid, + m_eth_payload_axis_tdest=m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + enable=enable, + drop=drop, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source.send(test_frame) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source.send(test_frame) + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source.send(test_frame1) + source.send(test_frame2) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame1 + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_eth_payload_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_eth_payload_axis_tvalid or s_eth_hdr_valid: + source_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_eth_payload_axis_tvalid or s_eth_hdr_valid: + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: enable") + current_test.next = 7 + + enable.next = False + select.next = 0 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + enable.next = True + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 8: drop") + current_test.next = 8 + + drop.next = True + select.next = 0 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + drop.next = False + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_eth_demux_64_4.v b/fpga/lib/eth/tb/test_eth_demux_64_4.v new file mode 100644 index 000000000..b7c998feb --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_demux_64_4.v @@ -0,0 +1,179 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for eth_demux + */ +module test_eth_demux_64_4; + +// Parameters +parameter M_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [DATA_WIDTH-1:0] s_eth_payload_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_eth_payload_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_eth_payload_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_eth_payload_axis_tuser = 0; + +reg [M_COUNT-1:0] m_eth_hdr_ready = 0; +reg [M_COUNT-1:0] m_eth_payload_axis_tready = 0; + +reg enable = 0; +reg drop = 0; +reg [1:0] select = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; + +wire [M_COUNT-1:0] m_eth_hdr_valid; +wire [M_COUNT*48-1:0] m_eth_dest_mac; +wire [M_COUNT*48-1:0] m_eth_src_mac; +wire [M_COUNT*16-1:0] m_eth_type; +wire [M_COUNT*DATA_WIDTH-1:0] m_eth_payload_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep; +wire [M_COUNT-1:0] m_eth_payload_axis_tvalid; +wire [M_COUNT-1:0] m_eth_payload_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_eth_payload_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_eth_payload_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_eth_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tid, + s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + enable, + drop, + select + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tid, + m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser + ); + + // dump file + $dumpfile("test_eth_demux_64_4.lxt"); + $dumpvars(0, test_eth_demux_64_4); +end + +eth_demux #( + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tid(s_eth_payload_axis_tid), + .s_eth_payload_axis_tdest(s_eth_payload_axis_tdest), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame outputs + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tid(m_eth_payload_axis_tid), + .m_eth_payload_axis_tdest(m_eth_payload_axis_tdest), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Control + .enable(enable), + .drop(drop), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_10g_32.py b/fpga/lib/eth/tb/test_eth_mac_10g_32.py new file mode 100755 index 000000000..f63897201 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_10g_32.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep + +module = 'eth_mac_10g' +testbench = 'test_%s_32' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_xgmii_rx_32.v") +srcs.append("../rtl/axis_xgmii_tx_32.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 32 + KEEP_WIDTH = (DATA_WIDTH/8) + CTRL_WIDTH = (DATA_WIDTH/8) + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + TX_PTP_TS_ENABLE = 0 + TX_PTP_TS_WIDTH = 96 + TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE + TX_PTP_TAG_WIDTH = 16 + RX_PTP_TS_ENABLE = 0 + RX_PTP_TS_WIDTH = 96 + TX_USER_WIDTH = (TX_PTP_TAG_WIDTH if TX_PTP_TAG_ENABLE else 0) + 1 + RX_USER_WIDTH = (RX_PTP_TS_WIDTH if RX_PTP_TS_ENABLE else 0) + 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(intbv(0)[TX_USER_WIDTH:]) + xgmii_rxd = Signal(intbv(0x0707070707070707)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0xff)[CTRL_WIDTH:]) + tx_ptp_ts = Signal(intbv(0)[TX_PTP_TS_WIDTH:]) + rx_ptp_ts = Signal(intbv(0)[RX_PTP_TS_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + rx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(intbv(0)[RX_USER_WIDTH:]) + xgmii_txd = Signal(intbv(0x0707070707070707)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0xff)[CTRL_WIDTH:]) + tx_axis_ptp_ts = Signal(intbv(0)[TX_PTP_TS_WIDTH:]) + tx_axis_ptp_ts_tag = Signal(intbv(0)[TX_PTP_TAG_WIDTH:]) + tx_axis_ptp_ts_valid = Signal(bool(0)) + tx_start_packet = Signal(intbv(0)[2:]) + tx_error_underflow = Signal(bool(0)) + rx_start_packet = Signal(intbv(0)[2:]) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + xgmii_source = xgmii_ep.XGMIISource() + + xgmii_source_logic = xgmii_source.create_logic( + clk, + rst, + txd=xgmii_rxd, + txc=xgmii_rxc, + name='xgmii_source' + ) + + xgmii_sink = xgmii_ep.XGMIISink() + + xgmii_sink_logic = xgmii_sink.create_logic( + clk, + rst, + rxd=xgmii_txd, + rxc=xgmii_txc, + name='xgmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + clk, + rst, + tdata=tx_axis_tdata, + tkeep=tx_axis_tkeep, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + clk, + rst, + tdata=rx_axis_tdata, + tkeep=rx_axis_tkeep, + tvalid=rx_axis_tvalid, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + + tx_axis_tdata=tx_axis_tdata, + tx_axis_tkeep=tx_axis_tkeep, + 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_tkeep=rx_axis_tkeep, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + + tx_ptp_ts=tx_ptp_ts, + rx_ptp_ts=rx_ptp_ts, + tx_axis_ptp_ts=tx_axis_ptp_ts, + tx_axis_ptp_ts_tag=tx_axis_ptp_ts_tag, + tx_axis_ptp_ts_valid=tx_axis_ptp_ts_valid, + + tx_start_packet=tx_start_packet, + tx_error_underflow=tx_error_underflow, + rx_start_packet=rx_start_packet, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + tx_clk.next = not tx_clk + rx_clk.next = not rx_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + xgmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield xgmii_sink.wait() + rx_frame = xgmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_10g_32.v b/fpga/lib/eth/tb/test_eth_mac_10g_32.v new file mode 100644 index 000000000..dd93fafc6 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_10g_32.v @@ -0,0 +1,182 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for eth_mac_10g + */ +module test_eth_mac_10g_32; + +// Parameters +parameter DATA_WIDTH = 32; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter TX_PTP_TS_ENABLE = 0; +parameter TX_PTP_TS_WIDTH = 96; +parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE; +parameter TX_PTP_TAG_WIDTH = 16; +parameter RX_PTP_TS_ENABLE = 0; +parameter RX_PTP_TS_WIDTH = 96; +parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1; +parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg [DATA_WIDTH-1:0] tx_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] tx_axis_tkeep = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg [TX_USER_WIDTH-1:0] tx_axis_tuser = 0; +reg [DATA_WIDTH-1:0] xgmii_rxd = 0; +reg [CTRL_WIDTH-1:0] xgmii_rxc = 0; +reg [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts = 0; +reg [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [DATA_WIDTH-1:0] rx_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire [RX_USER_WIDTH-1:0] rx_axis_tuser; +wire [DATA_WIDTH-1:0] xgmii_txd; +wire [CTRL_WIDTH-1:0] xgmii_txc; +wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts; +wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag; +wire tx_axis_ptp_ts_valid; +wire [1:0] tx_start_packet; +wire tx_error_underflow; +wire [1:0] rx_start_packet; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + tx_axis_tdata, + tx_axis_tkeep, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + xgmii_rxd, + xgmii_rxc, + tx_ptp_ts, + rx_ptp_ts, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tkeep, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + xgmii_txd, + xgmii_txc, + tx_axis_ptp_ts, + tx_axis_ptp_ts_tag, + tx_axis_ptp_ts_valid, + tx_start_packet, + tx_error_underflow, + rx_start_packet, + rx_error_bad_frame, + rx_error_bad_fcs + ); + + // dump file + $dumpfile("test_eth_mac_10g_32.lxt"); + $dumpvars(0, test_eth_mac_10g_32); +end + +eth_mac_10g #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .TX_PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .TX_PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .TX_PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .TX_PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .RX_PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .RX_PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .TX_USER_WIDTH(TX_USER_WIDTH), + .RX_USER_WIDTH(RX_USER_WIDTH) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .tx_ptp_ts(tx_ptp_ts), + .rx_ptp_ts(rx_ptp_ts), + .tx_axis_ptp_ts(tx_axis_ptp_ts), + .tx_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .tx_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .tx_start_packet(tx_start_packet), + .tx_error_underflow(tx_error_underflow), + .rx_start_packet(rx_start_packet), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_10g_64.py b/fpga/lib/eth/tb/test_eth_mac_10g_64.py new file mode 100755 index 000000000..6b7e960b4 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_10g_64.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep + +module = 'eth_mac_10g' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_xgmii_rx_64.v") +srcs.append("../rtl/axis_xgmii_tx_64.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + CTRL_WIDTH = (DATA_WIDTH/8) + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + PTP_PERIOD_NS = 0x6 + PTP_PERIOD_FNS = 0x6666 + TX_PTP_TS_ENABLE = 0 + TX_PTP_TS_WIDTH = 96 + TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE + TX_PTP_TAG_WIDTH = 16 + RX_PTP_TS_ENABLE = 0 + RX_PTP_TS_WIDTH = 96 + TX_USER_WIDTH = (TX_PTP_TAG_WIDTH if TX_PTP_TAG_ENABLE else 0) + 1 + RX_USER_WIDTH = (RX_PTP_TS_WIDTH if RX_PTP_TS_ENABLE else 0) + 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(intbv(0)[TX_USER_WIDTH:]) + xgmii_rxd = Signal(intbv(0x0707070707070707)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0xff)[CTRL_WIDTH:]) + tx_ptp_ts = Signal(intbv(0)[TX_PTP_TS_WIDTH:]) + rx_ptp_ts = Signal(intbv(0)[RX_PTP_TS_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + rx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(intbv(0)[RX_USER_WIDTH:]) + xgmii_txd = Signal(intbv(0x0707070707070707)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0xff)[CTRL_WIDTH:]) + tx_axis_ptp_ts = Signal(intbv(0)[TX_PTP_TS_WIDTH:]) + tx_axis_ptp_ts_tag = Signal(intbv(0)[TX_PTP_TAG_WIDTH:]) + tx_axis_ptp_ts_valid = Signal(bool(0)) + tx_start_packet = Signal(intbv(0)[2:]) + tx_error_underflow = Signal(bool(0)) + rx_start_packet = Signal(intbv(0)[2:]) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + xgmii_source = xgmii_ep.XGMIISource() + + xgmii_source_logic = xgmii_source.create_logic( + clk, + rst, + txd=xgmii_rxd, + txc=xgmii_rxc, + name='xgmii_source' + ) + + xgmii_sink = xgmii_ep.XGMIISink() + + xgmii_sink_logic = xgmii_sink.create_logic( + clk, + rst, + rxd=xgmii_txd, + rxc=xgmii_txc, + name='xgmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + clk, + rst, + tdata=tx_axis_tdata, + tkeep=tx_axis_tkeep, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + clk, + rst, + tdata=rx_axis_tdata, + tkeep=rx_axis_tkeep, + tvalid=rx_axis_tvalid, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + + tx_axis_tdata=tx_axis_tdata, + tx_axis_tkeep=tx_axis_tkeep, + 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_tkeep=rx_axis_tkeep, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + + tx_ptp_ts=tx_ptp_ts, + rx_ptp_ts=rx_ptp_ts, + tx_axis_ptp_ts=tx_axis_ptp_ts, + tx_axis_ptp_ts_tag=tx_axis_ptp_ts_tag, + tx_axis_ptp_ts_valid=tx_axis_ptp_ts_valid, + + tx_start_packet=tx_start_packet, + tx_error_underflow=tx_error_underflow, + rx_start_packet=rx_start_packet, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + tx_clk.next = not tx_clk + rx_clk.next = not rx_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + xgmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield xgmii_sink.wait() + rx_frame = xgmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_10g_64.v b/fpga/lib/eth/tb/test_eth_mac_10g_64.v new file mode 100644 index 000000000..20ffa25e0 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_10g_64.v @@ -0,0 +1,186 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for eth_mac_10g + */ +module test_eth_mac_10g_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter PTP_PERIOD_NS = 4'h6; +parameter PTP_PERIOD_FNS = 16'h6666; +parameter TX_PTP_TS_ENABLE = 0; +parameter TX_PTP_TS_WIDTH = 96; +parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE; +parameter TX_PTP_TAG_WIDTH = 16; +parameter RX_PTP_TS_ENABLE = 0; +parameter RX_PTP_TS_WIDTH = 96; +parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1; +parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg [DATA_WIDTH-1:0] tx_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] tx_axis_tkeep = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg [TX_USER_WIDTH-1:0] tx_axis_tuser = 0; +reg [DATA_WIDTH-1:0] xgmii_rxd = 0; +reg [CTRL_WIDTH-1:0] xgmii_rxc = 0; +reg [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts = 0; +reg [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [DATA_WIDTH-1:0] rx_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire [RX_USER_WIDTH-1:0] rx_axis_tuser; +wire [DATA_WIDTH-1:0] xgmii_txd; +wire [CTRL_WIDTH-1:0] xgmii_txc; +wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts; +wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag; +wire tx_axis_ptp_ts_valid; +wire [1:0] tx_start_packet; +wire tx_error_underflow; +wire [1:0] rx_start_packet; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + tx_axis_tdata, + tx_axis_tkeep, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + xgmii_rxd, + xgmii_rxc, + tx_ptp_ts, + rx_ptp_ts, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tkeep, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + xgmii_txd, + xgmii_txc, + tx_axis_ptp_ts, + tx_axis_ptp_ts_tag, + tx_axis_ptp_ts_valid, + tx_start_packet, + tx_error_underflow, + rx_start_packet, + rx_error_bad_frame, + rx_error_bad_fcs + ); + + // dump file + $dumpfile("test_eth_mac_10g_64.lxt"); + $dumpvars(0, test_eth_mac_10g_64); +end + +eth_mac_10g #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .TX_PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .TX_PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .TX_PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .TX_PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .RX_PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .RX_PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .TX_USER_WIDTH(TX_USER_WIDTH), + .RX_USER_WIDTH(RX_USER_WIDTH) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .tx_ptp_ts(tx_ptp_ts), + .rx_ptp_ts(rx_ptp_ts), + .tx_axis_ptp_ts(tx_axis_ptp_ts), + .tx_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .tx_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .tx_start_packet(tx_start_packet), + .tx_error_underflow(tx_error_underflow), + .rx_start_packet(rx_start_packet), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_10g_fifo_32.py b/fpga/lib/eth/tb/test_eth_mac_10g_fifo_32.py new file mode 100755 index 000000000..0488c8a3b --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_10g_fifo_32.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep + +module = 'eth_mac_10g_fifo' +testbench = 'test_%s_32' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_xgmii_rx_32.v") +srcs.append("../rtl/axis_xgmii_tx_32.v") +srcs.append("../rtl/eth_mac_10g.v") +srcs.append("../lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 32 + KEEP_WIDTH = (DATA_WIDTH/8) + CTRL_WIDTH = (DATA_WIDTH/8) + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + TX_FIFO_ADDR_WIDTH = 9 + RX_FIFO_ADDR_WIDTH = 9 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + logic_clk = Signal(bool(0)) + logic_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + rx_axis_tready = Signal(bool(0)) + xgmii_rxd = Signal(intbv(0x0707070707070707)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0xff)[CTRL_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + rx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + xgmii_txd = Signal(intbv(0x0707070707070707)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0xff)[CTRL_WIDTH:]) + tx_error_underflow = Signal(bool(0)) + tx_fifo_overflow = Signal(bool(0)) + tx_fifo_bad_frame = Signal(bool(0)) + tx_fifo_good_frame = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_fifo_overflow = Signal(bool(0)) + rx_fifo_bad_frame = Signal(bool(0)) + rx_fifo_good_frame = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + axis_sink_pause = Signal(bool(0)) + + xgmii_source = xgmii_ep.XGMIISource() + + xgmii_source_logic = xgmii_source.create_logic( + rx_clk, + rx_rst, + txd=xgmii_rxd, + txc=xgmii_rxc, + name='xgmii_source' + ) + + xgmii_sink = xgmii_ep.XGMIISink() + + xgmii_sink_logic = xgmii_sink.create_logic( + tx_clk, + tx_rst, + rxd=xgmii_txd, + rxc=xgmii_txc, + name='xgmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + logic_clk, + logic_rst, + tdata=tx_axis_tdata, + tkeep=tx_axis_tkeep, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + logic_clk, + logic_rst, + tdata=rx_axis_tdata, + tkeep=rx_axis_tkeep, + tvalid=rx_axis_tvalid, + tready=rx_axis_tready, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + pause=axis_sink_pause, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + logic_clk=logic_clk, + logic_rst=logic_rst, + + tx_axis_tdata=tx_axis_tdata, + tx_axis_tkeep=tx_axis_tkeep, + 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_tkeep=rx_axis_tkeep, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tready=rx_axis_tready, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + + tx_error_underflow=tx_error_underflow, + tx_fifo_overflow=tx_fifo_overflow, + tx_fifo_bad_frame=tx_fifo_bad_frame, + tx_fifo_good_frame=tx_fifo_good_frame, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_fifo_overflow=rx_fifo_overflow, + rx_fifo_bad_frame=rx_fifo_bad_frame, + rx_fifo_good_frame=rx_fifo_good_frame, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + tx_clk.next = not tx_clk + rx_clk.next = not rx_clk + logic_clk.next = not logic_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + logic_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + logic_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + xgmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield xgmii_sink.wait() + rx_frame = xgmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_10g_fifo_32.v b/fpga/lib/eth/tb/test_eth_mac_10g_fifo_32.v new file mode 100644 index 000000000..02592b069 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_10g_fifo_32.v @@ -0,0 +1,176 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for eth_mac_10g_fifo + */ +module test_eth_mac_10g_fifo_32; + +// Parameters +parameter DATA_WIDTH = 32; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter TX_FIFO_ADDR_WIDTH = 9; +parameter RX_FIFO_ADDR_WIDTH = 9; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg logic_clk = 0; +reg logic_rst = 0; +reg [DATA_WIDTH-1:0] tx_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] tx_axis_tkeep = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg rx_axis_tready = 0; +reg [DATA_WIDTH-1:0] xgmii_rxd = 0; +reg [CTRL_WIDTH-1:0] xgmii_rxc = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [DATA_WIDTH-1:0] rx_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire [DATA_WIDTH-1:0] xgmii_txd; +wire [CTRL_WIDTH-1:0] xgmii_txc; +wire tx_error_underflow; +wire tx_fifo_overflow; +wire tx_fifo_bad_frame; +wire tx_fifo_good_frame; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_fifo_overflow; +wire rx_fifo_bad_frame; +wire rx_fifo_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + logic_clk, + logic_rst, + tx_axis_tdata, + tx_axis_tkeep, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + rx_axis_tready, + xgmii_rxd, + xgmii_rxc, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tkeep, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + xgmii_txd, + xgmii_txc, + 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 + ); + + // dump file + $dumpfile("test_eth_mac_10g_fifo_32.lxt"); + $dumpvars(0, test_eth_mac_10g_fifo_32); +end + +eth_mac_10g_fifo #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .TX_FIFO_ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .RX_FIFO_ADDR_WIDTH(RX_FIFO_ADDR_WIDTH) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .logic_clk(logic_clk), + .logic_rst(logic_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .tx_error_underflow(tx_error_underflow), + .tx_fifo_overflow(tx_fifo_overflow), + .tx_fifo_bad_frame(tx_fifo_bad_frame), + .tx_fifo_good_frame(tx_fifo_good_frame), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_fifo_overflow(rx_fifo_overflow), + .rx_fifo_bad_frame(rx_fifo_bad_frame), + .rx_fifo_good_frame(rx_fifo_good_frame), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_10g_fifo_64.py b/fpga/lib/eth/tb/test_eth_mac_10g_fifo_64.py new file mode 100755 index 000000000..714028664 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_10g_fifo_64.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep + +module = 'eth_mac_10g_fifo' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_xgmii_rx_64.v") +srcs.append("../rtl/axis_xgmii_tx_64.v") +srcs.append("../rtl/eth_mac_10g.v") +srcs.append("../lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + CTRL_WIDTH = (DATA_WIDTH/8) + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + TX_FIFO_ADDR_WIDTH = 9 + RX_FIFO_ADDR_WIDTH = 9 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + logic_clk = Signal(bool(0)) + logic_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + rx_axis_tready = Signal(bool(0)) + xgmii_rxd = Signal(intbv(0x0707070707070707)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0xff)[CTRL_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + rx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + xgmii_txd = Signal(intbv(0x0707070707070707)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0xff)[CTRL_WIDTH:]) + tx_error_underflow = Signal(bool(0)) + tx_fifo_overflow = Signal(bool(0)) + tx_fifo_bad_frame = Signal(bool(0)) + tx_fifo_good_frame = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_fifo_overflow = Signal(bool(0)) + rx_fifo_bad_frame = Signal(bool(0)) + rx_fifo_good_frame = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + axis_sink_pause = Signal(bool(0)) + + xgmii_source = xgmii_ep.XGMIISource() + + xgmii_source_logic = xgmii_source.create_logic( + rx_clk, + rx_rst, + txd=xgmii_rxd, + txc=xgmii_rxc, + name='xgmii_source' + ) + + xgmii_sink = xgmii_ep.XGMIISink() + + xgmii_sink_logic = xgmii_sink.create_logic( + tx_clk, + tx_rst, + rxd=xgmii_txd, + rxc=xgmii_txc, + name='xgmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + logic_clk, + logic_rst, + tdata=tx_axis_tdata, + tkeep=tx_axis_tkeep, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + logic_clk, + logic_rst, + tdata=rx_axis_tdata, + tkeep=rx_axis_tkeep, + tvalid=rx_axis_tvalid, + tready=rx_axis_tready, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + pause=axis_sink_pause, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + logic_clk=logic_clk, + logic_rst=logic_rst, + + tx_axis_tdata=tx_axis_tdata, + tx_axis_tkeep=tx_axis_tkeep, + 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_tkeep=rx_axis_tkeep, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tready=rx_axis_tready, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + + tx_error_underflow=tx_error_underflow, + tx_fifo_overflow=tx_fifo_overflow, + tx_fifo_bad_frame=tx_fifo_bad_frame, + tx_fifo_good_frame=tx_fifo_good_frame, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_fifo_overflow=rx_fifo_overflow, + rx_fifo_bad_frame=rx_fifo_bad_frame, + rx_fifo_good_frame=rx_fifo_good_frame, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + tx_clk.next = not tx_clk + rx_clk.next = not rx_clk + logic_clk.next = not logic_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + logic_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + logic_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + xgmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield xgmii_sink.wait() + rx_frame = xgmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_10g_fifo_64.v b/fpga/lib/eth/tb/test_eth_mac_10g_fifo_64.v new file mode 100644 index 000000000..c035ceb07 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_10g_fifo_64.v @@ -0,0 +1,176 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for eth_mac_10g_fifo + */ +module test_eth_mac_10g_fifo_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter TX_FIFO_ADDR_WIDTH = 9; +parameter RX_FIFO_ADDR_WIDTH = 9; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg logic_clk = 0; +reg logic_rst = 0; +reg [DATA_WIDTH-1:0] tx_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] tx_axis_tkeep = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg rx_axis_tready = 0; +reg [DATA_WIDTH-1:0] xgmii_rxd = 0; +reg [CTRL_WIDTH-1:0] xgmii_rxc = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [DATA_WIDTH-1:0] rx_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire [DATA_WIDTH-1:0] xgmii_txd; +wire [CTRL_WIDTH-1:0] xgmii_txc; +wire tx_error_underflow; +wire tx_fifo_overflow; +wire tx_fifo_bad_frame; +wire tx_fifo_good_frame; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_fifo_overflow; +wire rx_fifo_bad_frame; +wire rx_fifo_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + logic_clk, + logic_rst, + tx_axis_tdata, + tx_axis_tkeep, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + rx_axis_tready, + xgmii_rxd, + xgmii_rxc, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tkeep, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + xgmii_txd, + xgmii_txc, + 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 + ); + + // dump file + $dumpfile("test_eth_mac_10g_fifo_64.lxt"); + $dumpvars(0, test_eth_mac_10g_fifo_64); +end + +eth_mac_10g_fifo #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .TX_FIFO_ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .RX_FIFO_ADDR_WIDTH(RX_FIFO_ADDR_WIDTH) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .logic_clk(logic_clk), + .logic_rst(logic_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .tx_error_underflow(tx_error_underflow), + .tx_fifo_overflow(tx_fifo_overflow), + .tx_fifo_bad_frame(tx_fifo_bad_frame), + .tx_fifo_good_frame(tx_fifo_good_frame), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_fifo_overflow(rx_fifo_overflow), + .rx_fifo_bad_frame(rx_fifo_bad_frame), + .rx_fifo_good_frame(rx_fifo_good_frame), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_1g.py b/fpga/lib/eth/tb/test_eth_mac_1g.py new file mode 100755 index 000000000..cf2ab16bf --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import gmii_ep + +module = 'eth_mac_1g' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_gmii_rx.v") +srcs.append("../rtl/axis_gmii_tx.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 8 + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + TX_PTP_TS_ENABLE = 0 + TX_PTP_TS_WIDTH = 96 + TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE + TX_PTP_TAG_WIDTH = 16 + RX_PTP_TS_ENABLE = 0 + RX_PTP_TS_WIDTH = 96 + TX_USER_WIDTH = (TX_PTP_TAG_WIDTH if TX_PTP_TAG_ENABLE else 0) + 1 + RX_USER_WIDTH = (RX_PTP_TS_WIDTH if RX_PTP_TS_ENABLE else 0) + 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(intbv(0)[TX_USER_WIDTH:]) + gmii_rxd = Signal(intbv(0)[DATA_WIDTH:]) + gmii_rx_dv = Signal(bool(0)) + gmii_rx_er = Signal(bool(0)) + tx_ptp_ts = Signal(intbv(0)[TX_PTP_TS_WIDTH:]) + rx_ptp_ts = Signal(intbv(0)[RX_PTP_TS_WIDTH:]) + rx_clk_enable = Signal(bool(1)) + tx_clk_enable = Signal(bool(1)) + rx_mii_select = Signal(bool(0)) + tx_mii_select = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(intbv(0)[RX_USER_WIDTH:]) + gmii_txd = Signal(intbv(0)[DATA_WIDTH:]) + gmii_tx_en = Signal(bool(0)) + gmii_tx_er = Signal(bool(0)) + tx_axis_ptp_ts = Signal(intbv(0)[TX_PTP_TS_WIDTH:]) + tx_axis_ptp_ts_tag = Signal(intbv(0)[TX_PTP_TAG_WIDTH:]) + tx_axis_ptp_ts_valid = Signal(bool(0)) + tx_start_packet = Signal(bool(0)) + tx_error_underflow = Signal(bool(0)) + rx_start_packet = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + clk, + rst, + txd=gmii_rxd, + tx_en=gmii_rx_dv, + tx_er=gmii_rx_er, + clk_enable=rx_clk_enable, + mii_select=rx_mii_select, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + clk, + rst, + rxd=gmii_txd, + rx_dv=gmii_tx_en, + rx_er=gmii_tx_er, + clk_enable=tx_clk_enable, + mii_select=tx_mii_select, + name='gmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + clk, + rst, + tdata=tx_axis_tdata, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + clk, + rst, + tdata=rx_axis_tdata, + tvalid=rx_axis_tvalid, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_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_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + gmii_rxd=gmii_rxd, + gmii_rx_dv=gmii_rx_dv, + gmii_rx_er=gmii_rx_er, + + gmii_txd=gmii_txd, + gmii_tx_en=gmii_tx_en, + gmii_tx_er=gmii_tx_er, + + tx_ptp_ts=tx_ptp_ts, + rx_ptp_ts=rx_ptp_ts, + tx_axis_ptp_ts=tx_axis_ptp_ts, + tx_axis_ptp_ts_tag=tx_axis_ptp_ts_tag, + tx_axis_ptp_ts_valid=tx_axis_ptp_ts_valid, + + rx_clk_enable=rx_clk_enable, + tx_clk_enable=tx_clk_enable, + + rx_mii_select=rx_mii_select, + tx_mii_select=tx_mii_select, + + tx_start_packet=tx_start_packet, + tx_error_underflow=tx_error_underflow, + rx_start_packet=rx_start_packet, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + tx_clk.next = not tx_clk + rx_clk.next = not rx_clk + + rx_error_bad_frame_asserted = Signal(bool(0)) + rx_error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_bad_frame): + rx_error_bad_frame_asserted.next = 1 + if (rx_error_bad_fcs): + rx_error_bad_fcs_asserted.next = 1 + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + rx_clk_enable.next = 0 + tx_clk_enable.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + rx_clk_enable.next = 1 + tx_clk_enable.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate, mii in [(1, 0), (10, 0), (5, 1)]: + clk_enable_rate.next = rate + rx_mii_select.next = mii + tx_mii_select.next = mii + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield gmii_sink.wait() + rx_frame = gmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_1g.v b/fpga/lib/eth/tb/test_eth_mac_1g.v new file mode 100644 index 000000000..5790affca --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g.v @@ -0,0 +1,188 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for eth_mac_1g + */ +module test_eth_mac_1g; + +// Parameters +parameter DATA_WIDTH = 8; +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter TX_PTP_TS_ENABLE = 0; +parameter TX_PTP_TS_WIDTH = 96; +parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE; +parameter TX_PTP_TAG_WIDTH = 16; +parameter RX_PTP_TS_ENABLE = 0; +parameter RX_PTP_TS_WIDTH = 96; +parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1; +parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg [DATA_WIDTH-1:0] tx_axis_tdata = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg [TX_USER_WIDTH-1:0] tx_axis_tuser = 0; +reg [DATA_WIDTH-1:0] gmii_rxd = 0; +reg gmii_rx_dv = 0; +reg gmii_rx_er = 0; +reg [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts = 0; +reg [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts = 0; +reg rx_clk_enable = 1; +reg tx_clk_enable = 1; +reg rx_mii_select = 0; +reg tx_mii_select = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [DATA_WIDTH-1:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire [RX_USER_WIDTH-1:0] rx_axis_tuser; +wire [DATA_WIDTH-1:0] gmii_txd; +wire gmii_tx_en; +wire gmii_tx_er; +wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts; +wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag; +wire tx_axis_ptp_ts_valid; +wire tx_start_packet; +wire tx_error_underflow; +wire rx_start_packet; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + tx_axis_tdata, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + gmii_rxd, + gmii_rx_dv, + gmii_rx_er, + tx_ptp_ts, + rx_ptp_ts, + rx_clk_enable, + tx_clk_enable, + rx_mii_select, + tx_mii_select, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + gmii_txd, + gmii_tx_en, + gmii_tx_er, + tx_axis_ptp_ts, + tx_axis_ptp_ts_tag, + tx_axis_ptp_ts_valid, + tx_start_packet, + tx_error_underflow, + rx_start_packet, + rx_error_bad_frame, + rx_error_bad_fcs + ); + + // dump file + $dumpfile("test_eth_mac_1g.lxt"); + $dumpvars(0, test_eth_mac_1g); +end + +eth_mac_1g #( + .DATA_WIDTH(DATA_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .TX_PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .TX_PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .TX_PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .TX_PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .RX_PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .RX_PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .TX_USER_WIDTH(TX_USER_WIDTH), + .RX_USER_WIDTH(RX_USER_WIDTH) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_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_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .tx_ptp_ts(tx_ptp_ts), + .rx_ptp_ts(rx_ptp_ts), + .tx_axis_ptp_ts(tx_axis_ptp_ts), + .tx_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .tx_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .rx_clk_enable(rx_clk_enable), + .tx_clk_enable(tx_clk_enable), + .rx_mii_select(rx_mii_select), + .tx_mii_select(tx_mii_select), + .tx_start_packet(tx_start_packet), + .tx_error_underflow(tx_error_underflow), + .rx_start_packet(rx_start_packet), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_1g_fifo.py b/fpga/lib/eth/tb/test_eth_mac_1g_fifo.py new file mode 100755 index 000000000..df1fecebb --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g_fifo.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import gmii_ep + +module = 'eth_mac_1g_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_gmii_rx.v") +srcs.append("../rtl/axis_gmii_tx.v") +srcs.append("../rtl/eth_mac_1g.v") +srcs.append("../lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + TX_FIFO_ADDR_WIDTH = 9 + RX_FIFO_ADDR_WIDTH = 9 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + logic_clk = Signal(bool(0)) + logic_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[8:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + rx_axis_tready = Signal(bool(0)) + gmii_rxd = Signal(intbv(0)[8:]) + gmii_rx_dv = Signal(bool(0)) + gmii_rx_er = Signal(bool(0)) + rx_clk_enable = Signal(bool(1)) + tx_clk_enable = Signal(bool(1)) + rx_mii_select = Signal(bool(0)) + tx_mii_select = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[8:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + gmii_txd = Signal(intbv(0)[8:]) + gmii_tx_en = Signal(bool(0)) + gmii_tx_er = Signal(bool(0)) + tx_error_underflow = Signal(bool(0)) + tx_fifo_overflow = Signal(bool(0)) + tx_fifo_bad_frame = Signal(bool(0)) + tx_fifo_good_frame = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_fifo_overflow = Signal(bool(0)) + rx_fifo_bad_frame = Signal(bool(0)) + rx_fifo_good_frame = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + axis_sink_pause = Signal(bool(0)) + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + rx_clk, + rx_rst, + txd=gmii_rxd, + tx_en=gmii_rx_dv, + tx_er=gmii_rx_er, + clk_enable=rx_clk_enable, + mii_select=rx_mii_select, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + tx_clk, + tx_rst, + rxd=gmii_txd, + rx_dv=gmii_tx_en, + rx_er=gmii_tx_er, + clk_enable=tx_clk_enable, + mii_select=tx_mii_select, + name='gmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + logic_clk, + logic_rst, + tdata=tx_axis_tdata, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + logic_clk, + logic_rst, + tdata=rx_axis_tdata, + tvalid=rx_axis_tvalid, + tready=rx_axis_tready, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + pause=axis_sink_pause, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + logic_clk=logic_clk, + logic_rst=logic_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_tready=rx_axis_tready, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + gmii_rxd=gmii_rxd, + gmii_rx_dv=gmii_rx_dv, + gmii_rx_er=gmii_rx_er, + + gmii_txd=gmii_txd, + gmii_tx_en=gmii_tx_en, + gmii_tx_er=gmii_tx_er, + + rx_clk_enable=rx_clk_enable, + tx_clk_enable=tx_clk_enable, + + rx_mii_select=rx_mii_select, + tx_mii_select=tx_mii_select, + + tx_error_underflow=tx_error_underflow, + tx_fifo_overflow=tx_fifo_overflow, + tx_fifo_bad_frame=tx_fifo_bad_frame, + tx_fifo_good_frame=tx_fifo_good_frame, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_fifo_overflow=rx_fifo_overflow, + rx_fifo_bad_frame=rx_fifo_bad_frame, + rx_fifo_good_frame=rx_fifo_good_frame, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + tx_clk.next = not tx_clk + rx_clk.next = not rx_clk + logic_clk.next = not logic_clk + + rx_error_bad_frame_asserted = Signal(bool(0)) + rx_error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_bad_frame): + rx_error_bad_frame_asserted.next = 1 + if (rx_error_bad_fcs): + rx_error_bad_fcs_asserted.next = 1 + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + rx_clk_enable.next = 0 + tx_clk_enable.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + rx_clk_enable.next = 1 + tx_clk_enable.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + logic_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + logic_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate, mii in [(1, 0), (10, 0), (5, 1)]: + clk_enable_rate.next = rate + rx_mii_select.next = mii + tx_mii_select.next = mii + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield gmii_sink.wait() + rx_frame = gmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_1g_fifo.v b/fpga/lib/eth/tb/test_eth_mac_1g_fifo.v new file mode 100644 index 000000000..ea7e9ccae --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g_fifo.v @@ -0,0 +1,180 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for eth_mac_1g_fifo + */ +module test_eth_mac_1g_fifo; + +// Parameters +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter TX_FIFO_ADDR_WIDTH = 9; +parameter RX_FIFO_ADDR_WIDTH = 9; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg logic_clk = 0; +reg logic_rst = 0; +reg [7:0] tx_axis_tdata = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg rx_axis_tready = 0; +reg [7:0] gmii_rxd = 0; +reg gmii_rx_dv = 0; +reg gmii_rx_er = 0; +reg rx_clk_enable = 1; +reg tx_clk_enable = 1; +reg rx_mii_select = 0; +reg tx_mii_select = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [7:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire [7:0] gmii_txd; +wire gmii_tx_en; +wire gmii_tx_er; +wire tx_error_underflow; +wire tx_fifo_overflow; +wire tx_fifo_bad_frame; +wire tx_fifo_good_frame; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_fifo_overflow; +wire rx_fifo_bad_frame; +wire rx_fifo_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + logic_clk, + logic_rst, + tx_axis_tdata, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + rx_axis_tready, + gmii_rxd, + gmii_rx_dv, + gmii_rx_er, + rx_clk_enable, + tx_clk_enable, + rx_mii_select, + tx_mii_select, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + gmii_txd, + gmii_tx_en, + gmii_tx_er, + 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 + ); + + // dump file + $dumpfile("test_eth_mac_1g_fifo.lxt"); + $dumpvars(0, test_eth_mac_1g_fifo); +end + +eth_mac_1g_fifo #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .TX_FIFO_ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .RX_FIFO_ADDR_WIDTH(RX_FIFO_ADDR_WIDTH) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .logic_clk(logic_clk), + .logic_rst(logic_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), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .rx_clk_enable(rx_clk_enable), + .tx_clk_enable(tx_clk_enable), + .rx_mii_select(rx_mii_select), + .tx_mii_select(tx_mii_select), + .tx_error_underflow(tx_error_underflow), + .tx_fifo_overflow(tx_fifo_overflow), + .tx_fifo_bad_frame(tx_fifo_bad_frame), + .tx_fifo_good_frame(tx_fifo_good_frame), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_fifo_overflow(rx_fifo_overflow), + .rx_fifo_bad_frame(rx_fifo_bad_frame), + .rx_fifo_good_frame(rx_fifo_good_frame), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_1g_gmii.py b/fpga/lib/eth/tb/test_eth_mac_1g_gmii.py new file mode 100755 index 000000000..d21f1335f --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g_gmii.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import gmii_ep + +module = 'eth_mac_1g_gmii' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_gmii_rx.v") +srcs.append("../rtl/axis_gmii_tx.v") +srcs.append("../rtl/eth_mac_1g.v") +srcs.append("../rtl/gmii_phy_if.v") +srcs.append("../rtl/oddr.v") +srcs.append("../rtl/ssio_sdr_in.v") +srcs.append("../rtl/ssio_sdr_out.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + IODDR_STYLE = "IODDR2" + CLOCK_INPUT_STYLE = "BUFIO2" + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + gtx_clk = Signal(bool(0)) + gtx_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[8:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + gmii_rx_clk = Signal(bool(0)) + gmii_rxd = Signal(intbv(0)[8:]) + gmii_rx_dv = Signal(bool(0)) + gmii_rx_er = Signal(bool(0)) + mii_tx_clk = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[8:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + gmii_tx_clk = Signal(bool(0)) + gmii_txd = Signal(intbv(0)[8:]) + gmii_tx_en = Signal(bool(0)) + gmii_tx_er = Signal(bool(0)) + tx_error_underflow = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + speed = Signal(intbv(0)[2:]) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + mii_select = Signal(bool(0)) + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + gmii_rx_clk, + rst, + txd=gmii_rxd, + tx_en=gmii_rx_dv, + tx_er=gmii_rx_er, + mii_select=mii_select, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + gmii_tx_clk, + rst, + rxd=gmii_txd, + rx_dv=gmii_tx_en, + rx_er=gmii_tx_er, + mii_select=mii_select, + name='gmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + gmii_rx_clk, #tx_clk, + tx_rst, + tdata=tx_axis_tdata, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + gmii_rx_clk, + rx_rst, + tdata=rx_axis_tdata, + tvalid=rx_axis_tvalid, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + gtx_clk=gtx_clk, + gtx_rst=gtx_rst, + + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_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_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + gmii_rx_clk=gmii_rx_clk, + gmii_rxd=gmii_rxd, + gmii_rx_dv=gmii_rx_dv, + gmii_rx_er=gmii_rx_er, + + gmii_tx_clk=gmii_tx_clk, + mii_tx_clk=mii_tx_clk, + gmii_txd=gmii_txd, + gmii_tx_en=gmii_tx_en, + gmii_tx_er=gmii_tx_er, + + tx_error_underflow=tx_error_underflow, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + speed=speed, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + gtx_clk.next = not clk + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + gmii_rx_clk.next = not gmii_rx_clk + mii_tx_clk.next = not gmii_rx_clk + + rx_error_bad_frame_asserted = Signal(bool(0)) + rx_error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_bad_frame): + rx_error_bad_frame_asserted.next = 1 + if (rx_error_bad_fcs): + rx_error_bad_fcs_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + gtx_rst.next = 1 + yield clk.posedge + rst.next = 0 + gtx_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate, mii in [(4, 0), (20, 1), (200, 1)]: + rx_clk_hp.next = rate + mii_select.next = mii + + yield delay(1000) + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield gmii_sink.wait() + rx_frame = gmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_1g_gmii.v b/fpga/lib/eth/tb/test_eth_mac_1g_gmii.v new file mode 100644 index 000000000..660bd6dba --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g_gmii.v @@ -0,0 +1,161 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for eth_mac_1g_gmii + */ +module test_eth_mac_1g_gmii; + +// Parameters +parameter TARGET = "SIM"; +parameter IODDR_STYLE = "IODDR2"; +parameter CLOCK_INPUT_STYLE = "BUFIO2"; +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg gtx_clk = 0; +reg gtx_rst = 0; +reg [7:0] tx_axis_tdata = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg gmii_rx_clk = 0; +reg [7:0] gmii_rxd = 0; +reg gmii_rx_dv = 0; +reg gmii_rx_er = 0; +reg mii_tx_clk = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire rx_clk; +wire rx_rst; +wire tx_clk; +wire tx_rst; +wire tx_axis_tready; +wire [7:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire gmii_tx_clk; +wire [7:0] gmii_txd; +wire gmii_tx_en; +wire gmii_tx_er; +wire tx_error_underflow; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire [1:0] speed; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + gtx_clk, + gtx_rst, + tx_axis_tdata, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + gmii_rx_clk, + gmii_rxd, + gmii_rx_dv, + gmii_rx_er, + mii_tx_clk, + ifg_delay + ); + $to_myhdl( + rx_clk, + rx_rst, + tx_clk, + tx_rst, + tx_axis_tready, + rx_axis_tdata, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + gmii_tx_clk, + gmii_txd, + gmii_tx_en, + gmii_tx_er, + tx_error_underflow, + rx_error_bad_frame, + rx_error_bad_fcs, + speed + ); + + // dump file + $dumpfile("test_eth_mac_1g_gmii.lxt"); + $dumpvars(0, test_eth_mac_1g_gmii); +end + +eth_mac_1g_gmii #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +UUT ( + .gtx_clk(gtx_clk), + .gtx_rst(gtx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_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_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rx_clk(gmii_rx_clk), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_tx_clk(gmii_tx_clk), + .mii_tx_clk(mii_tx_clk), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .tx_error_underflow(tx_error_underflow), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .speed(speed), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_1g_gmii_fifo.py b/fpga/lib/eth/tb/test_eth_mac_1g_gmii_fifo.py new file mode 100755 index 000000000..fef5f1447 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g_gmii_fifo.py @@ -0,0 +1,342 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import gmii_ep + +module = 'eth_mac_1g_gmii_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_gmii_rx.v") +srcs.append("../rtl/axis_gmii_tx.v") +srcs.append("../rtl/eth_mac_1g.v") +srcs.append("../rtl/eth_mac_1g_gmii.v") +srcs.append("../rtl/gmii_phy_if.v") +srcs.append("../rtl/oddr.v") +srcs.append("../rtl/ssio_sdr_in.v") +srcs.append("../rtl/ssio_sdr_out.v") +srcs.append("../lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + IODDR_STYLE = "IODDR2" + CLOCK_INPUT_STYLE = "BUFIO2" + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + TX_FIFO_ADDR_WIDTH = 9 + RX_FIFO_ADDR_WIDTH = 9 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + gtx_clk = Signal(bool(0)) + gtx_rst = Signal(bool(0)) + logic_clk = Signal(bool(0)) + logic_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[8:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + rx_axis_tready = Signal(bool(0)) + gmii_rx_clk = Signal(bool(0)) + gmii_rxd = Signal(intbv(0)[8:]) + gmii_rx_dv = Signal(bool(0)) + gmii_rx_er = Signal(bool(0)) + mii_tx_clk = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[8:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + gmii_tx_clk = Signal(bool(0)) + gmii_txd = Signal(intbv(0)[8:]) + gmii_tx_en = Signal(bool(0)) + gmii_tx_er = Signal(bool(0)) + tx_error_underflow = Signal(bool(0)) + tx_fifo_overflow = Signal(bool(0)) + tx_fifo_bad_frame = Signal(bool(0)) + tx_fifo_good_frame = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_fifo_overflow = Signal(bool(0)) + rx_fifo_bad_frame = Signal(bool(0)) + rx_fifo_good_frame = Signal(bool(0)) + speed = Signal(intbv(0)[1:0]) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + axis_sink_pause = Signal(bool(0)) + + mii_select = Signal(bool(0)) + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + gmii_rx_clk, + rst, + txd=gmii_rxd, + tx_en=gmii_rx_dv, + tx_er=gmii_rx_er, + mii_select=mii_select, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + gmii_tx_clk, + rst, + rxd=gmii_txd, + rx_dv=gmii_tx_en, + rx_er=gmii_tx_er, + mii_select=mii_select, + name='gmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + logic_clk, + logic_rst, + tdata=tx_axis_tdata, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + logic_clk, + logic_rst, + tdata=rx_axis_tdata, + tvalid=rx_axis_tvalid, + tready=rx_axis_tready, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + pause=axis_sink_pause, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + gtx_clk=gtx_clk, + gtx_rst=gtx_rst, + logic_clk=logic_clk, + logic_rst=logic_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_tready=rx_axis_tready, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + gmii_rx_clk=gmii_rx_clk, + gmii_rxd=gmii_rxd, + gmii_rx_dv=gmii_rx_dv, + gmii_rx_er=gmii_rx_er, + + gmii_tx_clk=gmii_tx_clk, + mii_tx_clk=mii_tx_clk, + gmii_txd=gmii_txd, + gmii_tx_en=gmii_tx_en, + gmii_tx_er=gmii_tx_er, + + tx_error_underflow=tx_error_underflow, + tx_fifo_overflow=tx_fifo_overflow, + tx_fifo_bad_frame=tx_fifo_bad_frame, + tx_fifo_good_frame=tx_fifo_good_frame, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_fifo_overflow=rx_fifo_overflow, + rx_fifo_bad_frame=rx_fifo_bad_frame, + rx_fifo_good_frame=rx_fifo_good_frame, + speed=speed, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + gtx_clk.next = not clk + logic_clk.next = not clk + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + gmii_rx_clk.next = not gmii_rx_clk + mii_tx_clk.next = not gmii_rx_clk + + rx_error_bad_frame_asserted = Signal(bool(0)) + rx_error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_bad_frame): + rx_error_bad_frame_asserted.next = 1 + if (rx_error_bad_fcs): + rx_error_bad_fcs_asserted.next = 1 + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + gtx_rst.next = 1 + logic_rst.next = 1 + yield clk.posedge + rst.next = 0 + gtx_rst.next = 0 + logic_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate, mii in [(4, 0), (20, 1), (200, 1)]: + rx_clk_hp.next = rate + mii_select.next = mii + + yield delay(1000) + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield gmii_sink.wait() + rx_frame = gmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_1g_gmii_fifo.v b/fpga/lib/eth/tb/test_eth_mac_1g_gmii_fifo.v new file mode 100644 index 000000000..6c60bba76 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g_gmii_fifo.v @@ -0,0 +1,180 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for eth_mac_1g_gmii_fifo + */ +module test_eth_mac_1g_gmii_fifo; + +// Parameters +parameter TARGET = "SIM"; +parameter IODDR_STYLE = "IODDR2"; +parameter CLOCK_INPUT_STYLE = "BUFIO2"; +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter TX_FIFO_ADDR_WIDTH = 9; +parameter RX_FIFO_ADDR_WIDTH = 9; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg gtx_clk = 0; +reg gtx_rst = 0; +reg logic_clk = 0; +reg logic_rst = 0; +reg [7:0] tx_axis_tdata = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg rx_axis_tready = 0; +reg gmii_rx_clk = 0; +reg [7:0] gmii_rxd = 0; +reg gmii_rx_dv = 0; +reg gmii_rx_er = 0; +reg mii_tx_clk = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [7:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire gmii_tx_clk; +wire [7:0] gmii_txd; +wire gmii_tx_en; +wire gmii_tx_er; +wire tx_error_underflow; +wire tx_fifo_overflow; +wire tx_fifo_bad_frame; +wire tx_fifo_good_frame; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_fifo_overflow; +wire rx_fifo_bad_frame; +wire rx_fifo_good_frame; +wire [1:0] speed; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + gtx_clk, + gtx_rst, + logic_clk, + logic_rst, + tx_axis_tdata, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + rx_axis_tready, + gmii_rx_clk, + gmii_rxd, + gmii_rx_dv, + gmii_rx_er, + mii_tx_clk, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + gmii_tx_clk, + gmii_txd, + gmii_tx_en, + gmii_tx_er, + 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, + speed + ); + + // dump file + $dumpfile("test_eth_mac_1g_gmii_fifo.lxt"); + $dumpvars(0, test_eth_mac_1g_gmii_fifo); +end + +eth_mac_1g_gmii_fifo #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .TX_FIFO_ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .RX_FIFO_ADDR_WIDTH(RX_FIFO_ADDR_WIDTH) +) +UUT ( + .gtx_clk(gtx_clk), + .gtx_rst(gtx_rst), + .logic_clk(logic_clk), + .logic_rst(logic_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), + .gmii_rx_clk(gmii_rx_clk), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_tx_clk(gmii_tx_clk), + .mii_tx_clk(mii_tx_clk), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .tx_error_underflow(tx_error_underflow), + .tx_fifo_overflow(tx_fifo_overflow), + .tx_fifo_bad_frame(tx_fifo_bad_frame), + .tx_fifo_good_frame(tx_fifo_good_frame), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_fifo_overflow(rx_fifo_overflow), + .rx_fifo_bad_frame(rx_fifo_bad_frame), + .rx_fifo_good_frame(rx_fifo_good_frame), + .speed(speed), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_1g_rgmii.py b/fpga/lib/eth/tb/test_eth_mac_1g_rgmii.py new file mode 100755 index 000000000..e84460f64 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g_rgmii.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import rgmii_ep + +module = 'eth_mac_1g_rgmii' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_gmii_rx.v") +srcs.append("../rtl/axis_gmii_tx.v") +srcs.append("../rtl/eth_mac_1g.v") +srcs.append("../rtl/rgmii_phy_if.v") +srcs.append("../rtl/iddr.v") +srcs.append("../rtl/oddr.v") +srcs.append("../rtl/ssio_ddr_in.v") +srcs.append("../rtl/ssio_ddr_out.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + IODDR_STYLE = "IODDR2" + CLOCK_INPUT_STYLE = "BUFIO2" + USE_CLK90 = "TRUE" + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + gtx_clk = Signal(bool(0)) + gtx_clk90 = Signal(bool(0)) + gtx_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[8:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + rgmii_rx_clk = Signal(bool(0)) + rgmii_rxd = Signal(intbv(0)[4:]) + rgmii_rx_ctl = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[8:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + rgmii_tx_clk = Signal(bool(0)) + rgmii_txd = Signal(intbv(0)[4:]) + rgmii_tx_ctl = Signal(bool(0)) + tx_error_underflow = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + speed = Signal(intbv(0)[2:]) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + mii_select = Signal(bool(0)) + + rgmii_source = rgmii_ep.RGMIISource() + + rgmii_source_logic = rgmii_source.create_logic( + rgmii_rx_clk, + rst, + txd=rgmii_rxd, + tx_ctl=rgmii_rx_ctl, + mii_select=mii_select, + name='rgmii_source' + ) + + rgmii_sink = rgmii_ep.RGMIISink() + + rgmii_sink_logic = rgmii_sink.create_logic( + rgmii_tx_clk, + rst, + rxd=rgmii_txd, + rx_ctl=rgmii_tx_ctl, + mii_select=mii_select, + name='rgmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + gtx_clk, #tx_clk, + tx_rst, + tdata=tx_axis_tdata, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + rgmii_rx_clk, + rx_rst, + tdata=rx_axis_tdata, + tvalid=rx_axis_tvalid, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + gtx_clk=gtx_clk, + gtx_clk90=gtx_clk90, + gtx_rst=gtx_rst, + + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_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_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + rgmii_rx_clk=rgmii_rx_clk, + rgmii_rxd=rgmii_rxd, + rgmii_rx_ctl=rgmii_rx_ctl, + + rgmii_tx_clk=rgmii_tx_clk, + rgmii_txd=rgmii_txd, + rgmii_tx_ctl=rgmii_tx_ctl, + + tx_error_underflow=tx_error_underflow, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + speed=speed, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + gtx_clk.next = not clk + + @instance + def clkgen2(): + yield delay(4+2) + while True: + gtx_clk90.next = not gtx_clk90 + yield delay(4) + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + rgmii_rx_clk.next = not rgmii_rx_clk + + rx_error_bad_frame_asserted = Signal(bool(0)) + rx_error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_bad_frame): + rx_error_bad_frame_asserted.next = 1 + if (rx_error_bad_fcs): + rx_error_bad_fcs_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + gtx_rst.next = 1 + yield clk.posedge + rst.next = 0 + gtx_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate, mii in [(4, 0), (20, 1), (200, 1)]: + rx_clk_hp.next = rate + mii_select.next = mii + + yield delay(1000) + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + rgmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield rgmii_sink.wait() + rx_frame = rgmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_1g_rgmii.v b/fpga/lib/eth/tb/test_eth_mac_1g_rgmii.v new file mode 100644 index 000000000..0313fa09f --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g_rgmii.v @@ -0,0 +1,157 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for eth_mac_1g_rgmii + */ +module test_eth_mac_1g_rgmii; + +// Parameters +parameter TARGET = "SIM"; +parameter IODDR_STYLE = "IODDR2"; +parameter CLOCK_INPUT_STYLE = "BUFIO2"; +parameter USE_CLK90 = "TRUE"; +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg gtx_clk = 0; +reg gtx_clk90 = 0; +reg gtx_rst = 0; +reg [7:0] tx_axis_tdata = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg rgmii_rx_clk = 0; +reg [3:0] rgmii_rxd = 0; +reg rgmii_rx_ctl = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire rx_clk; +wire rx_rst; +wire tx_clk; +wire tx_rst; +wire tx_axis_tready; +wire [7:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire rgmii_tx_clk; +wire [3:0] rgmii_txd; +wire rgmii_tx_ctl; +wire tx_error_underflow; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire [1:0] speed; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + gtx_clk, + gtx_clk90, + gtx_rst, + tx_axis_tdata, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + rgmii_rx_clk, + rgmii_rxd, + rgmii_rx_ctl, + ifg_delay + ); + $to_myhdl( + rx_clk, + rx_rst, + tx_clk, + tx_rst, + tx_axis_tready, + rx_axis_tdata, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + rgmii_tx_clk, + rgmii_txd, + rgmii_tx_ctl, + tx_error_underflow, + rx_error_bad_frame, + rx_error_bad_fcs, + speed + ); + + // dump file + $dumpfile("test_eth_mac_1g_rgmii.lxt"); + $dumpvars(0, test_eth_mac_1g_rgmii); +end + +eth_mac_1g_rgmii #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .USE_CLK90(USE_CLK90), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +UUT ( + .gtx_clk(gtx_clk), + .gtx_clk90(gtx_clk90), + .gtx_rst(gtx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_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_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .rgmii_rx_clk(rgmii_rx_clk), + .rgmii_rxd(rgmii_rxd), + .rgmii_rx_ctl(rgmii_rx_ctl), + .rgmii_tx_clk(rgmii_tx_clk), + .rgmii_txd(rgmii_txd), + .rgmii_tx_ctl(rgmii_tx_ctl), + .tx_error_underflow(tx_error_underflow), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .speed(speed), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_1g_rgmii_fifo.py b/fpga/lib/eth/tb/test_eth_mac_1g_rgmii_fifo.py new file mode 100755 index 000000000..2840402da --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g_rgmii_fifo.py @@ -0,0 +1,339 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import rgmii_ep + +module = 'eth_mac_1g_rgmii_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_gmii_rx.v") +srcs.append("../rtl/axis_gmii_tx.v") +srcs.append("../rtl/eth_mac_1g.v") +srcs.append("../rtl/eth_mac_1g_rgmii.v") +srcs.append("../rtl/rgmii_phy_if.v") +srcs.append("../rtl/iddr.v") +srcs.append("../rtl/oddr.v") +srcs.append("../rtl/ssio_ddr_in.v") +srcs.append("../rtl/ssio_ddr_out.v") +srcs.append("../lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + IODDR_STYLE = "IODDR2" + CLOCK_INPUT_STYLE = "BUFIO2" + USE_CLK90 = "TRUE" + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + gtx_clk = Signal(bool(0)) + gtx_clk90 = Signal(bool(0)) + gtx_rst = Signal(bool(0)) + logic_clk = Signal(bool(0)) + logic_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[8:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + rx_axis_tready = Signal(bool(0)) + rgmii_rx_clk = Signal(bool(0)) + rgmii_rxd = Signal(intbv(0)[4:]) + rgmii_rx_ctl = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[8:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + rgmii_tx_clk = Signal(bool(0)) + rgmii_txd = Signal(intbv(0)[4:]) + rgmii_tx_ctl = Signal(bool(0)) + tx_error_underflow = Signal(bool(0)) + tx_fifo_overflow = Signal(bool(0)) + tx_fifo_bad_frame = Signal(bool(0)) + tx_fifo_good_frame = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_fifo_overflow = Signal(bool(0)) + rx_fifo_bad_frame = Signal(bool(0)) + rx_fifo_good_frame = Signal(bool(0)) + speed = Signal(intbv(0)[2:]) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + axis_sink_pause = Signal(bool(0)) + + mii_select = Signal(bool(0)) + + rgmii_source = rgmii_ep.RGMIISource() + + rgmii_source_logic = rgmii_source.create_logic( + rgmii_rx_clk, + rst, + txd=rgmii_rxd, + tx_ctl=rgmii_rx_ctl, + mii_select=mii_select, + name='rgmii_source' + ) + + rgmii_sink = rgmii_ep.RGMIISink() + + rgmii_sink_logic = rgmii_sink.create_logic( + rgmii_tx_clk, + rst, + rxd=rgmii_txd, + rx_ctl=rgmii_tx_ctl, + mii_select=mii_select, + name='rgmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + logic_clk, + logic_rst, + tdata=tx_axis_tdata, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + logic_clk, + logic_rst, + tdata=rx_axis_tdata, + tvalid=rx_axis_tvalid, + tready=rx_axis_tready, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + pause=axis_sink_pause, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + gtx_clk=gtx_clk, + gtx_clk90=gtx_clk90, + gtx_rst=gtx_rst, + logic_clk=logic_clk, + logic_rst=logic_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=rgmii_rx_clk, + rgmii_rxd=rgmii_rxd, + rgmii_rx_ctl=rgmii_rx_ctl, + + rgmii_tx_clk=rgmii_tx_clk, + rgmii_txd=rgmii_txd, + rgmii_tx_ctl=rgmii_tx_ctl, + + tx_error_underflow=tx_error_underflow, + tx_fifo_overflow=tx_fifo_overflow, + tx_fifo_bad_frame=tx_fifo_bad_frame, + tx_fifo_good_frame=tx_fifo_good_frame, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_fifo_overflow=rx_fifo_overflow, + rx_fifo_bad_frame=rx_fifo_bad_frame, + rx_fifo_good_frame=rx_fifo_good_frame, + speed=speed, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + gtx_clk.next = not clk + logic_clk.next = not clk + + @instance + def clkgen2(): + yield delay(4+2) + while True: + gtx_clk90.next = not gtx_clk90 + yield delay(4) + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + rgmii_rx_clk.next = not rgmii_rx_clk + + rx_error_bad_frame_asserted = Signal(bool(0)) + rx_error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_bad_frame): + rx_error_bad_frame_asserted.next = 1 + if (rx_error_bad_fcs): + rx_error_bad_fcs_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + gtx_rst.next = 1 + logic_rst.next = 1 + yield clk.posedge + rst.next = 0 + gtx_rst.next = 0 + logic_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate, mii in [(4, 0), (20, 1), (200, 1)]: + rx_clk_hp.next = rate + mii_select.next = mii + + yield delay(1000) + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + rgmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield rgmii_sink.wait() + rx_frame = rgmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_1g_rgmii_fifo.v b/fpga/lib/eth/tb/test_eth_mac_1g_rgmii_fifo.v new file mode 100644 index 000000000..e141b3c27 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_1g_rgmii_fifo.v @@ -0,0 +1,176 @@ +/* + +Copyright (c) 2015-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 1ns / 1ps + +/* + * Testbench for eth_mac_1g_rgmii_fifo + */ +module test_eth_mac_1g_rgmii_fifo; + +// Parameters +parameter TARGET = "SIM"; +parameter IODDR_STYLE = "IODDR2"; +parameter CLOCK_INPUT_STYLE = "BUFIO2"; +parameter USE_CLK90 = "TRUE"; +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter TX_FIFO_ADDR_WIDTH = 9; +parameter RX_FIFO_ADDR_WIDTH = 9; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg gtx_clk = 0; +reg gtx_clk90 = 0; +reg gtx_rst = 0; +reg logic_clk = 0; +reg logic_rst = 0; +reg [7:0] tx_axis_tdata = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg rx_axis_tready = 0; +reg rgmii_rx_clk = 0; +reg [3:0] rgmii_rxd = 0; +reg rgmii_rx_ctl = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [7:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire rgmii_tx_clk; +wire [3:0] rgmii_txd; +wire rgmii_tx_ctl; +wire tx_error_underflow; +wire tx_fifo_overflow; +wire tx_fifo_bad_frame; +wire tx_fifo_good_frame; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_fifo_overflow; +wire rx_fifo_bad_frame; +wire rx_fifo_good_frame; +wire [1:0] speed; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + gtx_clk, + gtx_clk90, + gtx_rst, + logic_clk, + logic_rst, + tx_axis_tdata, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + rx_axis_tready, + rgmii_rx_clk, + rgmii_rxd, + rgmii_rx_ctl, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + rgmii_tx_clk, + rgmii_txd, + rgmii_tx_ctl, + 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, + speed + ); + + // dump file + $dumpfile("test_eth_mac_1g_rgmii_fifo.lxt"); + $dumpvars(0, test_eth_mac_1g_rgmii_fifo); +end + +eth_mac_1g_rgmii_fifo #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .USE_CLK90(USE_CLK90), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .TX_FIFO_ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .RX_FIFO_ADDR_WIDTH(RX_FIFO_ADDR_WIDTH) +) +UUT ( + .gtx_clk(gtx_clk), + .gtx_clk90(gtx_clk90), + .gtx_rst(gtx_rst), + .logic_clk(logic_clk), + .logic_rst(logic_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(rgmii_rx_clk), + .rgmii_rxd(rgmii_rxd), + .rgmii_rx_ctl(rgmii_rx_ctl), + .rgmii_tx_clk(rgmii_tx_clk), + .rgmii_txd(rgmii_txd), + .rgmii_tx_ctl(rgmii_tx_ctl), + .tx_error_underflow(tx_error_underflow), + .tx_fifo_overflow(tx_fifo_overflow), + .tx_fifo_bad_frame(tx_fifo_bad_frame), + .tx_fifo_good_frame(tx_fifo_good_frame), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_fifo_overflow(rx_fifo_overflow), + .rx_fifo_bad_frame(rx_fifo_bad_frame), + .rx_fifo_good_frame(rx_fifo_good_frame), + .speed(speed), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_mii.py b/fpga/lib/eth/tb/test_eth_mac_mii.py new file mode 100755 index 000000000..23946b983 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_mii.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import mii_ep + +module = 'eth_mac_mii' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_gmii_rx.v") +srcs.append("../rtl/axis_gmii_tx.v") +srcs.append("../rtl/eth_mac_1g.v") +srcs.append("../rtl/mii_phy_if.v") +srcs.append("../rtl/ssio_sdr_in.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + CLOCK_INPUT_STYLE = "BUFIO2" + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + tx_axis_tdata = Signal(intbv(0)[8:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + mii_rx_clk = Signal(bool(0)) + mii_rxd = Signal(intbv(0)[4:]) + mii_rx_dv = Signal(bool(0)) + mii_rx_er = Signal(bool(0)) + mii_tx_clk = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[8:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + mii_txd = Signal(intbv(0)[4:]) + mii_tx_en = Signal(bool(0)) + mii_tx_er = Signal(bool(0)) + tx_error_underflow = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + + mii_source = mii_ep.MIISource() + + mii_source_logic = mii_source.create_logic( + mii_rx_clk, + rst, + txd=mii_rxd, + tx_en=mii_rx_dv, + tx_er=mii_rx_er, + name='mii_source' + ) + + mii_sink = mii_ep.MIISink() + + mii_sink_logic = mii_sink.create_logic( + mii_tx_clk, + rst, + rxd=mii_txd, + rx_dv=mii_tx_en, + rx_er=mii_tx_er, + name='mii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + mii_rx_clk, #tx_clk, + tx_rst, + tdata=tx_axis_tdata, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + mii_rx_clk, + rx_rst, + tdata=rx_axis_tdata, + tvalid=rx_axis_tvalid, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_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_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + mii_rx_clk=mii_rx_clk, + mii_rxd=mii_rxd, + mii_rx_dv=mii_rx_dv, + mii_rx_er=mii_rx_er, + + mii_tx_clk=mii_tx_clk, + mii_txd=mii_txd, + mii_tx_en=mii_tx_en, + mii_tx_er=mii_tx_er, + + tx_error_underflow=tx_error_underflow, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + phy_clk_hp = Signal(int(20)) + + @instance + def phy_clk_gen(): + while True: + yield delay(int(phy_clk_hp)) + mii_rx_clk.next = not mii_rx_clk + mii_tx_clk.next = not mii_rx_clk + + rx_error_bad_frame_asserted = Signal(bool(0)) + rx_error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_bad_frame): + rx_error_bad_frame_asserted.next = 1 + if (rx_error_bad_fcs): + rx_error_bad_fcs_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate in [20, 200]: + phy_clk_hp.next = rate + + yield delay(1000) + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + mii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield mii_sink.wait() + rx_frame = mii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_mii.v b/fpga/lib/eth/tb/test_eth_mac_mii.v new file mode 100644 index 000000000..a4c2d97f6 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_mii.v @@ -0,0 +1,148 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for eth_mac_mii + */ +module test_eth_mac_mii; + +// Parameters +parameter TARGET = "SIM"; +parameter CLOCK_INPUT_STYLE = "BUFIO2"; +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] tx_axis_tdata = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg mii_rx_clk = 0; +reg [3:0] mii_rxd = 0; +reg mii_rx_dv = 0; +reg mii_rx_er = 0; +reg mii_tx_clk = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire rx_clk; +wire rx_rst; +wire tx_clk; +wire tx_rst; +wire tx_axis_tready; +wire [7:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire [3:0] mii_txd; +wire mii_tx_en; +wire mii_tx_er; +wire tx_error_underflow; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + tx_axis_tdata, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + mii_rx_clk, + mii_rxd, + mii_rx_dv, + mii_rx_er, + mii_tx_clk, + ifg_delay + ); + $to_myhdl( + rx_clk, + rx_rst, + tx_clk, + tx_rst, + tx_axis_tready, + rx_axis_tdata, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + mii_txd, + mii_tx_en, + mii_tx_er, + tx_error_underflow, + rx_error_bad_frame, + rx_error_bad_fcs + ); + + // dump file + $dumpfile("test_eth_mac_mii.lxt"); + $dumpvars(0, test_eth_mac_mii); +end + +eth_mac_mii #( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +UUT ( + .rst(rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_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_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .mii_rx_clk(mii_rx_clk), + .mii_rxd(mii_rxd), + .mii_rx_dv(mii_rx_dv), + .mii_rx_er(mii_rx_er), + .mii_tx_clk(mii_tx_clk), + .mii_txd(mii_txd), + .mii_tx_en(mii_tx_en), + .mii_tx_er(mii_tx_er), + .tx_error_underflow(tx_error_underflow), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_mii_fifo.py b/fpga/lib/eth/tb/test_eth_mac_mii_fifo.py new file mode 100755 index 000000000..1192112f0 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_mii_fifo.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import mii_ep + +module = 'eth_mac_mii_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_gmii_rx.v") +srcs.append("../rtl/axis_gmii_tx.v") +srcs.append("../rtl/eth_mac_1g.v") +srcs.append("../rtl/eth_mac_mii.v") +srcs.append("../rtl/mii_phy_if.v") +srcs.append("../rtl/ssio_sdr_in.v") +srcs.append("../lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + CLOCK_INPUT_STYLE = "BUFIO2" + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + TX_FIFO_ADDR_WIDTH = 9 + RX_FIFO_ADDR_WIDTH = 9 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + logic_clk = Signal(bool(0)) + logic_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[8:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + rx_axis_tready = Signal(bool(0)) + mii_rx_clk = Signal(bool(0)) + mii_rxd = Signal(intbv(0)[4:]) + mii_rx_dv = Signal(bool(0)) + mii_rx_er = Signal(bool(0)) + mii_tx_clk = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[8:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + mii_txd = Signal(intbv(0)[4:]) + mii_tx_en = Signal(bool(0)) + mii_tx_er = Signal(bool(0)) + tx_error_underflow = Signal(bool(0)) + tx_fifo_overflow = Signal(bool(0)) + tx_fifo_bad_frame = Signal(bool(0)) + tx_fifo_good_frame = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_fifo_overflow = Signal(bool(0)) + rx_fifo_bad_frame = Signal(bool(0)) + rx_fifo_good_frame = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + axis_sink_pause = Signal(bool(0)) + + mii_source = mii_ep.MIISource() + + mii_source_logic = mii_source.create_logic( + mii_rx_clk, + rst, + txd=mii_rxd, + tx_en=mii_rx_dv, + tx_er=mii_rx_er, + name='mii_source' + ) + + mii_sink = mii_ep.MIISink() + + mii_sink_logic = mii_sink.create_logic( + mii_tx_clk, + rst, + rxd=mii_txd, + rx_dv=mii_tx_en, + rx_er=mii_tx_er, + name='mii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + logic_clk, + logic_rst, + tdata=tx_axis_tdata, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + logic_clk, + logic_rst, + tdata=rx_axis_tdata, + tvalid=rx_axis_tvalid, + tready=rx_axis_tready, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + pause=axis_sink_pause, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + logic_clk=logic_clk, + logic_rst=logic_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_tready=rx_axis_tready, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + mii_rx_clk=mii_rx_clk, + mii_rxd=mii_rxd, + mii_rx_dv=mii_rx_dv, + mii_rx_er=mii_rx_er, + + mii_tx_clk=mii_tx_clk, + mii_txd=mii_txd, + mii_tx_en=mii_tx_en, + mii_tx_er=mii_tx_er, + + tx_error_underflow=tx_error_underflow, + tx_fifo_overflow=tx_fifo_overflow, + tx_fifo_bad_frame=tx_fifo_bad_frame, + tx_fifo_good_frame=tx_fifo_good_frame, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_fifo_overflow=rx_fifo_overflow, + rx_fifo_bad_frame=rx_fifo_bad_frame, + rx_fifo_good_frame=rx_fifo_good_frame, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + logic_clk.next = not clk + + phy_clk_hp = Signal(int(20)) + + @instance + def phy_clk_gen(): + while True: + yield delay(int(phy_clk_hp)) + mii_rx_clk.next = not mii_rx_clk + mii_tx_clk.next = not mii_rx_clk + + rx_error_bad_frame_asserted = Signal(bool(0)) + rx_error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_bad_frame): + rx_error_bad_frame_asserted.next = 1 + if (rx_error_bad_fcs): + rx_error_bad_fcs_asserted.next = 1 + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + logic_rst.next = 1 + yield clk.posedge + rst.next = 0 + logic_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate in [20, 200]: + phy_clk_hp.next = rate + + yield delay(1000) + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + mii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield mii_sink.wait() + rx_frame = mii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_mii_fifo.v b/fpga/lib/eth/tb/test_eth_mac_mii_fifo.v new file mode 100644 index 000000000..5eb2eff45 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_mii_fifo.v @@ -0,0 +1,167 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for eth_mac_mii_fifo + */ +module test_eth_mac_mii_fifo; + +// Parameters +parameter TARGET = "SIM"; +parameter CLOCK_INPUT_STYLE = "BUFIO2"; +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter TX_FIFO_ADDR_WIDTH = 9; +parameter RX_FIFO_ADDR_WIDTH = 9; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg logic_clk = 0; +reg logic_rst = 0; +reg [7:0] tx_axis_tdata = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg rx_axis_tready = 0; +reg mii_rx_clk = 0; +reg [3:0] mii_rxd = 0; +reg mii_rx_dv = 0; +reg mii_rx_er = 0; +reg mii_tx_clk = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [7:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire [3:0] mii_txd; +wire mii_tx_en; +wire mii_tx_er; +wire tx_error_underflow; +wire tx_fifo_overflow; +wire tx_fifo_bad_frame; +wire tx_fifo_good_frame; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_fifo_overflow; +wire rx_fifo_bad_frame; +wire rx_fifo_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + logic_clk, + logic_rst, + tx_axis_tdata, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + rx_axis_tready, + mii_rx_clk, + mii_rxd, + mii_rx_dv, + mii_rx_er, + mii_tx_clk, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + mii_txd, + mii_tx_en, + mii_tx_er, + 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 + ); + + // dump file + $dumpfile("test_eth_mac_mii_fifo.lxt"); + $dumpvars(0, test_eth_mac_mii_fifo); +end + +eth_mac_mii_fifo #( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .TX_FIFO_ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .RX_FIFO_ADDR_WIDTH(RX_FIFO_ADDR_WIDTH) +) +UUT ( + .rst(rst), + .logic_clk(logic_clk), + .logic_rst(logic_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), + .mii_rx_clk(mii_rx_clk), + .mii_rxd(mii_rxd), + .mii_rx_dv(mii_rx_dv), + .mii_rx_er(mii_rx_er), + .mii_tx_clk(mii_tx_clk), + .mii_txd(mii_txd), + .mii_tx_en(mii_tx_en), + .mii_tx_er(mii_tx_er), + .tx_error_underflow(tx_error_underflow), + .tx_fifo_overflow(tx_fifo_overflow), + .tx_fifo_bad_frame(tx_fifo_bad_frame), + .tx_fifo_good_frame(tx_fifo_good_frame), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_fifo_overflow(rx_fifo_overflow), + .rx_fifo_bad_frame(rx_fifo_bad_frame), + .rx_fifo_good_frame(rx_fifo_good_frame), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_phy_10g.py b/fpga/lib/eth/tb/test_eth_mac_phy_10g.py new file mode 100755 index 000000000..e1f885308 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_phy_10g.py @@ -0,0 +1,359 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep +import baser_serdes_ep + +module = 'eth_mac_phy_10g' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_baser_tx_64.v") +srcs.append("../rtl/axis_baser_rx_64.v") +srcs.append("../rtl/eth_mac_phy_10g_rx.v") +srcs.append("../rtl/eth_mac_phy_10g_tx.v") +srcs.append("../rtl/eth_phy_10g_rx_if.v") +srcs.append("../rtl/eth_phy_10g_tx_if.v") +srcs.append("../rtl/eth_phy_10g_rx_ber_mon.v") +srcs.append("../rtl/eth_phy_10g_rx_frame_sync.v") +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + CTRL_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = (DATA_WIDTH/32) + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + PTP_PERIOD_NS = 0x6 + PTP_PERIOD_FNS = 0x6666 + TX_PTP_TS_ENABLE = 0 + TX_PTP_TS_WIDTH = 96 + TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE + TX_PTP_TAG_WIDTH = 16 + RX_PTP_TS_ENABLE = 0 + RX_PTP_TS_WIDTH = 96 + TX_USER_WIDTH = (TX_PTP_TAG_WIDTH if TX_PTP_TAG_ENABLE else 0) + 1 + RX_USER_WIDTH = (RX_PTP_TS_WIDTH if RX_PTP_TS_ENABLE else 0) + 1 + BIT_REVERSE = 0 + SCRAMBLER_DISABLE = 0 + PRBS31_ENABLE = 1 + TX_SERDES_PIPELINE = 2 + RX_SERDES_PIPELINE = 2 + SLIP_COUNT_WIDTH = 3 + COUNT_125US = 125000/6.4 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(intbv(0)[TX_USER_WIDTH:]) + serdes_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + tx_ptp_ts = Signal(intbv(0)[TX_PTP_TS_WIDTH:]) + rx_ptp_ts = Signal(intbv(0)[RX_PTP_TS_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + tx_prbs31_enable = Signal(bool(0)) + rx_prbs31_enable = Signal(bool(0)) + + serdes_rx_data_int = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr_int = Signal(intbv(1)[HDR_WIDTH:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + rx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(intbv(0)[RX_USER_WIDTH:]) + serdes_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_tx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + serdes_rx_bitslip = Signal(bool(0)) + tx_axis_ptp_ts = Signal(intbv(0)[TX_PTP_TS_WIDTH:]) + tx_axis_ptp_ts_tag = Signal(intbv(0)[TX_PTP_TAG_WIDTH:]) + tx_axis_ptp_ts_valid = Signal(bool(0)) + tx_start_packet = Signal(intbv(0)[2:]) + tx_error_underflow = Signal(bool(0)) + rx_start_packet = Signal(intbv(0)[2:]) + rx_error_count = Signal(intbv(0)[7:]) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_bad_block = Signal(bool(0)) + rx_block_lock = Signal(bool(0)) + rx_high_ber = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + serdes_source = baser_serdes_ep.BaseRSerdesSource() + + serdes_source_logic = serdes_source.create_logic( + rx_clk, + tx_data=serdes_rx_data_int, + tx_header=serdes_rx_hdr_int, + name='serdes_source' + ) + + serdes_sink = baser_serdes_ep.BaseRSerdesSink() + + serdes_sink_logic = serdes_sink.create_logic( + tx_clk, + rx_data=serdes_tx_data, + rx_header=serdes_tx_hdr, + name='serdes_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + tx_clk, + tx_rst, + tdata=tx_axis_tdata, + tkeep=tx_axis_tkeep, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + rx_clk, + rx_rst, + tdata=rx_axis_tdata, + tkeep=rx_axis_tkeep, + tvalid=rx_axis_tvalid, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + tx_axis_tdata=tx_axis_tdata, + tx_axis_tkeep=tx_axis_tkeep, + 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_tkeep=rx_axis_tkeep, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + serdes_tx_data=serdes_tx_data, + serdes_tx_hdr=serdes_tx_hdr, + serdes_rx_data=serdes_rx_data, + serdes_rx_hdr=serdes_rx_hdr, + serdes_rx_bitslip=serdes_rx_bitslip, + tx_ptp_ts=tx_ptp_ts, + rx_ptp_ts=rx_ptp_ts, + tx_axis_ptp_ts=tx_axis_ptp_ts, + tx_axis_ptp_ts_tag=tx_axis_ptp_ts_tag, + tx_axis_ptp_ts_valid=tx_axis_ptp_ts_valid, + tx_start_packet=tx_start_packet, + tx_error_underflow=tx_error_underflow, + rx_start_packet=rx_start_packet, + rx_error_count=rx_error_count, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_bad_block=rx_bad_block, + rx_block_lock=rx_block_lock, + rx_high_ber=rx_high_ber, + ifg_delay=ifg_delay, + tx_prbs31_enable=tx_prbs31_enable, + rx_prbs31_enable=rx_prbs31_enable + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + tx_clk.next = not tx_clk + rx_clk.next = not rx_clk + + load_bit_offset = [] + + @instance + def shift_bits(): + bit_offset = 0 + last_data = 0 + + while True: + yield clk.posedge + + if load_bit_offset: + bit_offset = load_bit_offset.pop(0) + + if serdes_rx_bitslip: + bit_offset += 1 + + bit_offset = bit_offset % 66 + + data = int(serdes_rx_data_int) << 2 | int(serdes_rx_hdr_int) + + out_data = ((last_data | data << 66) >> 66-bit_offset) & 0x3ffffffffffffffff + + last_data = data + + serdes_rx_data.next = out_data >> 2 + serdes_rx_hdr.next = out_data & 3 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + # wait for block lock + while not rx_block_lock: + yield clk.posedge + + # dump garbage + while not axis_sink.empty(): + axis_sink.recv() + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + serdes_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield serdes_sink.wait() + rx_frame = serdes_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_phy_10g.v b/fpga/lib/eth/tb/test_eth_mac_phy_10g.v new file mode 100644 index 000000000..06033637e --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_phy_10g.v @@ -0,0 +1,223 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for eth_mac_phy_10g + */ +module test_eth_mac_phy_10g; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = (DATA_WIDTH/32); +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter PTP_PERIOD_NS = 4'h6; +parameter PTP_PERIOD_FNS = 16'h6666; +parameter TX_PTP_TS_ENABLE = 0; +parameter TX_PTP_TS_WIDTH = 96; +parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE; +parameter TX_PTP_TAG_WIDTH = 16; +parameter RX_PTP_TS_ENABLE = 0; +parameter RX_PTP_TS_WIDTH = 96; +parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1; +parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1; +parameter BIT_REVERSE = 0; +parameter SCRAMBLER_DISABLE = 0; +parameter PRBS31_ENABLE = 1; +parameter TX_SERDES_PIPELINE = 2; +parameter RX_SERDES_PIPELINE = 2; +parameter SLIP_COUNT_WIDTH = 3; +parameter COUNT_125US = 125000/6.4; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg [DATA_WIDTH-1:0] tx_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] tx_axis_tkeep = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg [TX_USER_WIDTH-1:0] tx_axis_tuser = 0; +reg [DATA_WIDTH-1:0] serdes_rx_data = 0; +reg [HDR_WIDTH-1:0] serdes_rx_hdr = 1; +reg [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts = 0; +reg [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts = 0; +reg [7:0] ifg_delay = 0; +reg tx_prbs31_enable = 0; +reg rx_prbs31_enable = 0; + +// Outputs +wire tx_axis_tready; +wire [DATA_WIDTH-1:0] rx_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire [RX_USER_WIDTH-1:0] rx_axis_tuser; +wire [DATA_WIDTH-1:0] serdes_tx_data; +wire [HDR_WIDTH-1:0] serdes_tx_hdr; +wire serdes_rx_bitslip; +wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts; +wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag; +wire tx_axis_ptp_ts_valid; +wire [1:0] tx_start_packet; +wire tx_error_underflow; +wire [1:0] rx_start_packet; +wire [6:0] rx_error_count; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_bad_block; +wire rx_block_lock; +wire rx_high_ber; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + tx_axis_tdata, + tx_axis_tkeep, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + serdes_rx_data, + serdes_rx_hdr, + tx_ptp_ts, + rx_ptp_ts, + ifg_delay, + tx_prbs31_enable, + rx_prbs31_enable + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tkeep, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + serdes_tx_data, + serdes_tx_hdr, + serdes_rx_bitslip, + tx_axis_ptp_ts, + tx_axis_ptp_ts_tag, + tx_axis_ptp_ts_valid, + tx_start_packet, + tx_error_underflow, + rx_error_count, + rx_start_packet, + rx_error_bad_frame, + rx_error_bad_fcs, + rx_bad_block, + rx_block_lock, + rx_high_ber + ); + + // dump file + $dumpfile("test_eth_mac_phy_10g.lxt"); + $dumpvars(0, test_eth_mac_phy_10g); +end + +eth_mac_phy_10g #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .TX_PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .TX_PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .TX_PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .TX_PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .RX_PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .RX_PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .TX_USER_WIDTH(TX_USER_WIDTH), + .RX_USER_WIDTH(RX_USER_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .TX_SERDES_PIPELINE(TX_SERDES_PIPELINE), + .RX_SERDES_PIPELINE(RX_SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .tx_ptp_ts(tx_ptp_ts), + .rx_ptp_ts(rx_ptp_ts), + .tx_axis_ptp_ts(tx_axis_ptp_ts), + .tx_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .tx_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .tx_start_packet(tx_start_packet), + .tx_error_underflow(tx_error_underflow), + .rx_start_packet(rx_start_packet), + .rx_error_count(rx_error_count), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_bad_block(rx_bad_block), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .ifg_delay(ifg_delay), + .tx_prbs31_enable(tx_prbs31_enable), + .rx_prbs31_enable(rx_prbs31_enable) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mac_phy_10g_fifo.py b/fpga/lib/eth/tb/test_eth_mac_phy_10g_fifo.py new file mode 100755 index 000000000..c2f13fc85 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_phy_10g_fifo.py @@ -0,0 +1,365 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep +import baser_serdes_ep + +module = 'eth_mac_phy_10g_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_baser_tx_64.v") +srcs.append("../rtl/axis_baser_rx_64.v") +srcs.append("../rtl/eth_mac_phy_10g.v") +srcs.append("../rtl/eth_mac_phy_10g_rx.v") +srcs.append("../rtl/eth_mac_phy_10g_tx.v") +srcs.append("../rtl/eth_phy_10g_rx_if.v") +srcs.append("../rtl/eth_phy_10g_tx_if.v") +srcs.append("../rtl/eth_phy_10g_rx_ber_mon.v") +srcs.append("../rtl/eth_phy_10g_rx_frame_sync.v") +srcs.append("../rtl/lfsr.v") +srcs.append("../lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_WIDTH = int(DATA_WIDTH/8) + CTRL_WIDTH = int(DATA_WIDTH/8) + HDR_WIDTH = int(DATA_WIDTH/32) + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + BIT_REVERSE = 0 + SCRAMBLER_DISABLE = 0 + PRBS31_ENABLE = 1 + TX_SERDES_PIPELINE = 2 + RX_SERDES_PIPELINE = 2 + SLIP_COUNT_WIDTH = 3 + COUNT_125US = 125000/6.4 + TX_FIFO_ADDR_WIDTH = 12-(KEEP_WIDTH-1).bit_length() + TX_FRAME_FIFO = 1 + TX_DROP_BAD_FRAME = TX_FRAME_FIFO + TX_DROP_WHEN_FULL = 0 + RX_FIFO_ADDR_WIDTH = 12-(KEEP_WIDTH-1).bit_length() + RX_FRAME_FIFO = 1 + RX_DROP_BAD_FRAME = RX_FRAME_FIFO + RX_DROP_WHEN_FULL = RX_FRAME_FIFO + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + logic_clk = Signal(bool(0)) + logic_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + rx_axis_tready = Signal(bool(0)) + serdes_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + tx_prbs31_enable = Signal(bool(0)) + rx_prbs31_enable = Signal(bool(0)) + + serdes_rx_data_int = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr_int = Signal(intbv(1)[HDR_WIDTH:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + rx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + serdes_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_tx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + serdes_rx_bitslip = Signal(bool(0)) + tx_error_underflow = Signal(bool(0)) + tx_fifo_overflow = Signal(bool(0)) + tx_fifo_bad_frame = Signal(bool(0)) + tx_fifo_good_frame = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_bad_block = Signal(bool(0)) + rx_block_lock = Signal(bool(0)) + rx_high_ber = Signal(bool(0)) + rx_fifo_overflow = Signal(bool(0)) + rx_fifo_bad_frame = Signal(bool(0)) + rx_fifo_good_frame = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + serdes_source = baser_serdes_ep.BaseRSerdesSource() + + serdes_source_logic = serdes_source.create_logic( + rx_clk, + tx_data=serdes_rx_data_int, + tx_header=serdes_rx_hdr_int, + name='serdes_source' + ) + + serdes_sink = baser_serdes_ep.BaseRSerdesSink() + + serdes_sink_logic = serdes_sink.create_logic( + tx_clk, + rx_data=serdes_tx_data, + rx_header=serdes_tx_hdr, + name='serdes_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + logic_clk, + logic_rst, + tdata=tx_axis_tdata, + tkeep=tx_axis_tkeep, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + logic_clk, + logic_rst, + tdata=rx_axis_tdata, + tkeep=rx_axis_tkeep, + tvalid=rx_axis_tvalid, + tready=rx_axis_tready, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + logic_clk=logic_clk, + logic_rst=logic_rst, + tx_axis_tdata=tx_axis_tdata, + tx_axis_tkeep=tx_axis_tkeep, + 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_tkeep=rx_axis_tkeep, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tready=rx_axis_tready, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + serdes_tx_data=serdes_tx_data, + serdes_tx_hdr=serdes_tx_hdr, + serdes_rx_data=serdes_rx_data, + serdes_rx_hdr=serdes_rx_hdr, + serdes_rx_bitslip=serdes_rx_bitslip, + tx_error_underflow=tx_error_underflow, + tx_fifo_overflow=tx_fifo_overflow, + tx_fifo_bad_frame=tx_fifo_bad_frame, + tx_fifo_good_frame=tx_fifo_good_frame, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_bad_block=rx_bad_block, + rx_block_lock=rx_block_lock, + rx_high_ber=rx_high_ber, + rx_fifo_overflow=rx_fifo_overflow, + rx_fifo_bad_frame=rx_fifo_bad_frame, + rx_fifo_good_frame=rx_fifo_good_frame, + ifg_delay=ifg_delay, + tx_prbs31_enable=tx_prbs31_enable, + rx_prbs31_enable=rx_prbs31_enable + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + tx_clk.next = not tx_clk + rx_clk.next = not rx_clk + logic_clk.next = not logic_clk + + load_bit_offset = [] + + @instance + def shift_bits(): + bit_offset = 0 + last_data = 0 + + while True: + yield clk.posedge + + if load_bit_offset: + bit_offset = load_bit_offset.pop(0) + + if serdes_rx_bitslip: + bit_offset += 1 + + bit_offset = bit_offset % 66 + + data = int(serdes_rx_data_int) << 2 | int(serdes_rx_hdr_int) + + out_data = ((last_data | data << 66) >> 66-bit_offset) & 0x3ffffffffffffffff + + last_data = data + + serdes_rx_data.next = out_data >> 2 + serdes_rx_hdr.next = out_data & 3 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + logic_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + logic_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + # wait for block lock + while not rx_block_lock: + yield clk.posedge + + # dump garbage + while not axis_sink.empty(): + axis_sink.recv() + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + serdes_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield serdes_sink.wait() + rx_frame = serdes_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_mac_phy_10g_fifo.v b/fpga/lib/eth/tb/test_eth_mac_phy_10g_fifo.v new file mode 100644 index 000000000..00276002a --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mac_phy_10g_fifo.v @@ -0,0 +1,222 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for eth_mac_phy_10g_fifo + */ +module test_eth_mac_phy_10g_fifo; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = (DATA_WIDTH/32); +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter BIT_REVERSE = 0; +parameter SCRAMBLER_DISABLE = 0; +parameter PRBS31_ENABLE = 1; +parameter TX_SERDES_PIPELINE = 2; +parameter RX_SERDES_PIPELINE = 2; +parameter SLIP_COUNT_WIDTH = 3; +parameter COUNT_125US = 125000/6.4; +parameter TX_FIFO_ADDR_WIDTH = 12-$clog2(KEEP_WIDTH); +parameter TX_FRAME_FIFO = 1; +parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO; +parameter TX_DROP_WHEN_FULL = 0; +parameter RX_FIFO_ADDR_WIDTH = 12-$clog2(KEEP_WIDTH); +parameter RX_FRAME_FIFO = 1; +parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO; +parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg logic_clk = 0; +reg logic_rst = 0; +reg [DATA_WIDTH-1:0] tx_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] tx_axis_tkeep = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg rx_axis_tready = 0; +reg [DATA_WIDTH-1:0] serdes_rx_data = 0; +reg [HDR_WIDTH-1:0] serdes_rx_hdr = 1; +reg [7:0] ifg_delay = 0; +reg tx_prbs31_enable = 0; +reg rx_prbs31_enable = 0; + +// Outputs +wire tx_axis_tready; +wire [DATA_WIDTH-1:0] rx_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire [DATA_WIDTH-1:0] serdes_tx_data; +wire [HDR_WIDTH-1:0] serdes_tx_hdr; +wire serdes_rx_bitslip; +wire tx_error_underflow; +wire tx_fifo_overflow; +wire tx_fifo_bad_frame; +wire tx_fifo_good_frame; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_bad_block; +wire rx_block_lock; +wire rx_high_ber; +wire rx_fifo_overflow; +wire rx_fifo_bad_frame; +wire rx_fifo_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + logic_clk, + logic_rst, + tx_axis_tdata, + tx_axis_tkeep, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + rx_axis_tready, + serdes_rx_data, + serdes_rx_hdr, + ifg_delay, + tx_prbs31_enable, + rx_prbs31_enable + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tkeep, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + serdes_tx_data, + serdes_tx_hdr, + serdes_rx_bitslip, + tx_error_underflow, + tx_fifo_overflow, + tx_fifo_bad_frame, + tx_fifo_good_frame, + rx_error_bad_frame, + rx_error_bad_fcs, + rx_bad_block, + rx_block_lock, + rx_high_ber, + rx_fifo_overflow, + rx_fifo_bad_frame, + rx_fifo_good_frame + ); + + // dump file + $dumpfile("test_eth_mac_phy_10g_fifo.lxt"); + $dumpvars(0, test_eth_mac_phy_10g_fifo); +end + +eth_mac_phy_10g_fifo #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .TX_SERDES_PIPELINE(TX_SERDES_PIPELINE), + .RX_SERDES_PIPELINE(RX_SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US), + .TX_FIFO_ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .TX_FRAME_FIFO(TX_FRAME_FIFO), + .TX_DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .TX_DROP_WHEN_FULL(TX_DROP_WHEN_FULL), + .RX_FIFO_ADDR_WIDTH(RX_FIFO_ADDR_WIDTH), + .RX_FRAME_FIFO(RX_FRAME_FIFO), + .RX_DROP_BAD_FRAME(RX_DROP_BAD_FRAME), + .RX_DROP_WHEN_FULL(RX_DROP_WHEN_FULL) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .logic_clk(logic_clk), + .logic_rst(logic_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .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_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .tx_error_underflow(tx_error_underflow), + .tx_fifo_overflow(tx_fifo_overflow), + .tx_fifo_bad_frame(tx_fifo_bad_frame), + .tx_fifo_good_frame(tx_fifo_good_frame), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_bad_block(rx_bad_block), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_fifo_overflow(rx_fifo_overflow), + .rx_fifo_bad_frame(rx_fifo_bad_frame), + .rx_fifo_good_frame(rx_fifo_good_frame), + .ifg_delay(ifg_delay), + .tx_prbs31_enable(tx_prbs31_enable), + .rx_prbs31_enable(rx_prbs31_enable) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mux_4.py b/fpga/lib/eth/tb/test_eth_mux_4.py new file mode 100755 index 000000000..e96d41a38 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mux_4.py @@ -0,0 +1,437 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep + +module = 'eth_mux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_eth_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_eth_hdr_valid = ConcatSignal(*reversed(s_eth_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_eth_payload_axis_tdata = ConcatSignal(*reversed(s_eth_payload_axis_tdata_list)) + s_eth_payload_axis_tkeep = ConcatSignal(*reversed(s_eth_payload_axis_tkeep_list)) + s_eth_payload_axis_tvalid = ConcatSignal(*reversed(s_eth_payload_axis_tvalid_list)) + s_eth_payload_axis_tlast = ConcatSignal(*reversed(s_eth_payload_axis_tlast_list)) + s_eth_payload_axis_tid = ConcatSignal(*reversed(s_eth_payload_axis_tid_list)) + s_eth_payload_axis_tdest = ConcatSignal(*reversed(s_eth_payload_axis_tdest_list)) + s_eth_payload_axis_tuser = ConcatSignal(*reversed(s_eth_payload_axis_tuser_list)) + + m_eth_hdr_ready = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + + enable = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_eth_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_eth_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_eth_hdr_ready_list = [s_eth_hdr_ready(i) for i in range(S_COUNT)] + s_eth_payload_axis_tready_list = [s_eth_payload_axis_tready(i) for i in range(S_COUNT)] + + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_eth_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_eth_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_eth_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = eth_ep.EthFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready_list[k], + eth_hdr_valid=s_eth_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + eth_payload_tdata=s_eth_payload_axis_tdata_list[k], + eth_payload_tkeep=s_eth_payload_axis_tkeep_list[k], + eth_payload_tvalid=s_eth_payload_axis_tvalid_list[k], + eth_payload_tready=s_eth_payload_axis_tready_list[k], + eth_payload_tlast=s_eth_payload_axis_tlast_list[k], + eth_payload_tuser=s_eth_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = eth_ep.EthFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tid=s_eth_payload_axis_tid, + s_eth_payload_axis_tdest=s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tid=m_eth_payload_axis_tid, + m_eth_payload_axis_tdest=m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + enable=enable, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + select.next = 2 + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_eth_mux_4.v b/fpga/lib/eth/tb/test_eth_mux_4.v new file mode 100644 index 000000000..243903a3f --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mux_4.v @@ -0,0 +1,176 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for eth_mux + */ +module test_eth_mux_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_eth_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_eth_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_eth_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_eth_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_eth_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_eth_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_eth_payload_axis_tuser = 0; + +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +reg enable = 0; +reg [1:0] select = 0; + +// Outputs +wire [S_COUNT-1:0] s_eth_hdr_ready; +wire [S_COUNT-1:0] s_eth_payload_axis_tready; + +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_eth_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_eth_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tid, + s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + enable, + select + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tid, + m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser + ); + + // dump file + $dumpfile("test_eth_mux_4.lxt"); + $dumpvars(0, test_eth_mux_4); +end + +eth_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tid(s_eth_payload_axis_tid), + .s_eth_payload_axis_tdest(s_eth_payload_axis_tdest), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tid(m_eth_payload_axis_tid), + .m_eth_payload_axis_tdest(m_eth_payload_axis_tdest), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Control + .enable(enable), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_mux_64_4.py b/fpga/lib/eth/tb/test_eth_mux_64_4.py new file mode 100755 index 000000000..2b263523f --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mux_64_4.py @@ -0,0 +1,437 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep + +module = 'eth_mux' +testbench = 'test_%s_64_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_eth_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_eth_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_eth_hdr_valid = ConcatSignal(*reversed(s_eth_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_eth_payload_axis_tdata = ConcatSignal(*reversed(s_eth_payload_axis_tdata_list)) + s_eth_payload_axis_tkeep = ConcatSignal(*reversed(s_eth_payload_axis_tkeep_list)) + s_eth_payload_axis_tvalid = ConcatSignal(*reversed(s_eth_payload_axis_tvalid_list)) + s_eth_payload_axis_tlast = ConcatSignal(*reversed(s_eth_payload_axis_tlast_list)) + s_eth_payload_axis_tid = ConcatSignal(*reversed(s_eth_payload_axis_tid_list)) + s_eth_payload_axis_tdest = ConcatSignal(*reversed(s_eth_payload_axis_tdest_list)) + s_eth_payload_axis_tuser = ConcatSignal(*reversed(s_eth_payload_axis_tuser_list)) + + m_eth_hdr_ready = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + + enable = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_eth_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_eth_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_eth_hdr_ready_list = [s_eth_hdr_ready(i) for i in range(S_COUNT)] + s_eth_payload_axis_tready_list = [s_eth_payload_axis_tready(i) for i in range(S_COUNT)] + + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_eth_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_eth_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_eth_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = eth_ep.EthFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready_list[k], + eth_hdr_valid=s_eth_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + eth_payload_tdata=s_eth_payload_axis_tdata_list[k], + eth_payload_tkeep=s_eth_payload_axis_tkeep_list[k], + eth_payload_tvalid=s_eth_payload_axis_tvalid_list[k], + eth_payload_tready=s_eth_payload_axis_tready_list[k], + eth_payload_tlast=s_eth_payload_axis_tlast_list[k], + eth_payload_tuser=s_eth_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = eth_ep.EthFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tid=s_eth_payload_axis_tid, + s_eth_payload_axis_tdest=s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tid=m_eth_payload_axis_tid, + m_eth_payload_axis_tdest=m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + enable=enable, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + select.next = 2 + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(32)) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(32)) + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_eth_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_eth_mux_64_4.v b/fpga/lib/eth/tb/test_eth_mux_64_4.v new file mode 100644 index 000000000..3353806a4 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_mux_64_4.v @@ -0,0 +1,176 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for eth_mux + */ +module test_eth_mux_64_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_eth_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_eth_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_eth_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_eth_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_eth_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_eth_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_eth_payload_axis_tuser = 0; + +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +reg enable = 0; +reg [1:0] select = 0; + +// Outputs +wire [S_COUNT-1:0] s_eth_hdr_ready; +wire [S_COUNT-1:0] s_eth_payload_axis_tready; + +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_eth_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_eth_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tid, + s_eth_payload_axis_tdest, + s_eth_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + enable, + select + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tid, + m_eth_payload_axis_tdest, + m_eth_payload_axis_tuser + ); + + // dump file + $dumpfile("test_eth_mux_64_4.lxt"); + $dumpvars(0, test_eth_mux_64_4); +end + +eth_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tid(s_eth_payload_axis_tid), + .s_eth_payload_axis_tdest(s_eth_payload_axis_tdest), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tid(m_eth_payload_axis_tid), + .m_eth_payload_axis_tdest(m_eth_payload_axis_tdest), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Control + .enable(enable), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_phy_10g_64.py b/fpga/lib/eth/tb/test_eth_phy_10g_64.py new file mode 100755 index 000000000..174877801 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_phy_10g_64.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python +""" + +Copyright (c) 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep +import baser_serdes_ep + +module = 'eth_phy_10g' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/eth_phy_10g_rx.v") +srcs.append("../rtl/eth_phy_10g_rx_if.v") +srcs.append("../rtl/eth_phy_10g_rx_ber_mon.v") +srcs.append("../rtl/eth_phy_10g_rx_frame_sync.v") +srcs.append("../rtl/eth_phy_10g_tx.v") +srcs.append("../rtl/eth_phy_10g_tx_if.v") +srcs.append("../rtl/xgmii_baser_dec_64.v") +srcs.append("../rtl/xgmii_baser_enc_64.v") +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + CTRL_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + BIT_REVERSE = 0 + SCRAMBLER_DISABLE = 0 + PRBS31_ENABLE = 1 + TX_SERDES_PIPELINE = 2 + RX_SERDES_PIPELINE = 2 + SLIP_COUNT_WIDTH = 3 + COUNT_125US = 1250/6.4 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + xgmii_txd = Signal(intbv(0)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0)[CTRL_WIDTH:]) + serdes_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + tx_prbs31_enable = Signal(bool(0)) + rx_prbs31_enable = Signal(bool(0)) + + serdes_rx_data_int = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr_int = Signal(intbv(1)[HDR_WIDTH:]) + + # Outputs + xgmii_rxd = Signal(intbv(0)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0)[CTRL_WIDTH:]) + serdes_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_tx_hdr = Signal(intbv(0)[HDR_WIDTH:]) + serdes_rx_bitslip = Signal(bool(0)) + rx_error_count = Signal(intbv(0)[7:]) + rx_bad_block = Signal(bool(0)) + rx_block_lock = Signal(bool(0)) + rx_high_ber = Signal(bool(0)) + + # sources and sinks + xgmii_source = xgmii_ep.XGMIISource() + + xgmii_source_logic = xgmii_source.create_logic( + tx_clk, + tx_rst, + txd=xgmii_txd, + txc=xgmii_txc, + name='xgmii_source' + ) + + xgmii_sink = xgmii_ep.XGMIISink() + + xgmii_sink_logic = xgmii_sink.create_logic( + rx_clk, + rx_rst, + rxd=xgmii_rxd, + rxc=xgmii_rxc, + name='xgmii_sink' + ) + + serdes_source = baser_serdes_ep.BaseRSerdesSource() + + serdes_source_logic = serdes_source.create_logic( + rx_clk, + tx_data=serdes_rx_data_int, + tx_header=serdes_rx_hdr_int, + name='serdes_source' + ) + + serdes_sink = baser_serdes_ep.BaseRSerdesSink() + + serdes_sink_logic = serdes_sink.create_logic( + tx_clk, + rx_data=serdes_tx_data, + rx_header=serdes_tx_hdr, + name='serdes_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + serdes_tx_data=serdes_tx_data, + serdes_tx_hdr=serdes_tx_hdr, + serdes_rx_data=serdes_rx_data, + serdes_rx_hdr=serdes_rx_hdr, + serdes_rx_bitslip=serdes_rx_bitslip, + rx_error_count=rx_error_count, + rx_bad_block=rx_bad_block, + rx_block_lock=rx_block_lock, + rx_high_ber=rx_high_ber, + tx_prbs31_enable=tx_prbs31_enable, + rx_prbs31_enable=rx_prbs31_enable + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + rx_clk.next = not rx_clk + tx_clk.next = not tx_clk + + load_bit_offset = [] + + @instance + def shift_bits(): + bit_offset = 0 + last_data = 0 + + while True: + yield clk.posedge + + if load_bit_offset: + bit_offset = load_bit_offset.pop(0) + + if serdes_rx_bitslip: + bit_offset += 1 + + bit_offset = bit_offset % 66 + + data = int(serdes_rx_data_int) << 2 | int(serdes_rx_hdr_int) + + out_data = ((last_data | data << 66) >> 66-bit_offset) & 0x3ffffffffffffffff + + last_data = data + + serdes_rx_data.next = out_data >> 2 + serdes_rx_hdr.next = out_data & 3 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + # wait for block lock + while not rx_block_lock: + yield clk.posedge + + # dump garbage + while not xgmii_sink.empty(): + xgmii_sink.recv() + + yield clk.posedge + print("test 1: test RX packet") + current_test.next = 1 + + test_frame = bytearray(range(128)) + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame) + + xgmii_source.send(xgmii_frame) + + yield serdes_sink.wait() + rx_frame = serdes_sink.recv() + + assert rx_frame.data == xgmii_frame.data + + assert xgmii_sink.empty() + assert serdes_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test TX packet") + current_test.next = 2 + + test_frame = bytearray(range(128)) + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame) + + serdes_source.send(xgmii_frame) + + yield xgmii_sink.wait() + rx_frame = xgmii_sink.recv() + + assert rx_frame.data == xgmii_frame.data + + assert xgmii_sink.empty() + assert serdes_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_phy_10g_64.v b/fpga/lib/eth/tb/test_eth_phy_10g_64.v new file mode 100644 index 000000000..51939ce3b --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_phy_10g_64.v @@ -0,0 +1,141 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * Testbench for eth_phy_10g + */ +module test_eth_phy_10g_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; +parameter BIT_REVERSE = 0; +parameter SCRAMBLER_DISABLE = 0; +parameter PRBS31_ENABLE = 1; +parameter TX_SERDES_PIPELINE = 2; +parameter RX_SERDES_PIPELINE = 2; +parameter SLIP_COUNT_WIDTH = 3; +parameter COUNT_125US = 125000/6.4; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg [DATA_WIDTH-1:0] xgmii_txd = 0; +reg [CTRL_WIDTH-1:0] xgmii_txc = 0; +reg [DATA_WIDTH-1:0] serdes_rx_data = 0; +reg [HDR_WIDTH-1:0] serdes_rx_hdr = 1; +reg tx_prbs31_enable = 0; +reg rx_prbs31_enable = 0; + +// Outputs +wire [DATA_WIDTH-1:0] xgmii_rxd; +wire [CTRL_WIDTH-1:0] xgmii_rxc; +wire [DATA_WIDTH-1:0] serdes_tx_data; +wire [HDR_WIDTH-1:0] serdes_tx_hdr; +wire serdes_rx_bitslip; +wire [6:0] rx_error_count; +wire rx_bad_block; +wire rx_block_lock; +wire rx_high_ber; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + xgmii_txd, + xgmii_txc, + serdes_rx_data, + serdes_rx_hdr, + tx_prbs31_enable, + rx_prbs31_enable + ); + $to_myhdl( + xgmii_rxd, + xgmii_rxc, + serdes_tx_data, + serdes_tx_hdr, + serdes_rx_bitslip, + rx_error_count, + rx_bad_block, + rx_block_lock, + rx_high_ber + ); + + // dump file + $dumpfile("test_eth_phy_10g_64.lxt"); + $dumpvars(0, test_eth_phy_10g_64); +end + +eth_phy_10g #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .TX_SERDES_PIPELINE(TX_SERDES_PIPELINE), + .RX_SERDES_PIPELINE(RX_SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_error_count(rx_error_count), + .rx_bad_block(rx_bad_block), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .tx_prbs31_enable(tx_prbs31_enable), + .rx_prbs31_enable(rx_prbs31_enable) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_phy_10g_rx_64.py b/fpga/lib/eth/tb/test_eth_phy_10g_rx_64.py new file mode 100755 index 000000000..22be35f79 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_phy_10g_rx_64.py @@ -0,0 +1,330 @@ +#!/usr/bin/env python +""" + +Copyright (c) 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep +import baser_serdes_ep + +module = 'eth_phy_10g_rx' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/eth_phy_10g_rx_if.v") +srcs.append("../rtl/eth_phy_10g_rx_ber_mon.v") +srcs.append("../rtl/eth_phy_10g_rx_frame_sync.v") +srcs.append("../rtl/xgmii_baser_dec_64.v") +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def prbs31(width=8, state=0x7fffffff): + while True: + out = 0 + for i in range(width): + if bool(state & 0x08000000) ^ bool(state & 0x40000000): + state = ((state & 0x3fffffff) << 1) | 1 + out = out | 2**i + else: + state = (state & 0x3fffffff) << 1 + yield ~out & (2**width-1) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + CTRL_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + BIT_REVERSE = 0 + SCRAMBLER_DISABLE = 0 + PRBS31_ENABLE = 1 + SERDES_PIPELINE = 2 + SLIP_COUNT_WIDTH = 3 + COUNT_125US = 1250/6.4 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + serdes_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + rx_prbs31_enable = Signal(bool(0)) + + serdes_rx_data_int = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr_int = Signal(intbv(1)[HDR_WIDTH:]) + + # Outputs + xgmii_rxd = Signal(intbv(0)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0)[CTRL_WIDTH:]) + serdes_rx_bitslip = Signal(bool(0)) + rx_error_count = Signal(intbv(0)[7:]) + rx_bad_block = Signal(bool(0)) + rx_block_lock = Signal(bool(0)) + rx_high_ber = Signal(bool(0)) + + # sources and sinks + source = baser_serdes_ep.BaseRSerdesSource() + + source_logic = source.create_logic( + clk, + tx_data=serdes_rx_data_int, + tx_header=serdes_rx_hdr_int, + name='source' + ) + + sink = xgmii_ep.XGMIISink() + + sink_logic = sink.create_logic( + clk, + rst, + rxd=xgmii_rxd, + rxc=xgmii_rxc, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + serdes_rx_data=serdes_rx_data, + serdes_rx_hdr=serdes_rx_hdr, + serdes_rx_bitslip=serdes_rx_bitslip, + rx_error_count=rx_error_count, + rx_bad_block=rx_bad_block, + rx_block_lock=rx_block_lock, + rx_high_ber=rx_high_ber, + rx_prbs31_enable=rx_prbs31_enable + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + load_bit_offset = [] + prbs_en = Signal(bool(0)) + + @instance + def shift_bits(): + bit_offset = 0 + last_data = 0 + + prbs_gen = prbs31(66) + + while True: + yield clk.posedge + + if load_bit_offset: + bit_offset = load_bit_offset.pop(0) + + if serdes_rx_bitslip: + bit_offset += 1 + + bit_offset = bit_offset % 66 + + data = int(serdes_rx_data_int) << 2 | int(serdes_rx_hdr_int) + + out_data = ((last_data | data << 66) >> 66-bit_offset) & 0x3ffffffffffffffff + + last_data = data + + if prbs_en: + out_data = next(prbs_gen) + + serdes_rx_data.next = out_data >> 2 + serdes_rx_hdr.next = out_data & 3 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + # wait for block lock + while not rx_block_lock: + yield clk.posedge + + # dump garbage + while not sink.empty(): + sink.recv() + + for payload_len in list(range(16,34)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = bytearray(range(payload_len)) + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: errored frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + #assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: test frame sync") + current_test.next = 4 + + assert rx_block_lock + + load_bit_offset.append(33) + + yield delay(600) + + assert not rx_block_lock + assert rx_high_ber + + yield delay(3000) + + assert rx_block_lock + + yield delay(2000) + + assert not rx_high_ber + + yield delay(100) + + yield clk.posedge + print("test 5: PRBS31 check") + current_test.next = 5 + + rx_prbs31_enable.next = True + + yield delay(100) + + for k in range(20): + yield clk.posedge + assert rx_error_count > 0 + + prbs_en.next = True + + yield delay(100) + + for k in range(20): + yield clk.posedge + assert rx_error_count == 0 + + prbs_en.next = False + + rx_prbs31_enable.next = False + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_phy_10g_rx_64.v b/fpga/lib/eth/tb/test_eth_phy_10g_rx_64.v new file mode 100644 index 000000000..846088af6 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_phy_10g_rx_64.v @@ -0,0 +1,114 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * Testbench for eth_phy_10g_rx + */ +module test_eth_phy_10g_rx_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; +parameter BIT_REVERSE = 0; +parameter SCRAMBLER_DISABLE = 0; +parameter PRBS31_ENABLE = 1; +parameter SERDES_PIPELINE = 2; +parameter SLIP_COUNT_WIDTH = 3; +parameter COUNT_125US = 1250/6.4; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] serdes_rx_data = 0; +reg [HDR_WIDTH-1:0] serdes_rx_hdr = 1; +reg rx_prbs31_enable = 0; + +// Outputs +wire [DATA_WIDTH-1:0] xgmii_rxd; +wire [CTRL_WIDTH-1:0] xgmii_rxc; +wire serdes_rx_bitslip; +wire [6:0] rx_error_count; +wire rx_bad_block; +wire rx_block_lock; +wire rx_high_ber; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + serdes_rx_data, + serdes_rx_hdr, + rx_prbs31_enable + ); + $to_myhdl( + xgmii_rxd, + xgmii_rxc, + serdes_rx_bitslip, + rx_error_count, + rx_bad_block, + rx_block_lock, + rx_high_ber + ); + + // dump file + $dumpfile("test_eth_phy_10g_rx_64.lxt"); + $dumpvars(0, test_eth_phy_10g_rx_64); +end + +eth_phy_10g_rx #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +UUT ( + .clk(clk), + .rst(rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_error_count(rx_error_count), + .rx_bad_block(rx_bad_block), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_prbs31_enable(rx_prbs31_enable) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_eth_phy_10g_tx_64.py b/fpga/lib/eth/tb/test_eth_phy_10g_tx_64.py new file mode 100755 index 000000000..1bed6c7a9 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_phy_10g_tx_64.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python +""" + +Copyright (c) 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep +import baser_serdes_ep + +module = 'eth_phy_10g_tx' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/eth_phy_10g_tx_if.v") +srcs.append("../rtl/xgmii_baser_enc_64.v") +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def prbs31(width=8, state=0x7fffffff): + while True: + out = 0 + for i in range(width): + if bool(state & 0x08000000) ^ bool(state & 0x40000000): + state = ((state & 0x3fffffff) << 1) | 1 + out = out | 2**i + else: + state = (state & 0x3fffffff) << 1 + yield ~out & (2**width-1) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + CTRL_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + BIT_REVERSE = 0 + SCRAMBLER_DISABLE = 0 + PRBS31_ENABLE = 1 + SERDES_PIPELINE = 2 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + xgmii_txd = Signal(intbv(0)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0)[CTRL_WIDTH:]) + tx_prbs31_enable = Signal(bool(0)) + + # Outputs + serdes_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_tx_hdr = Signal(intbv(0)[HDR_WIDTH:]) + + # sources and sinks + source = xgmii_ep.XGMIISource() + + source_logic = source.create_logic( + clk, + rst, + txd=xgmii_txd, + txc=xgmii_txc, + name='source' + ) + + sink = baser_serdes_ep.BaseRSerdesSink() + + sink_logic = sink.create_logic( + clk, + rx_data=serdes_tx_data, + rx_header=serdes_tx_hdr, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + serdes_tx_data=serdes_tx_data, + serdes_tx_hdr=serdes_tx_hdr, + tx_prbs31_enable=tx_prbs31_enable + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(16,34)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = bytearray(range(payload_len)) + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: errored frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + #assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: PRBS31 generation") + current_test.next = 4 + + tx_prbs31_enable.next = True + + yield delay(100) + + prbs_gen = prbs31(66) + prbs_data = [next(prbs_gen) for x in range(100)] + + for k in range(20): + yield clk.posedge + data = int(serdes_tx_data) << 2 | int(serdes_tx_hdr) + assert data in prbs_data + + tx_prbs31_enable.next = False + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_eth_phy_10g_tx_64.v b/fpga/lib/eth/tb/test_eth_phy_10g_tx_64.v new file mode 100644 index 000000000..eb45ce9a1 --- /dev/null +++ b/fpga/lib/eth/tb/test_eth_phy_10g_tx_64.v @@ -0,0 +1,95 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * Testbench for eth_phy_10g_tx + */ +module test_eth_phy_10g_tx_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; +parameter BIT_REVERSE = 0; +parameter SCRAMBLER_DISABLE = 0; +parameter PRBS31_ENABLE = 1; +parameter SERDES_PIPELINE = 2; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] xgmii_txd = 0; +reg [CTRL_WIDTH-1:0] xgmii_txc = 0; +reg tx_prbs31_enable = 0; + +// Outputs +wire [DATA_WIDTH-1:0] serdes_tx_data; +wire [HDR_WIDTH-1:0] serdes_tx_hdr; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + xgmii_txd, + xgmii_txc, + tx_prbs31_enable + ); + $to_myhdl( + serdes_tx_data, + serdes_tx_hdr + ); + + // dump file + $dumpfile("test_eth_phy_10g_tx_64.lxt"); + $dumpvars(0, test_eth_phy_10g_tx_64); +end + +eth_phy_10g_tx #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(SERDES_PIPELINE) +) +UUT ( + .clk(clk), + .rst(rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .tx_prbs31_enable(tx_prbs31_enable) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip.py b/fpga/lib/eth/tb/test_ip.py new file mode 100755 index 000000000..354c8632c --- /dev/null +++ b/fpga/lib/eth/tb/test_ip.py @@ -0,0 +1,545 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import ip_ep + +module = 'ip' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/ip_eth_rx.v") +srcs.append("../rtl/ip_eth_tx.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + arp_request_ready = Signal(bool(0)) + arp_response_valid = Signal(bool(0)) + arp_response_error = Signal(bool(0)) + arp_response_mac = Signal(intbv(0)[48:]) + s_ip_hdr_valid = Signal(bool(0)) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + arp_request_valid = Signal(bool(0)) + arp_request_ip = Signal(intbv(0)[32:]) + arp_response_ready = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_ip_eth_dest_mac = Signal(intbv(0)[48:]) + m_ip_eth_src_mac = Signal(intbv(0)[48:]) + m_ip_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + rx_busy = Signal(bool(0)) + tx_busy = Signal(bool(0)) + rx_error_header_early_termination = Signal(bool(0)) + rx_error_payload_early_termination = Signal(bool(0)) + rx_error_invalid_header = Signal(bool(0)) + rx_error_invalid_checksum = Signal(bool(0)) + tx_error_payload_early_termination = Signal(bool(0)) + tx_error_arp_failed = Signal(bool(0)) + local_mac = Signal(intbv(0)[48:]) + local_ip = Signal(intbv(0)[32:]) + + # sources and sinks + eth_source_pause = Signal(bool(0)) + eth_sink_pause = Signal(bool(0)) + ip_source_pause = Signal(bool(0)) + ip_sink_pause = Signal(bool(0)) + + eth_source = eth_ep.EthFrameSource() + + eth_source_logic = eth_source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=eth_source_pause, + name='eth_source' + ) + + eth_sink = eth_ep.EthFrameSink() + + eth_sink_logic = eth_sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=eth_sink_pause, + name='eth_sink' + ) + + ip_source = ip_ep.IPFrameSource() + + ip_source_logic = ip_source.create_logic( + clk, + rst, + ip_hdr_valid=s_ip_hdr_valid, + ip_hdr_ready=s_ip_hdr_ready, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=ip_source_pause, + name='ip_source' + ) + + ip_sink = ip_ep.IPFrameSink() + + ip_sink_logic = ip_sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_ip_eth_dest_mac, + eth_src_mac=m_ip_eth_src_mac, + eth_type=m_ip_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=ip_sink_pause, + name='ip_sink' + ) + + arp_request_sink = axis_ep.AXIStreamSink() + + arp_request_sink_logic = arp_request_sink.create_logic( + clk, + rst, + tdata=(arp_request_ip,), + tvalid=arp_request_valid, + tready=arp_request_ready, + name='arp_request_sink' + ) + + arp_response_source = axis_ep.AXIStreamSource() + + arp_response_source_logic = arp_response_source.create_logic( + clk, + rst, + tdata=(arp_response_error, arp_response_mac), + tvalid=arp_response_valid, + tready=arp_response_ready, + name='arp_response_source' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + arp_request_valid=arp_request_valid, + arp_request_ready=arp_request_ready, + arp_request_ip=arp_request_ip, + arp_response_valid=arp_response_valid, + arp_response_ready=arp_response_ready, + arp_response_error=arp_response_error, + arp_response_mac=arp_response_mac, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_ip_eth_dest_mac=m_ip_eth_dest_mac, + m_ip_eth_src_mac=m_ip_eth_src_mac, + m_ip_eth_type=m_ip_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + rx_busy=rx_busy, + tx_busy=tx_busy, + rx_error_header_early_termination=rx_error_header_early_termination, + rx_error_payload_early_termination=rx_error_payload_early_termination, + rx_error_invalid_header=rx_error_invalid_header, + rx_error_invalid_checksum=rx_error_invalid_checksum, + tx_error_payload_early_termination=tx_error_payload_early_termination, + tx_error_arp_failed=tx_error_arp_failed, + + local_mac=local_mac, + local_ip=local_ip + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + arp_table = {} + + @instance + def arp_emu(): + while True: + yield clk.posedge + + if not arp_request_sink.empty(): + req_ip = arp_request_sink.recv().data[0][0] + + if req_ip in arp_table: + arp_response_source.send([(0, arp_table[req_ip])]) + else: + arp_response_source.send([(1, 0)]) + + rx_error_header_early_termination_asserted = Signal(bool(0)) + rx_error_payload_early_termination_asserted = Signal(bool(0)) + rx_error_invalid_header_asserted = Signal(bool(0)) + rx_error_invalid_checksum_asserted = Signal(bool(0)) + tx_error_payload_early_termination_asserted = Signal(bool(0)) + tx_error_arp_failed_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_header_early_termination): + rx_error_header_early_termination_asserted.next = 1 + if (rx_error_payload_early_termination): + rx_error_payload_early_termination_asserted.next = 1 + if (rx_error_invalid_header): + rx_error_invalid_header_asserted.next = 1 + if (rx_error_invalid_checksum): + rx_error_invalid_checksum_asserted.next = 1 + if (tx_error_payload_early_termination): + tx_error_payload_early_termination_asserted.next = 1 + if (tx_error_arp_failed): + tx_error_arp_failed_asserted.next = 1 + + def wait_normal(): + while (s_eth_payload_axis_tvalid or s_ip_payload_axis_tvalid or + m_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or + s_eth_hdr_valid or s_ip_hdr_valid): + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # set MAC and IP address + local_mac.next = 0x5A5152535455 + local_ip.next = 0xc0a80164 + + # put an entry in the ARP table + arp_table[0xc0a80165] = 0xDAD1D2D3D4D5 + + yield clk.posedge + print("test 1: test IP RX packet") + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + eth_frame = test_frame.build_eth() + + eth_source.send(eth_frame) + + yield ip_sink.wait() + rx_frame = ip_sink.recv() + + assert rx_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test IP TX packet") + current_test.next = 2 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + yield eth_sink.wait() + rx_frame = eth_sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: test IP TX arp fail packet") + current_test.next = 2 + + tx_error_arp_failed_asserted.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80166 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + yield clk.posedge + yield clk.posedge + + yield wait_normal() + + yield clk.posedge + yield clk.posedge + + assert tx_error_arp_failed_asserted + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip.v b/fpga/lib/eth/tb/test_ip.v new file mode 100644 index 000000000..68ca311d9 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip.v @@ -0,0 +1,292 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip + */ +module test_ip; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [7:0] s_eth_payload_axis_tdata = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg arp_request_ready = 0; +reg arp_response_valid = 0; +reg arp_response_error = 0; +reg [47:0] arp_response_mac = 0; +reg s_ip_hdr_valid = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [7:0] s_ip_payload_axis_tdata = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; +reg [47:0] local_mac = 0; +reg [31:0] local_ip = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [7:0] m_eth_payload_axis_tdata; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire arp_request_valid; +wire [31:0] arp_request_ip; +wire arp_response_ready; +wire m_ip_hdr_valid; +wire [47:0] m_ip_eth_dest_mac; +wire [47:0] m_ip_eth_src_mac; +wire [15:0] m_ip_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [7:0] m_ip_payload_axis_tdata; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire rx_busy; +wire tx_busy; +wire rx_error_header_early_termination; +wire rx_error_payload_early_termination; +wire rx_error_invalid_header; +wire rx_error_invalid_checksum; +wire tx_error_payload_early_termination; +wire tx_error_arp_failed; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + arp_request_ready, + arp_response_valid, + arp_response_error, + arp_response_mac, + s_ip_hdr_valid, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_ttl, + s_ip_protocol, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + local_mac, + local_ip + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + arp_request_valid, + arp_request_ip, + arp_response_ready, + m_ip_hdr_valid, + m_ip_eth_dest_mac, + m_ip_eth_src_mac, + m_ip_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + rx_busy, + tx_busy, + rx_error_header_early_termination, + rx_error_payload_early_termination, + rx_error_invalid_header, + rx_error_invalid_checksum, + tx_error_payload_early_termination, + tx_error_arp_failed + ); + + // dump file + $dumpfile("test_ip.lxt"); + $dumpvars(0, test_ip); +end + +ip +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .rx_busy(rx_busy), + .tx_busy(tx_busy), + .rx_error_header_early_termination(rx_error_header_early_termination), + .rx_error_payload_early_termination(rx_error_payload_early_termination), + .rx_error_invalid_header(rx_error_invalid_header), + .rx_error_invalid_checksum(rx_error_invalid_checksum), + .tx_error_payload_early_termination(tx_error_payload_early_termination), + .tx_error_arp_failed(tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_64.py b/fpga/lib/eth/tb/test_ip_64.py new file mode 100755 index 000000000..e46b77e25 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_64.py @@ -0,0 +1,557 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import ip_ep + +module = 'ip_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/ip_eth_rx_64.v") +srcs.append("../rtl/ip_eth_tx_64.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + s_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + arp_request_ready = Signal(bool(0)) + arp_response_valid = Signal(bool(0)) + arp_response_error = Signal(bool(0)) + arp_response_mac = Signal(intbv(0)[48:]) + s_ip_hdr_valid = Signal(bool(0)) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + s_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + m_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + arp_request_valid = Signal(bool(0)) + arp_request_ip = Signal(intbv(0)[32:]) + arp_response_ready = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_ip_eth_dest_mac = Signal(intbv(0)[48:]) + m_ip_eth_src_mac = Signal(intbv(0)[48:]) + m_ip_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + m_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + rx_busy = Signal(bool(0)) + tx_busy = Signal(bool(0)) + rx_error_header_early_termination = Signal(bool(0)) + rx_error_payload_early_termination = Signal(bool(0)) + rx_error_invalid_header = Signal(bool(0)) + rx_error_invalid_checksum = Signal(bool(0)) + tx_error_payload_early_termination = Signal(bool(0)) + tx_error_arp_failed = Signal(bool(0)) + local_mac = Signal(intbv(0)[48:]) + local_ip = Signal(intbv(0)[32:]) + + # sources and sinks + eth_source_pause = Signal(bool(0)) + eth_sink_pause = Signal(bool(0)) + ip_source_pause = Signal(bool(0)) + ip_sink_pause = Signal(bool(0)) + + eth_source = eth_ep.EthFrameSource() + + eth_source_logic = eth_source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tkeep=s_eth_payload_axis_tkeep, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=eth_source_pause, + name='eth_source' + ) + + eth_sink = eth_ep.EthFrameSink() + + eth_sink_logic = eth_sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=eth_sink_pause, + name='eth_sink' + ) + + ip_source = ip_ep.IPFrameSource() + + ip_source_logic = ip_source.create_logic( + clk, + rst, + ip_hdr_valid=s_ip_hdr_valid, + ip_hdr_ready=s_ip_hdr_ready, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tkeep=s_ip_payload_axis_tkeep, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=ip_source_pause, + name='ip_source' + ) + + ip_sink = ip_ep.IPFrameSink() + + ip_sink_logic = ip_sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_ip_eth_dest_mac, + eth_src_mac=m_ip_eth_src_mac, + eth_type=m_ip_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tkeep=m_ip_payload_axis_tkeep, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=ip_sink_pause, + name='ip_sink' + ) + + arp_request_sink = axis_ep.AXIStreamSink() + + arp_request_sink_logic = arp_request_sink.create_logic( + clk, + rst, + tdata=(arp_request_ip,), + tvalid=arp_request_valid, + tready=arp_request_ready, + name='arp_request_sink' + ) + + arp_response_source = axis_ep.AXIStreamSource() + + arp_response_source_logic = arp_response_source.create_logic( + clk, + rst, + tdata=(arp_response_error, arp_response_mac), + tvalid=arp_response_valid, + tready=arp_response_ready, + name='arp_response_source' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + arp_request_valid=arp_request_valid, + arp_request_ready=arp_request_ready, + arp_request_ip=arp_request_ip, + arp_response_valid=arp_response_valid, + arp_response_ready=arp_response_ready, + arp_response_error=arp_response_error, + arp_response_mac=arp_response_mac, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_ip_eth_dest_mac=m_ip_eth_dest_mac, + m_ip_eth_src_mac=m_ip_eth_src_mac, + m_ip_eth_type=m_ip_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + rx_busy=rx_busy, + tx_busy=tx_busy, + rx_error_header_early_termination=rx_error_header_early_termination, + rx_error_payload_early_termination=rx_error_payload_early_termination, + rx_error_invalid_header=rx_error_invalid_header, + rx_error_invalid_checksum=rx_error_invalid_checksum, + tx_error_payload_early_termination=tx_error_payload_early_termination, + tx_error_arp_failed=tx_error_arp_failed, + + local_mac=local_mac, + local_ip=local_ip + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + arp_table = {} + + @instance + def arp_emu(): + while True: + yield clk.posedge + + if not arp_request_sink.empty(): + req_ip = arp_request_sink.recv().data[0][0] + + if req_ip in arp_table: + arp_response_source.send([(0, arp_table[req_ip])]) + else: + arp_response_source.send([(1, 0)]) + + rx_error_header_early_termination_asserted = Signal(bool(0)) + rx_error_payload_early_termination_asserted = Signal(bool(0)) + rx_error_invalid_header_asserted = Signal(bool(0)) + rx_error_invalid_checksum_asserted = Signal(bool(0)) + tx_error_payload_early_termination_asserted = Signal(bool(0)) + tx_error_arp_failed_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_header_early_termination): + rx_error_header_early_termination_asserted.next = 1 + if (rx_error_payload_early_termination): + rx_error_payload_early_termination_asserted.next = 1 + if (rx_error_invalid_header): + rx_error_invalid_header_asserted.next = 1 + if (rx_error_invalid_checksum): + rx_error_invalid_checksum_asserted.next = 1 + if (tx_error_payload_early_termination): + tx_error_payload_early_termination_asserted.next = 1 + if (tx_error_arp_failed): + tx_error_arp_failed_asserted.next = 1 + + def wait_normal(): + while (s_eth_payload_axis_tvalid or s_ip_payload_axis_tvalid or + m_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or + s_eth_hdr_valid or s_ip_hdr_valid): + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # set MAC and IP address + local_mac.next = 0x5A5152535455 + local_ip.next = 0xc0a80164 + + # put an entry in the ARP table + arp_table[0xc0a80165] = 0xDAD1D2D3D4D5 + + yield clk.posedge + print("test 1: test IP RX packet") + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + eth_frame = test_frame.build_eth() + + eth_source.send(eth_frame) + + yield ip_sink.wait() + rx_frame = ip_sink.recv() + + assert rx_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test IP TX packet") + current_test.next = 2 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + yield eth_sink.wait() + rx_frame = eth_sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: test IP TX arp fail packet") + current_test.next = 2 + + tx_error_arp_failed_asserted.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80166 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + yield clk.posedge + yield clk.posedge + + yield wait_normal() + + yield clk.posedge + yield clk.posedge + + assert tx_error_arp_failed_asserted + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_64.v b/fpga/lib/eth/tb/test_ip_64.v new file mode 100644 index 000000000..a444ec362 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_64.v @@ -0,0 +1,303 @@ +/* + +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. + +*/ + + +`timescale 1ns / 1ps + +/* + * Testbench for ip_64 + */ +module test_ip_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [63:0] s_eth_payload_axis_tdata = 0; +reg [7:0] s_eth_payload_axis_tkeep = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg arp_request_ready = 0; +reg arp_response_valid = 0; +reg arp_response_error = 0; +reg [47:0] arp_response_mac = 0; +reg s_ip_hdr_valid = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [63:0] s_ip_payload_axis_tdata = 0; +reg [7:0] s_ip_payload_axis_tkeep = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; +reg [47:0] local_mac = 0; +reg [31:0] local_ip = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [63:0] m_eth_payload_axis_tdata; +wire [7:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire arp_request_valid; +wire [31:0] arp_request_ip; +wire arp_response_ready; +wire m_ip_hdr_valid; +wire [47:0] m_ip_eth_dest_mac; +wire [47:0] m_ip_eth_src_mac; +wire [15:0] m_ip_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [63:0] m_ip_payload_axis_tdata; +wire [7:0] m_ip_payload_axis_tkeep; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire rx_busy; +wire tx_busy; +wire rx_error_header_early_termination; +wire rx_error_payload_early_termination; +wire rx_error_invalid_header; +wire rx_error_invalid_checksum; +wire tx_error_payload_early_termination; +wire tx_error_arp_failed; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + arp_request_ready, + arp_response_valid, + arp_response_error, + arp_response_mac, + s_ip_hdr_valid, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_ttl, + s_ip_protocol, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + local_mac, + local_ip + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + arp_request_valid, + arp_request_ip, + arp_response_ready, + m_ip_hdr_valid, + m_ip_eth_dest_mac, + m_ip_eth_src_mac, + m_ip_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + rx_busy, + tx_busy, + rx_error_header_early_termination, + rx_error_payload_early_termination, + rx_error_invalid_header, + rx_error_invalid_checksum, + tx_error_payload_early_termination, + tx_error_arp_failed + ); + + // dump file + $dumpfile("test_ip_64.lxt"); + $dumpvars(0, test_ip_64); +end + +ip_64 +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .rx_busy(rx_busy), + .tx_busy(tx_busy), + .rx_error_header_early_termination(rx_error_header_early_termination), + .rx_error_payload_early_termination(rx_error_payload_early_termination), + .rx_error_invalid_header(rx_error_invalid_header), + .rx_error_invalid_checksum(rx_error_invalid_checksum), + .tx_error_payload_early_termination(tx_error_payload_early_termination), + .tx_error_arp_failed(tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_arb_mux_4.py b/fpga/lib/eth/tb/test_ip_arb_mux_4.py new file mode 100755 index 000000000..2127c395c --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_arb_mux_4.py @@ -0,0 +1,732 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import ip_ep + +module = 'ip_arb_mux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/axis/rtl/arbiter.v") +srcs.append("../lib/axis/rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + ARB_TYPE = "PRIORITY" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_version_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_ihl_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_dscp_list = [Signal(intbv(0)[6:]) for i in range(S_COUNT)] + s_ip_ecn_list = [Signal(intbv(0)[2:]) for i in range(S_COUNT)] + s_ip_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_identification_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_flags_list = [Signal(intbv(0)[3:]) for i in range(S_COUNT)] + s_ip_fragment_offset_list = [Signal(intbv(0)[13:]) for i in range(S_COUNT)] + s_ip_ttl_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_protocol_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_header_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_source_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_dest_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_ip_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_ip_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_ip_hdr_valid = ConcatSignal(*reversed(s_ip_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_ip_version = ConcatSignal(*reversed(s_ip_version_list)) + s_ip_ihl = ConcatSignal(*reversed(s_ip_ihl_list)) + s_ip_dscp = ConcatSignal(*reversed(s_ip_dscp_list)) + s_ip_ecn = ConcatSignal(*reversed(s_ip_ecn_list)) + s_ip_length = ConcatSignal(*reversed(s_ip_length_list)) + s_ip_identification = ConcatSignal(*reversed(s_ip_identification_list)) + s_ip_flags = ConcatSignal(*reversed(s_ip_flags_list)) + s_ip_fragment_offset = ConcatSignal(*reversed(s_ip_fragment_offset_list)) + s_ip_ttl = ConcatSignal(*reversed(s_ip_ttl_list)) + s_ip_protocol = ConcatSignal(*reversed(s_ip_protocol_list)) + s_ip_header_checksum = ConcatSignal(*reversed(s_ip_header_checksum_list)) + s_ip_source_ip = ConcatSignal(*reversed(s_ip_source_ip_list)) + s_ip_dest_ip = ConcatSignal(*reversed(s_ip_dest_ip_list)) + s_ip_payload_axis_tdata = ConcatSignal(*reversed(s_ip_payload_axis_tdata_list)) + s_ip_payload_axis_tkeep = ConcatSignal(*reversed(s_ip_payload_axis_tkeep_list)) + s_ip_payload_axis_tvalid = ConcatSignal(*reversed(s_ip_payload_axis_tvalid_list)) + s_ip_payload_axis_tlast = ConcatSignal(*reversed(s_ip_payload_axis_tlast_list)) + s_ip_payload_axis_tid = ConcatSignal(*reversed(s_ip_payload_axis_tid_list)) + s_ip_payload_axis_tdest = ConcatSignal(*reversed(s_ip_payload_axis_tdest_list)) + s_ip_payload_axis_tuser = ConcatSignal(*reversed(s_ip_payload_axis_tuser_list)) + + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_ip_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_ip_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_ip_hdr_ready_list = [s_ip_hdr_ready(i) for i in range(S_COUNT)] + s_ip_payload_axis_tready_list = [s_ip_payload_axis_tready(i) for i in range(S_COUNT)] + + m_ip_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_ip_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_ip_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_ip_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = ip_ep.IPFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready_list[k], + ip_hdr_valid=s_ip_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + ip_version=s_ip_version_list[k], + ip_ihl=s_ip_ihl_list[k], + ip_dscp=s_ip_dscp_list[k], + ip_ecn=s_ip_ecn_list[k], + ip_length=s_ip_length_list[k], + ip_identification=s_ip_identification_list[k], + ip_flags=s_ip_flags_list[k], + ip_fragment_offset=s_ip_fragment_offset_list[k], + ip_ttl=s_ip_ttl_list[k], + ip_protocol=s_ip_protocol_list[k], + ip_header_checksum=s_ip_header_checksum_list[k], + ip_source_ip=s_ip_source_ip_list[k], + ip_dest_ip=s_ip_dest_ip_list[k], + ip_payload_tdata=s_ip_payload_axis_tdata_list[k], + ip_payload_tkeep=s_ip_payload_axis_tkeep_list[k], + ip_payload_tvalid=s_ip_payload_axis_tvalid_list[k], + ip_payload_tready=s_ip_payload_axis_tready_list[k], + ip_payload_tlast=s_ip_payload_axis_tlast_list[k], + ip_payload_tuser=s_ip_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = ip_ep.IPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tkeep=m_ip_payload_axis_tkeep, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tid=s_ip_payload_axis_tid, + s_ip_payload_axis_tdest=s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tid=m_ip_payload_axis_tid, + m_ip_payload_axis_tdest=m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: port 0") + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: port 1") + current_test.next = 2 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_ip_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_ip_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: back-to-back packets, different ports, arbitration test") + current_test.next = 7 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + yield clk.posedge + + yield delay(800) + yield clk.posedge + source_list[1].send(test_frame1) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_arb_mux_4.v b/fpga/lib/eth/tb/test_ip_arb_mux_4.v new file mode 100644 index 000000000..400c0bb1c --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_arb_mux_4.v @@ -0,0 +1,250 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_arb_mux + */ +module test_ip_arb_mux_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter ARB_TYPE = "PRIORITY"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_ip_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*4-1:0] s_ip_version = 0; +reg [S_COUNT*4-1:0] s_ip_ihl = 0; +reg [S_COUNT*6-1:0] s_ip_dscp = 0; +reg [S_COUNT*2-1:0] s_ip_ecn = 0; +reg [S_COUNT*16-1:0] s_ip_length = 0; +reg [S_COUNT*16-1:0] s_ip_identification = 0; +reg [S_COUNT*3-1:0] s_ip_flags = 0; +reg [S_COUNT*13-1:0] s_ip_fragment_offset = 0; +reg [S_COUNT*8-1:0] s_ip_ttl = 0; +reg [S_COUNT*8-1:0] s_ip_protocol = 0; +reg [S_COUNT*16-1:0] s_ip_header_checksum = 0; +reg [S_COUNT*32-1:0] s_ip_source_ip = 0; +reg [S_COUNT*32-1:0] s_ip_dest_ip = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_ip_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_ip_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_ip_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_ip_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_ip_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_ip_payload_axis_tuser = 0; + +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_ip_hdr_ready; +wire [S_COUNT-1:0] s_ip_payload_axis_tready; + +wire m_ip_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [DATA_WIDTH-1:0] m_ip_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_ip_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_ip_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tid, + s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_ip_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tid, + m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser + ); + + // dump file + $dumpfile("test_ip_arb_mux_4.lxt"); + $dumpvars(0, test_ip_arb_mux_4); +end + +ip_arb_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tid(s_ip_payload_axis_tid), + .s_ip_payload_axis_tdest(s_ip_payload_axis_tdest), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // Ethernet frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tid(m_ip_payload_axis_tid), + .m_ip_payload_axis_tdest(m_ip_payload_axis_tdest), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_arb_mux_64_4.py b/fpga/lib/eth/tb/test_ip_arb_mux_64_4.py new file mode 100755 index 000000000..ee633c952 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_arb_mux_64_4.py @@ -0,0 +1,732 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import ip_ep + +module = 'ip_arb_mux' +testbench = 'test_%s_64_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/axis/rtl/arbiter.v") +srcs.append("../lib/axis/rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + ARB_TYPE = "PRIORITY" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_version_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_ihl_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_dscp_list = [Signal(intbv(0)[6:]) for i in range(S_COUNT)] + s_ip_ecn_list = [Signal(intbv(0)[2:]) for i in range(S_COUNT)] + s_ip_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_identification_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_flags_list = [Signal(intbv(0)[3:]) for i in range(S_COUNT)] + s_ip_fragment_offset_list = [Signal(intbv(0)[13:]) for i in range(S_COUNT)] + s_ip_ttl_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_protocol_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_header_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_source_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_dest_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_ip_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_ip_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_ip_hdr_valid = ConcatSignal(*reversed(s_ip_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_ip_version = ConcatSignal(*reversed(s_ip_version_list)) + s_ip_ihl = ConcatSignal(*reversed(s_ip_ihl_list)) + s_ip_dscp = ConcatSignal(*reversed(s_ip_dscp_list)) + s_ip_ecn = ConcatSignal(*reversed(s_ip_ecn_list)) + s_ip_length = ConcatSignal(*reversed(s_ip_length_list)) + s_ip_identification = ConcatSignal(*reversed(s_ip_identification_list)) + s_ip_flags = ConcatSignal(*reversed(s_ip_flags_list)) + s_ip_fragment_offset = ConcatSignal(*reversed(s_ip_fragment_offset_list)) + s_ip_ttl = ConcatSignal(*reversed(s_ip_ttl_list)) + s_ip_protocol = ConcatSignal(*reversed(s_ip_protocol_list)) + s_ip_header_checksum = ConcatSignal(*reversed(s_ip_header_checksum_list)) + s_ip_source_ip = ConcatSignal(*reversed(s_ip_source_ip_list)) + s_ip_dest_ip = ConcatSignal(*reversed(s_ip_dest_ip_list)) + s_ip_payload_axis_tdata = ConcatSignal(*reversed(s_ip_payload_axis_tdata_list)) + s_ip_payload_axis_tkeep = ConcatSignal(*reversed(s_ip_payload_axis_tkeep_list)) + s_ip_payload_axis_tvalid = ConcatSignal(*reversed(s_ip_payload_axis_tvalid_list)) + s_ip_payload_axis_tlast = ConcatSignal(*reversed(s_ip_payload_axis_tlast_list)) + s_ip_payload_axis_tid = ConcatSignal(*reversed(s_ip_payload_axis_tid_list)) + s_ip_payload_axis_tdest = ConcatSignal(*reversed(s_ip_payload_axis_tdest_list)) + s_ip_payload_axis_tuser = ConcatSignal(*reversed(s_ip_payload_axis_tuser_list)) + + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_ip_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_ip_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_ip_hdr_ready_list = [s_ip_hdr_ready(i) for i in range(S_COUNT)] + s_ip_payload_axis_tready_list = [s_ip_payload_axis_tready(i) for i in range(S_COUNT)] + + m_ip_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_ip_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_ip_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_ip_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = ip_ep.IPFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready_list[k], + ip_hdr_valid=s_ip_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + ip_version=s_ip_version_list[k], + ip_ihl=s_ip_ihl_list[k], + ip_dscp=s_ip_dscp_list[k], + ip_ecn=s_ip_ecn_list[k], + ip_length=s_ip_length_list[k], + ip_identification=s_ip_identification_list[k], + ip_flags=s_ip_flags_list[k], + ip_fragment_offset=s_ip_fragment_offset_list[k], + ip_ttl=s_ip_ttl_list[k], + ip_protocol=s_ip_protocol_list[k], + ip_header_checksum=s_ip_header_checksum_list[k], + ip_source_ip=s_ip_source_ip_list[k], + ip_dest_ip=s_ip_dest_ip_list[k], + ip_payload_tdata=s_ip_payload_axis_tdata_list[k], + ip_payload_tkeep=s_ip_payload_axis_tkeep_list[k], + ip_payload_tvalid=s_ip_payload_axis_tvalid_list[k], + ip_payload_tready=s_ip_payload_axis_tready_list[k], + ip_payload_tlast=s_ip_payload_axis_tlast_list[k], + ip_payload_tuser=s_ip_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = ip_ep.IPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tkeep=m_ip_payload_axis_tkeep, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tid=s_ip_payload_axis_tid, + s_ip_payload_axis_tdest=s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tid=m_ip_payload_axis_tid, + m_ip_payload_axis_tdest=m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: port 0") + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: port 1") + current_test.next = 2 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_ip_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_ip_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: back-to-back packets, different ports, arbitration test") + current_test.next = 7 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + yield clk.posedge + + yield delay(120) + yield clk.posedge + source_list[1].send(test_frame1) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_arb_mux_64_4.v b/fpga/lib/eth/tb/test_ip_arb_mux_64_4.v new file mode 100644 index 000000000..5559ebb12 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_arb_mux_64_4.v @@ -0,0 +1,250 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_arb_mux + */ +module test_ip_arb_mux_64_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter ARB_TYPE = "PRIORITY"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_ip_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*4-1:0] s_ip_version = 0; +reg [S_COUNT*4-1:0] s_ip_ihl = 0; +reg [S_COUNT*6-1:0] s_ip_dscp = 0; +reg [S_COUNT*2-1:0] s_ip_ecn = 0; +reg [S_COUNT*16-1:0] s_ip_length = 0; +reg [S_COUNT*16-1:0] s_ip_identification = 0; +reg [S_COUNT*3-1:0] s_ip_flags = 0; +reg [S_COUNT*13-1:0] s_ip_fragment_offset = 0; +reg [S_COUNT*8-1:0] s_ip_ttl = 0; +reg [S_COUNT*8-1:0] s_ip_protocol = 0; +reg [S_COUNT*16-1:0] s_ip_header_checksum = 0; +reg [S_COUNT*32-1:0] s_ip_source_ip = 0; +reg [S_COUNT*32-1:0] s_ip_dest_ip = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_ip_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_ip_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_ip_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_ip_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_ip_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_ip_payload_axis_tuser = 0; + +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_ip_hdr_ready; +wire [S_COUNT-1:0] s_ip_payload_axis_tready; + +wire m_ip_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [DATA_WIDTH-1:0] m_ip_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_ip_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_ip_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tid, + s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_ip_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tid, + m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser + ); + + // dump file + $dumpfile("test_ip_arb_mux_64_4.lxt"); + $dumpvars(0, test_ip_arb_mux_64_4); +end + +ip_arb_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tid(s_ip_payload_axis_tid), + .s_ip_payload_axis_tdest(s_ip_payload_axis_tdest), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // Ethernet frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tid(m_ip_payload_axis_tid), + .m_ip_payload_axis_tdest(m_ip_payload_axis_tdest), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_complete.py b/fpga/lib/eth/tb/test_ip_complete.py new file mode 100755 index 000000000..f8a100b70 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_complete.py @@ -0,0 +1,568 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import ip_ep + +module = 'ip_complete' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/ip.v") +srcs.append("../rtl/ip_eth_rx.v") +srcs.append("../rtl/ip_eth_tx.v") +srcs.append("../rtl/arp.v") +srcs.append("../rtl/arp_cache.v") +srcs.append("../rtl/arp_eth_rx.v") +srcs.append("../rtl/arp_eth_tx.v") +srcs.append("../rtl/eth_arb_mux.v") +srcs.append("../rtl/lfsr.v") +srcs.append("../lib/axis/rtl/arbiter.v") +srcs.append("../lib/axis/rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + s_ip_hdr_valid = Signal(bool(0)) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_ip_eth_dest_mac = Signal(intbv(0)[48:]) + m_ip_eth_src_mac = Signal(intbv(0)[48:]) + m_ip_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + rx_busy = Signal(bool(0)) + tx_busy = Signal(bool(0)) + rx_error_header_early_termination = Signal(bool(0)) + rx_error_payload_early_termination = Signal(bool(0)) + rx_error_invalid_header = Signal(bool(0)) + rx_error_invalid_checksum = Signal(bool(0)) + tx_error_payload_early_termination = Signal(bool(0)) + tx_error_arp_failed = Signal(bool(0)) + local_mac = Signal(intbv(0)[48:]) + local_ip = Signal(intbv(0)[32:]) + gateway_ip = Signal(intbv(0)[32:]) + subnet_mask = Signal(intbv(0)[32:]) + clear_arp_cache = Signal(bool(0)) + + # sources and sinks + eth_source_pause = Signal(bool(0)) + eth_sink_pause = Signal(bool(0)) + ip_source_pause = Signal(bool(0)) + ip_sink_pause = Signal(bool(0)) + + eth_source = eth_ep.EthFrameSource() + + eth_source_logic = eth_source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=eth_source_pause, + name='eth_source' + ) + + eth_sink = eth_ep.EthFrameSink() + + eth_sink_logic = eth_sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=eth_sink_pause, + name='eth_sink' + ) + + ip_source = ip_ep.IPFrameSource() + + ip_source_logic = ip_source.create_logic( + clk, + rst, + ip_hdr_valid=s_ip_hdr_valid, + ip_hdr_ready=s_ip_hdr_ready, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=ip_source_pause, + name='ip_source' + ) + + ip_sink = ip_ep.IPFrameSink() + + ip_sink_logic = ip_sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_ip_eth_dest_mac, + eth_src_mac=m_ip_eth_src_mac, + eth_type=m_ip_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=ip_sink_pause, + name='ip_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_ip_eth_dest_mac=m_ip_eth_dest_mac, + m_ip_eth_src_mac=m_ip_eth_src_mac, + m_ip_eth_type=m_ip_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + rx_busy=rx_busy, + tx_busy=tx_busy, + rx_error_header_early_termination=rx_error_header_early_termination, + rx_error_payload_early_termination=rx_error_payload_early_termination, + rx_error_invalid_header=rx_error_invalid_header, + rx_error_invalid_checksum=rx_error_invalid_checksum, + tx_error_payload_early_termination=tx_error_payload_early_termination, + tx_error_arp_failed=tx_error_arp_failed, + + local_mac=local_mac, + local_ip=local_ip, + gateway_ip=gateway_ip, + subnet_mask=subnet_mask, + clear_arp_cache=clear_arp_cache + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + rx_error_header_early_termination_asserted = Signal(bool(0)) + rx_error_payload_early_termination_asserted = Signal(bool(0)) + rx_error_invalid_header_asserted = Signal(bool(0)) + rx_error_invalid_checksum_asserted = Signal(bool(0)) + tx_error_payload_early_termination_asserted = Signal(bool(0)) + tx_error_arp_failed_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_header_early_termination): + rx_error_header_early_termination_asserted.next = 1 + if (rx_error_payload_early_termination): + rx_error_payload_early_termination_asserted.next = 1 + if (rx_error_invalid_header): + rx_error_invalid_header_asserted.next = 1 + if (rx_error_invalid_checksum): + rx_error_invalid_checksum_asserted.next = 1 + if (tx_error_payload_early_termination): + tx_error_payload_early_termination_asserted.next = 1 + if (tx_error_arp_failed): + tx_error_arp_failed_asserted.next = 1 + + def wait_normal(): + while (s_eth_payload_axis_tvalid or s_ip_payload_axis_tvalid or + m_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or + s_eth_hdr_valid or s_ip_hdr_valid): + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # set MAC and IP address + local_mac.next = 0x5A5152535455 + local_ip.next = 0xc0a80164 + gateway_ip.next = 0xc0a80101 + subnet_mask.next = 0xffffff00 + + yield clk.posedge + print("test 1: test IP RX packet") + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + eth_frame = test_frame.build_eth() + + eth_source.send(eth_frame) + + yield ip_sink.wait() + rx_frame = ip_sink.recv() + + assert rx_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test IP TX packet") + current_test.next = 2 + + # send IP packet + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80166 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + # wait for ARP request packet + yield eth_sink.wait() + rx_frame = eth_sink.recv() + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x5A5152535455 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x5A5152535455 + assert check_frame.arp_spa == 0xc0a80164 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80166 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x5A5152535455 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80166 + arp_frame.arp_tha = 0x5A5152535455 + arp_frame.arp_tpa = 0xc0a80164 + eth_source.send(arp_frame.build_eth()) + + yield eth_sink.wait() + rx_frame = eth_sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + print(test_frame) + print(check_frame) + + assert check_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: test IP TX arp fail packet") + current_test.next = 2 + + tx_error_arp_failed_asserted.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80167 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + yield clk.posedge + yield clk.posedge + + yield wait_normal() + + yield clk.posedge + yield clk.posedge + + assert tx_error_arp_failed_asserted + + # check for 4 ARP requests + assert eth_sink.count() == 4 + + while not eth_sink.empty(): + rx_frame = eth_sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x5A5152535455 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x5A5152535455 + assert check_frame.arp_spa == 0xc0a80164 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80167 + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_complete.v b/fpga/lib/eth/tb/test_ip_complete.v new file mode 100644 index 000000000..758841649 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_complete.v @@ -0,0 +1,295 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_complete + */ +module test_ip_complete; + +// Parameters +parameter ARP_CACHE_ADDR_WIDTH = 2; +parameter ARP_REQUEST_RETRY_COUNT = 4; +parameter ARP_REQUEST_RETRY_INTERVAL = 150; +parameter ARP_REQUEST_TIMEOUT = 400; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [7:0] s_eth_payload_axis_tdata = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg arp_response_valid = 0; +reg arp_response_error = 0; +reg [47:0] arp_response_mac = 0; +reg s_ip_hdr_valid = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [7:0] s_ip_payload_axis_tdata = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; +reg [47:0] local_mac = 0; +reg [31:0] local_ip = 0; +reg [31:0] gateway_ip = 0; +reg [31:0] subnet_mask = 0; +reg clear_arp_cache = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [7:0] m_eth_payload_axis_tdata; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire arp_request_valid; +wire [31:0] arp_request_ip; +wire m_ip_hdr_valid; +wire [47:0] m_ip_eth_dest_mac; +wire [47:0] m_ip_eth_src_mac; +wire [15:0] m_ip_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [7:0] m_ip_payload_axis_tdata; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire rx_busy; +wire tx_busy; +wire rx_error_header_early_termination; +wire rx_error_payload_early_termination; +wire rx_error_invalid_header; +wire rx_error_invalid_checksum; +wire tx_error_payload_early_termination; +wire tx_error_arp_failed; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + s_ip_hdr_valid, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_ttl, + s_ip_protocol, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + local_mac, + local_ip, + gateway_ip, + subnet_mask, + clear_arp_cache + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + m_ip_hdr_valid, + m_ip_eth_dest_mac, + m_ip_eth_src_mac, + m_ip_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + rx_busy, + tx_busy, + rx_error_header_early_termination, + rx_error_payload_early_termination, + rx_error_invalid_header, + rx_error_invalid_checksum, + tx_error_payload_early_termination, + tx_error_arp_failed + ); + + // dump file + $dumpfile("test_ip_complete.lxt"); + $dumpvars(0, test_ip_complete); +end + +ip_complete #( + .ARP_CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .ARP_REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .ARP_REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .ARP_REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .rx_busy(rx_busy), + .tx_busy(tx_busy), + .rx_error_header_early_termination(rx_error_header_early_termination), + .rx_error_payload_early_termination(rx_error_payload_early_termination), + .rx_error_invalid_header(rx_error_invalid_header), + .rx_error_invalid_checksum(rx_error_invalid_checksum), + .tx_error_payload_early_termination(tx_error_payload_early_termination), + .tx_error_arp_failed(tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_arp_cache(clear_arp_cache) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_complete_64.py b/fpga/lib/eth/tb/test_ip_complete_64.py new file mode 100755 index 000000000..bdf0f6da5 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_complete_64.py @@ -0,0 +1,580 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import ip_ep + +module = 'ip_complete_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/ip_64.v") +srcs.append("../rtl/ip_eth_rx_64.v") +srcs.append("../rtl/ip_eth_tx_64.v") +srcs.append("../rtl/arp_64.v") +srcs.append("../rtl/arp_cache.v") +srcs.append("../rtl/arp_eth_rx_64.v") +srcs.append("../rtl/arp_eth_tx_64.v") +srcs.append("../rtl/eth_arb_mux.v") +srcs.append("../rtl/lfsr.v") +srcs.append("../lib/axis/rtl/arbiter.v") +srcs.append("../lib/axis/rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + s_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + s_ip_hdr_valid = Signal(bool(0)) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + s_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + m_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_ip_eth_dest_mac = Signal(intbv(0)[48:]) + m_ip_eth_src_mac = Signal(intbv(0)[48:]) + m_ip_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + m_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + rx_busy = Signal(bool(0)) + tx_busy = Signal(bool(0)) + rx_error_header_early_termination = Signal(bool(0)) + rx_error_payload_early_termination = Signal(bool(0)) + rx_error_invalid_header = Signal(bool(0)) + rx_error_invalid_checksum = Signal(bool(0)) + tx_error_payload_early_termination = Signal(bool(0)) + tx_error_arp_failed = Signal(bool(0)) + local_mac = Signal(intbv(0)[48:]) + local_ip = Signal(intbv(0)[32:]) + gateway_ip = Signal(intbv(0)[32:]) + subnet_mask = Signal(intbv(0)[32:]) + clear_arp_cache = Signal(bool(0)) + + # sources and sinks + eth_source_pause = Signal(bool(0)) + eth_sink_pause = Signal(bool(0)) + ip_source_pause = Signal(bool(0)) + ip_sink_pause = Signal(bool(0)) + + eth_source = eth_ep.EthFrameSource() + + eth_source_logic = eth_source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tkeep=s_eth_payload_axis_tkeep, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=eth_source_pause, + name='eth_source' + ) + + eth_sink = eth_ep.EthFrameSink() + + eth_sink_logic = eth_sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=eth_sink_pause, + name='eth_sink' + ) + + ip_source = ip_ep.IPFrameSource() + + ip_source_logic = ip_source.create_logic( + clk, + rst, + ip_hdr_valid=s_ip_hdr_valid, + ip_hdr_ready=s_ip_hdr_ready, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tkeep=s_ip_payload_axis_tkeep, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=ip_source_pause, + name='ip_source' + ) + + ip_sink = ip_ep.IPFrameSink() + + ip_sink_logic = ip_sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_ip_eth_dest_mac, + eth_src_mac=m_ip_eth_src_mac, + eth_type=m_ip_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tkeep=m_ip_payload_axis_tkeep, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=ip_sink_pause, + name='ip_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_ip_eth_dest_mac=m_ip_eth_dest_mac, + m_ip_eth_src_mac=m_ip_eth_src_mac, + m_ip_eth_type=m_ip_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + rx_busy=rx_busy, + tx_busy=tx_busy, + rx_error_header_early_termination=rx_error_header_early_termination, + rx_error_payload_early_termination=rx_error_payload_early_termination, + rx_error_invalid_header=rx_error_invalid_header, + rx_error_invalid_checksum=rx_error_invalid_checksum, + tx_error_payload_early_termination=tx_error_payload_early_termination, + tx_error_arp_failed=tx_error_arp_failed, + + local_mac=local_mac, + local_ip=local_ip, + gateway_ip=gateway_ip, + subnet_mask=subnet_mask, + clear_arp_cache=clear_arp_cache + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + rx_error_header_early_termination_asserted = Signal(bool(0)) + rx_error_payload_early_termination_asserted = Signal(bool(0)) + rx_error_invalid_header_asserted = Signal(bool(0)) + rx_error_invalid_checksum_asserted = Signal(bool(0)) + tx_error_payload_early_termination_asserted = Signal(bool(0)) + tx_error_arp_failed_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_header_early_termination): + rx_error_header_early_termination_asserted.next = 1 + if (rx_error_payload_early_termination): + rx_error_payload_early_termination_asserted.next = 1 + if (rx_error_invalid_header): + rx_error_invalid_header_asserted.next = 1 + if (rx_error_invalid_checksum): + rx_error_invalid_checksum_asserted.next = 1 + if (tx_error_payload_early_termination): + tx_error_payload_early_termination_asserted.next = 1 + if (tx_error_arp_failed): + tx_error_arp_failed_asserted.next = 1 + + def wait_normal(): + while (s_eth_payload_axis_tvalid or s_ip_payload_axis_tvalid or + m_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or + s_eth_hdr_valid or s_ip_hdr_valid): + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # set MAC and IP address + local_mac.next = 0x5A5152535455 + local_ip.next = 0xc0a80164 + gateway_ip.next = 0xc0a80101 + subnet_mask.next = 0xffffff00 + + yield clk.posedge + print("test 1: test IP RX packet") + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + eth_frame = test_frame.build_eth() + + eth_source.send(eth_frame) + + yield ip_sink.wait() + rx_frame = ip_sink.recv() + + assert rx_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test IP TX packet") + current_test.next = 2 + + # send IP packet + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80166 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + # wait for ARP request packet + yield eth_sink.wait() + rx_frame = eth_sink.recv() + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x5A5152535455 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x5A5152535455 + assert check_frame.arp_spa == 0xc0a80164 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80166 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x5A5152535455 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80166 + arp_frame.arp_tha = 0x5A5152535455 + arp_frame.arp_tpa = 0xc0a80164 + eth_source.send(arp_frame.build_eth()) + + yield eth_sink.wait() + rx_frame = eth_sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + print(test_frame) + print(check_frame) + + assert check_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: test IP TX arp fail packet") + current_test.next = 2 + + tx_error_arp_failed_asserted.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80167 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + yield clk.posedge + yield clk.posedge + + yield wait_normal() + + yield clk.posedge + yield clk.posedge + + assert tx_error_arp_failed_asserted + + # check for 4 ARP requests + assert eth_sink.count() == 4 + + while not eth_sink.empty(): + rx_frame = eth_sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x5A5152535455 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x5A5152535455 + assert check_frame.arp_spa == 0xc0a80164 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80167 + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_complete_64.v b/fpga/lib/eth/tb/test_ip_complete_64.v new file mode 100644 index 000000000..f58bff6a7 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_complete_64.v @@ -0,0 +1,307 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_complete_64 + */ +module test_ip_complete_64; + +// Parameters +parameter ARP_CACHE_ADDR_WIDTH = 2; +parameter ARP_REQUEST_RETRY_COUNT = 4; +parameter ARP_REQUEST_RETRY_INTERVAL = 150; +parameter ARP_REQUEST_TIMEOUT = 400; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [63:0] s_eth_payload_axis_tdata = 0; +reg [7:0] s_eth_payload_axis_tkeep = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg arp_response_valid = 0; +reg arp_response_error = 0; +reg [47:0] arp_response_mac = 0; +reg s_ip_hdr_valid = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [63:0] s_ip_payload_axis_tdata = 0; +reg [7:0] s_ip_payload_axis_tkeep = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; +reg [47:0] local_mac = 0; +reg [31:0] local_ip = 0; +reg [31:0] gateway_ip = 0; +reg [31:0] subnet_mask = 0; +reg clear_arp_cache = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [63:0] m_eth_payload_axis_tdata; +wire [7:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire arp_request_valid; +wire [31:0] arp_request_ip; +wire m_ip_hdr_valid; +wire [47:0] m_ip_eth_dest_mac; +wire [47:0] m_ip_eth_src_mac; +wire [15:0] m_ip_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [63:0] m_ip_payload_axis_tdata; +wire [7:0] m_ip_payload_axis_tkeep; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire rx_busy; +wire tx_busy; +wire rx_error_header_early_termination; +wire rx_error_payload_early_termination; +wire rx_error_invalid_header; +wire rx_error_invalid_checksum; +wire tx_error_payload_early_termination; +wire tx_error_arp_failed; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + s_ip_hdr_valid, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_ttl, + s_ip_protocol, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + local_mac, + local_ip, + gateway_ip, + subnet_mask, + clear_arp_cache + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + m_ip_hdr_valid, + m_ip_eth_dest_mac, + m_ip_eth_src_mac, + m_ip_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + rx_busy, + tx_busy, + rx_error_header_early_termination, + rx_error_payload_early_termination, + rx_error_invalid_header, + rx_error_invalid_checksum, + tx_error_payload_early_termination, + tx_error_arp_failed + ); + + // dump file + $dumpfile("test_ip_complete_64.lxt"); + $dumpvars(0, test_ip_complete_64); +end + +ip_complete_64 #( + .ARP_CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .ARP_REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .ARP_REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .ARP_REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .rx_busy(rx_busy), + .tx_busy(tx_busy), + .rx_error_header_early_termination(rx_error_header_early_termination), + .rx_error_payload_early_termination(rx_error_payload_early_termination), + .rx_error_invalid_header(rx_error_invalid_header), + .rx_error_invalid_checksum(rx_error_invalid_checksum), + .tx_error_payload_early_termination(tx_error_payload_early_termination), + .tx_error_arp_failed(tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_arp_cache(clear_arp_cache) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_demux_4.py b/fpga/lib/eth/tb/test_ip_demux_4.py new file mode 100755 index 000000000..a5d467d5d --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_demux_4.py @@ -0,0 +1,743 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import ip_ep + +module = 'ip_demux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + M_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_ip_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_ip_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_ip_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + m_ip_hdr_ready_list = [Signal(bool(0)) for i in range(M_COUNT)] + m_ip_payload_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_ip_hdr_ready = ConcatSignal(*reversed(m_ip_hdr_ready_list)) + m_ip_payload_axis_tready = ConcatSignal(*reversed(m_ip_payload_axis_tready_list)) + + enable = Signal(bool(0)) + drop = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + + m_ip_hdr_valid = Signal(intbv(0)[M_COUNT:]) + m_eth_dest_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_src_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_type = Signal(intbv(0)[M_COUNT*16:]) + m_ip_version = Signal(intbv(0)[M_COUNT*4:]) + m_ip_ihl = Signal(intbv(0)[M_COUNT*4:]) + m_ip_dscp = Signal(intbv(0)[M_COUNT*6:]) + m_ip_ecn = Signal(intbv(0)[M_COUNT*2:]) + m_ip_length = Signal(intbv(0)[M_COUNT*16:]) + m_ip_identification = Signal(intbv(0)[M_COUNT*16:]) + m_ip_flags = Signal(intbv(0)[M_COUNT*3:]) + m_ip_fragment_offset = Signal(intbv(0)[M_COUNT*13:]) + m_ip_ttl = Signal(intbv(0)[M_COUNT*8:]) + m_ip_protocol = Signal(intbv(0)[M_COUNT*8:]) + m_ip_header_checksum = Signal(intbv(0)[M_COUNT*16:]) + m_ip_source_ip = Signal(intbv(0)[M_COUNT*32:]) + m_ip_dest_ip = Signal(intbv(0)[M_COUNT*32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_ip_payload_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_ip_payload_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_ip_payload_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_ip_payload_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_ip_payload_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_ip_payload_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_ip_hdr_valid_list = [m_ip_hdr_valid(i) for i in range(M_COUNT)] + m_eth_dest_mac_list = [m_eth_dest_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_src_mac_list = [m_eth_src_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_type_list = [m_eth_type((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_version_list = [m_ip_version((i+1)*4, i*4) for i in range(M_COUNT)] + m_ip_ihl_list = [m_ip_ihl((i+1)*4, i*4) for i in range(M_COUNT)] + m_ip_dscp_list = [m_ip_dscp((i+1)*6, i*6) for i in range(M_COUNT)] + m_ip_ecn_list = [m_ip_ecn((i+1)*2, i*2) for i in range(M_COUNT)] + m_ip_length_list = [m_ip_length((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_identification_list = [m_ip_identification((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_flags_list = [m_ip_flags((i+1)*3, i*3) for i in range(M_COUNT)] + m_ip_fragment_offset_list = [m_ip_fragment_offset((i+1)*13, i*13) for i in range(M_COUNT)] + m_ip_ttl_list = [m_ip_ttl((i+1)*8, i*8) for i in range(M_COUNT)] + m_ip_protocol_list = [m_ip_protocol((i+1)*8, i*8) for i in range(M_COUNT)] + m_ip_header_checksum_list = [m_ip_header_checksum((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_source_ip_list = [m_ip_source_ip((i+1)*32, i*32) for i in range(M_COUNT)] + m_ip_dest_ip_list = [m_ip_dest_ip((i+1)*32, i*32) for i in range(M_COUNT)] + m_ip_payload_axis_tdata_list = [m_ip_payload_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_ip_payload_axis_tkeep_list = [m_ip_payload_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_ip_payload_axis_tvalid_list = [m_ip_payload_axis_tvalid(i) for i in range(M_COUNT)] + m_ip_payload_axis_tlast_list = [m_ip_payload_axis_tlast(i) for i in range(M_COUNT)] + m_ip_payload_axis_tid_list = [m_ip_payload_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_ip_payload_axis_tdest_list = [m_ip_payload_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_ip_payload_axis_tuser_list = [m_ip_payload_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + source = ip_ep.IPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready, + ip_hdr_valid=s_ip_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tkeep=s_ip_payload_axis_tkeep, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + for k in range(M_COUNT): + s = ip_ep.IPFrameSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready_list[k], + ip_hdr_valid=m_ip_hdr_valid_list[k], + eth_dest_mac=m_eth_dest_mac_list[k], + eth_src_mac=m_eth_src_mac_list[k], + eth_type=m_eth_type_list[k], + ip_version=m_ip_version_list[k], + ip_ihl=m_ip_ihl_list[k], + ip_dscp=m_ip_dscp_list[k], + ip_ecn=m_ip_ecn_list[k], + ip_length=m_ip_length_list[k], + ip_identification=m_ip_identification_list[k], + ip_flags=m_ip_flags_list[k], + ip_fragment_offset=m_ip_fragment_offset_list[k], + ip_ttl=m_ip_ttl_list[k], + ip_protocol=m_ip_protocol_list[k], + ip_header_checksum=m_ip_header_checksum_list[k], + ip_source_ip=m_ip_source_ip_list[k], + ip_dest_ip=m_ip_dest_ip_list[k], + ip_payload_tdata=m_ip_payload_axis_tdata_list[k], + ip_payload_tkeep=m_ip_payload_axis_tkeep_list[k], + ip_payload_tvalid=m_ip_payload_axis_tvalid_list[k], + ip_payload_tready=m_ip_payload_axis_tready_list[k], + ip_payload_tlast=m_ip_payload_axis_tlast_list[k], + ip_payload_tuser=m_ip_payload_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tid=s_ip_payload_axis_tid, + s_ip_payload_axis_tdest=s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tid=m_ip_payload_axis_tid, + m_ip_payload_axis_tdest=m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + enable=enable, + drop=drop, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame1 + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_ip_payload_axis_tvalid or s_ip_hdr_valid: + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_ip_payload_axis_tvalid or s_ip_hdr_valid: + source_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_ip_payload_axis_tvalid or s_ip_hdr_valid: + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: enable") + current_test.next = 7 + + enable.next = False + select.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + enable.next = True + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 8: drop") + current_test.next = 8 + + drop.next = True + select.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + drop.next = False + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_demux_4.v b/fpga/lib/eth/tb/test_ip_demux_4.v new file mode 100644 index 000000000..f5f789db1 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_demux_4.v @@ -0,0 +1,257 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_demux + */ +module test_ip_demux_4; + +// Parameters +parameter M_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_ip_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [DATA_WIDTH-1:0] s_ip_payload_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_ip_payload_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_ip_payload_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_ip_payload_axis_tuser = 0; + +reg [M_COUNT-1:0] m_ip_hdr_ready = 0; +reg [M_COUNT-1:0] m_ip_payload_axis_tready = 0; + +reg enable = 0; +reg drop = 0; +reg [1:0] select = 0; + +// Outputs +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; + +wire [M_COUNT-1:0] m_ip_hdr_valid; +wire [M_COUNT*48-1:0] m_eth_dest_mac; +wire [M_COUNT*48-1:0] m_eth_src_mac; +wire [M_COUNT*16-1:0] m_eth_type; +wire [M_COUNT*4-1:0] m_ip_version; +wire [M_COUNT*4-1:0] m_ip_ihl; +wire [M_COUNT*6-1:0] m_ip_dscp; +wire [M_COUNT*2-1:0] m_ip_ecn; +wire [M_COUNT*16-1:0] m_ip_length; +wire [M_COUNT*16-1:0] m_ip_identification; +wire [M_COUNT*3-1:0] m_ip_flags; +wire [M_COUNT*13-1:0] m_ip_fragment_offset; +wire [M_COUNT*8-1:0] m_ip_ttl; +wire [M_COUNT*8-1:0] m_ip_protocol; +wire [M_COUNT*16-1:0] m_ip_header_checksum; +wire [M_COUNT*32-1:0] m_ip_source_ip; +wire [M_COUNT*32-1:0] m_ip_dest_ip; +wire [M_COUNT*DATA_WIDTH-1:0] m_ip_payload_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep; +wire [M_COUNT-1:0] m_ip_payload_axis_tvalid; +wire [M_COUNT-1:0] m_ip_payload_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_ip_payload_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_ip_payload_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_ip_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tid, + s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + enable, + drop, + select + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_ip_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tid, + m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser + ); + + // dump file + $dumpfile("test_ip_demux_4.lxt"); + $dumpvars(0, test_ip_demux_4); +end + +ip_demux #( + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tid(s_ip_payload_axis_tid), + .s_ip_payload_axis_tdest(s_ip_payload_axis_tdest), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame outputs + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tid(m_ip_payload_axis_tid), + .m_ip_payload_axis_tdest(m_ip_payload_axis_tdest), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Control + .enable(enable), + .drop(drop), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_demux_64_4.py b/fpga/lib/eth/tb/test_ip_demux_64_4.py new file mode 100755 index 000000000..69f65356b --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_demux_64_4.py @@ -0,0 +1,743 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import ip_ep + +module = 'ip_demux' +testbench = 'test_%s_64_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + M_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_ip_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_ip_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_ip_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + m_ip_hdr_ready_list = [Signal(bool(0)) for i in range(M_COUNT)] + m_ip_payload_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_ip_hdr_ready = ConcatSignal(*reversed(m_ip_hdr_ready_list)) + m_ip_payload_axis_tready = ConcatSignal(*reversed(m_ip_payload_axis_tready_list)) + + enable = Signal(bool(0)) + drop = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + + m_ip_hdr_valid = Signal(intbv(0)[M_COUNT:]) + m_eth_dest_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_src_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_type = Signal(intbv(0)[M_COUNT*16:]) + m_ip_version = Signal(intbv(0)[M_COUNT*4:]) + m_ip_ihl = Signal(intbv(0)[M_COUNT*4:]) + m_ip_dscp = Signal(intbv(0)[M_COUNT*6:]) + m_ip_ecn = Signal(intbv(0)[M_COUNT*2:]) + m_ip_length = Signal(intbv(0)[M_COUNT*16:]) + m_ip_identification = Signal(intbv(0)[M_COUNT*16:]) + m_ip_flags = Signal(intbv(0)[M_COUNT*3:]) + m_ip_fragment_offset = Signal(intbv(0)[M_COUNT*13:]) + m_ip_ttl = Signal(intbv(0)[M_COUNT*8:]) + m_ip_protocol = Signal(intbv(0)[M_COUNT*8:]) + m_ip_header_checksum = Signal(intbv(0)[M_COUNT*16:]) + m_ip_source_ip = Signal(intbv(0)[M_COUNT*32:]) + m_ip_dest_ip = Signal(intbv(0)[M_COUNT*32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_ip_payload_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_ip_payload_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_ip_payload_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_ip_payload_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_ip_payload_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_ip_payload_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_ip_hdr_valid_list = [m_ip_hdr_valid(i) for i in range(M_COUNT)] + m_eth_dest_mac_list = [m_eth_dest_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_src_mac_list = [m_eth_src_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_type_list = [m_eth_type((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_version_list = [m_ip_version((i+1)*4, i*4) for i in range(M_COUNT)] + m_ip_ihl_list = [m_ip_ihl((i+1)*4, i*4) for i in range(M_COUNT)] + m_ip_dscp_list = [m_ip_dscp((i+1)*6, i*6) for i in range(M_COUNT)] + m_ip_ecn_list = [m_ip_ecn((i+1)*2, i*2) for i in range(M_COUNT)] + m_ip_length_list = [m_ip_length((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_identification_list = [m_ip_identification((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_flags_list = [m_ip_flags((i+1)*3, i*3) for i in range(M_COUNT)] + m_ip_fragment_offset_list = [m_ip_fragment_offset((i+1)*13, i*13) for i in range(M_COUNT)] + m_ip_ttl_list = [m_ip_ttl((i+1)*8, i*8) for i in range(M_COUNT)] + m_ip_protocol_list = [m_ip_protocol((i+1)*8, i*8) for i in range(M_COUNT)] + m_ip_header_checksum_list = [m_ip_header_checksum((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_source_ip_list = [m_ip_source_ip((i+1)*32, i*32) for i in range(M_COUNT)] + m_ip_dest_ip_list = [m_ip_dest_ip((i+1)*32, i*32) for i in range(M_COUNT)] + m_ip_payload_axis_tdata_list = [m_ip_payload_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_ip_payload_axis_tkeep_list = [m_ip_payload_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_ip_payload_axis_tvalid_list = [m_ip_payload_axis_tvalid(i) for i in range(M_COUNT)] + m_ip_payload_axis_tlast_list = [m_ip_payload_axis_tlast(i) for i in range(M_COUNT)] + m_ip_payload_axis_tid_list = [m_ip_payload_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_ip_payload_axis_tdest_list = [m_ip_payload_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_ip_payload_axis_tuser_list = [m_ip_payload_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + source = ip_ep.IPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready, + ip_hdr_valid=s_ip_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tkeep=s_ip_payload_axis_tkeep, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + for k in range(M_COUNT): + s = ip_ep.IPFrameSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready_list[k], + ip_hdr_valid=m_ip_hdr_valid_list[k], + eth_dest_mac=m_eth_dest_mac_list[k], + eth_src_mac=m_eth_src_mac_list[k], + eth_type=m_eth_type_list[k], + ip_version=m_ip_version_list[k], + ip_ihl=m_ip_ihl_list[k], + ip_dscp=m_ip_dscp_list[k], + ip_ecn=m_ip_ecn_list[k], + ip_length=m_ip_length_list[k], + ip_identification=m_ip_identification_list[k], + ip_flags=m_ip_flags_list[k], + ip_fragment_offset=m_ip_fragment_offset_list[k], + ip_ttl=m_ip_ttl_list[k], + ip_protocol=m_ip_protocol_list[k], + ip_header_checksum=m_ip_header_checksum_list[k], + ip_source_ip=m_ip_source_ip_list[k], + ip_dest_ip=m_ip_dest_ip_list[k], + ip_payload_tdata=m_ip_payload_axis_tdata_list[k], + ip_payload_tkeep=m_ip_payload_axis_tkeep_list[k], + ip_payload_tvalid=m_ip_payload_axis_tvalid_list[k], + ip_payload_tready=m_ip_payload_axis_tready_list[k], + ip_payload_tlast=m_ip_payload_axis_tlast_list[k], + ip_payload_tuser=m_ip_payload_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tid=s_ip_payload_axis_tid, + s_ip_payload_axis_tdest=s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tid=m_ip_payload_axis_tid, + m_ip_payload_axis_tdest=m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + enable=enable, + drop=drop, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame1 + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_ip_payload_axis_tvalid or s_ip_hdr_valid: + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_ip_payload_axis_tvalid or s_ip_hdr_valid: + source_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_ip_payload_axis_tvalid or s_ip_hdr_valid: + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: enable") + current_test.next = 7 + + enable.next = False + select.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + enable.next = True + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 8: drop") + current_test.next = 8 + + drop.next = True + select.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + drop.next = False + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_demux_64_4.v b/fpga/lib/eth/tb/test_ip_demux_64_4.v new file mode 100644 index 000000000..eba7dfdd7 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_demux_64_4.v @@ -0,0 +1,257 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_demux + */ +module test_ip_demux_64_4; + +// Parameters +parameter M_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_ip_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [DATA_WIDTH-1:0] s_ip_payload_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_ip_payload_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_ip_payload_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_ip_payload_axis_tuser = 0; + +reg [M_COUNT-1:0] m_ip_hdr_ready = 0; +reg [M_COUNT-1:0] m_ip_payload_axis_tready = 0; + +reg enable = 0; +reg drop = 0; +reg [1:0] select = 0; + +// Outputs +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; + +wire [M_COUNT-1:0] m_ip_hdr_valid; +wire [M_COUNT*48-1:0] m_eth_dest_mac; +wire [M_COUNT*48-1:0] m_eth_src_mac; +wire [M_COUNT*16-1:0] m_eth_type; +wire [M_COUNT*4-1:0] m_ip_version; +wire [M_COUNT*4-1:0] m_ip_ihl; +wire [M_COUNT*6-1:0] m_ip_dscp; +wire [M_COUNT*2-1:0] m_ip_ecn; +wire [M_COUNT*16-1:0] m_ip_length; +wire [M_COUNT*16-1:0] m_ip_identification; +wire [M_COUNT*3-1:0] m_ip_flags; +wire [M_COUNT*13-1:0] m_ip_fragment_offset; +wire [M_COUNT*8-1:0] m_ip_ttl; +wire [M_COUNT*8-1:0] m_ip_protocol; +wire [M_COUNT*16-1:0] m_ip_header_checksum; +wire [M_COUNT*32-1:0] m_ip_source_ip; +wire [M_COUNT*32-1:0] m_ip_dest_ip; +wire [M_COUNT*DATA_WIDTH-1:0] m_ip_payload_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep; +wire [M_COUNT-1:0] m_ip_payload_axis_tvalid; +wire [M_COUNT-1:0] m_ip_payload_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_ip_payload_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_ip_payload_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_ip_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tid, + s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + enable, + drop, + select + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_ip_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tid, + m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser + ); + + // dump file + $dumpfile("test_ip_demux_64_4.lxt"); + $dumpvars(0, test_ip_demux_64_4); +end + +ip_demux #( + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tid(s_ip_payload_axis_tid), + .s_ip_payload_axis_tdest(s_ip_payload_axis_tdest), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame outputs + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tid(m_ip_payload_axis_tid), + .m_ip_payload_axis_tdest(m_ip_payload_axis_tdest), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Control + .enable(enable), + .drop(drop), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_eth_rx.py b/fpga/lib/eth/tb/test_ip_eth_rx.py new file mode 100755 index 000000000..2996f196d --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_eth_rx.py @@ -0,0 +1,1034 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import ip_ep + +module = 'ip_eth_rx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_header_early_termination = Signal(bool(0)) + error_payload_early_termination = Signal(bool(0)) + error_invalid_header = Signal(bool(0)) + error_invalid_checksum = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = eth_ep.EthFrameSource() + + source_logic = source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = ip_ep.IPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + busy=busy, + error_header_early_termination=error_header_early_termination, + error_payload_early_termination=error_payload_early_termination, + error_invalid_header=error_invalid_header, + error_invalid_checksum=error_invalid_checksum + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_header_early_termination_asserted = Signal(bool(0)) + error_payload_early_termination_asserted = Signal(bool(0)) + error_invalid_header_asserted = Signal(bool(0)) + error_invalid_checksum_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_header_early_termination): + error_header_early_termination_asserted.next = 1 + if (error_payload_early_termination): + error_payload_early_termination_asserted.next = 1 + if (error_invalid_header): + error_invalid_header_asserted.next = 1 + if (error_invalid_checksum): + error_invalid_checksum_asserted.next = 1 + + def wait_normal(): + while s_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + + def wait_pause_source(): + while s_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_eth_hdr_valid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.payload = bytearray(range(payload_len)) + test_frame.build() + eth_frame = test_frame.build_eth() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: trailing bytes (1), length %d" % payload_len) + current_test.next = 4 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data += bytearray(b'\x00') + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: trailing bytes (10), length %d" % payload_len) + current_test.next = 5 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data += bytearray(b'\x00'*10) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 6: trailing bytes with tuser assert (1), length %d" % payload_len) + current_test.next = 6 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data += bytearray(b'\x00') + eth_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 7: trailing bytes with tuser assert (10), length %d" % payload_len) + current_test.next = 7 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data += bytearray(b'\x00'*10) + eth_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 8: truncated payload (1), length %d" % payload_len) + current_test.next = 8 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len+1)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data = eth_frame1.payload.data[:-1] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 9: truncated payload (10), length %d" % payload_len) + current_test.next = 9 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len+10)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data = eth_frame1.payload.data[:-10] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 10: bad IHL, length %d" % payload_len) + current_test.next = 10 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 6 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_invalid_header_asserted.next = 0 + + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert error_invalid_header_asserted + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 11: bad checksum, length %d" % payload_len) + current_test.next = 11 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = 0x1234 + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_invalid_checksum_asserted.next = 0 + + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert error_invalid_checksum_asserted + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + for length in range(1,21): + yield clk.posedge + print("test 12: truncated header, length %d" % length) + current_test.next = 12 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(16)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(16)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data = eth_frame1.payload.data[:length] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_header_early_termination_asserted.next = 0 + + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert error_header_early_termination_asserted + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_eth_rx.v b/fpga/lib/eth/tb/test_ip_eth_rx.v new file mode 100644 index 000000000..7c3b5d34b --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_eth_rx.v @@ -0,0 +1,180 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_eth_rx + */ +module test_ip_eth_rx; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [7:0] s_eth_payload_axis_tdata = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; +wire m_ip_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [7:0] m_ip_payload_axis_tdata; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire busy; +wire error_header_early_termination; +wire error_payload_early_termination; +wire error_invalid_header; +wire error_invalid_checksum; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_ip_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + busy, + error_header_early_termination, + error_payload_early_termination, + error_invalid_header, + error_invalid_checksum + ); + + // dump file + $dumpfile("test_ip_eth_rx.lxt"); + $dumpvars(0, test_ip_eth_rx); +end + +ip_eth_rx +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(busy), + .error_header_early_termination(error_header_early_termination), + .error_payload_early_termination(error_payload_early_termination), + .error_invalid_header(error_invalid_header), + .error_invalid_checksum(error_invalid_checksum) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_eth_rx_64.py b/fpga/lib/eth/tb/test_ip_eth_rx_64.py new file mode 100755 index 000000000..e55cdbb3a --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_eth_rx_64.py @@ -0,0 +1,1060 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import ip_ep + +module = 'ip_eth_rx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + s_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + m_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_header_early_termination = Signal(bool(0)) + error_payload_early_termination = Signal(bool(0)) + error_invalid_header = Signal(bool(0)) + error_invalid_checksum = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = eth_ep.EthFrameSource() + + source_logic = source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tkeep=s_eth_payload_axis_tkeep, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = ip_ep.IPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tkeep=m_ip_payload_axis_tkeep, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + busy=busy, + error_header_early_termination=error_header_early_termination, + error_payload_early_termination=error_payload_early_termination, + error_invalid_header=error_invalid_header, + error_invalid_checksum=error_invalid_checksum + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_header_early_termination_asserted = Signal(bool(0)) + error_payload_early_termination_asserted = Signal(bool(0)) + error_invalid_header_asserted = Signal(bool(0)) + error_invalid_checksum_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_header_early_termination): + error_header_early_termination_asserted.next = 1 + if (error_payload_early_termination): + error_payload_early_termination_asserted.next = 1 + if (error_invalid_header): + error_invalid_header_asserted.next = 1 + if (error_invalid_checksum): + error_invalid_checksum_asserted.next = 1 + + def wait_normal(): + while s_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + + def wait_pause_source(): + while s_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_eth_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_eth_hdr_valid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.payload = bytearray(range(payload_len)) + test_frame.build() + eth_frame = test_frame.build_eth() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: trailing bytes (1), length %d" % payload_len) + current_test.next = 4 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data += bytearray(b'\x00') + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: trailing bytes (10), length %d" % payload_len) + current_test.next = 5 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data += bytearray(b'\x00'*10) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 6: trailing bytes with tuser assert (1), length %d" % payload_len) + current_test.next = 6 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data += bytearray(b'\x00') + eth_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 7: trailing bytes with tuser assert (10), length %d" % payload_len) + current_test.next = 7 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data += bytearray(b'\x00'*10) + eth_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 8: truncated payload (1), length %d" % payload_len) + current_test.next = 8 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len+1)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data = eth_frame1.payload.data[:-1] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 9: truncated payload (10), length %d" % payload_len) + current_test.next = 9 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len+10)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data = eth_frame1.payload.data[:-10] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 10: bad IHL, length %d" % payload_len) + current_test.next = 10 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 6 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_invalid_header_asserted.next = 0 + + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert error_invalid_header_asserted + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 11: bad checksum, length %d" % payload_len) + current_test.next = 11 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = 0x1234 + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_invalid_checksum_asserted.next = 0 + + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert error_invalid_checksum_asserted + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + for length in range(1,21): + yield clk.posedge + print("test 12: truncated header, length %d" % length) + current_test.next = 12 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(16)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(16)) + test_frame2.build() + + eth_frame1 = test_frame1.build_eth() + eth_frame2 = test_frame2.build_eth() + + eth_frame1.payload.data = eth_frame1.payload.data[:length] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_header_early_termination_asserted.next = 0 + + source.send(eth_frame1) + source.send(eth_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield clk.posedge + yield sink.wait() + rx_frame = sink.recv() + + assert error_header_early_termination_asserted + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_eth_rx_64.v b/fpga/lib/eth/tb/test_ip_eth_rx_64.v new file mode 100644 index 000000000..0293a2b02 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_eth_rx_64.v @@ -0,0 +1,186 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_eth_rx_64 + */ +module test_ip_eth_rx_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [63:0] s_eth_payload_axis_tdata = 0; +reg [7:0] s_eth_payload_axis_tkeep = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; +wire m_ip_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [63:0] m_ip_payload_axis_tdata; +wire [7:0] m_ip_payload_axis_tkeep; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire busy; +wire error_header_early_termination; +wire error_payload_early_termination; +wire error_invalid_header; +wire error_invalid_checksum; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + m_ip_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + busy, + error_header_early_termination, + error_payload_early_termination, + error_invalid_header, + error_invalid_checksum + ); + + // dump file + $dumpfile("test_ip_eth_rx_64.lxt"); + $dumpvars(0, test_ip_eth_rx_64); +end + +ip_eth_rx_64 +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(busy), + .error_header_early_termination(error_header_early_termination), + .error_payload_early_termination(error_payload_early_termination), + .error_invalid_header(error_invalid_header), + .error_invalid_checksum(error_invalid_checksum) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_eth_tx.py b/fpga/lib/eth/tb/test_ip_eth_tx.py new file mode 100755 index 000000000..799de5202 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_eth_tx.py @@ -0,0 +1,844 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import ip_ep + +module = 'ip_eth_tx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + + # Outputs + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_payload_early_termination = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = ip_ep.IPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready, + ip_hdr_valid=s_ip_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = eth_ep.EthFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + busy=busy, + error_payload_early_termination=error_payload_early_termination + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_payload_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_payload_early_termination): + error_payload_early_termination_asserted.next = 1 + + def wait_normal(): + while s_ip_payload_axis_tvalid or m_eth_payload_axis_tvalid or s_ip_hdr_valid: + yield clk.posedge + + def wait_pause_source(): + while s_ip_payload_axis_tvalid or m_eth_payload_axis_tvalid or s_ip_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_ip_payload_axis_tvalid or m_eth_payload_axis_tvalid or s_ip_hdr_valid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.payload = bytearray(range(payload_len)) + test_frame.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: trailing bytes (1), length %d" % payload_len) + current_test.next = 4 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00') + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: trailing bytes (10), length %d" % payload_len) + current_test.next = 5 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00'*10) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 6: trailing bytes with tuser assert (1), length %d" % payload_len) + current_test.next = 6 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00') + test_frame1a.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 7: trailing bytes with tuser assert (10), length %d" % payload_len) + current_test.next = 7 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00'*10) + test_frame1a.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 8: truncated payload (1), length %d" % payload_len) + current_test.next = 8 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len+1)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data = test_frame1a.payload.data[:-1] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 9: truncated payload (10), length %d" % payload_len) + current_test.next = 9 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len+10)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data = test_frame1a.payload.data[:-10] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_eth_tx.v b/fpga/lib/eth/tb/test_ip_eth_tx.v new file mode 100644 index 000000000..5a2a289f2 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_eth_tx.v @@ -0,0 +1,162 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_eth_tx + */ +module test_ip_eth_tx; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_ip_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [7:0] s_ip_payload_axis_tdata = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +// Outputs +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [7:0] m_eth_payload_axis_tdata; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire busy; +wire error_payload_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + busy, + error_payload_early_termination + ); + + // dump file + $dumpfile("test_ip_eth_tx.lxt"); + $dumpvars(0, test_ip_eth_tx); +end + +ip_eth_tx +UUT ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy(busy), + .error_payload_early_termination(error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_eth_tx_64.py b/fpga/lib/eth/tb/test_ip_eth_tx_64.py new file mode 100755 index 000000000..185fb140f --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_eth_tx_64.py @@ -0,0 +1,850 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import ip_ep + +module = 'ip_eth_tx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + s_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + + # Outputs + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + m_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_payload_early_termination = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = ip_ep.IPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready, + ip_hdr_valid=s_ip_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tkeep=s_ip_payload_axis_tkeep, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = eth_ep.EthFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + busy=busy, + error_payload_early_termination=error_payload_early_termination + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_payload_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_payload_early_termination): + error_payload_early_termination_asserted.next = 1 + + def wait_normal(): + while s_ip_payload_axis_tvalid or m_eth_payload_axis_tvalid or s_ip_hdr_valid: + yield clk.posedge + + def wait_pause_source(): + while s_ip_payload_axis_tvalid or m_eth_payload_axis_tvalid or s_ip_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_ip_payload_axis_tvalid or m_eth_payload_axis_tvalid or s_ip_hdr_valid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.payload = bytearray(range(payload_len)) + test_frame.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: trailing bytes (1), length %d" % payload_len) + current_test.next = 4 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00') + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: trailing bytes (10), length %d" % payload_len) + current_test.next = 5 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00'*10) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 6: trailing bytes with tuser assert (1), length %d" % payload_len) + current_test.next = 6 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00') + test_frame1a.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 7: trailing bytes with tuser assert (10), length %d" % payload_len) + current_test.next = 7 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00'*10) + test_frame1a.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 8: truncated payload (1), length %d" % payload_len) + current_test.next = 8 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len+1)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data = test_frame1a.payload.data[:-1] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 9: truncated payload (10), length %d" % payload_len) + current_test.next = 9 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.payload = bytearray(range(payload_len+10)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = ip_ep.IPFrame(test_frame1) + test_frame1a.payload.data = test_frame1a.payload.data[:-10] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_eth_tx_64.v b/fpga/lib/eth/tb/test_ip_eth_tx_64.v new file mode 100644 index 000000000..7d52d554d --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_eth_tx_64.v @@ -0,0 +1,168 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_eth_tx_64 + */ +module test_ip_eth_tx_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_ip_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [63:0] s_ip_payload_axis_tdata = 0; +reg [7:0] s_ip_payload_axis_tkeep = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; + +// Outputs +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [63:0] m_eth_payload_axis_tdata; +wire [7:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire busy; +wire error_payload_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + busy, + error_payload_early_termination + ); + + // dump file + $dumpfile("test_ip_eth_tx_64.lxt"); + $dumpvars(0, test_ip_eth_tx_64); +end + +ip_eth_tx_64 +UUT ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy(busy), + .error_payload_early_termination(error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_mux_4.py b/fpga/lib/eth/tb/test_ip_mux_4.py new file mode 100755 index 000000000..c19349bec --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_mux_4.py @@ -0,0 +1,668 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import ip_ep + +module = 'ip_mux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_version_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_ihl_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_dscp_list = [Signal(intbv(0)[6:]) for i in range(S_COUNT)] + s_ip_ecn_list = [Signal(intbv(0)[2:]) for i in range(S_COUNT)] + s_ip_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_identification_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_flags_list = [Signal(intbv(0)[3:]) for i in range(S_COUNT)] + s_ip_fragment_offset_list = [Signal(intbv(0)[13:]) for i in range(S_COUNT)] + s_ip_ttl_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_protocol_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_header_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_source_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_dest_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_ip_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_ip_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_ip_hdr_valid = ConcatSignal(*reversed(s_ip_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_ip_version = ConcatSignal(*reversed(s_ip_version_list)) + s_ip_ihl = ConcatSignal(*reversed(s_ip_ihl_list)) + s_ip_dscp = ConcatSignal(*reversed(s_ip_dscp_list)) + s_ip_ecn = ConcatSignal(*reversed(s_ip_ecn_list)) + s_ip_length = ConcatSignal(*reversed(s_ip_length_list)) + s_ip_identification = ConcatSignal(*reversed(s_ip_identification_list)) + s_ip_flags = ConcatSignal(*reversed(s_ip_flags_list)) + s_ip_fragment_offset = ConcatSignal(*reversed(s_ip_fragment_offset_list)) + s_ip_ttl = ConcatSignal(*reversed(s_ip_ttl_list)) + s_ip_protocol = ConcatSignal(*reversed(s_ip_protocol_list)) + s_ip_header_checksum = ConcatSignal(*reversed(s_ip_header_checksum_list)) + s_ip_source_ip = ConcatSignal(*reversed(s_ip_source_ip_list)) + s_ip_dest_ip = ConcatSignal(*reversed(s_ip_dest_ip_list)) + s_ip_payload_axis_tdata = ConcatSignal(*reversed(s_ip_payload_axis_tdata_list)) + s_ip_payload_axis_tkeep = ConcatSignal(*reversed(s_ip_payload_axis_tkeep_list)) + s_ip_payload_axis_tvalid = ConcatSignal(*reversed(s_ip_payload_axis_tvalid_list)) + s_ip_payload_axis_tlast = ConcatSignal(*reversed(s_ip_payload_axis_tlast_list)) + s_ip_payload_axis_tid = ConcatSignal(*reversed(s_ip_payload_axis_tid_list)) + s_ip_payload_axis_tdest = ConcatSignal(*reversed(s_ip_payload_axis_tdest_list)) + s_ip_payload_axis_tuser = ConcatSignal(*reversed(s_ip_payload_axis_tuser_list)) + + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + + enable = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_ip_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_ip_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_ip_hdr_ready_list = [s_ip_hdr_ready(i) for i in range(S_COUNT)] + s_ip_payload_axis_tready_list = [s_ip_payload_axis_tready(i) for i in range(S_COUNT)] + + m_ip_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_ip_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_ip_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_ip_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = ip_ep.IPFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready_list[k], + ip_hdr_valid=s_ip_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + ip_version=s_ip_version_list[k], + ip_ihl=s_ip_ihl_list[k], + ip_dscp=s_ip_dscp_list[k], + ip_ecn=s_ip_ecn_list[k], + ip_length=s_ip_length_list[k], + ip_identification=s_ip_identification_list[k], + ip_flags=s_ip_flags_list[k], + ip_fragment_offset=s_ip_fragment_offset_list[k], + ip_ttl=s_ip_ttl_list[k], + ip_protocol=s_ip_protocol_list[k], + ip_header_checksum=s_ip_header_checksum_list[k], + ip_source_ip=s_ip_source_ip_list[k], + ip_dest_ip=s_ip_dest_ip_list[k], + ip_payload_tdata=s_ip_payload_axis_tdata_list[k], + ip_payload_tkeep=s_ip_payload_axis_tkeep_list[k], + ip_payload_tvalid=s_ip_payload_axis_tvalid_list[k], + ip_payload_tready=s_ip_payload_axis_tready_list[k], + ip_payload_tlast=s_ip_payload_axis_tlast_list[k], + ip_payload_tuser=s_ip_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = ip_ep.IPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tkeep=m_ip_payload_axis_tkeep, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tid=s_ip_payload_axis_tid, + s_ip_payload_axis_tdest=s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tid=m_ip_payload_axis_tid, + m_ip_payload_axis_tdest=m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + enable=enable, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_ip_payload_axis_tvalid: + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_ip_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + select.next = 2 + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_ip_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_mux_4.v b/fpga/lib/eth/tb/test_ip_mux_4.v new file mode 100644 index 000000000..100426023 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_mux_4.v @@ -0,0 +1,254 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_mux + */ +module test_ip_mux_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_ip_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*4-1:0] s_ip_version = 0; +reg [S_COUNT*4-1:0] s_ip_ihl = 0; +reg [S_COUNT*6-1:0] s_ip_dscp = 0; +reg [S_COUNT*2-1:0] s_ip_ecn = 0; +reg [S_COUNT*16-1:0] s_ip_length = 0; +reg [S_COUNT*16-1:0] s_ip_identification = 0; +reg [S_COUNT*3-1:0] s_ip_flags = 0; +reg [S_COUNT*13-1:0] s_ip_fragment_offset = 0; +reg [S_COUNT*8-1:0] s_ip_ttl = 0; +reg [S_COUNT*8-1:0] s_ip_protocol = 0; +reg [S_COUNT*16-1:0] s_ip_header_checksum = 0; +reg [S_COUNT*32-1:0] s_ip_source_ip = 0; +reg [S_COUNT*32-1:0] s_ip_dest_ip = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_ip_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_ip_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_ip_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_ip_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_ip_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_ip_payload_axis_tuser = 0; + +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; + +reg enable = 0; +reg [1:0] select = 0; + +// Outputs +wire [S_COUNT-1:0] s_ip_hdr_ready; +wire [S_COUNT-1:0] s_ip_payload_axis_tready; + +wire m_ip_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [DATA_WIDTH-1:0] m_ip_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_ip_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_ip_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tid, + s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + enable, + select + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_ip_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tid, + m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser + ); + + // dump file + $dumpfile("test_ip_mux_4.lxt"); + $dumpvars(0, test_ip_mux_4); +end + +ip_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // IP frame inputs + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tid(s_ip_payload_axis_tid), + .s_ip_payload_axis_tdest(s_ip_payload_axis_tdest), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tid(m_ip_payload_axis_tid), + .m_ip_payload_axis_tdest(m_ip_payload_axis_tdest), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Control + .enable(enable), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ip_mux_64_4.py b/fpga/lib/eth/tb/test_ip_mux_64_4.py new file mode 100755 index 000000000..ea7ca2f19 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_mux_64_4.py @@ -0,0 +1,668 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import ip_ep + +module = 'ip_mux' +testbench = 'test_%s_64_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_version_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_ihl_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_dscp_list = [Signal(intbv(0)[6:]) for i in range(S_COUNT)] + s_ip_ecn_list = [Signal(intbv(0)[2:]) for i in range(S_COUNT)] + s_ip_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_identification_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_flags_list = [Signal(intbv(0)[3:]) for i in range(S_COUNT)] + s_ip_fragment_offset_list = [Signal(intbv(0)[13:]) for i in range(S_COUNT)] + s_ip_ttl_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_protocol_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_header_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_source_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_dest_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_ip_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_ip_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_ip_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_ip_hdr_valid = ConcatSignal(*reversed(s_ip_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_ip_version = ConcatSignal(*reversed(s_ip_version_list)) + s_ip_ihl = ConcatSignal(*reversed(s_ip_ihl_list)) + s_ip_dscp = ConcatSignal(*reversed(s_ip_dscp_list)) + s_ip_ecn = ConcatSignal(*reversed(s_ip_ecn_list)) + s_ip_length = ConcatSignal(*reversed(s_ip_length_list)) + s_ip_identification = ConcatSignal(*reversed(s_ip_identification_list)) + s_ip_flags = ConcatSignal(*reversed(s_ip_flags_list)) + s_ip_fragment_offset = ConcatSignal(*reversed(s_ip_fragment_offset_list)) + s_ip_ttl = ConcatSignal(*reversed(s_ip_ttl_list)) + s_ip_protocol = ConcatSignal(*reversed(s_ip_protocol_list)) + s_ip_header_checksum = ConcatSignal(*reversed(s_ip_header_checksum_list)) + s_ip_source_ip = ConcatSignal(*reversed(s_ip_source_ip_list)) + s_ip_dest_ip = ConcatSignal(*reversed(s_ip_dest_ip_list)) + s_ip_payload_axis_tdata = ConcatSignal(*reversed(s_ip_payload_axis_tdata_list)) + s_ip_payload_axis_tkeep = ConcatSignal(*reversed(s_ip_payload_axis_tkeep_list)) + s_ip_payload_axis_tvalid = ConcatSignal(*reversed(s_ip_payload_axis_tvalid_list)) + s_ip_payload_axis_tlast = ConcatSignal(*reversed(s_ip_payload_axis_tlast_list)) + s_ip_payload_axis_tid = ConcatSignal(*reversed(s_ip_payload_axis_tid_list)) + s_ip_payload_axis_tdest = ConcatSignal(*reversed(s_ip_payload_axis_tdest_list)) + s_ip_payload_axis_tuser = ConcatSignal(*reversed(s_ip_payload_axis_tuser_list)) + + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + + enable = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_ip_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_ip_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_ip_hdr_ready_list = [s_ip_hdr_ready(i) for i in range(S_COUNT)] + s_ip_payload_axis_tready_list = [s_ip_payload_axis_tready(i) for i in range(S_COUNT)] + + m_ip_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_ip_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_ip_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_ip_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = ip_ep.IPFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready_list[k], + ip_hdr_valid=s_ip_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + ip_version=s_ip_version_list[k], + ip_ihl=s_ip_ihl_list[k], + ip_dscp=s_ip_dscp_list[k], + ip_ecn=s_ip_ecn_list[k], + ip_length=s_ip_length_list[k], + ip_identification=s_ip_identification_list[k], + ip_flags=s_ip_flags_list[k], + ip_fragment_offset=s_ip_fragment_offset_list[k], + ip_ttl=s_ip_ttl_list[k], + ip_protocol=s_ip_protocol_list[k], + ip_header_checksum=s_ip_header_checksum_list[k], + ip_source_ip=s_ip_source_ip_list[k], + ip_dest_ip=s_ip_dest_ip_list[k], + ip_payload_tdata=s_ip_payload_axis_tdata_list[k], + ip_payload_tkeep=s_ip_payload_axis_tkeep_list[k], + ip_payload_tvalid=s_ip_payload_axis_tvalid_list[k], + ip_payload_tready=s_ip_payload_axis_tready_list[k], + ip_payload_tlast=s_ip_payload_axis_tlast_list[k], + ip_payload_tuser=s_ip_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = ip_ep.IPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tkeep=m_ip_payload_axis_tkeep, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tid=s_ip_payload_axis_tid, + s_ip_payload_axis_tdest=s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tid=m_ip_payload_axis_tid, + m_ip_payload_axis_tdest=m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + enable=enable, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_ip_payload_axis_tvalid: + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_ip_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + select.next = 2 + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = ip_ep.IPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = ip_ep.IPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_ip_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_ip_mux_64_4.v b/fpga/lib/eth/tb/test_ip_mux_64_4.v new file mode 100644 index 000000000..e03664a81 --- /dev/null +++ b/fpga/lib/eth/tb/test_ip_mux_64_4.v @@ -0,0 +1,254 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for ip_mux + */ +module test_ip_mux_64_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_ip_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*4-1:0] s_ip_version = 0; +reg [S_COUNT*4-1:0] s_ip_ihl = 0; +reg [S_COUNT*6-1:0] s_ip_dscp = 0; +reg [S_COUNT*2-1:0] s_ip_ecn = 0; +reg [S_COUNT*16-1:0] s_ip_length = 0; +reg [S_COUNT*16-1:0] s_ip_identification = 0; +reg [S_COUNT*3-1:0] s_ip_flags = 0; +reg [S_COUNT*13-1:0] s_ip_fragment_offset = 0; +reg [S_COUNT*8-1:0] s_ip_ttl = 0; +reg [S_COUNT*8-1:0] s_ip_protocol = 0; +reg [S_COUNT*16-1:0] s_ip_header_checksum = 0; +reg [S_COUNT*32-1:0] s_ip_source_ip = 0; +reg [S_COUNT*32-1:0] s_ip_dest_ip = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_ip_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_ip_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_ip_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_ip_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_ip_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_ip_payload_axis_tuser = 0; + +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; + +reg enable = 0; +reg [1:0] select = 0; + +// Outputs +wire [S_COUNT-1:0] s_ip_hdr_ready; +wire [S_COUNT-1:0] s_ip_payload_axis_tready; + +wire m_ip_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [DATA_WIDTH-1:0] m_ip_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_ip_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_ip_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tid, + s_ip_payload_axis_tdest, + s_ip_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + enable, + select + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_ip_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tid, + m_ip_payload_axis_tdest, + m_ip_payload_axis_tuser + ); + + // dump file + $dumpfile("test_ip_mux_64_4.lxt"); + $dumpvars(0, test_ip_mux_64_4); +end + +ip_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // IP frame inputs + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tid(s_ip_payload_axis_tid), + .s_ip_payload_axis_tdest(s_ip_payload_axis_tdest), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tid(m_ip_payload_axis_tid), + .m_ip_payload_axis_tdest(m_ip_payload_axis_tdest), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Control + .enable(enable), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ptp_clock.py b/fpga/lib/eth/tb/test_ptp_clock.py new file mode 100755 index 000000000..462c58305 --- /dev/null +++ b/fpga/lib/eth/tb/test_ptp_clock.py @@ -0,0 +1,394 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-2019 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. + +""" + +from myhdl import * +import os + +module = 'ptp_clock' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + PERIOD_NS_WIDTH = 4 + OFFSET_NS_WIDTH = 4 + DRIFT_NS_WIDTH = 4 + FNS_WIDTH = 16 + PERIOD_NS = 0x6 + PERIOD_FNS = 0x6666 + DRIFT_ENABLE = 1 + DRIFT_NS = 0x0 + DRIFT_FNS = 0x0002 + DRIFT_RATE = 0x0005 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_ts_96 = Signal(intbv(0)[96:]) + input_ts_96_valid = Signal(bool(0)) + input_ts_64 = Signal(intbv(0)[64:]) + input_ts_64_valid = Signal(bool(0)) + input_period_ns = Signal(intbv(0)[PERIOD_NS_WIDTH:]) + input_period_fns = Signal(intbv(0)[FNS_WIDTH:]) + input_period_valid = Signal(bool(0)) + input_adj_ns = Signal(intbv(0)[OFFSET_NS_WIDTH:]) + input_adj_fns = Signal(intbv(0)[FNS_WIDTH:]) + input_adj_count = Signal(intbv(0)[16:]) + input_adj_valid = Signal(bool(0)) + input_drift_ns = Signal(intbv(0)[DRIFT_NS_WIDTH:]) + input_drift_fns = Signal(intbv(0)[FNS_WIDTH:]) + input_drift_rate = Signal(intbv(0)[16:]) + input_drift_valid = Signal(bool(0)) + + # Outputs + input_adj_active = Signal(bool(0)) + output_ts_96 = Signal(intbv(0)[96:]) + output_ts_64 = Signal(intbv(0)[64:]) + output_ts_step = Signal(bool(0)) + output_pps = Signal(bool(0)) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + input_ts_96=input_ts_96, + input_ts_96_valid=input_ts_96_valid, + input_ts_64=input_ts_64, + input_ts_64_valid=input_ts_64_valid, + + input_period_ns=input_period_ns, + input_period_fns=input_period_fns, + input_period_valid=input_period_valid, + + input_adj_ns=input_adj_ns, + input_adj_fns=input_adj_fns, + input_adj_count=input_adj_count, + input_adj_valid=input_adj_valid, + input_adj_active=input_adj_active, + + input_drift_ns=input_drift_ns, + input_drift_fns=input_drift_fns, + input_drift_rate=input_drift_rate, + input_drift_valid=input_drift_valid, + + output_ts_96=output_ts_96, + output_ts_64=output_ts_64, + output_ts_step=output_ts_step, + + output_pps=output_pps + ) + + @always(delay(3200)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100000) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100000) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: Default rate and drift") + current_test.next = 1 + + yield clk.posedge + start_time = now()*1e-12 + start_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + start_ts_64 = output_ts_64/2**16*1e-9 + + for i in range(10000): + yield clk.posedge + + stop_time = now()*1e-12 + stop_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + stop_ts_64 = output_ts_64/2**16*1e-9 + + print(stop_time-start_time) + print(stop_ts_96-start_ts_96) + print(stop_ts_64-start_ts_64) + + assert abs((stop_time-start_time) - (stop_ts_96-start_ts_96)) < 1e-12 + assert abs((stop_time-start_time) - (stop_ts_64-start_ts_64)) < 1e-12 + + yield delay(100000) + + yield clk.posedge + print("test 2: Load timestamps") + current_test.next = 2 + + input_ts_96.next = 12345678 + input_ts_96_valid.next = 1 + input_ts_64.next = 87654321 + input_ts_64_valid.next = 1 + + yield clk.posedge + + input_ts_96_valid.next = 0 + input_ts_64_valid.next = 0 + + yield clk.posedge + + assert output_ts_96 == 12345678 + assert output_ts_64 == 87654321 + + start_time = now()*1e-12 + start_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + start_ts_64 = output_ts_64/2**16*1e-9 + + for i in range(2000): + yield clk.posedge + + stop_time = now()*1e-12 + stop_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + stop_ts_64 = output_ts_64/2**16*1e-9 + + print(stop_time-start_time) + print(stop_ts_96-start_ts_96) + print(stop_ts_64-start_ts_64) + + assert abs((stop_time-start_time) - (stop_ts_96-start_ts_96)) < 1e-12 + assert abs((stop_time-start_time) - (stop_ts_64-start_ts_64)) < 1e-12 + + yield delay(100000) + + yield clk.posedge + print("test 3: Seconds increment") + current_test.next = 3 + + input_ts_96.next = 999990000*2**16 + input_ts_96_valid.next = 1 + input_ts_64.next = 999990000*2**16 + input_ts_64_valid.next = 1 + + yield clk.posedge + + input_ts_96_valid.next = 0 + input_ts_64_valid.next = 0 + + yield clk.posedge + + assert output_ts_96 == 999990000*2**16 + assert output_ts_64 == 999990000*2**16 + + start_time = now()*1e-12 + start_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + start_ts_64 = output_ts_64/2**16*1e-9 + + for i in range(3000): + yield clk.posedge + + if output_pps: + assert output_ts_96[96:48] == 1 + assert output_ts_96[48:0] < 10*2**16 + + stop_time = now()*1e-12 + stop_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + stop_ts_64 = output_ts_64/2**16*1e-9 + + print(stop_time-start_time) + print(stop_ts_96-start_ts_96) + print(stop_ts_64-start_ts_64) + + assert abs((stop_time-start_time) - (stop_ts_96-start_ts_96)) < 1e-12 + assert abs((stop_time-start_time) - (stop_ts_64-start_ts_64)) < 1e-12 + + yield delay(100000) + + yield clk.posedge + print("test 4: Offset adjust") + current_test.next = 4 + + input_ts_96.next = 0 + input_ts_96_valid.next = 1 + input_ts_64.next = 0 + input_ts_64_valid.next = 1 + + yield clk.posedge + + input_ts_96_valid.next = 0 + input_ts_64_valid.next = 0 + + yield clk.posedge + + start_time = now()*1e-12 + start_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + start_ts_64 = output_ts_64/2**16*1e-9 + + for i in range(2000): + yield clk.posedge + + # 1 ns offset - 1024*64/65536 = 1 + input_adj_ns.next = 0 + input_adj_fns.next = 64 + input_adj_count.next = 1024 + input_adj_valid.next = 1 + + for i in range(2000): + yield clk.posedge + input_adj_valid.next = 0 + + stop_time = now()*1e-12 + stop_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + stop_ts_64 = output_ts_64/2**16*1e-9 + + print(stop_time-start_time) + print(stop_ts_96-start_ts_96) + print(stop_ts_64-start_ts_64) + + assert abs((stop_time-start_time) - (stop_ts_96-start_ts_96) + 1e-9) < 1e-12 + assert abs((stop_time-start_time) - (stop_ts_64-start_ts_64) + 1e-9) < 1e-12 + + yield delay(100000) + + yield clk.posedge + print("test 5: Frequency adjust") + current_test.next = 5 + + input_ts_96.next = 0 + input_ts_96_valid.next = 1 + input_ts_64.next = 0 + input_ts_64_valid.next = 1 + + input_period_ns.next = 6 + input_period_fns.next = 0x6624 + input_period_valid.next = 1 + + # flush old period out of pipeline registers + yield clk.posedge + yield clk.posedge + yield clk.posedge + + input_ts_96_valid.next = 0 + input_ts_64_valid.next = 0 + input_period_valid.next = 0 + + yield clk.posedge + + start_time = now()*1e-12 + start_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + start_ts_64 = output_ts_64/2**16*1e-9 + + for i in range(10000): + yield clk.posedge + + stop_time = now()*1e-12 + stop_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + stop_ts_64 = output_ts_64/2**16*1e-9 + + print(stop_time-start_time) + print(stop_ts_96-start_ts_96) + print(stop_ts_64-start_ts_64) + + assert abs((stop_time-start_time) - (stop_ts_96-start_ts_96) * 6.4/(6+(0x6624+2/5)/2**16)) < 1e-12 + assert abs((stop_time-start_time) - (stop_ts_64-start_ts_64) * 6.4/(6+(0x6624+2/5)/2**16)) < 1e-12 + + yield delay(100000) + + yield clk.posedge + print("test 6: Drift adjust") + current_test.next = 6 + + input_ts_96.next = 0 + input_ts_96_valid.next = 1 + input_ts_64.next = 0 + input_ts_64_valid.next = 1 + + input_period_ns.next = 6 + input_period_fns.next = 0x6666 + input_period_valid.next = 1 + + input_drift_ns.next = 0 + input_drift_fns.next = 20 + input_drift_rate.next = 5 + input_drift_valid.next = 1 + + # flush old period out of pipeline registers + yield clk.posedge + yield clk.posedge + yield clk.posedge + + input_ts_96_valid.next = 0 + input_ts_64_valid.next = 0 + input_period_valid.next = 0 + input_drift_valid.next = 0 + + yield clk.posedge + + start_time = now()*1e-12 + start_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + start_ts_64 = output_ts_64/2**16*1e-9 + + for i in range(10000): + yield clk.posedge + + stop_time = now()*1e-12 + stop_ts_96 = output_ts_96[96:48] + (output_ts_96[48:0]/2**16*1e-9) + stop_ts_64 = output_ts_64/2**16*1e-9 + + print(stop_time-start_time) + print(stop_ts_96-start_ts_96) + print(stop_ts_64-start_ts_64) + + assert abs((stop_time-start_time) - (stop_ts_96-start_ts_96) * 6.4/(6+(0x6666+20/5)/2**16)) < 1e-12 + assert abs((stop_time-start_time) - (stop_ts_64-start_ts_64) * 6.4/(6+(0x6666+20/5)/2**16)) < 1e-12 + + yield delay(100000) + + raise StopSimulation + + return dut, clkgen, check + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_ptp_clock.v b/fpga/lib/eth/tb/test_ptp_clock.v new file mode 100644 index 000000000..a95da483d --- /dev/null +++ b/fpga/lib/eth/tb/test_ptp_clock.v @@ -0,0 +1,146 @@ +/* + +Copyright (c) 2015-2019 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 1ps / 1fs + +/* + * Testbench for ptp_clock + */ +module test_ptp_clock; + +// Parameters +parameter PERIOD_NS_WIDTH = 4; +parameter OFFSET_NS_WIDTH = 4; +parameter DRIFT_NS_WIDTH = 4; +parameter FNS_WIDTH = 16; +parameter PERIOD_NS = 4'h6; +parameter PERIOD_FNS = 16'h6666; +parameter DRIFT_ENABLE = 1; +parameter DRIFT_NS = 4'h0; +parameter DRIFT_FNS = 16'h0002; +parameter DRIFT_RATE = 16'h0005; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [95:0] input_ts_96 = 0; +reg input_ts_96_valid = 0; +reg [63:0] input_ts_64 = 0; +reg input_ts_64_valid = 0; +reg [PERIOD_NS_WIDTH-1:0] input_period_ns = 0; +reg [FNS_WIDTH-1:0] input_period_fns = 0; +reg input_period_valid = 0; +reg [OFFSET_NS_WIDTH-1:0] input_adj_ns = 0; +reg [FNS_WIDTH-1:0] input_adj_fns = 0; +reg [15:0] input_adj_count = 0; +reg input_adj_valid = 0; +reg [DRIFT_NS_WIDTH-1:0] input_drift_ns = 0; +reg [FNS_WIDTH-1:0] input_drift_fns = 0; +reg [15:0] input_drift_rate = 0; +reg input_drift_valid = 0; + +// Outputs +wire input_adj_active; +wire [95:0] output_ts_96; +wire [63:0] output_ts_64; +wire output_ts_step; +wire output_pps; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + input_ts_96, + input_ts_96_valid, + input_ts_64, + input_ts_64_valid, + input_period_ns, + input_period_fns, + input_period_valid, + input_adj_ns, + input_adj_fns, + input_adj_count, + input_adj_valid, + input_drift_ns, + input_drift_fns, + input_drift_rate, + input_drift_valid + ); + $to_myhdl( + input_adj_active, + output_ts_96, + output_ts_64, + output_ts_step, + output_pps + ); + + // dump file + $dumpfile("test_ptp_clock.lxt"); + $dumpvars(0, test_ptp_clock); +end + +ptp_clock #( + .PERIOD_NS_WIDTH(PERIOD_NS_WIDTH), + .OFFSET_NS_WIDTH(OFFSET_NS_WIDTH), + .DRIFT_NS_WIDTH (DRIFT_NS_WIDTH), + .FNS_WIDTH(FNS_WIDTH), + .PERIOD_NS(PERIOD_NS), + .PERIOD_FNS(PERIOD_FNS), + .DRIFT_ENABLE(DRIFT_ENABLE), + .DRIFT_NS(DRIFT_NS), + .DRIFT_FNS(DRIFT_FNS), + .DRIFT_RATE(DRIFT_RATE) +) +UUT ( + .clk(clk), + .rst(rst), + .input_ts_96(input_ts_96), + .input_ts_96_valid(input_ts_96_valid), + .input_ts_64(input_ts_64), + .input_ts_64_valid(input_ts_64_valid), + .input_period_ns(input_period_ns), + .input_period_fns(input_period_fns), + .input_period_valid(input_period_valid), + .input_adj_ns(input_adj_ns), + .input_adj_fns(input_adj_fns), + .input_adj_count(input_adj_count), + .input_adj_valid(input_adj_valid), + .input_adj_active(input_adj_active), + .input_drift_ns(input_drift_ns), + .input_drift_fns(input_drift_fns), + .input_drift_rate(input_drift_rate), + .input_drift_valid(input_drift_valid), + .output_ts_96(output_ts_96), + .output_ts_64(output_ts_64), + .output_ts_step(output_ts_step), + .output_pps(output_pps) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_ptp_perout.py b/fpga/lib/eth/tb/test_ptp_perout.py new file mode 100755 index 000000000..b908368ae --- /dev/null +++ b/fpga/lib/eth/tb/test_ptp_perout.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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. + +""" + +from myhdl import * +import os + +import ptp + +module = 'ptp_perout' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + FNS_ENABLE = 1 + OUT_START_S = 0x0 + OUT_START_NS = 0x0 + OUT_START_FNS = 0x0000 + OUT_PERIOD_S = 1 + OUT_PERIOD_NS = 0 + OUT_PERIOD_FNS = 0x0000 + OUT_WIDTH_S = 0x0 + OUT_WIDTH_NS = 1000 + OUT_WIDTH_FNS = 0x0000 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_ts_96 = Signal(intbv(0)[96:]) + input_ts_step = Signal(bool(0)) + enable = Signal(bool(0)) + input_start = Signal(intbv(0)[96:]) + input_start_valid = Signal(bool(0)) + input_period = Signal(intbv(0)[96:]) + input_period_valid = Signal(bool(0)) + input_width = Signal(intbv(0)[96:]) + input_width_valid = Signal(bool(0)) + + # Outputs + locked = Signal(bool(0)) + error = Signal(bool(0)) + output_pulse = Signal(bool(0)) + + # PTP clock + ptp_clock = ptp.PtpClock() + + ptp_logic = ptp_clock.create_logic( + clk, + rst, + ts_96=input_ts_96 + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + input_ts_96=input_ts_96, + input_ts_step=input_ts_step, + enable=enable, + input_start=input_start, + input_start_valid=input_start_valid, + input_period=input_period, + input_period_valid=input_period_valid, + input_width=input_width, + input_width_valid=input_width_valid, + locked=locked, + error=error, + output_pulse=output_pulse + ) + + @always(delay(32)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + enable.next = 1 + + yield clk.posedge + print("test 1: Test pulse out") + current_test.next = 1 + + input_start.next = 100 << 16 + input_start_valid.next = 1 + input_period.next = 100 << 16 + input_period_valid.next = 1 + input_width.next = 50 << 16 + input_width_valid.next = 1 + + yield clk.posedge + + input_start_valid.next = 0 + input_period_valid.next = 0 + input_width_valid.next = 0 + + + yield delay(10000) + + yield delay(100) + + yield clk.posedge + print("test 2: Test pulse out") + current_test.next = 2 + + input_start.next = 0 << 16 + input_start_valid.next = 1 + input_period.next = 100 << 16 + input_period_valid.next = 1 + input_width.next = 50 << 16 + input_width_valid.next = 1 + + yield clk.posedge + + input_start_valid.next = 0 + input_period_valid.next = 0 + input_width_valid.next = 0 + + + yield delay(10000) + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_ptp_perout.v b/fpga/lib/eth/tb/test_ptp_perout.v new file mode 100644 index 000000000..160a44dd0 --- /dev/null +++ b/fpga/lib/eth/tb/test_ptp_perout.v @@ -0,0 +1,122 @@ +/* + +Copyright (c) 2019 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 1ns / 1ps + +/* + * Testbench for ptp_perout + */ +module test_ptp_perout; + +// Parameters +parameter FNS_ENABLE = 1; +parameter OUT_START_S = 48'h0; +parameter OUT_START_NS = 30'h0; +parameter OUT_START_FNS = 16'h0000; +parameter OUT_PERIOD_S = 48'd1; +parameter OUT_PERIOD_NS = 30'd0; +parameter OUT_PERIOD_FNS = 16'h0000; +parameter OUT_WIDTH_S = 48'h0; +parameter OUT_WIDTH_NS = 30'd1000; +parameter OUT_WIDTH_FNS = 16'h0000; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [95:0] input_ts_96 = 0; +reg input_ts_step = 0; +reg enable = 0; +reg [95:0] input_start = 0; +reg input_start_valid = 0; +reg [95:0] input_period = 0; +reg input_period_valid = 0; +reg [95:0] input_width = 0; +reg input_width_valid = 0; + +// Outputs +wire locked; +wire error; +wire output_pulse; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + input_ts_96, + input_ts_step, + enable, + input_start, + input_start_valid, + input_period, + input_period_valid, + input_width, + input_width_valid + ); + $to_myhdl( + locked, + error, + output_pulse + ); + + // dump file + $dumpfile("test_ptp_perout.lxt"); + $dumpvars(0, test_ptp_perout); +end + +ptp_perout #( + .FNS_ENABLE(FNS_ENABLE), + .OUT_START_S(OUT_START_S), + .OUT_START_NS(OUT_START_NS), + .OUT_START_FNS(OUT_START_FNS), + .OUT_PERIOD_S(OUT_PERIOD_S), + .OUT_PERIOD_NS(OUT_PERIOD_NS), + .OUT_PERIOD_FNS(OUT_PERIOD_FNS), + .OUT_WIDTH_S(OUT_WIDTH_S), + .OUT_WIDTH_NS(OUT_WIDTH_NS), + .OUT_WIDTH_FNS(OUT_WIDTH_FNS) +) +UUT ( + .clk(clk), + .rst(rst), + .input_ts_96(input_ts_96), + .input_ts_step(input_ts_step), + .enable(enable), + .input_start(input_start), + .input_start_valid(input_start_valid), + .input_period(input_period), + .input_period_valid(input_period_valid), + .input_width(input_width), + .input_width_valid(input_width_valid), + .locked(locked), + .error(error), + .output_pulse(output_pulse) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp.py b/fpga/lib/eth/tb/test_udp.py new file mode 100755 index 000000000..a1e2da1a8 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp.py @@ -0,0 +1,544 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import ip_ep +import udp_ep + +module = 'udp' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/udp_checksum_gen.v") +srcs.append("../rtl/udp_ip_rx.v") +srcs.append("../rtl/udp_ip_tx.v") +srcs.append("../lib/axis/rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid = Signal(bool(0)) + s_ip_eth_dest_mac = Signal(intbv(0)[48:]) + s_ip_eth_src_mac = Signal(intbv(0)[48:]) + s_ip_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + s_udp_hdr_valid = Signal(bool(0)) + s_udp_eth_dest_mac = Signal(intbv(0)[48:]) + s_udp_eth_src_mac = Signal(intbv(0)[48:]) + s_udp_eth_type = Signal(intbv(0)[16:]) + s_udp_ip_version = Signal(intbv(0)[4:]) + s_udp_ip_ihl = Signal(intbv(0)[4:]) + s_udp_ip_dscp = Signal(intbv(0)[6:]) + s_udp_ip_ecn = Signal(intbv(0)[2:]) + s_udp_ip_identification = Signal(intbv(0)[16:]) + s_udp_ip_flags = Signal(intbv(0)[3:]) + s_udp_ip_fragment_offset = Signal(intbv(0)[13:]) + s_udp_ip_ttl = Signal(intbv(0)[8:]) + s_udp_ip_header_checksum = Signal(intbv(0)[16:]) + s_udp_ip_source_ip = Signal(intbv(0)[32:]) + s_udp_ip_dest_ip = Signal(intbv(0)[32:]) + s_udp_source_port = Signal(intbv(0)[16:]) + s_udp_dest_port = Signal(intbv(0)[16:]) + s_udp_length = Signal(intbv(0)[16:]) + s_udp_checksum = Signal(intbv(0)[16:]) + s_udp_payload_axis_tdata = Signal(intbv(0)[8:]) + s_udp_payload_axis_tvalid = Signal(bool(0)) + s_udp_payload_axis_tlast = Signal(bool(0)) + s_udp_payload_axis_tuser = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + s_udp_hdr_ready = Signal(bool(0)) + s_udp_payload_axis_tready = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_ip_eth_dest_mac = Signal(intbv(0)[48:]) + m_ip_eth_src_mac = Signal(intbv(0)[48:]) + m_ip_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + m_udp_hdr_valid = Signal(bool(0)) + m_udp_eth_dest_mac = Signal(intbv(0)[48:]) + m_udp_eth_src_mac = Signal(intbv(0)[48:]) + m_udp_eth_type = Signal(intbv(0)[16:]) + m_udp_ip_version = Signal(intbv(0)[4:]) + m_udp_ip_ihl = Signal(intbv(0)[4:]) + m_udp_ip_dscp = Signal(intbv(0)[6:]) + m_udp_ip_ecn = Signal(intbv(0)[2:]) + m_udp_ip_length = Signal(intbv(0)[16:]) + m_udp_ip_identification = Signal(intbv(0)[16:]) + m_udp_ip_flags = Signal(intbv(0)[3:]) + m_udp_ip_fragment_offset = Signal(intbv(0)[13:]) + m_udp_ip_ttl = Signal(intbv(0)[8:]) + m_udp_ip_protocol = Signal(intbv(0)[8:]) + m_udp_ip_header_checksum = Signal(intbv(0)[16:]) + m_udp_ip_source_ip = Signal(intbv(0)[32:]) + m_udp_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[8:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tuser = Signal(bool(0)) + rx_busy = Signal(bool(0)) + tx_busy = Signal(bool(0)) + rx_error_header_early_termination = Signal(bool(0)) + rx_error_payload_early_termination = Signal(bool(0)) + tx_error_payload_early_termination = Signal(bool(0)) + + # sources and sinks + ip_source_pause = Signal(bool(0)) + ip_sink_pause = Signal(bool(0)) + udp_source_pause = Signal(bool(0)) + udp_sink_pause = Signal(bool(0)) + + ip_source = ip_ep.IPFrameSource() + + ip_source_logic = ip_source.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready, + ip_hdr_valid=s_ip_hdr_valid, + eth_dest_mac=s_ip_eth_dest_mac, + eth_src_mac=s_ip_eth_src_mac, + eth_type=s_ip_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=ip_source_pause, + name='ip_source' + ) + + ip_sink = ip_ep.IPFrameSink() + + ip_sink_logic = ip_sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_ip_eth_dest_mac, + eth_src_mac=m_ip_eth_src_mac, + eth_type=m_ip_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=ip_sink_pause, + name='ip_sink' + ) + + udp_source = udp_ep.UDPFrameSource() + + udp_source_logic = udp_source.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready, + udp_hdr_valid=s_udp_hdr_valid, + eth_dest_mac=s_udp_eth_dest_mac, + eth_src_mac=s_udp_eth_src_mac, + eth_type=s_udp_eth_type, + ip_version=s_udp_ip_version, + ip_ihl=s_udp_ip_ihl, + ip_dscp=s_udp_ip_dscp, + ip_ecn=s_udp_ip_ecn, + ip_identification=s_udp_ip_identification, + ip_flags=s_udp_ip_flags, + ip_fragment_offset=s_udp_ip_fragment_offset, + ip_ttl=s_udp_ip_ttl, + ip_header_checksum=s_udp_ip_header_checksum, + ip_source_ip=s_udp_ip_source_ip, + ip_dest_ip=s_udp_ip_dest_ip, + udp_source_port=s_udp_source_port, + udp_dest_port=s_udp_dest_port, + udp_length=s_udp_length, + udp_checksum=s_udp_checksum, + udp_payload_tdata=s_udp_payload_axis_tdata, + udp_payload_tvalid=s_udp_payload_axis_tvalid, + udp_payload_tready=s_udp_payload_axis_tready, + udp_payload_tlast=s_udp_payload_axis_tlast, + udp_payload_tuser=s_udp_payload_axis_tuser, + pause=udp_source_pause, + name='udp_source' + ) + + udp_sink = udp_ep.UDPFrameSink() + + udp_sink_logic = udp_sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_udp_eth_dest_mac, + eth_src_mac=m_udp_eth_src_mac, + eth_type=m_udp_eth_type, + ip_version=m_udp_ip_version, + ip_ihl=m_udp_ip_ihl, + ip_dscp=m_udp_ip_dscp, + ip_ecn=m_udp_ip_ecn, + ip_length=m_udp_ip_length, + ip_identification=m_udp_ip_identification, + ip_flags=m_udp_ip_flags, + ip_fragment_offset=m_udp_ip_fragment_offset, + ip_ttl=m_udp_ip_ttl, + ip_protocol=m_udp_ip_protocol, + ip_header_checksum=m_udp_ip_header_checksum, + ip_source_ip=m_udp_ip_source_ip, + ip_dest_ip=m_udp_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=udp_sink_pause, + name='udp_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_ip_eth_dest_mac=s_ip_eth_dest_mac, + s_ip_eth_src_mac=s_ip_eth_src_mac, + s_ip_eth_type=s_ip_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_ip_eth_dest_mac=m_ip_eth_dest_mac, + m_ip_eth_src_mac=m_ip_eth_src_mac, + m_ip_eth_type=m_ip_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_udp_eth_dest_mac=s_udp_eth_dest_mac, + s_udp_eth_src_mac=s_udp_eth_src_mac, + s_udp_eth_type=s_udp_eth_type, + s_udp_ip_version=s_udp_ip_version, + s_udp_ip_ihl=s_udp_ip_ihl, + s_udp_ip_dscp=s_udp_ip_dscp, + s_udp_ip_ecn=s_udp_ip_ecn, + s_udp_ip_identification=s_udp_ip_identification, + s_udp_ip_flags=s_udp_ip_flags, + s_udp_ip_fragment_offset=s_udp_ip_fragment_offset, + s_udp_ip_ttl=s_udp_ip_ttl, + s_udp_ip_header_checksum=s_udp_ip_header_checksum, + s_udp_ip_source_ip=s_udp_ip_source_ip, + s_udp_ip_dest_ip=s_udp_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_udp_eth_dest_mac=m_udp_eth_dest_mac, + m_udp_eth_src_mac=m_udp_eth_src_mac, + m_udp_eth_type=m_udp_eth_type, + m_udp_ip_version=m_udp_ip_version, + m_udp_ip_ihl=m_udp_ip_ihl, + m_udp_ip_dscp=m_udp_ip_dscp, + m_udp_ip_ecn=m_udp_ip_ecn, + m_udp_ip_length=m_udp_ip_length, + m_udp_ip_identification=m_udp_ip_identification, + m_udp_ip_flags=m_udp_ip_flags, + m_udp_ip_fragment_offset=m_udp_ip_fragment_offset, + m_udp_ip_ttl=m_udp_ip_ttl, + m_udp_ip_protocol=m_udp_ip_protocol, + m_udp_ip_header_checksum=m_udp_ip_header_checksum, + m_udp_ip_source_ip=m_udp_ip_source_ip, + m_udp_ip_dest_ip=m_udp_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + rx_busy=rx_busy, + tx_busy=tx_busy, + rx_error_header_early_termination=rx_error_header_early_termination, + rx_error_payload_early_termination=rx_error_payload_early_termination, + tx_error_payload_early_termination=tx_error_payload_early_termination + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + rx_error_header_early_termination_asserted = Signal(bool(0)) + rx_error_payload_early_termination_asserted = Signal(bool(0)) + tx_error_payload_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_header_early_termination): + rx_error_header_early_termination_asserted.next = 1 + if (rx_error_payload_early_termination): + rx_error_payload_early_termination_asserted.next = 1 + if (tx_error_payload_early_termination): + tx_error_payload_early_termination_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1234 + test_frame.udp_dest_port = 5678 + test_frame.payload = bytearray(range(32)) + test_frame.build() + ip_frame = test_frame.build_ip() + + ip_source.send(ip_frame) + + yield udp_sink.wait() + rx_frame = udp_sink.recv() + + assert rx_frame == test_frame + + assert ip_source.empty() + assert ip_sink.empty() + assert udp_source.empty() + assert udp_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test UDP TX packet") + current_test.next = 2 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1234 + test_frame.udp_dest_port = 5678 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + udp_source.send(test_frame) + + yield ip_sink.wait() + rx_frame = ip_sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame + + assert ip_source.empty() + assert ip_sink.empty() + assert udp_source.empty() + assert udp_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp.v b/fpga/lib/eth/tb/test_udp.v new file mode 100644 index 000000000..aa0c71b40 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp.v @@ -0,0 +1,386 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp + */ +module test_udp; + +// Parameters +parameter CHECKSUM_GEN_ENABLE = 1; +parameter CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11; +parameter CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_ip_hdr_valid = 0; +reg [47:0] s_ip_eth_dest_mac = 0; +reg [47:0] s_ip_eth_src_mac = 0; +reg [15:0] s_ip_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [7:0] s_ip_payload_axis_tdata = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg s_udp_hdr_valid = 0; +reg [47:0] s_udp_eth_dest_mac = 0; +reg [47:0] s_udp_eth_src_mac = 0; +reg [15:0] s_udp_eth_type = 0; +reg [3:0] s_udp_ip_version = 0; +reg [3:0] s_udp_ip_ihl = 0; +reg [5:0] s_udp_ip_dscp = 0; +reg [1:0] s_udp_ip_ecn = 0; +reg [15:0] s_udp_ip_identification = 0; +reg [2:0] s_udp_ip_flags = 0; +reg [12:0] s_udp_ip_fragment_offset = 0; +reg [7:0] s_udp_ip_ttl = 0; +reg [15:0] s_udp_ip_header_checksum = 0; +reg [31:0] s_udp_ip_source_ip = 0; +reg [31:0] s_udp_ip_dest_ip = 0; +reg [15:0] s_udp_source_port = 0; +reg [15:0] s_udp_dest_port = 0; +reg [15:0] s_udp_length = 0; +reg [15:0] s_udp_checksum = 0; +reg [7:0] s_udp_payload_axis_tdata = 0; +reg s_udp_payload_axis_tvalid = 0; +reg s_udp_payload_axis_tlast = 0; +reg s_udp_payload_axis_tuser = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; + +// Outputs +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire s_udp_hdr_ready; +wire s_udp_payload_axis_tready; +wire m_ip_hdr_valid; +wire [47:0] m_ip_eth_dest_mac; +wire [47:0] m_ip_eth_src_mac; +wire [15:0] m_ip_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [7:0] m_ip_payload_axis_tdata; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire m_udp_hdr_valid; +wire [47:0] m_udp_eth_dest_mac; +wire [47:0] m_udp_eth_src_mac; +wire [15:0] m_udp_eth_type; +wire [3:0] m_udp_ip_version; +wire [3:0] m_udp_ip_ihl; +wire [5:0] m_udp_ip_dscp; +wire [1:0] m_udp_ip_ecn; +wire [15:0] m_udp_ip_length; +wire [15:0] m_udp_ip_identification; +wire [2:0] m_udp_ip_flags; +wire [12:0] m_udp_ip_fragment_offset; +wire [7:0] m_udp_ip_ttl; +wire [7:0] m_udp_ip_protocol; +wire [15:0] m_udp_ip_header_checksum; +wire [31:0] m_udp_ip_source_ip; +wire [31:0] m_udp_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [7:0] m_udp_payload_axis_tdata; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire m_udp_payload_axis_tuser; +wire rx_busy; +wire tx_busy; +wire rx_error_header_early_termination; +wire rx_error_payload_early_termination; +wire tx_error_payload_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_ip_eth_dest_mac, + s_ip_eth_src_mac, + s_ip_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + s_udp_hdr_valid, + s_udp_eth_dest_mac, + s_udp_eth_src_mac, + s_udp_eth_type, + s_udp_ip_version, + s_udp_ip_ihl, + s_udp_ip_dscp, + s_udp_ip_ecn, + s_udp_ip_identification, + s_udp_ip_flags, + s_udp_ip_fragment_offset, + s_udp_ip_ttl, + s_udp_ip_header_checksum, + s_udp_ip_source_ip, + s_udp_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + m_udp_hdr_ready, + m_udp_payload_axis_tready + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_ip_hdr_valid, + m_ip_eth_dest_mac, + m_ip_eth_src_mac, + m_ip_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + m_udp_hdr_valid, + m_udp_eth_dest_mac, + m_udp_eth_src_mac, + m_udp_eth_type, + m_udp_ip_version, + m_udp_ip_ihl, + m_udp_ip_dscp, + m_udp_ip_ecn, + m_udp_ip_length, + m_udp_ip_identification, + m_udp_ip_flags, + m_udp_ip_fragment_offset, + m_udp_ip_ttl, + m_udp_ip_protocol, + m_udp_ip_header_checksum, + m_udp_ip_source_ip, + m_udp_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser, + rx_busy, + tx_busy, + rx_error_header_early_termination, + rx_error_payload_early_termination, + tx_error_payload_early_termination + ); + + // dump file + $dumpfile("test_udp.lxt"); + $dumpvars(0, test_udp); +end + +udp #( + .CHECKSUM_GEN_ENABLE(CHECKSUM_GEN_ENABLE), + .CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH(CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH), + .CHECKSUM_HEADER_FIFO_ADDR_WIDTH(CHECKSUM_HEADER_FIFO_ADDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_eth_dest_mac(s_ip_eth_dest_mac), + .s_ip_eth_src_mac(s_ip_eth_src_mac), + .s_ip_eth_type(s_ip_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_udp_eth_dest_mac(s_udp_eth_dest_mac), + .s_udp_eth_src_mac(s_udp_eth_src_mac), + .s_udp_eth_type(s_udp_eth_type), + .s_udp_ip_version(s_udp_ip_version), + .s_udp_ip_ihl(s_udp_ip_ihl), + .s_udp_ip_dscp(s_udp_ip_dscp), + .s_udp_ip_ecn(s_udp_ip_ecn), + .s_udp_ip_identification(s_udp_ip_identification), + .s_udp_ip_flags(s_udp_ip_flags), + .s_udp_ip_fragment_offset(s_udp_ip_fragment_offset), + .s_udp_ip_ttl(s_udp_ip_ttl), + .s_udp_ip_header_checksum(s_udp_ip_header_checksum), + .s_udp_ip_source_ip(s_udp_ip_source_ip), + .s_udp_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_udp_eth_dest_mac(m_udp_eth_dest_mac), + .m_udp_eth_src_mac(m_udp_eth_src_mac), + .m_udp_eth_type(m_udp_eth_type), + .m_udp_ip_version(m_udp_ip_version), + .m_udp_ip_ihl(m_udp_ip_ihl), + .m_udp_ip_dscp(m_udp_ip_dscp), + .m_udp_ip_ecn(m_udp_ip_ecn), + .m_udp_ip_length(m_udp_ip_length), + .m_udp_ip_identification(m_udp_ip_identification), + .m_udp_ip_flags(m_udp_ip_flags), + .m_udp_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_udp_ip_ttl(m_udp_ip_ttl), + .m_udp_ip_protocol(m_udp_ip_protocol), + .m_udp_ip_header_checksum(m_udp_ip_header_checksum), + .m_udp_ip_source_ip(m_udp_ip_source_ip), + .m_udp_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status signals + .rx_busy(rx_busy), + .tx_busy(tx_busy), + .rx_error_header_early_termination(rx_error_header_early_termination), + .rx_error_payload_early_termination(rx_error_payload_early_termination), + .tx_error_payload_early_termination(tx_error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_64.py b/fpga/lib/eth/tb/test_udp_64.py new file mode 100755 index 000000000..941f3dcf8 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_64.py @@ -0,0 +1,556 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import ip_ep +import udp_ep + +module = 'udp_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/udp_checksum_gen_64.v") +srcs.append("../rtl/udp_ip_rx_64.v") +srcs.append("../rtl/udp_ip_tx_64.v") +srcs.append("../lib/axis/rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid = Signal(bool(0)) + s_ip_eth_dest_mac = Signal(intbv(0)[48:]) + s_ip_eth_src_mac = Signal(intbv(0)[48:]) + s_ip_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + s_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + s_udp_hdr_valid = Signal(bool(0)) + s_udp_eth_dest_mac = Signal(intbv(0)[48:]) + s_udp_eth_src_mac = Signal(intbv(0)[48:]) + s_udp_eth_type = Signal(intbv(0)[16:]) + s_udp_ip_version = Signal(intbv(0)[4:]) + s_udp_ip_ihl = Signal(intbv(0)[4:]) + s_udp_ip_dscp = Signal(intbv(0)[6:]) + s_udp_ip_ecn = Signal(intbv(0)[2:]) + s_udp_ip_identification = Signal(intbv(0)[16:]) + s_udp_ip_flags = Signal(intbv(0)[3:]) + s_udp_ip_fragment_offset = Signal(intbv(0)[13:]) + s_udp_ip_ttl = Signal(intbv(0)[8:]) + s_udp_ip_header_checksum = Signal(intbv(0)[16:]) + s_udp_ip_source_ip = Signal(intbv(0)[32:]) + s_udp_ip_dest_ip = Signal(intbv(0)[32:]) + s_udp_source_port = Signal(intbv(0)[16:]) + s_udp_dest_port = Signal(intbv(0)[16:]) + s_udp_length = Signal(intbv(0)[16:]) + s_udp_checksum = Signal(intbv(0)[16:]) + s_udp_payload_axis_tdata = Signal(intbv(0)[64:]) + s_udp_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_udp_payload_axis_tvalid = Signal(bool(0)) + s_udp_payload_axis_tlast = Signal(bool(0)) + s_udp_payload_axis_tuser = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + s_udp_hdr_ready = Signal(bool(0)) + s_udp_payload_axis_tready = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_ip_eth_dest_mac = Signal(intbv(0)[48:]) + m_ip_eth_src_mac = Signal(intbv(0)[48:]) + m_ip_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + m_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + m_udp_hdr_valid = Signal(bool(0)) + m_udp_eth_dest_mac = Signal(intbv(0)[48:]) + m_udp_eth_src_mac = Signal(intbv(0)[48:]) + m_udp_eth_type = Signal(intbv(0)[16:]) + m_udp_ip_version = Signal(intbv(0)[4:]) + m_udp_ip_ihl = Signal(intbv(0)[4:]) + m_udp_ip_dscp = Signal(intbv(0)[6:]) + m_udp_ip_ecn = Signal(intbv(0)[2:]) + m_udp_ip_length = Signal(intbv(0)[16:]) + m_udp_ip_identification = Signal(intbv(0)[16:]) + m_udp_ip_flags = Signal(intbv(0)[3:]) + m_udp_ip_fragment_offset = Signal(intbv(0)[13:]) + m_udp_ip_ttl = Signal(intbv(0)[8:]) + m_udp_ip_protocol = Signal(intbv(0)[8:]) + m_udp_ip_header_checksum = Signal(intbv(0)[16:]) + m_udp_ip_source_ip = Signal(intbv(0)[32:]) + m_udp_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[64:]) + m_udp_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tuser = Signal(bool(0)) + rx_busy = Signal(bool(0)) + tx_busy = Signal(bool(0)) + rx_error_header_early_termination = Signal(bool(0)) + rx_error_payload_early_termination = Signal(bool(0)) + tx_error_payload_early_termination = Signal(bool(0)) + + # sources and sinks + ip_source_pause = Signal(bool(0)) + ip_sink_pause = Signal(bool(0)) + udp_source_pause = Signal(bool(0)) + udp_sink_pause = Signal(bool(0)) + + ip_source = ip_ep.IPFrameSource() + + ip_source_logic = ip_source.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready, + ip_hdr_valid=s_ip_hdr_valid, + eth_dest_mac=s_ip_eth_dest_mac, + eth_src_mac=s_ip_eth_src_mac, + eth_type=s_ip_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tkeep=s_ip_payload_axis_tkeep, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=ip_source_pause, + name='ip_source' + ) + + ip_sink = ip_ep.IPFrameSink() + + ip_sink_logic = ip_sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_ip_eth_dest_mac, + eth_src_mac=m_ip_eth_src_mac, + eth_type=m_ip_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tkeep=m_ip_payload_axis_tkeep, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=ip_sink_pause, + name='ip_sink' + ) + + udp_source = udp_ep.UDPFrameSource() + + udp_source_logic = udp_source.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready, + udp_hdr_valid=s_udp_hdr_valid, + eth_dest_mac=s_udp_eth_dest_mac, + eth_src_mac=s_udp_eth_src_mac, + eth_type=s_udp_eth_type, + ip_version=s_udp_ip_version, + ip_ihl=s_udp_ip_ihl, + ip_dscp=s_udp_ip_dscp, + ip_ecn=s_udp_ip_ecn, + ip_identification=s_udp_ip_identification, + ip_flags=s_udp_ip_flags, + ip_fragment_offset=s_udp_ip_fragment_offset, + ip_ttl=s_udp_ip_ttl, + ip_header_checksum=s_udp_ip_header_checksum, + ip_source_ip=s_udp_ip_source_ip, + ip_dest_ip=s_udp_ip_dest_ip, + udp_source_port=s_udp_source_port, + udp_dest_port=s_udp_dest_port, + udp_length=s_udp_length, + udp_checksum=s_udp_checksum, + udp_payload_tdata=s_udp_payload_axis_tdata, + udp_payload_tkeep=s_udp_payload_axis_tkeep, + udp_payload_tvalid=s_udp_payload_axis_tvalid, + udp_payload_tready=s_udp_payload_axis_tready, + udp_payload_tlast=s_udp_payload_axis_tlast, + udp_payload_tuser=s_udp_payload_axis_tuser, + pause=udp_source_pause, + name='udp_source' + ) + + udp_sink = udp_ep.UDPFrameSink() + + udp_sink_logic = udp_sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_udp_eth_dest_mac, + eth_src_mac=m_udp_eth_src_mac, + eth_type=m_udp_eth_type, + ip_version=m_udp_ip_version, + ip_ihl=m_udp_ip_ihl, + ip_dscp=m_udp_ip_dscp, + ip_ecn=m_udp_ip_ecn, + ip_length=m_udp_ip_length, + ip_identification=m_udp_ip_identification, + ip_flags=m_udp_ip_flags, + ip_fragment_offset=m_udp_ip_fragment_offset, + ip_ttl=m_udp_ip_ttl, + ip_protocol=m_udp_ip_protocol, + ip_header_checksum=m_udp_ip_header_checksum, + ip_source_ip=m_udp_ip_source_ip, + ip_dest_ip=m_udp_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tkeep=m_udp_payload_axis_tkeep, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=udp_sink_pause, + name='udp_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_ip_eth_dest_mac=s_ip_eth_dest_mac, + s_ip_eth_src_mac=s_ip_eth_src_mac, + s_ip_eth_type=s_ip_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_ip_eth_dest_mac=m_ip_eth_dest_mac, + m_ip_eth_src_mac=m_ip_eth_src_mac, + m_ip_eth_type=m_ip_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_udp_eth_dest_mac=s_udp_eth_dest_mac, + s_udp_eth_src_mac=s_udp_eth_src_mac, + s_udp_eth_type=s_udp_eth_type, + s_udp_ip_version=s_udp_ip_version, + s_udp_ip_ihl=s_udp_ip_ihl, + s_udp_ip_dscp=s_udp_ip_dscp, + s_udp_ip_ecn=s_udp_ip_ecn, + s_udp_ip_identification=s_udp_ip_identification, + s_udp_ip_flags=s_udp_ip_flags, + s_udp_ip_fragment_offset=s_udp_ip_fragment_offset, + s_udp_ip_ttl=s_udp_ip_ttl, + s_udp_ip_header_checksum=s_udp_ip_header_checksum, + s_udp_ip_source_ip=s_udp_ip_source_ip, + s_udp_ip_dest_ip=s_udp_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep=s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_udp_eth_dest_mac=m_udp_eth_dest_mac, + m_udp_eth_src_mac=m_udp_eth_src_mac, + m_udp_eth_type=m_udp_eth_type, + m_udp_ip_version=m_udp_ip_version, + m_udp_ip_ihl=m_udp_ip_ihl, + m_udp_ip_dscp=m_udp_ip_dscp, + m_udp_ip_ecn=m_udp_ip_ecn, + m_udp_ip_length=m_udp_ip_length, + m_udp_ip_identification=m_udp_ip_identification, + m_udp_ip_flags=m_udp_ip_flags, + m_udp_ip_fragment_offset=m_udp_ip_fragment_offset, + m_udp_ip_ttl=m_udp_ip_ttl, + m_udp_ip_protocol=m_udp_ip_protocol, + m_udp_ip_header_checksum=m_udp_ip_header_checksum, + m_udp_ip_source_ip=m_udp_ip_source_ip, + m_udp_ip_dest_ip=m_udp_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep=m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + rx_busy=rx_busy, + tx_busy=tx_busy, + rx_error_header_early_termination=rx_error_header_early_termination, + rx_error_payload_early_termination=rx_error_payload_early_termination, + tx_error_payload_early_termination=tx_error_payload_early_termination + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + rx_error_header_early_termination_asserted = Signal(bool(0)) + rx_error_payload_early_termination_asserted = Signal(bool(0)) + tx_error_payload_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_header_early_termination): + rx_error_header_early_termination_asserted.next = 1 + if (rx_error_payload_early_termination): + rx_error_payload_early_termination_asserted.next = 1 + if (tx_error_payload_early_termination): + tx_error_payload_early_termination_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + print("test 1: test UDP RX packet") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1234 + test_frame.udp_dest_port = 5678 + test_frame.payload = bytearray(range(32)) + test_frame.build() + ip_frame = test_frame.build_ip() + + ip_source.send(ip_frame) + + yield udp_sink.wait() + rx_frame = udp_sink.recv() + + assert rx_frame == test_frame + + assert ip_source.empty() + assert ip_sink.empty() + assert udp_source.empty() + assert udp_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test UDP TX packet") + current_test.next = 2 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1234 + test_frame.udp_dest_port = 5678 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + udp_source.send(test_frame) + + yield ip_sink.wait() + rx_frame = ip_sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame + + assert ip_source.empty() + assert ip_sink.empty() + assert udp_source.empty() + assert udp_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_64.v b/fpga/lib/eth/tb/test_udp_64.v new file mode 100644 index 000000000..96623cde2 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_64.v @@ -0,0 +1,398 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_64 + */ +module test_udp_64; + +// Parameters +parameter CHECKSUM_GEN_ENABLE = 1; +parameter CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11; +parameter CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_ip_hdr_valid = 0; +reg [47:0] s_ip_eth_dest_mac = 0; +reg [47:0] s_ip_eth_src_mac = 0; +reg [15:0] s_ip_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [63:0] s_ip_payload_axis_tdata = 0; +reg [7:0] s_ip_payload_axis_tkeep = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg s_udp_hdr_valid = 0; +reg [47:0] s_udp_eth_dest_mac = 0; +reg [47:0] s_udp_eth_src_mac = 0; +reg [15:0] s_udp_eth_type = 0; +reg [3:0] s_udp_ip_version = 0; +reg [3:0] s_udp_ip_ihl = 0; +reg [5:0] s_udp_ip_dscp = 0; +reg [1:0] s_udp_ip_ecn = 0; +reg [15:0] s_udp_ip_identification = 0; +reg [2:0] s_udp_ip_flags = 0; +reg [12:0] s_udp_ip_fragment_offset = 0; +reg [7:0] s_udp_ip_ttl = 0; +reg [15:0] s_udp_ip_header_checksum = 0; +reg [31:0] s_udp_ip_source_ip = 0; +reg [31:0] s_udp_ip_dest_ip = 0; +reg [15:0] s_udp_source_port = 0; +reg [15:0] s_udp_dest_port = 0; +reg [15:0] s_udp_length = 0; +reg [15:0] s_udp_checksum = 0; +reg [63:0] s_udp_payload_axis_tdata = 0; +reg [7:0] s_udp_payload_axis_tkeep = 0; +reg s_udp_payload_axis_tvalid = 0; +reg s_udp_payload_axis_tlast = 0; +reg s_udp_payload_axis_tuser = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; + +// Outputs +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire s_udp_hdr_ready; +wire s_udp_payload_axis_tready; +wire m_ip_hdr_valid; +wire [47:0] m_ip_eth_dest_mac; +wire [47:0] m_ip_eth_src_mac; +wire [15:0] m_ip_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [63:0] m_ip_payload_axis_tdata; +wire [7:0] m_ip_payload_axis_tkeep; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire m_udp_hdr_valid; +wire [47:0] m_udp_eth_dest_mac; +wire [47:0] m_udp_eth_src_mac; +wire [15:0] m_udp_eth_type; +wire [3:0] m_udp_ip_version; +wire [3:0] m_udp_ip_ihl; +wire [5:0] m_udp_ip_dscp; +wire [1:0] m_udp_ip_ecn; +wire [15:0] m_udp_ip_length; +wire [15:0] m_udp_ip_identification; +wire [2:0] m_udp_ip_flags; +wire [12:0] m_udp_ip_fragment_offset; +wire [7:0] m_udp_ip_ttl; +wire [7:0] m_udp_ip_protocol; +wire [15:0] m_udp_ip_header_checksum; +wire [31:0] m_udp_ip_source_ip; +wire [31:0] m_udp_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [63:0] m_udp_payload_axis_tdata; +wire [7:0] m_udp_payload_axis_tkeep; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire m_udp_payload_axis_tuser; +wire rx_busy; +wire tx_busy; +wire rx_error_header_early_termination; +wire rx_error_payload_early_termination; +wire tx_error_payload_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_ip_eth_dest_mac, + s_ip_eth_src_mac, + s_ip_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + s_udp_hdr_valid, + s_udp_eth_dest_mac, + s_udp_eth_src_mac, + s_udp_eth_type, + s_udp_ip_version, + s_udp_ip_ihl, + s_udp_ip_dscp, + s_udp_ip_ecn, + s_udp_ip_identification, + s_udp_ip_flags, + s_udp_ip_fragment_offset, + s_udp_ip_ttl, + s_udp_ip_header_checksum, + s_udp_ip_source_ip, + s_udp_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + m_udp_hdr_ready, + m_udp_payload_axis_tready + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_ip_hdr_valid, + m_ip_eth_dest_mac, + m_ip_eth_src_mac, + m_ip_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + m_udp_hdr_valid, + m_udp_eth_dest_mac, + m_udp_eth_src_mac, + m_udp_eth_type, + m_udp_ip_version, + m_udp_ip_ihl, + m_udp_ip_dscp, + m_udp_ip_ecn, + m_udp_ip_length, + m_udp_ip_identification, + m_udp_ip_flags, + m_udp_ip_fragment_offset, + m_udp_ip_ttl, + m_udp_ip_protocol, + m_udp_ip_header_checksum, + m_udp_ip_source_ip, + m_udp_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser, + rx_busy, + tx_busy, + rx_error_header_early_termination, + rx_error_payload_early_termination, + tx_error_payload_early_termination + ); + + // dump file + $dumpfile("test_udp_64.lxt"); + $dumpvars(0, test_udp_64); +end + +udp_64 #( + .CHECKSUM_GEN_ENABLE(CHECKSUM_GEN_ENABLE), + .CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH(CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH), + .CHECKSUM_HEADER_FIFO_ADDR_WIDTH(CHECKSUM_HEADER_FIFO_ADDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_eth_dest_mac(s_ip_eth_dest_mac), + .s_ip_eth_src_mac(s_ip_eth_src_mac), + .s_ip_eth_type(s_ip_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_udp_eth_dest_mac(s_udp_eth_dest_mac), + .s_udp_eth_src_mac(s_udp_eth_src_mac), + .s_udp_eth_type(s_udp_eth_type), + .s_udp_ip_version(s_udp_ip_version), + .s_udp_ip_ihl(s_udp_ip_ihl), + .s_udp_ip_dscp(s_udp_ip_dscp), + .s_udp_ip_ecn(s_udp_ip_ecn), + .s_udp_ip_identification(s_udp_ip_identification), + .s_udp_ip_flags(s_udp_ip_flags), + .s_udp_ip_fragment_offset(s_udp_ip_fragment_offset), + .s_udp_ip_ttl(s_udp_ip_ttl), + .s_udp_ip_header_checksum(s_udp_ip_header_checksum), + .s_udp_ip_source_ip(s_udp_ip_source_ip), + .s_udp_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_udp_eth_dest_mac(m_udp_eth_dest_mac), + .m_udp_eth_src_mac(m_udp_eth_src_mac), + .m_udp_eth_type(m_udp_eth_type), + .m_udp_ip_version(m_udp_ip_version), + .m_udp_ip_ihl(m_udp_ip_ihl), + .m_udp_ip_dscp(m_udp_ip_dscp), + .m_udp_ip_ecn(m_udp_ip_ecn), + .m_udp_ip_length(m_udp_ip_length), + .m_udp_ip_identification(m_udp_ip_identification), + .m_udp_ip_flags(m_udp_ip_flags), + .m_udp_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_udp_ip_ttl(m_udp_ip_ttl), + .m_udp_ip_protocol(m_udp_ip_protocol), + .m_udp_ip_header_checksum(m_udp_ip_header_checksum), + .m_udp_ip_source_ip(m_udp_ip_source_ip), + .m_udp_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status signals + .rx_busy(rx_busy), + .tx_busy(tx_busy), + .rx_error_header_early_termination(rx_error_header_early_termination), + .rx_error_payload_early_termination(rx_error_payload_early_termination), + .tx_error_payload_early_termination(tx_error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_arb_mux_4.py b/fpga/lib/eth/tb/test_udp_arb_mux_4.py new file mode 100755 index 000000000..69c07767e --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_arb_mux_4.py @@ -0,0 +1,808 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import udp_ep + +module = 'udp_arb_mux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/axis/rtl/arbiter.v") +srcs.append("../lib/axis/rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + ARB_TYPE = "PRIORITY" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_udp_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_version_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_ihl_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_dscp_list = [Signal(intbv(0)[6:]) for i in range(S_COUNT)] + s_ip_ecn_list = [Signal(intbv(0)[2:]) for i in range(S_COUNT)] + s_ip_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_identification_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_flags_list = [Signal(intbv(0)[3:]) for i in range(S_COUNT)] + s_ip_fragment_offset_list = [Signal(intbv(0)[13:]) for i in range(S_COUNT)] + s_ip_ttl_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_protocol_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_header_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_source_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_dest_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_udp_source_port_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_dest_port_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_udp_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_udp_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_udp_hdr_valid = ConcatSignal(*reversed(s_udp_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_ip_version = ConcatSignal(*reversed(s_ip_version_list)) + s_ip_ihl = ConcatSignal(*reversed(s_ip_ihl_list)) + s_ip_dscp = ConcatSignal(*reversed(s_ip_dscp_list)) + s_ip_ecn = ConcatSignal(*reversed(s_ip_ecn_list)) + s_ip_length = ConcatSignal(*reversed(s_ip_length_list)) + s_ip_identification = ConcatSignal(*reversed(s_ip_identification_list)) + s_ip_flags = ConcatSignal(*reversed(s_ip_flags_list)) + s_ip_fragment_offset = ConcatSignal(*reversed(s_ip_fragment_offset_list)) + s_ip_ttl = ConcatSignal(*reversed(s_ip_ttl_list)) + s_ip_protocol = ConcatSignal(*reversed(s_ip_protocol_list)) + s_ip_header_checksum = ConcatSignal(*reversed(s_ip_header_checksum_list)) + s_ip_source_ip = ConcatSignal(*reversed(s_ip_source_ip_list)) + s_ip_dest_ip = ConcatSignal(*reversed(s_ip_dest_ip_list)) + s_udp_source_port = ConcatSignal(*reversed(s_udp_source_port_list)) + s_udp_dest_port = ConcatSignal(*reversed(s_udp_dest_port_list)) + s_udp_length = ConcatSignal(*reversed(s_udp_length_list)) + s_udp_checksum = ConcatSignal(*reversed(s_udp_checksum_list)) + s_udp_payload_axis_tdata = ConcatSignal(*reversed(s_udp_payload_axis_tdata_list)) + s_udp_payload_axis_tkeep = ConcatSignal(*reversed(s_udp_payload_axis_tkeep_list)) + s_udp_payload_axis_tvalid = ConcatSignal(*reversed(s_udp_payload_axis_tvalid_list)) + s_udp_payload_axis_tlast = ConcatSignal(*reversed(s_udp_payload_axis_tlast_list)) + s_udp_payload_axis_tid = ConcatSignal(*reversed(s_udp_payload_axis_tid_list)) + s_udp_payload_axis_tdest = ConcatSignal(*reversed(s_udp_payload_axis_tdest_list)) + s_udp_payload_axis_tuser = ConcatSignal(*reversed(s_udp_payload_axis_tuser_list)) + + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_udp_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_udp_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_udp_hdr_ready_list = [s_udp_hdr_ready(i) for i in range(S_COUNT)] + s_udp_payload_axis_tready_list = [s_udp_payload_axis_tready(i) for i in range(S_COUNT)] + + m_udp_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_udp_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_udp_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_udp_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = udp_ep.UDPFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready_list[k], + udp_hdr_valid=s_udp_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + ip_version=s_ip_version_list[k], + ip_ihl=s_ip_ihl_list[k], + ip_dscp=s_ip_dscp_list[k], + ip_ecn=s_ip_ecn_list[k], + ip_length=s_ip_length_list[k], + ip_identification=s_ip_identification_list[k], + ip_flags=s_ip_flags_list[k], + ip_fragment_offset=s_ip_fragment_offset_list[k], + ip_ttl=s_ip_ttl_list[k], + ip_protocol=s_ip_protocol_list[k], + ip_header_checksum=s_ip_header_checksum_list[k], + ip_source_ip=s_ip_source_ip_list[k], + ip_dest_ip=s_ip_dest_ip_list[k], + udp_source_port=s_udp_source_port_list[k], + udp_dest_port=s_udp_dest_port_list[k], + udp_length=s_udp_length_list[k], + udp_checksum=s_udp_checksum_list[k], + udp_payload_tdata=s_udp_payload_axis_tdata_list[k], + udp_payload_tkeep=s_udp_payload_axis_tkeep_list[k], + udp_payload_tvalid=s_udp_payload_axis_tvalid_list[k], + udp_payload_tready=s_udp_payload_axis_tready_list[k], + udp_payload_tlast=s_udp_payload_axis_tlast_list[k], + udp_payload_tuser=s_udp_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = udp_ep.UDPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tkeep=m_udp_payload_axis_tkeep, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep=s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tid=s_udp_payload_axis_tid, + s_udp_payload_axis_tdest=s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep=m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tid=m_udp_payload_axis_tid, + m_udp_payload_axis_tdest=m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: port 0") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: port 1") + current_test.next = 2 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_udp_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_udp_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: back-to-back packets, different ports, arbitration test") + current_test.next = 7 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + yield clk.posedge + + yield delay(800) + yield clk.posedge + source_list[1].send(test_frame1) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_arb_mux_4.v b/fpga/lib/eth/tb/test_udp_arb_mux_4.v new file mode 100644 index 000000000..4072e624e --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_arb_mux_4.v @@ -0,0 +1,274 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_arb_mux + */ +module test_udp_arb_mux_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter ARB_TYPE = "PRIORITY"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_udp_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*4-1:0] s_ip_version = 0; +reg [S_COUNT*4-1:0] s_ip_ihl = 0; +reg [S_COUNT*6-1:0] s_ip_dscp = 0; +reg [S_COUNT*2-1:0] s_ip_ecn = 0; +reg [S_COUNT*16-1:0] s_ip_length = 0; +reg [S_COUNT*16-1:0] s_ip_identification = 0; +reg [S_COUNT*3-1:0] s_ip_flags = 0; +reg [S_COUNT*13-1:0] s_ip_fragment_offset = 0; +reg [S_COUNT*8-1:0] s_ip_ttl = 0; +reg [S_COUNT*8-1:0] s_ip_protocol = 0; +reg [S_COUNT*16-1:0] s_ip_header_checksum = 0; +reg [S_COUNT*32-1:0] s_ip_source_ip = 0; +reg [S_COUNT*32-1:0] s_ip_dest_ip = 0; +reg [S_COUNT*16-1:0] s_udp_source_port = 0; +reg [S_COUNT*16-1:0] s_udp_dest_port = 0; +reg [S_COUNT*16-1:0] s_udp_length = 0; +reg [S_COUNT*16-1:0] s_udp_checksum = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_udp_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_udp_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_udp_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_udp_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_udp_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_udp_payload_axis_tuser = 0; + +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_udp_hdr_ready; +wire [S_COUNT-1:0] s_udp_payload_axis_tready; + +wire m_udp_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [DATA_WIDTH-1:0] m_udp_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_udp_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_udp_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_udp_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_udp_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tid, + s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser, + m_udp_hdr_ready, + m_udp_payload_axis_tready + ); + $to_myhdl( + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_udp_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tid, + m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser + ); + + // dump file + $dumpfile("test_udp_arb_mux_4.lxt"); + $dumpvars(0, test_udp_arb_mux_4); +end + +udp_arb_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tid(s_udp_payload_axis_tid), + .s_udp_payload_axis_tdest(s_udp_payload_axis_tdest), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // Ethernet frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tid(m_udp_payload_axis_tid), + .m_udp_payload_axis_tdest(m_udp_payload_axis_tdest), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_arb_mux_64_4.py b/fpga/lib/eth/tb/test_udp_arb_mux_64_4.py new file mode 100755 index 000000000..a4d0b3de8 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_arb_mux_64_4.py @@ -0,0 +1,808 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import udp_ep + +module = 'udp_arb_mux' +testbench = 'test_%s_64_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/axis/rtl/arbiter.v") +srcs.append("../lib/axis/rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + ARB_TYPE = "PRIORITY" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_udp_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_version_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_ihl_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_dscp_list = [Signal(intbv(0)[6:]) for i in range(S_COUNT)] + s_ip_ecn_list = [Signal(intbv(0)[2:]) for i in range(S_COUNT)] + s_ip_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_identification_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_flags_list = [Signal(intbv(0)[3:]) for i in range(S_COUNT)] + s_ip_fragment_offset_list = [Signal(intbv(0)[13:]) for i in range(S_COUNT)] + s_ip_ttl_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_protocol_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_header_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_source_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_dest_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_udp_source_port_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_dest_port_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_udp_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_udp_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_udp_hdr_valid = ConcatSignal(*reversed(s_udp_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_ip_version = ConcatSignal(*reversed(s_ip_version_list)) + s_ip_ihl = ConcatSignal(*reversed(s_ip_ihl_list)) + s_ip_dscp = ConcatSignal(*reversed(s_ip_dscp_list)) + s_ip_ecn = ConcatSignal(*reversed(s_ip_ecn_list)) + s_ip_length = ConcatSignal(*reversed(s_ip_length_list)) + s_ip_identification = ConcatSignal(*reversed(s_ip_identification_list)) + s_ip_flags = ConcatSignal(*reversed(s_ip_flags_list)) + s_ip_fragment_offset = ConcatSignal(*reversed(s_ip_fragment_offset_list)) + s_ip_ttl = ConcatSignal(*reversed(s_ip_ttl_list)) + s_ip_protocol = ConcatSignal(*reversed(s_ip_protocol_list)) + s_ip_header_checksum = ConcatSignal(*reversed(s_ip_header_checksum_list)) + s_ip_source_ip = ConcatSignal(*reversed(s_ip_source_ip_list)) + s_ip_dest_ip = ConcatSignal(*reversed(s_ip_dest_ip_list)) + s_udp_source_port = ConcatSignal(*reversed(s_udp_source_port_list)) + s_udp_dest_port = ConcatSignal(*reversed(s_udp_dest_port_list)) + s_udp_length = ConcatSignal(*reversed(s_udp_length_list)) + s_udp_checksum = ConcatSignal(*reversed(s_udp_checksum_list)) + s_udp_payload_axis_tdata = ConcatSignal(*reversed(s_udp_payload_axis_tdata_list)) + s_udp_payload_axis_tkeep = ConcatSignal(*reversed(s_udp_payload_axis_tkeep_list)) + s_udp_payload_axis_tvalid = ConcatSignal(*reversed(s_udp_payload_axis_tvalid_list)) + s_udp_payload_axis_tlast = ConcatSignal(*reversed(s_udp_payload_axis_tlast_list)) + s_udp_payload_axis_tid = ConcatSignal(*reversed(s_udp_payload_axis_tid_list)) + s_udp_payload_axis_tdest = ConcatSignal(*reversed(s_udp_payload_axis_tdest_list)) + s_udp_payload_axis_tuser = ConcatSignal(*reversed(s_udp_payload_axis_tuser_list)) + + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_udp_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_udp_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_udp_hdr_ready_list = [s_udp_hdr_ready(i) for i in range(S_COUNT)] + s_udp_payload_axis_tready_list = [s_udp_payload_axis_tready(i) for i in range(S_COUNT)] + + m_udp_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_udp_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_udp_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_udp_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = udp_ep.UDPFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready_list[k], + udp_hdr_valid=s_udp_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + ip_version=s_ip_version_list[k], + ip_ihl=s_ip_ihl_list[k], + ip_dscp=s_ip_dscp_list[k], + ip_ecn=s_ip_ecn_list[k], + ip_length=s_ip_length_list[k], + ip_identification=s_ip_identification_list[k], + ip_flags=s_ip_flags_list[k], + ip_fragment_offset=s_ip_fragment_offset_list[k], + ip_ttl=s_ip_ttl_list[k], + ip_protocol=s_ip_protocol_list[k], + ip_header_checksum=s_ip_header_checksum_list[k], + ip_source_ip=s_ip_source_ip_list[k], + ip_dest_ip=s_ip_dest_ip_list[k], + udp_source_port=s_udp_source_port_list[k], + udp_dest_port=s_udp_dest_port_list[k], + udp_length=s_udp_length_list[k], + udp_checksum=s_udp_checksum_list[k], + udp_payload_tdata=s_udp_payload_axis_tdata_list[k], + udp_payload_tkeep=s_udp_payload_axis_tkeep_list[k], + udp_payload_tvalid=s_udp_payload_axis_tvalid_list[k], + udp_payload_tready=s_udp_payload_axis_tready_list[k], + udp_payload_tlast=s_udp_payload_axis_tlast_list[k], + udp_payload_tuser=s_udp_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = udp_ep.UDPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tkeep=m_udp_payload_axis_tkeep, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep=s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tid=s_udp_payload_axis_tid, + s_udp_payload_axis_tdest=s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep=m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tid=m_udp_payload_axis_tid, + m_udp_payload_axis_tdest=m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: port 0") + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: port 1") + current_test.next = 2 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_udp_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_udp_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: back-to-back packets, different ports, arbitration test") + current_test.next = 7 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A0152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A0252535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + source_list[2].send(test_frame2) + yield clk.posedge + + yield delay(120) + yield clk.posedge + source_list[1].send(test_frame1) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_arb_mux_64_4.v b/fpga/lib/eth/tb/test_udp_arb_mux_64_4.v new file mode 100644 index 000000000..db33c2352 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_arb_mux_64_4.v @@ -0,0 +1,274 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_arb_mux + */ +module test_udp_arb_mux_64_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter ARB_TYPE = "PRIORITY"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_udp_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*4-1:0] s_ip_version = 0; +reg [S_COUNT*4-1:0] s_ip_ihl = 0; +reg [S_COUNT*6-1:0] s_ip_dscp = 0; +reg [S_COUNT*2-1:0] s_ip_ecn = 0; +reg [S_COUNT*16-1:0] s_ip_length = 0; +reg [S_COUNT*16-1:0] s_ip_identification = 0; +reg [S_COUNT*3-1:0] s_ip_flags = 0; +reg [S_COUNT*13-1:0] s_ip_fragment_offset = 0; +reg [S_COUNT*8-1:0] s_ip_ttl = 0; +reg [S_COUNT*8-1:0] s_ip_protocol = 0; +reg [S_COUNT*16-1:0] s_ip_header_checksum = 0; +reg [S_COUNT*32-1:0] s_ip_source_ip = 0; +reg [S_COUNT*32-1:0] s_ip_dest_ip = 0; +reg [S_COUNT*16-1:0] s_udp_source_port = 0; +reg [S_COUNT*16-1:0] s_udp_dest_port = 0; +reg [S_COUNT*16-1:0] s_udp_length = 0; +reg [S_COUNT*16-1:0] s_udp_checksum = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_udp_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_udp_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_udp_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_udp_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_udp_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_udp_payload_axis_tuser = 0; + +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_udp_hdr_ready; +wire [S_COUNT-1:0] s_udp_payload_axis_tready; + +wire m_udp_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [DATA_WIDTH-1:0] m_udp_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_udp_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_udp_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_udp_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_udp_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tid, + s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser, + m_udp_hdr_ready, + m_udp_payload_axis_tready + ); + $to_myhdl( + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_udp_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tid, + m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser + ); + + // dump file + $dumpfile("test_udp_arb_mux_64_4.lxt"); + $dumpvars(0, test_udp_arb_mux_64_4); +end + +udp_arb_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tid(s_udp_payload_axis_tid), + .s_udp_payload_axis_tdest(s_udp_payload_axis_tdest), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // Ethernet frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tid(m_udp_payload_axis_tid), + .m_udp_payload_axis_tdest(m_udp_payload_axis_tdest), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_checksum_gen.py b/fpga/lib/eth/tb/test_udp_checksum_gen.py new file mode 100755 index 000000000..25eb3ec14 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_checksum_gen.py @@ -0,0 +1,497 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import ip_ep +import udp_ep + +module = 'udp_checksum_gen' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/axis/rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + PAYLOAD_FIFO_ADDR_WIDTH = 11 + HEADER_FIFO_ADDR_WIDTH = 3 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_udp_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_udp_source_port = Signal(intbv(0)[16:]) + s_udp_dest_port = Signal(intbv(0)[16:]) + s_udp_payload_axis_tdata = Signal(intbv(0)[8:]) + s_udp_payload_axis_tvalid = Signal(bool(0)) + s_udp_payload_axis_tlast = Signal(bool(0)) + s_udp_payload_axis_tuser = Signal(bool(0)) + + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_udp_hdr_ready = Signal(bool(0)) + s_udp_payload_axis_tready = Signal(bool(0)) + + m_udp_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0x11)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[8:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tuser = Signal(bool(0)) + + busy = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = udp_ep.UDPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready, + udp_hdr_valid=s_udp_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + udp_source_port=s_udp_source_port, + udp_dest_port=s_udp_dest_port, + udp_payload_tdata=s_udp_payload_axis_tdata, + udp_payload_tvalid=s_udp_payload_axis_tvalid, + udp_payload_tready=s_udp_payload_axis_tready, + udp_payload_tlast=s_udp_payload_axis_tlast, + udp_payload_tuser=s_udp_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = udp_ep.UDPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + busy=busy + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + i = 4 + while i > 0: + i = max(0, i-1) + if s_udp_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_udp_hdr_valid or not source.empty(): + i = 4 + yield clk.posedge + + def wait_pause_source(): + i = 2 + while i > 0: + i = max(0, i-1) + if s_udp_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_udp_hdr_valid or not source.empty(): + i = 2 + source_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + + def wait_pause_sink(): + i = 2 + while i > 0: + i = max(0, i-1) + if s_udp_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_udp_hdr_valid or not source.empty(): + i = 2 + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(payload_len)) + test_frame.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_udp_checksum_gen.v b/fpga/lib/eth/tb/test_udp_checksum_gen.v new file mode 100644 index 000000000..82410420c --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_checksum_gen.v @@ -0,0 +1,223 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for udp_checksum_gen + */ +module test_udp_checksum_gen; + +// Parameters +parameter PAYLOAD_FIFO_ADDR_WIDTH = 11; +parameter HEADER_FIFO_ADDR_WIDTH = 3; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_udp_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [15:0] s_udp_source_port = 0; +reg [15:0] s_udp_dest_port = 0; +reg [7:0] s_udp_payload_axis_tdata = 0; +reg s_udp_payload_axis_tvalid = 0; +reg s_udp_payload_axis_tlast = 0; +reg s_udp_payload_axis_tuser = 0; +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; + +// Outputs +wire s_udp_hdr_ready; +wire s_udp_payload_axis_tready; +wire m_udp_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [7:0] m_udp_payload_axis_tdata; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire m_udp_payload_axis_tuser; +wire busy; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_udp_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser, + m_udp_hdr_ready, + m_udp_payload_axis_tready + ); + $to_myhdl( + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_udp_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser, + busy + ); + + // dump file + $dumpfile("test_udp_checksum_gen.lxt"); + $dumpvars(0, test_udp_checksum_gen); +end + +udp_checksum_gen #( + .PAYLOAD_FIFO_ADDR_WIDTH(PAYLOAD_FIFO_ADDR_WIDTH), + .HEADER_FIFO_ADDR_WIDTH(HEADER_FIFO_ADDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + .busy(busy) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_checksum_gen_64.py b/fpga/lib/eth/tb/test_udp_checksum_gen_64.py new file mode 100755 index 000000000..1202d29f6 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_checksum_gen_64.py @@ -0,0 +1,503 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2016-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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import ip_ep +import udp_ep + +module = 'udp_checksum_gen_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../lib/axis/rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + PAYLOAD_FIFO_ADDR_WIDTH = 8 + HEADER_FIFO_ADDR_WIDTH = 3 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_udp_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_udp_source_port = Signal(intbv(0)[16:]) + s_udp_dest_port = Signal(intbv(0)[16:]) + s_udp_payload_axis_tdata = Signal(intbv(0)[64:]) + s_udp_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_udp_payload_axis_tvalid = Signal(bool(0)) + s_udp_payload_axis_tlast = Signal(bool(0)) + s_udp_payload_axis_tuser = Signal(bool(0)) + + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_udp_hdr_ready = Signal(bool(0)) + s_udp_payload_axis_tready = Signal(bool(0)) + + m_udp_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0x11)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[64:]) + m_udp_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tuser = Signal(bool(0)) + + busy = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = udp_ep.UDPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready, + udp_hdr_valid=s_udp_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + udp_source_port=s_udp_source_port, + udp_dest_port=s_udp_dest_port, + udp_payload_tdata=s_udp_payload_axis_tdata, + udp_payload_tkeep=s_udp_payload_axis_tkeep, + udp_payload_tvalid=s_udp_payload_axis_tvalid, + udp_payload_tready=s_udp_payload_axis_tready, + udp_payload_tlast=s_udp_payload_axis_tlast, + udp_payload_tuser=s_udp_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = udp_ep.UDPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tkeep=m_udp_payload_axis_tkeep, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep=s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep=m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + busy=busy + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + i = 4 + while i > 0: + i = max(0, i-1) + if s_udp_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_udp_hdr_valid or not source.empty(): + i = 4 + yield clk.posedge + + def wait_pause_source(): + i = 2 + while i > 0: + i = max(0, i-1) + if s_udp_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_udp_hdr_valid or not source.empty(): + i = 2 + source_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + + def wait_pause_sink(): + i = 2 + while i > 0: + i = max(0, i-1) + if s_udp_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_udp_hdr_valid or not source.empty(): + i = 2 + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(payload_len)) + test_frame.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_udp_checksum_gen_64.v b/fpga/lib/eth/tb/test_udp_checksum_gen_64.v new file mode 100644 index 000000000..79afa8512 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_checksum_gen_64.v @@ -0,0 +1,229 @@ +/* + +Copyright (c) 2016-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 1ns / 1ps + +/* + * Testbench for udp_checksum_gen_64 + */ +module test_udp_checksum_gen_64; + +// Parameters +parameter PAYLOAD_FIFO_ADDR_WIDTH = 8; +parameter HEADER_FIFO_ADDR_WIDTH = 3; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_udp_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [15:0] s_udp_source_port = 0; +reg [15:0] s_udp_dest_port = 0; +reg [63:0] s_udp_payload_axis_tdata = 0; +reg [7:0] s_udp_payload_axis_tkeep = 0; +reg s_udp_payload_axis_tvalid = 0; +reg s_udp_payload_axis_tlast = 0; +reg s_udp_payload_axis_tuser = 0; +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; + +// Outputs +wire s_udp_hdr_ready; +wire s_udp_payload_axis_tready; +wire m_udp_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [63:0] m_udp_payload_axis_tdata; +wire [7:0] m_udp_payload_axis_tkeep; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire m_udp_payload_axis_tuser; +wire busy; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_udp_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser, + m_udp_hdr_ready, + m_udp_payload_axis_tready + ); + $to_myhdl( + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_udp_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser, + busy + ); + + // dump file + $dumpfile("test_udp_checksum_gen_64.lxt"); + $dumpvars(0, test_udp_checksum_gen_64); +end + +udp_checksum_gen_64 #( + .PAYLOAD_FIFO_ADDR_WIDTH(PAYLOAD_FIFO_ADDR_WIDTH), + .HEADER_FIFO_ADDR_WIDTH(HEADER_FIFO_ADDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + .busy(busy) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_complete.py b/fpga/lib/eth/tb/test_udp_complete.py new file mode 100755 index 000000000..7ebd4ba57 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_complete.py @@ -0,0 +1,834 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import ip_ep +import udp_ep + +module = 'udp_complete' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/udp.v") +srcs.append("../rtl/udp_checksum_gen.v") +srcs.append("../rtl/udp_ip_rx.v") +srcs.append("../rtl/udp_ip_tx.v") +srcs.append("../rtl/ip_complete.v") +srcs.append("../rtl/ip.v") +srcs.append("../rtl/ip_eth_rx.v") +srcs.append("../rtl/ip_eth_tx.v") +srcs.append("../rtl/ip_arb_mux.v") +srcs.append("../rtl/arp.v") +srcs.append("../rtl/arp_cache.v") +srcs.append("../rtl/arp_eth_rx.v") +srcs.append("../rtl/arp_eth_tx.v") +srcs.append("../rtl/eth_arb_mux.v") +srcs.append("../rtl/lfsr.v") +srcs.append("../lib/axis/rtl/arbiter.v") +srcs.append("../lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/axis/rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + s_ip_hdr_valid = Signal(bool(0)) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + s_udp_hdr_valid = Signal(bool(0)) + s_udp_ip_dscp = Signal(intbv(0)[6:]) + s_udp_ip_ecn = Signal(intbv(0)[2:]) + s_udp_ip_ttl = Signal(intbv(0)[8:]) + s_udp_ip_source_ip = Signal(intbv(0)[32:]) + s_udp_ip_dest_ip = Signal(intbv(0)[32:]) + s_udp_source_port = Signal(intbv(0)[16:]) + s_udp_dest_port = Signal(intbv(0)[16:]) + s_udp_length = Signal(intbv(0)[16:]) + s_udp_checksum = Signal(intbv(0)[16:]) + s_udp_payload_axis_tdata = Signal(intbv(0)[8:]) + s_udp_payload_axis_tvalid = Signal(bool(0)) + s_udp_payload_axis_tlast = Signal(bool(0)) + s_udp_payload_axis_tuser = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + s_udp_hdr_ready = Signal(bool(0)) + s_udp_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_ip_eth_dest_mac = Signal(intbv(0)[48:]) + m_ip_eth_src_mac = Signal(intbv(0)[48:]) + m_ip_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + m_udp_hdr_valid = Signal(bool(0)) + m_udp_eth_dest_mac = Signal(intbv(0)[48:]) + m_udp_eth_src_mac = Signal(intbv(0)[48:]) + m_udp_eth_type = Signal(intbv(0)[16:]) + m_udp_ip_version = Signal(intbv(0)[4:]) + m_udp_ip_ihl = Signal(intbv(0)[4:]) + m_udp_ip_dscp = Signal(intbv(0)[6:]) + m_udp_ip_ecn = Signal(intbv(0)[2:]) + m_udp_ip_length = Signal(intbv(0)[16:]) + m_udp_ip_identification = Signal(intbv(0)[16:]) + m_udp_ip_flags = Signal(intbv(0)[3:]) + m_udp_ip_fragment_offset = Signal(intbv(0)[13:]) + m_udp_ip_ttl = Signal(intbv(0)[8:]) + m_udp_ip_protocol = Signal(intbv(0)[8:]) + m_udp_ip_header_checksum = Signal(intbv(0)[16:]) + m_udp_ip_source_ip = Signal(intbv(0)[32:]) + m_udp_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[8:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tuser = Signal(bool(0)) + ip_rx_busy = Signal(bool(0)) + ip_tx_busy = Signal(bool(0)) + udp_rx_busy = Signal(bool(0)) + udp_tx_busy = Signal(bool(0)) + ip_rx_error_header_early_termination = Signal(bool(0)) + ip_rx_error_payload_early_termination = Signal(bool(0)) + ip_rx_error_invalid_header = Signal(bool(0)) + ip_rx_error_invalid_checksum = Signal(bool(0)) + ip_tx_error_payload_early_termination = Signal(bool(0)) + ip_tx_error_arp_failed = Signal(bool(0)) + udp_rx_error_header_early_termination = Signal(bool(0)) + udp_rx_error_payload_early_termination = Signal(bool(0)) + udp_tx_error_payload_early_termination = Signal(bool(0)) + local_mac = Signal(intbv(0)[48:]) + local_ip = Signal(intbv(0)[32:]) + gateway_ip = Signal(intbv(0)[32:]) + subnet_mask = Signal(intbv(0)[32:]) + clear_arp_cache = Signal(bool(0)) + + # sources and sinks + eth_source_pause = Signal(bool(0)) + eth_sink_pause = Signal(bool(0)) + ip_source_pause = Signal(bool(0)) + ip_sink_pause = Signal(bool(0)) + udp_source_pause = Signal(bool(0)) + udp_sink_pause = Signal(bool(0)) + + eth_source = eth_ep.EthFrameSource() + + eth_source_logic = eth_source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=eth_source_pause, + name='eth_source' + ) + + eth_sink = eth_ep.EthFrameSink() + + eth_sink_logic = eth_sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=eth_sink_pause, + name='eth_sink' + ) + + ip_source = ip_ep.IPFrameSource() + + ip_source_logic = ip_source.create_logic( + clk, + rst, + ip_hdr_valid=s_ip_hdr_valid, + ip_hdr_ready=s_ip_hdr_ready, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=ip_source_pause, + name='ip_source' + ) + + ip_sink = ip_ep.IPFrameSink() + + ip_sink_logic = ip_sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_ip_eth_dest_mac, + eth_src_mac=m_ip_eth_src_mac, + eth_type=m_ip_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=ip_sink_pause, + name='ip_sink' + ) + + udp_source = udp_ep.UDPFrameSource() + + udp_source_logic = udp_source.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready, + udp_hdr_valid=s_udp_hdr_valid, + ip_dscp=s_udp_ip_dscp, + ip_ecn=s_udp_ip_ecn, + ip_ttl=s_udp_ip_ttl, + ip_source_ip=s_udp_ip_source_ip, + ip_dest_ip=s_udp_ip_dest_ip, + udp_source_port=s_udp_source_port, + udp_dest_port=s_udp_dest_port, + udp_length=s_udp_length, + udp_checksum=s_udp_checksum, + udp_payload_tdata=s_udp_payload_axis_tdata, + udp_payload_tvalid=s_udp_payload_axis_tvalid, + udp_payload_tready=s_udp_payload_axis_tready, + udp_payload_tlast=s_udp_payload_axis_tlast, + udp_payload_tuser=s_udp_payload_axis_tuser, + pause=udp_source_pause, + name='udp_source' + ) + + udp_sink = udp_ep.UDPFrameSink() + + udp_sink_logic = udp_sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_udp_eth_dest_mac, + eth_src_mac=m_udp_eth_src_mac, + eth_type=m_udp_eth_type, + ip_version=m_udp_ip_version, + ip_ihl=m_udp_ip_ihl, + ip_dscp=m_udp_ip_dscp, + ip_ecn=m_udp_ip_ecn, + ip_length=m_udp_ip_length, + ip_identification=m_udp_ip_identification, + ip_flags=m_udp_ip_flags, + ip_fragment_offset=m_udp_ip_fragment_offset, + ip_ttl=m_udp_ip_ttl, + ip_protocol=m_udp_ip_protocol, + ip_header_checksum=m_udp_ip_header_checksum, + ip_source_ip=m_udp_ip_source_ip, + ip_dest_ip=m_udp_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=udp_sink_pause, + name='udp_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_ip_eth_dest_mac=m_ip_eth_dest_mac, + m_ip_eth_src_mac=m_ip_eth_src_mac, + m_ip_eth_type=m_ip_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_udp_ip_dscp=s_udp_ip_dscp, + s_udp_ip_ecn=s_udp_ip_ecn, + s_udp_ip_ttl=s_udp_ip_ttl, + s_udp_ip_source_ip=s_udp_ip_source_ip, + s_udp_ip_dest_ip=s_udp_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_udp_eth_dest_mac=m_udp_eth_dest_mac, + m_udp_eth_src_mac=m_udp_eth_src_mac, + m_udp_eth_type=m_udp_eth_type, + m_udp_ip_version=m_udp_ip_version, + m_udp_ip_ihl=m_udp_ip_ihl, + m_udp_ip_dscp=m_udp_ip_dscp, + m_udp_ip_ecn=m_udp_ip_ecn, + m_udp_ip_length=m_udp_ip_length, + m_udp_ip_identification=m_udp_ip_identification, + m_udp_ip_flags=m_udp_ip_flags, + m_udp_ip_fragment_offset=m_udp_ip_fragment_offset, + m_udp_ip_ttl=m_udp_ip_ttl, + m_udp_ip_protocol=m_udp_ip_protocol, + m_udp_ip_header_checksum=m_udp_ip_header_checksum, + m_udp_ip_source_ip=m_udp_ip_source_ip, + m_udp_ip_dest_ip=m_udp_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + ip_rx_busy=ip_rx_busy, + ip_tx_busy=ip_tx_busy, + udp_rx_busy=udp_rx_busy, + udp_tx_busy=udp_tx_busy, + ip_rx_error_header_early_termination=ip_rx_error_header_early_termination, + ip_rx_error_payload_early_termination=ip_rx_error_payload_early_termination, + ip_rx_error_invalid_header=ip_rx_error_invalid_header, + ip_rx_error_invalid_checksum=ip_rx_error_invalid_checksum, + ip_tx_error_payload_early_termination=ip_tx_error_payload_early_termination, + ip_tx_error_arp_failed=ip_tx_error_arp_failed, + udp_rx_error_header_early_termination=udp_rx_error_header_early_termination, + udp_rx_error_payload_early_termination=udp_rx_error_payload_early_termination, + udp_tx_error_payload_early_termination=udp_tx_error_payload_early_termination, + + local_mac=local_mac, + local_ip=local_ip, + gateway_ip=gateway_ip, + subnet_mask=subnet_mask, + clear_arp_cache=clear_arp_cache + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + ip_rx_error_header_early_termination_asserted = Signal(bool(0)) + ip_rx_error_payload_early_termination_asserted = Signal(bool(0)) + ip_rx_error_invalid_header_asserted = Signal(bool(0)) + ip_rx_error_invalid_checksum_asserted = Signal(bool(0)) + ip_tx_error_payload_early_termination_asserted = Signal(bool(0)) + ip_tx_error_arp_failed_asserted = Signal(bool(0)) + udp_rx_error_header_early_termination_asserted = Signal(bool(0)) + udp_rx_error_payload_early_termination_asserted = Signal(bool(0)) + udp_tx_error_payload_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (ip_rx_error_header_early_termination): + ip_rx_error_header_early_termination_asserted.next = 1 + if (ip_rx_error_payload_early_termination): + ip_rx_error_payload_early_termination_asserted.next = 1 + if (ip_rx_error_invalid_header): + ip_rx_error_invalid_header_asserted.next = 1 + if (ip_rx_error_invalid_checksum): + ip_rx_error_invalid_checksum_asserted.next = 1 + if (ip_tx_error_payload_early_termination): + ip_tx_error_payload_early_termination_asserted.next = 1 + if (ip_tx_error_arp_failed): + ip_tx_error_arp_failed_asserted.next = 1 + if (udp_rx_error_header_early_termination): + udp_rx_error_header_early_termination_asserted.next = 1 + if (udp_rx_error_payload_early_termination): + udp_rx_error_payload_early_termination_asserted.next = 1 + if (udp_tx_error_payload_early_termination): + udp_tx_error_payload_early_termination_asserted.next = 1 + + def wait_normal(): + i = 20 + while i > 0: + i = max(0, i-1) + if (s_eth_payload_axis_tvalid or s_ip_payload_axis_tvalid or s_udp_payload_axis_tvalid or + m_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or m_udp_payload_axis_tvalid or + s_eth_hdr_valid or s_ip_hdr_valid or s_udp_hdr_valid): + i = 20 + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # set MAC and IP address + local_mac.next = 0x5A5152535455 + local_ip.next = 0xc0a80164 + gateway_ip.next = 0xc0a80101 + subnet_mask.next = 0xffffff00 + + yield clk.posedge + print("test 1: test IP RX packet") + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x10 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + eth_frame = test_frame.build_eth() + + eth_source.send(eth_frame) + + yield ip_sink.wait() + rx_frame = ip_sink.recv() + + assert rx_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test IP TX packet") + current_test.next = 2 + + # send IP packet + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x10 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80166 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + # wait for ARP request packet + yield eth_sink.wait() + rx_frame = eth_sink.recv() + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x5A5152535455 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x5A5152535455 + assert check_frame.arp_spa == 0xc0a80164 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80166 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x5A5152535455 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80166 + arp_frame.arp_tha = 0x5A5152535455 + arp_frame.arp_tpa = 0xc0a80164 + eth_source.send(arp_frame.build_eth()) + + yield eth_sink.wait() + rx_frame = eth_sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + print(test_frame) + print(check_frame) + + assert check_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: test IP TX arp fail packet") + current_test.next = 2 + + ip_tx_error_arp_failed_asserted.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x10 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80167 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + yield clk.posedge + yield clk.posedge + + yield wait_normal() + + yield clk.posedge + yield clk.posedge + + assert ip_tx_error_arp_failed_asserted + + # check for 4 ARP requests + assert eth_sink.count() == 4 + + while not eth_sink.empty(): + rx_frame = eth_sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x5A5152535455 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x5A5152535455 + assert check_frame.arp_spa == 0xc0a80164 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80167 + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: test UDP RX packet") + current_test.next = 4 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1234 + test_frame.udp_dest_port = 5678 + test_frame.payload = bytearray(range(32)) + test_frame.build() + eth_frame = test_frame.build_eth() + + eth_source.send(eth_frame) + + yield udp_sink.wait() + rx_frame = udp_sink.recv() + + assert rx_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert udp_source.empty() + assert udp_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: test UDP TX packet") + current_test.next = 5 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80166 + test_frame.udp_source_port = 1234 + test_frame.udp_dest_port = 5678 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + udp_source.send(test_frame) + + yield eth_sink.wait() + rx_frame = eth_sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert udp_source.empty() + assert udp_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_complete.v b/fpga/lib/eth/tb/test_udp_complete.v new file mode 100644 index 000000000..8f89a3f6d --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_complete.v @@ -0,0 +1,447 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_complete + */ +module test_udp_complete; + +// Parameters +parameter ARP_CACHE_ADDR_WIDTH = 2; +parameter ARP_REQUEST_RETRY_COUNT = 4; +parameter ARP_REQUEST_RETRY_INTERVAL = 150; +parameter ARP_REQUEST_TIMEOUT = 400; +parameter UDP_CHECKSUM_GEN_ENABLE = 1; +parameter UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11; +parameter UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [7:0] s_eth_payload_axis_tdata = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg arp_response_valid = 0; +reg arp_response_error = 0; +reg [47:0] arp_response_mac = 0; +reg s_ip_hdr_valid = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [7:0] s_ip_payload_axis_tdata = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg s_udp_hdr_valid = 0; +reg [5:0] s_udp_ip_dscp = 0; +reg [1:0] s_udp_ip_ecn = 0; +reg [7:0] s_udp_ip_ttl = 0; +reg [31:0] s_udp_ip_source_ip = 0; +reg [31:0] s_udp_ip_dest_ip = 0; +reg [15:0] s_udp_source_port = 0; +reg [15:0] s_udp_dest_port = 0; +reg [15:0] s_udp_length = 0; +reg [15:0] s_udp_checksum = 0; +reg [7:0] s_udp_payload_axis_tdata = 0; +reg s_udp_payload_axis_tvalid = 0; +reg s_udp_payload_axis_tlast = 0; +reg s_udp_payload_axis_tuser = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; +reg [47:0] local_mac = 0; +reg [31:0] local_ip = 0; +reg [31:0] gateway_ip = 0; +reg [31:0] subnet_mask = 0; +reg clear_arp_cache = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire s_udp_hdr_ready; +wire s_udp_payload_axis_tready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [7:0] m_eth_payload_axis_tdata; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire arp_request_valid; +wire [31:0] arp_request_ip; +wire m_ip_hdr_valid; +wire [47:0] m_ip_eth_dest_mac; +wire [47:0] m_ip_eth_src_mac; +wire [15:0] m_ip_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [7:0] m_ip_payload_axis_tdata; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire m_udp_hdr_valid; +wire [47:0] m_udp_eth_dest_mac; +wire [47:0] m_udp_eth_src_mac; +wire [15:0] m_udp_eth_type; +wire [3:0] m_udp_ip_version; +wire [3:0] m_udp_ip_ihl; +wire [5:0] m_udp_ip_dscp; +wire [1:0] m_udp_ip_ecn; +wire [15:0] m_udp_ip_length; +wire [15:0] m_udp_ip_identification; +wire [2:0] m_udp_ip_flags; +wire [12:0] m_udp_ip_fragment_offset; +wire [7:0] m_udp_ip_ttl; +wire [7:0] m_udp_ip_protocol; +wire [15:0] m_udp_ip_header_checksum; +wire [31:0] m_udp_ip_source_ip; +wire [31:0] m_udp_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [7:0] m_udp_payload_axis_tdata; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire m_udp_payload_axis_tuser; +wire ip_rx_busy; +wire ip_tx_busy; +wire udp_rx_busy; +wire udp_tx_busy; +wire ip_rx_error_header_early_termination; +wire ip_rx_error_payload_early_termination; +wire ip_rx_error_invalid_header; +wire ip_rx_error_invalid_checksum; +wire ip_tx_error_payload_early_termination; +wire ip_tx_error_arp_failed; +wire udp_rx_error_header_early_termination; +wire udp_rx_error_payload_early_termination; +wire udp_tx_error_payload_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + s_ip_hdr_valid, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_ttl, + s_ip_protocol, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + s_udp_hdr_valid, + s_udp_ip_dscp, + s_udp_ip_ecn, + s_udp_ip_ttl, + s_udp_ip_source_ip, + s_udp_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + m_udp_hdr_ready, + m_udp_payload_axis_tready, + local_mac, + local_ip, + gateway_ip, + subnet_mask, + clear_arp_cache + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + s_ip_hdr_ready, + s_ip_payload_axis_tready, + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + m_ip_hdr_valid, + m_ip_eth_dest_mac, + m_ip_eth_src_mac, + m_ip_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + m_udp_hdr_valid, + m_udp_eth_dest_mac, + m_udp_eth_src_mac, + m_udp_eth_type, + m_udp_ip_version, + m_udp_ip_ihl, + m_udp_ip_dscp, + m_udp_ip_ecn, + m_udp_ip_length, + m_udp_ip_identification, + m_udp_ip_flags, + m_udp_ip_fragment_offset, + m_udp_ip_ttl, + m_udp_ip_protocol, + m_udp_ip_header_checksum, + m_udp_ip_source_ip, + m_udp_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser, + 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 + ); + + // dump file + $dumpfile("test_udp_complete.lxt"); + $dumpvars(0, test_udp_complete); +end + +udp_complete #( + .ARP_CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .ARP_REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .ARP_REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .ARP_REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT), + .UDP_CHECKSUM_GEN_ENABLE(UDP_CHECKSUM_GEN_ENABLE), + .UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH(UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH), + .UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH(UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_udp_ip_dscp(s_udp_ip_dscp), + .s_udp_ip_ecn(s_udp_ip_ecn), + .s_udp_ip_ttl(s_udp_ip_ttl), + .s_udp_ip_source_ip(s_udp_ip_source_ip), + .s_udp_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_udp_eth_dest_mac(m_udp_eth_dest_mac), + .m_udp_eth_src_mac(m_udp_eth_src_mac), + .m_udp_eth_type(m_udp_eth_type), + .m_udp_ip_version(m_udp_ip_version), + .m_udp_ip_ihl(m_udp_ip_ihl), + .m_udp_ip_dscp(m_udp_ip_dscp), + .m_udp_ip_ecn(m_udp_ip_ecn), + .m_udp_ip_length(m_udp_ip_length), + .m_udp_ip_identification(m_udp_ip_identification), + .m_udp_ip_flags(m_udp_ip_flags), + .m_udp_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_udp_ip_ttl(m_udp_ip_ttl), + .m_udp_ip_protocol(m_udp_ip_protocol), + .m_udp_ip_header_checksum(m_udp_ip_header_checksum), + .m_udp_ip_source_ip(m_udp_ip_source_ip), + .m_udp_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status signals + .ip_rx_busy(ip_rx_busy), + .ip_tx_busy(ip_tx_busy), + .udp_rx_busy(udp_rx_busy), + .udp_tx_busy(udp_tx_busy), + .ip_rx_error_header_early_termination(ip_rx_error_header_early_termination), + .ip_rx_error_payload_early_termination(ip_rx_error_payload_early_termination), + .ip_rx_error_invalid_header(ip_rx_error_invalid_header), + .ip_rx_error_invalid_checksum(ip_rx_error_invalid_checksum), + .ip_tx_error_payload_early_termination(ip_tx_error_payload_early_termination), + .ip_tx_error_arp_failed(ip_tx_error_arp_failed), + .udp_rx_error_header_early_termination(udp_rx_error_header_early_termination), + .udp_rx_error_payload_early_termination(udp_rx_error_payload_early_termination), + .udp_tx_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(clear_arp_cache) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_complete_64.py b/fpga/lib/eth/tb/test_udp_complete_64.py new file mode 100755 index 000000000..c285a4b92 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_complete_64.py @@ -0,0 +1,852 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import arp_ep +import ip_ep +import udp_ep + +module = 'udp_complete_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/udp_64.v") +srcs.append("../rtl/udp_checksum_gen_64.v") +srcs.append("../rtl/udp_ip_rx_64.v") +srcs.append("../rtl/udp_ip_tx_64.v") +srcs.append("../rtl/ip_complete_64.v") +srcs.append("../rtl/ip_64.v") +srcs.append("../rtl/ip_eth_rx_64.v") +srcs.append("../rtl/ip_eth_tx_64.v") +srcs.append("../rtl/ip_arb_mux.v") +srcs.append("../rtl/arp_64.v") +srcs.append("../rtl/arp_cache.v") +srcs.append("../rtl/arp_eth_rx_64.v") +srcs.append("../rtl/arp_eth_tx_64.v") +srcs.append("../rtl/eth_arb_mux.v") +srcs.append("../rtl/lfsr.v") +srcs.append("../lib/axis/rtl/arbiter.v") +srcs.append("../lib/axis/rtl/priority_encoder.v") +srcs.append("../lib/axis/rtl/axis_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_eth_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + s_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_eth_payload_axis_tvalid = Signal(bool(0)) + s_eth_payload_axis_tlast = Signal(bool(0)) + s_eth_payload_axis_tuser = Signal(bool(0)) + s_ip_hdr_valid = Signal(bool(0)) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + s_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + s_udp_hdr_valid = Signal(bool(0)) + s_udp_ip_dscp = Signal(intbv(0)[6:]) + s_udp_ip_ecn = Signal(intbv(0)[2:]) + s_udp_ip_ttl = Signal(intbv(0)[8:]) + s_udp_ip_source_ip = Signal(intbv(0)[32:]) + s_udp_ip_dest_ip = Signal(intbv(0)[32:]) + s_udp_source_port = Signal(intbv(0)[16:]) + s_udp_dest_port = Signal(intbv(0)[16:]) + s_udp_length = Signal(intbv(0)[16:]) + s_udp_checksum = Signal(intbv(0)[16:]) + s_udp_payload_axis_tdata = Signal(intbv(0)[64:]) + s_udp_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_udp_payload_axis_tvalid = Signal(bool(0)) + s_udp_payload_axis_tlast = Signal(bool(0)) + s_udp_payload_axis_tuser = Signal(bool(0)) + m_eth_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_ready = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_eth_hdr_ready = Signal(bool(0)) + s_eth_payload_axis_tready = Signal(bool(0)) + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + s_udp_hdr_ready = Signal(bool(0)) + s_udp_payload_axis_tready = Signal(bool(0)) + m_eth_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_eth_payload_axis_tdata = Signal(intbv(0)[64:]) + m_eth_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_eth_payload_axis_tvalid = Signal(bool(0)) + m_eth_payload_axis_tlast = Signal(bool(0)) + m_eth_payload_axis_tuser = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_ip_eth_dest_mac = Signal(intbv(0)[48:]) + m_ip_eth_src_mac = Signal(intbv(0)[48:]) + m_ip_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + m_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + m_udp_hdr_valid = Signal(bool(0)) + m_udp_eth_dest_mac = Signal(intbv(0)[48:]) + m_udp_eth_src_mac = Signal(intbv(0)[48:]) + m_udp_eth_type = Signal(intbv(0)[16:]) + m_udp_ip_version = Signal(intbv(0)[4:]) + m_udp_ip_ihl = Signal(intbv(0)[4:]) + m_udp_ip_dscp = Signal(intbv(0)[6:]) + m_udp_ip_ecn = Signal(intbv(0)[2:]) + m_udp_ip_length = Signal(intbv(0)[16:]) + m_udp_ip_identification = Signal(intbv(0)[16:]) + m_udp_ip_flags = Signal(intbv(0)[3:]) + m_udp_ip_fragment_offset = Signal(intbv(0)[13:]) + m_udp_ip_ttl = Signal(intbv(0)[8:]) + m_udp_ip_protocol = Signal(intbv(0)[8:]) + m_udp_ip_header_checksum = Signal(intbv(0)[16:]) + m_udp_ip_source_ip = Signal(intbv(0)[32:]) + m_udp_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[64:]) + m_udp_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tuser = Signal(bool(0)) + ip_rx_busy = Signal(bool(0)) + ip_tx_busy = Signal(bool(0)) + udp_rx_busy = Signal(bool(0)) + udp_tx_busy = Signal(bool(0)) + ip_rx_error_header_early_termination = Signal(bool(0)) + ip_rx_error_payload_early_termination = Signal(bool(0)) + ip_rx_error_invalid_header = Signal(bool(0)) + ip_rx_error_invalid_checksum = Signal(bool(0)) + ip_tx_error_payload_early_termination = Signal(bool(0)) + ip_tx_error_arp_failed = Signal(bool(0)) + udp_rx_error_header_early_termination = Signal(bool(0)) + udp_rx_error_payload_early_termination = Signal(bool(0)) + udp_tx_error_payload_early_termination = Signal(bool(0)) + local_mac = Signal(intbv(0)[48:]) + local_ip = Signal(intbv(0)[32:]) + gateway_ip = Signal(intbv(0)[32:]) + subnet_mask = Signal(intbv(0)[32:]) + clear_arp_cache = Signal(bool(0)) + + # sources and sinks + eth_source_pause = Signal(bool(0)) + eth_sink_pause = Signal(bool(0)) + ip_source_pause = Signal(bool(0)) + ip_sink_pause = Signal(bool(0)) + udp_source_pause = Signal(bool(0)) + udp_sink_pause = Signal(bool(0)) + + eth_source = eth_ep.EthFrameSource() + + eth_source_logic = eth_source.create_logic( + clk, + rst, + eth_hdr_ready=s_eth_hdr_ready, + eth_hdr_valid=s_eth_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + eth_payload_tdata=s_eth_payload_axis_tdata, + eth_payload_tkeep=s_eth_payload_axis_tkeep, + eth_payload_tvalid=s_eth_payload_axis_tvalid, + eth_payload_tready=s_eth_payload_axis_tready, + eth_payload_tlast=s_eth_payload_axis_tlast, + eth_payload_tuser=s_eth_payload_axis_tuser, + pause=eth_source_pause, + name='eth_source' + ) + + eth_sink = eth_ep.EthFrameSink() + + eth_sink_logic = eth_sink.create_logic( + clk, + rst, + eth_hdr_ready=m_eth_hdr_ready, + eth_hdr_valid=m_eth_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + eth_payload_tdata=m_eth_payload_axis_tdata, + eth_payload_tkeep=m_eth_payload_axis_tkeep, + eth_payload_tvalid=m_eth_payload_axis_tvalid, + eth_payload_tready=m_eth_payload_axis_tready, + eth_payload_tlast=m_eth_payload_axis_tlast, + eth_payload_tuser=m_eth_payload_axis_tuser, + pause=eth_sink_pause, + name='eth_sink' + ) + + ip_source = ip_ep.IPFrameSource() + + ip_source_logic = ip_source.create_logic( + clk, + rst, + ip_hdr_valid=s_ip_hdr_valid, + ip_hdr_ready=s_ip_hdr_ready, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tkeep=s_ip_payload_axis_tkeep, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=ip_source_pause, + name='ip_source' + ) + + ip_sink = ip_ep.IPFrameSink() + + ip_sink_logic = ip_sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_ip_eth_dest_mac, + eth_src_mac=m_ip_eth_src_mac, + eth_type=m_ip_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tkeep=m_ip_payload_axis_tkeep, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=ip_sink_pause, + name='ip_sink' + ) + + udp_source = udp_ep.UDPFrameSource() + + udp_source_logic = udp_source.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready, + udp_hdr_valid=s_udp_hdr_valid, + ip_dscp=s_udp_ip_dscp, + ip_ecn=s_udp_ip_ecn, + ip_ttl=s_udp_ip_ttl, + ip_source_ip=s_udp_ip_source_ip, + ip_dest_ip=s_udp_ip_dest_ip, + udp_source_port=s_udp_source_port, + udp_dest_port=s_udp_dest_port, + udp_length=s_udp_length, + udp_checksum=s_udp_checksum, + udp_payload_tdata=s_udp_payload_axis_tdata, + udp_payload_tkeep=s_udp_payload_axis_tkeep, + udp_payload_tvalid=s_udp_payload_axis_tvalid, + udp_payload_tready=s_udp_payload_axis_tready, + udp_payload_tlast=s_udp_payload_axis_tlast, + udp_payload_tuser=s_udp_payload_axis_tuser, + pause=udp_source_pause, + name='udp_source' + ) + + udp_sink = udp_ep.UDPFrameSink() + + udp_sink_logic = udp_sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_udp_eth_dest_mac, + eth_src_mac=m_udp_eth_src_mac, + eth_type=m_udp_eth_type, + ip_version=m_udp_ip_version, + ip_ihl=m_udp_ip_ihl, + ip_dscp=m_udp_ip_dscp, + ip_ecn=m_udp_ip_ecn, + ip_length=m_udp_ip_length, + ip_identification=m_udp_ip_identification, + ip_flags=m_udp_ip_flags, + ip_fragment_offset=m_udp_ip_fragment_offset, + ip_ttl=m_udp_ip_ttl, + ip_protocol=m_udp_ip_protocol, + ip_header_checksum=m_udp_ip_header_checksum, + ip_source_ip=m_udp_ip_source_ip, + ip_dest_ip=m_udp_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tkeep=m_udp_payload_axis_tkeep, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=udp_sink_pause, + name='udp_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_eth_hdr_valid=s_eth_hdr_valid, + s_eth_hdr_ready=s_eth_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_eth_payload_axis_tdata=s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep=s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid=s_eth_payload_axis_tvalid, + s_eth_payload_axis_tready=s_eth_payload_axis_tready, + s_eth_payload_axis_tlast=s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser=s_eth_payload_axis_tuser, + + m_eth_hdr_valid=m_eth_hdr_valid, + m_eth_hdr_ready=m_eth_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_eth_payload_axis_tdata=m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep=m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid=m_eth_payload_axis_tvalid, + m_eth_payload_axis_tready=m_eth_payload_axis_tready, + m_eth_payload_axis_tlast=m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser=m_eth_payload_axis_tuser, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_ip_eth_dest_mac=m_ip_eth_dest_mac, + m_ip_eth_src_mac=m_ip_eth_src_mac, + m_ip_eth_type=m_ip_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_udp_ip_dscp=s_udp_ip_dscp, + s_udp_ip_ecn=s_udp_ip_ecn, + s_udp_ip_ttl=s_udp_ip_ttl, + s_udp_ip_source_ip=s_udp_ip_source_ip, + s_udp_ip_dest_ip=s_udp_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep=s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_udp_eth_dest_mac=m_udp_eth_dest_mac, + m_udp_eth_src_mac=m_udp_eth_src_mac, + m_udp_eth_type=m_udp_eth_type, + m_udp_ip_version=m_udp_ip_version, + m_udp_ip_ihl=m_udp_ip_ihl, + m_udp_ip_dscp=m_udp_ip_dscp, + m_udp_ip_ecn=m_udp_ip_ecn, + m_udp_ip_length=m_udp_ip_length, + m_udp_ip_identification=m_udp_ip_identification, + m_udp_ip_flags=m_udp_ip_flags, + m_udp_ip_fragment_offset=m_udp_ip_fragment_offset, + m_udp_ip_ttl=m_udp_ip_ttl, + m_udp_ip_protocol=m_udp_ip_protocol, + m_udp_ip_header_checksum=m_udp_ip_header_checksum, + m_udp_ip_source_ip=m_udp_ip_source_ip, + m_udp_ip_dest_ip=m_udp_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep=m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + ip_rx_busy=ip_rx_busy, + ip_tx_busy=ip_tx_busy, + udp_rx_busy=udp_rx_busy, + udp_tx_busy=udp_tx_busy, + ip_rx_error_header_early_termination=ip_rx_error_header_early_termination, + ip_rx_error_payload_early_termination=ip_rx_error_payload_early_termination, + ip_rx_error_invalid_header=ip_rx_error_invalid_header, + ip_rx_error_invalid_checksum=ip_rx_error_invalid_checksum, + ip_tx_error_payload_early_termination=ip_tx_error_payload_early_termination, + ip_tx_error_arp_failed=ip_tx_error_arp_failed, + udp_rx_error_header_early_termination=udp_rx_error_header_early_termination, + udp_rx_error_payload_early_termination=udp_rx_error_payload_early_termination, + udp_tx_error_payload_early_termination=udp_tx_error_payload_early_termination, + + local_mac=local_mac, + local_ip=local_ip, + gateway_ip=gateway_ip, + subnet_mask=subnet_mask, + clear_arp_cache=clear_arp_cache + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + ip_rx_error_header_early_termination_asserted = Signal(bool(0)) + ip_rx_error_payload_early_termination_asserted = Signal(bool(0)) + ip_rx_error_invalid_header_asserted = Signal(bool(0)) + ip_rx_error_invalid_checksum_asserted = Signal(bool(0)) + ip_tx_error_payload_early_termination_asserted = Signal(bool(0)) + ip_tx_error_arp_failed_asserted = Signal(bool(0)) + udp_rx_error_header_early_termination_asserted = Signal(bool(0)) + udp_rx_error_payload_early_termination_asserted = Signal(bool(0)) + udp_tx_error_payload_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (ip_rx_error_header_early_termination): + ip_rx_error_header_early_termination_asserted.next = 1 + if (ip_rx_error_payload_early_termination): + ip_rx_error_payload_early_termination_asserted.next = 1 + if (ip_rx_error_invalid_header): + ip_rx_error_invalid_header_asserted.next = 1 + if (ip_rx_error_invalid_checksum): + ip_rx_error_invalid_checksum_asserted.next = 1 + if (ip_tx_error_payload_early_termination): + ip_tx_error_payload_early_termination_asserted.next = 1 + if (ip_tx_error_arp_failed): + ip_tx_error_arp_failed_asserted.next = 1 + if (udp_rx_error_header_early_termination): + udp_rx_error_header_early_termination_asserted.next = 1 + if (udp_rx_error_payload_early_termination): + udp_rx_error_payload_early_termination_asserted.next = 1 + if (udp_tx_error_payload_early_termination): + udp_tx_error_payload_early_termination_asserted.next = 1 + + def wait_normal(): + i = 20 + while i > 0: + i = max(0, i-1) + if (s_eth_payload_axis_tvalid or s_ip_payload_axis_tvalid or s_udp_payload_axis_tvalid or + m_eth_payload_axis_tvalid or m_ip_payload_axis_tvalid or m_udp_payload_axis_tvalid or + s_eth_hdr_valid or s_ip_hdr_valid or s_udp_hdr_valid): + i = 20 + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # set MAC and IP address + local_mac.next = 0x5A5152535455 + local_ip.next = 0xc0a80164 + gateway_ip.next = 0xc0a80101 + subnet_mask.next = 0xffffff00 + + yield clk.posedge + print("test 1: test IP RX packet") + current_test.next = 1 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x10 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.payload = bytearray(range(32)) + test_frame.build() + eth_frame = test_frame.build_eth() + + eth_source.send(eth_frame) + + yield ip_sink.wait() + rx_frame = ip_sink.recv() + + assert rx_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test IP TX packet") + current_test.next = 2 + + # send IP packet + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x10 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80166 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + # wait for ARP request packet + yield eth_sink.wait() + rx_frame = eth_sink.recv() + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x5A5152535455 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x5A5152535455 + assert check_frame.arp_spa == 0xc0a80164 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80166 + + # generate response + arp_frame = arp_ep.ARPFrame() + arp_frame.eth_dest_mac = 0x5A5152535455 + arp_frame.eth_src_mac = 0xDAD1D2D3D4D5 + arp_frame.eth_type = 0x0806 + arp_frame.arp_htype = 0x0001 + arp_frame.arp_ptype = 0x0800 + arp_frame.arp_hlen = 6 + arp_frame.arp_plen = 4 + arp_frame.arp_oper = 2 + arp_frame.arp_sha = 0xDAD1D2D3D4D5 + arp_frame.arp_spa = 0xc0a80166 + arp_frame.arp_tha = 0x5A5152535455 + arp_frame.arp_tpa = 0xc0a80164 + eth_source.send(arp_frame.build_eth()) + + yield eth_sink.wait() + rx_frame = eth_sink.recv() + + check_frame = ip_ep.IPFrame() + check_frame.parse_eth(rx_frame) + + print(test_frame) + print(check_frame) + + assert check_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: test IP TX arp fail packet") + current_test.next = 2 + + ip_tx_error_arp_failed_asserted.next = 0 + + test_frame = ip_ep.IPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x10 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80167 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + ip_source.send(test_frame) + + yield clk.posedge + yield clk.posedge + + yield wait_normal() + + yield clk.posedge + yield clk.posedge + + assert ip_tx_error_arp_failed_asserted + + # check for 4 ARP requests + assert eth_sink.count() == 4 + + while not eth_sink.empty(): + rx_frame = eth_sink.recv() + + check_frame = arp_ep.ARPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame.eth_dest_mac == 0xFFFFFFFFFFFF + assert check_frame.eth_src_mac == 0x5A5152535455 + assert check_frame.eth_type == 0x0806 + assert check_frame.arp_htype == 0x0001 + assert check_frame.arp_ptype == 0x0800 + assert check_frame.arp_hlen == 6 + assert check_frame.arp_plen == 4 + assert check_frame.arp_oper == 1 + assert check_frame.arp_sha == 0x5A5152535455 + assert check_frame.arp_spa == 0xc0a80164 + assert check_frame.arp_tha == 0x000000000000 + assert check_frame.arp_tpa == 0xc0a80167 + + assert eth_source.empty() + assert eth_sink.empty() + assert ip_source.empty() + assert ip_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: test UDP RX packet") + current_test.next = 4 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0x5A5152535455 + test_frame.eth_src_mac = 0xDAD1D2D3D4D5 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1234 + test_frame.udp_dest_port = 5678 + test_frame.payload = bytearray(range(32)) + test_frame.build() + eth_frame = test_frame.build_eth() + + eth_source.send(eth_frame) + + yield udp_sink.wait() + rx_frame = udp_sink.recv() + + assert rx_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert udp_source.empty() + assert udp_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: test UDP TX packet") + current_test.next = 5 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80166 + test_frame.udp_source_port = 1234 + test_frame.udp_dest_port = 5678 + test_frame.payload = bytearray(range(32)) + test_frame.build() + + udp_source.send(test_frame) + + yield eth_sink.wait() + rx_frame = eth_sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_eth(rx_frame) + + assert check_frame == test_frame + + assert eth_source.empty() + assert eth_sink.empty() + assert udp_source.empty() + assert udp_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_complete_64.v b/fpga/lib/eth/tb/test_udp_complete_64.v new file mode 100644 index 000000000..7822a6841 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_complete_64.v @@ -0,0 +1,465 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_complete_64 + */ +module test_udp_complete_64; + +// Parameters +parameter ARP_CACHE_ADDR_WIDTH = 2; +parameter ARP_REQUEST_RETRY_COUNT = 4; +parameter ARP_REQUEST_RETRY_INTERVAL = 150; +parameter ARP_REQUEST_TIMEOUT = 400; +parameter UDP_CHECKSUM_GEN_ENABLE = 1; +parameter UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11; +parameter UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_eth_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [63:0] s_eth_payload_axis_tdata = 0; +reg [7:0] s_eth_payload_axis_tkeep = 0; +reg s_eth_payload_axis_tvalid = 0; +reg s_eth_payload_axis_tlast = 0; +reg s_eth_payload_axis_tuser = 0; +reg arp_response_valid = 0; +reg arp_response_error = 0; +reg [47:0] arp_response_mac = 0; +reg s_ip_hdr_valid = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [63:0] s_ip_payload_axis_tdata = 0; +reg [7:0] s_ip_payload_axis_tkeep = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg s_udp_hdr_valid = 0; +reg [5:0] s_udp_ip_dscp = 0; +reg [1:0] s_udp_ip_ecn = 0; +reg [7:0] s_udp_ip_ttl = 0; +reg [31:0] s_udp_ip_source_ip = 0; +reg [31:0] s_udp_ip_dest_ip = 0; +reg [15:0] s_udp_source_port = 0; +reg [15:0] s_udp_dest_port = 0; +reg [15:0] s_udp_length = 0; +reg [15:0] s_udp_checksum = 0; +reg [63:0] s_udp_payload_axis_tdata = 0; +reg [7:0] s_udp_payload_axis_tkeep = 0; +reg s_udp_payload_axis_tvalid = 0; +reg s_udp_payload_axis_tlast = 0; +reg s_udp_payload_axis_tuser = 0; +reg m_eth_hdr_ready = 0; +reg m_eth_payload_axis_tready = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; +reg [47:0] local_mac = 0; +reg [31:0] local_ip = 0; +reg [31:0] gateway_ip = 0; +reg [31:0] subnet_mask = 0; +reg clear_arp_cache = 0; + +// Outputs +wire s_eth_hdr_ready; +wire s_eth_payload_axis_tready; +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire s_udp_hdr_ready; +wire s_udp_payload_axis_tready; +wire m_eth_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [63:0] m_eth_payload_axis_tdata; +wire [7:0] m_eth_payload_axis_tkeep; +wire m_eth_payload_axis_tvalid; +wire m_eth_payload_axis_tlast; +wire m_eth_payload_axis_tuser; +wire arp_request_valid; +wire [31:0] arp_request_ip; +wire m_ip_hdr_valid; +wire [47:0] m_ip_eth_dest_mac; +wire [47:0] m_ip_eth_src_mac; +wire [15:0] m_ip_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [63:0] m_ip_payload_axis_tdata; +wire [7:0] m_ip_payload_axis_tkeep; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire m_udp_hdr_valid; +wire [47:0] m_udp_eth_dest_mac; +wire [47:0] m_udp_eth_src_mac; +wire [15:0] m_udp_eth_type; +wire [3:0] m_udp_ip_version; +wire [3:0] m_udp_ip_ihl; +wire [5:0] m_udp_ip_dscp; +wire [1:0] m_udp_ip_ecn; +wire [15:0] m_udp_ip_length; +wire [15:0] m_udp_ip_identification; +wire [2:0] m_udp_ip_flags; +wire [12:0] m_udp_ip_fragment_offset; +wire [7:0] m_udp_ip_ttl; +wire [7:0] m_udp_ip_protocol; +wire [15:0] m_udp_ip_header_checksum; +wire [31:0] m_udp_ip_source_ip; +wire [31:0] m_udp_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [63:0] m_udp_payload_axis_tdata; +wire [7:0] m_udp_payload_axis_tkeep; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire m_udp_payload_axis_tuser; +wire ip_rx_busy; +wire ip_tx_busy; +wire udp_rx_busy; +wire udp_tx_busy; +wire ip_rx_error_header_early_termination; +wire ip_rx_error_payload_early_termination; +wire ip_rx_error_invalid_header; +wire ip_rx_error_invalid_checksum; +wire ip_tx_error_payload_early_termination; +wire ip_tx_error_arp_failed; +wire udp_rx_error_header_early_termination; +wire udp_rx_error_payload_early_termination; +wire udp_tx_error_payload_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_eth_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_eth_payload_axis_tdata, + s_eth_payload_axis_tkeep, + s_eth_payload_axis_tvalid, + s_eth_payload_axis_tlast, + s_eth_payload_axis_tuser, + s_ip_hdr_valid, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_ttl, + s_ip_protocol, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + s_udp_hdr_valid, + s_udp_ip_dscp, + s_udp_ip_ecn, + s_udp_ip_ttl, + s_udp_ip_source_ip, + s_udp_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser, + m_eth_hdr_ready, + m_eth_payload_axis_tready, + m_ip_hdr_ready, + m_ip_payload_axis_tready, + m_udp_hdr_ready, + m_udp_payload_axis_tready, + local_mac, + local_ip, + gateway_ip, + subnet_mask, + clear_arp_cache + ); + $to_myhdl( + s_eth_hdr_ready, + s_eth_payload_axis_tready, + s_ip_hdr_ready, + s_ip_payload_axis_tready, + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_eth_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_eth_payload_axis_tdata, + m_eth_payload_axis_tkeep, + m_eth_payload_axis_tvalid, + m_eth_payload_axis_tlast, + m_eth_payload_axis_tuser, + m_ip_hdr_valid, + m_ip_eth_dest_mac, + m_ip_eth_src_mac, + m_ip_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + m_udp_hdr_valid, + m_udp_eth_dest_mac, + m_udp_eth_src_mac, + m_udp_eth_type, + m_udp_ip_version, + m_udp_ip_ihl, + m_udp_ip_dscp, + m_udp_ip_ecn, + m_udp_ip_length, + m_udp_ip_identification, + m_udp_ip_flags, + m_udp_ip_fragment_offset, + m_udp_ip_ttl, + m_udp_ip_protocol, + m_udp_ip_header_checksum, + m_udp_ip_source_ip, + m_udp_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser, + 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 + ); + + // dump file + $dumpfile("test_udp_complete_64.lxt"); + $dumpvars(0, test_udp_complete_64); +end + +udp_complete_64 #( + .ARP_CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .ARP_REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .ARP_REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .ARP_REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT), + .UDP_CHECKSUM_GEN_ENABLE(UDP_CHECKSUM_GEN_ENABLE), + .UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH(UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH), + .UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH(UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_udp_ip_dscp(s_udp_ip_dscp), + .s_udp_ip_ecn(s_udp_ip_ecn), + .s_udp_ip_ttl(s_udp_ip_ttl), + .s_udp_ip_source_ip(s_udp_ip_source_ip), + .s_udp_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_udp_eth_dest_mac(m_udp_eth_dest_mac), + .m_udp_eth_src_mac(m_udp_eth_src_mac), + .m_udp_eth_type(m_udp_eth_type), + .m_udp_ip_version(m_udp_ip_version), + .m_udp_ip_ihl(m_udp_ip_ihl), + .m_udp_ip_dscp(m_udp_ip_dscp), + .m_udp_ip_ecn(m_udp_ip_ecn), + .m_udp_ip_length(m_udp_ip_length), + .m_udp_ip_identification(m_udp_ip_identification), + .m_udp_ip_flags(m_udp_ip_flags), + .m_udp_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_udp_ip_ttl(m_udp_ip_ttl), + .m_udp_ip_protocol(m_udp_ip_protocol), + .m_udp_ip_header_checksum(m_udp_ip_header_checksum), + .m_udp_ip_source_ip(m_udp_ip_source_ip), + .m_udp_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status signals + .ip_rx_busy(ip_rx_busy), + .ip_tx_busy(ip_tx_busy), + .udp_rx_busy(udp_rx_busy), + .udp_tx_busy(udp_tx_busy), + .ip_rx_error_header_early_termination(ip_rx_error_header_early_termination), + .ip_rx_error_payload_early_termination(ip_rx_error_payload_early_termination), + .ip_rx_error_invalid_header(ip_rx_error_invalid_header), + .ip_rx_error_invalid_checksum(ip_rx_error_invalid_checksum), + .ip_tx_error_payload_early_termination(ip_tx_error_payload_early_termination), + .ip_tx_error_arp_failed(ip_tx_error_arp_failed), + .udp_rx_error_header_early_termination(udp_rx_error_header_early_termination), + .udp_rx_error_payload_early_termination(udp_rx_error_payload_early_termination), + .udp_tx_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(clear_arp_cache) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_demux_4.py b/fpga/lib/eth/tb/test_udp_demux_4.py new file mode 100755 index 000000000..16352cb08 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_demux_4.py @@ -0,0 +1,819 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import udp_ep + +module = 'udp_demux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + M_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_udp_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_udp_source_port = Signal(intbv(0)[16:]) + s_udp_dest_port = Signal(intbv(0)[16:]) + s_udp_length = Signal(intbv(0)[16:]) + s_udp_checksum = Signal(intbv(0)[16:]) + s_udp_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_udp_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_udp_payload_axis_tvalid = Signal(bool(0)) + s_udp_payload_axis_tlast = Signal(bool(0)) + s_udp_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_udp_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_udp_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + m_udp_hdr_ready_list = [Signal(bool(0)) for i in range(M_COUNT)] + m_udp_payload_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_udp_hdr_ready = ConcatSignal(*reversed(m_udp_hdr_ready_list)) + m_udp_payload_axis_tready = ConcatSignal(*reversed(m_udp_payload_axis_tready_list)) + + enable = Signal(bool(0)) + drop = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_udp_hdr_ready = Signal(bool(0)) + s_udp_payload_axis_tready = Signal(bool(0)) + + m_udp_hdr_valid = Signal(intbv(0)[M_COUNT:]) + m_eth_dest_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_src_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_type = Signal(intbv(0)[M_COUNT*16:]) + m_ip_version = Signal(intbv(0)[M_COUNT*4:]) + m_ip_ihl = Signal(intbv(0)[M_COUNT*4:]) + m_ip_dscp = Signal(intbv(0)[M_COUNT*6:]) + m_ip_ecn = Signal(intbv(0)[M_COUNT*2:]) + m_ip_length = Signal(intbv(0)[M_COUNT*16:]) + m_ip_identification = Signal(intbv(0)[M_COUNT*16:]) + m_ip_flags = Signal(intbv(0)[M_COUNT*3:]) + m_ip_fragment_offset = Signal(intbv(0)[M_COUNT*13:]) + m_ip_ttl = Signal(intbv(0)[M_COUNT*8:]) + m_ip_protocol = Signal(intbv(0)[M_COUNT*8:]) + m_ip_header_checksum = Signal(intbv(0)[M_COUNT*16:]) + m_ip_source_ip = Signal(intbv(0)[M_COUNT*32:]) + m_ip_dest_ip = Signal(intbv(0)[M_COUNT*32:]) + m_udp_source_port = Signal(intbv(0)[M_COUNT*16:]) + m_udp_dest_port = Signal(intbv(0)[M_COUNT*16:]) + m_udp_length = Signal(intbv(0)[M_COUNT*16:]) + m_udp_checksum = Signal(intbv(0)[M_COUNT*16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_udp_payload_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_udp_payload_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_udp_payload_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_udp_payload_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_udp_payload_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_udp_payload_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_udp_hdr_valid_list = [m_udp_hdr_valid(i) for i in range(M_COUNT)] + m_eth_dest_mac_list = [m_eth_dest_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_src_mac_list = [m_eth_src_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_type_list = [m_eth_type((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_version_list = [m_ip_version((i+1)*4, i*4) for i in range(M_COUNT)] + m_ip_ihl_list = [m_ip_ihl((i+1)*4, i*4) for i in range(M_COUNT)] + m_ip_dscp_list = [m_ip_dscp((i+1)*6, i*6) for i in range(M_COUNT)] + m_ip_ecn_list = [m_ip_ecn((i+1)*2, i*2) for i in range(M_COUNT)] + m_ip_length_list = [m_ip_length((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_identification_list = [m_ip_identification((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_flags_list = [m_ip_flags((i+1)*3, i*3) for i in range(M_COUNT)] + m_ip_fragment_offset_list = [m_ip_fragment_offset((i+1)*13, i*13) for i in range(M_COUNT)] + m_ip_ttl_list = [m_ip_ttl((i+1)*8, i*8) for i in range(M_COUNT)] + m_ip_protocol_list = [m_ip_protocol((i+1)*8, i*8) for i in range(M_COUNT)] + m_ip_header_checksum_list = [m_ip_header_checksum((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_source_ip_list = [m_ip_source_ip((i+1)*32, i*32) for i in range(M_COUNT)] + m_ip_dest_ip_list = [m_ip_dest_ip((i+1)*32, i*32) for i in range(M_COUNT)] + m_udp_source_port_list = [m_udp_source_port((i+1)*16, i*16) for i in range(M_COUNT)] + m_udp_dest_port_list = [m_udp_dest_port((i+1)*16, i*16) for i in range(M_COUNT)] + m_udp_length_list = [m_udp_length((i+1)*16, i*16) for i in range(M_COUNT)] + m_udp_checksum_list = [m_udp_checksum((i+1)*16, i*16) for i in range(M_COUNT)] + m_udp_payload_axis_tdata_list = [m_udp_payload_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_udp_payload_axis_tkeep_list = [m_udp_payload_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_udp_payload_axis_tvalid_list = [m_udp_payload_axis_tvalid(i) for i in range(M_COUNT)] + m_udp_payload_axis_tlast_list = [m_udp_payload_axis_tlast(i) for i in range(M_COUNT)] + m_udp_payload_axis_tid_list = [m_udp_payload_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_udp_payload_axis_tdest_list = [m_udp_payload_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_udp_payload_axis_tuser_list = [m_udp_payload_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + source = udp_ep.UDPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready, + udp_hdr_valid=s_udp_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + udp_source_port=s_udp_source_port, + udp_dest_port=s_udp_dest_port, + udp_length=s_udp_length, + udp_checksum=s_udp_checksum, + udp_payload_tdata=s_udp_payload_axis_tdata, + udp_payload_tkeep=s_udp_payload_axis_tkeep, + udp_payload_tvalid=s_udp_payload_axis_tvalid, + udp_payload_tready=s_udp_payload_axis_tready, + udp_payload_tlast=s_udp_payload_axis_tlast, + udp_payload_tuser=s_udp_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + for k in range(M_COUNT): + s = udp_ep.UDPFrameSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready_list[k], + udp_hdr_valid=m_udp_hdr_valid_list[k], + eth_dest_mac=m_eth_dest_mac_list[k], + eth_src_mac=m_eth_src_mac_list[k], + eth_type=m_eth_type_list[k], + ip_version=m_ip_version_list[k], + ip_ihl=m_ip_ihl_list[k], + ip_dscp=m_ip_dscp_list[k], + ip_ecn=m_ip_ecn_list[k], + ip_length=m_ip_length_list[k], + ip_identification=m_ip_identification_list[k], + ip_flags=m_ip_flags_list[k], + ip_fragment_offset=m_ip_fragment_offset_list[k], + ip_ttl=m_ip_ttl_list[k], + ip_protocol=m_ip_protocol_list[k], + ip_header_checksum=m_ip_header_checksum_list[k], + ip_source_ip=m_ip_source_ip_list[k], + ip_dest_ip=m_ip_dest_ip_list[k], + udp_source_port=m_udp_source_port_list[k], + udp_dest_port=m_udp_dest_port_list[k], + udp_length=m_udp_length_list[k], + udp_checksum=m_udp_checksum_list[k], + udp_payload_tdata=m_udp_payload_axis_tdata_list[k], + udp_payload_tkeep=m_udp_payload_axis_tkeep_list[k], + udp_payload_tvalid=m_udp_payload_axis_tvalid_list[k], + udp_payload_tready=m_udp_payload_axis_tready_list[k], + udp_payload_tlast=m_udp_payload_axis_tlast_list[k], + udp_payload_tuser=m_udp_payload_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep=s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tid=s_udp_payload_axis_tid, + s_udp_payload_axis_tdest=s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep=m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tid=m_udp_payload_axis_tid, + m_udp_payload_axis_tdest=m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + enable=enable, + drop=drop, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame1 + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_udp_payload_axis_tvalid or s_udp_hdr_valid: + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_udp_payload_axis_tvalid or s_udp_hdr_valid: + source_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_udp_payload_axis_tvalid or s_udp_hdr_valid: + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: enable") + current_test.next = 7 + + enable.next = False + select.next = 0 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + enable.next = True + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 8: drop") + current_test.next = 8 + + drop.next = True + select.next = 0 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + drop.next = False + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_demux_4.v b/fpga/lib/eth/tb/test_udp_demux_4.v new file mode 100644 index 000000000..1540f51c6 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_demux_4.v @@ -0,0 +1,281 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_demux + */ +module test_udp_demux_4; + +// Parameters +parameter M_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_udp_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [15:0] s_udp_source_port = 0; +reg [15:0] s_udp_dest_port = 0; +reg [15:0] s_udp_length = 0; +reg [15:0] s_udp_checksum = 0; +reg [DATA_WIDTH-1:0] s_udp_payload_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep = 0; +reg s_udp_payload_axis_tvalid = 0; +reg s_udp_payload_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_udp_payload_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_udp_payload_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_udp_payload_axis_tuser = 0; + +reg [M_COUNT-1:0] m_udp_hdr_ready = 0; +reg [M_COUNT-1:0] m_udp_payload_axis_tready = 0; + +reg enable = 0; +reg drop = 0; +reg [1:0] select = 0; + +// Outputs +wire s_udp_hdr_ready; +wire s_udp_payload_axis_tready; + +wire [M_COUNT-1:0] m_udp_hdr_valid; +wire [M_COUNT*48-1:0] m_eth_dest_mac; +wire [M_COUNT*48-1:0] m_eth_src_mac; +wire [M_COUNT*16-1:0] m_eth_type; +wire [M_COUNT*4-1:0] m_ip_version; +wire [M_COUNT*4-1:0] m_ip_ihl; +wire [M_COUNT*6-1:0] m_ip_dscp; +wire [M_COUNT*2-1:0] m_ip_ecn; +wire [M_COUNT*16-1:0] m_ip_length; +wire [M_COUNT*16-1:0] m_ip_identification; +wire [M_COUNT*3-1:0] m_ip_flags; +wire [M_COUNT*13-1:0] m_ip_fragment_offset; +wire [M_COUNT*8-1:0] m_ip_ttl; +wire [M_COUNT*8-1:0] m_ip_protocol; +wire [M_COUNT*16-1:0] m_ip_header_checksum; +wire [M_COUNT*32-1:0] m_ip_source_ip; +wire [M_COUNT*32-1:0] m_ip_dest_ip; +wire [M_COUNT*16-1:0] m_udp_source_port; +wire [M_COUNT*16-1:0] m_udp_dest_port; +wire [M_COUNT*16-1:0] m_udp_length; +wire [M_COUNT*16-1:0] m_udp_checksum; +wire [M_COUNT*DATA_WIDTH-1:0] m_udp_payload_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep; +wire [M_COUNT-1:0] m_udp_payload_axis_tvalid; +wire [M_COUNT-1:0] m_udp_payload_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_udp_payload_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_udp_payload_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_udp_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_udp_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tid, + s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser, + m_udp_hdr_ready, + m_udp_payload_axis_tready, + enable, + drop, + select + ); + $to_myhdl( + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_udp_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tid, + m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser + ); + + // dump file + $dumpfile("test_udp_demux_4.lxt"); + $dumpvars(0, test_udp_demux_4); +end + +udp_demux #( + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tid(s_udp_payload_axis_tid), + .s_udp_payload_axis_tdest(s_udp_payload_axis_tdest), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame outputs + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tid(m_udp_payload_axis_tid), + .m_udp_payload_axis_tdest(m_udp_payload_axis_tdest), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Control + .enable(enable), + .drop(drop), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_demux_64_4.py b/fpga/lib/eth/tb/test_udp_demux_64_4.py new file mode 100755 index 000000000..5b0e0935b --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_demux_64_4.py @@ -0,0 +1,819 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import udp_ep + +module = 'udp_demux' +testbench = 'test_%s_64_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + M_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_udp_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_udp_source_port = Signal(intbv(0)[16:]) + s_udp_dest_port = Signal(intbv(0)[16:]) + s_udp_length = Signal(intbv(0)[16:]) + s_udp_checksum = Signal(intbv(0)[16:]) + s_udp_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_udp_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + s_udp_payload_axis_tvalid = Signal(bool(0)) + s_udp_payload_axis_tlast = Signal(bool(0)) + s_udp_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + s_udp_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + s_udp_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + m_udp_hdr_ready_list = [Signal(bool(0)) for i in range(M_COUNT)] + m_udp_payload_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_udp_hdr_ready = ConcatSignal(*reversed(m_udp_hdr_ready_list)) + m_udp_payload_axis_tready = ConcatSignal(*reversed(m_udp_payload_axis_tready_list)) + + enable = Signal(bool(0)) + drop = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_udp_hdr_ready = Signal(bool(0)) + s_udp_payload_axis_tready = Signal(bool(0)) + + m_udp_hdr_valid = Signal(intbv(0)[M_COUNT:]) + m_eth_dest_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_src_mac = Signal(intbv(0)[M_COUNT*48:]) + m_eth_type = Signal(intbv(0)[M_COUNT*16:]) + m_ip_version = Signal(intbv(0)[M_COUNT*4:]) + m_ip_ihl = Signal(intbv(0)[M_COUNT*4:]) + m_ip_dscp = Signal(intbv(0)[M_COUNT*6:]) + m_ip_ecn = Signal(intbv(0)[M_COUNT*2:]) + m_ip_length = Signal(intbv(0)[M_COUNT*16:]) + m_ip_identification = Signal(intbv(0)[M_COUNT*16:]) + m_ip_flags = Signal(intbv(0)[M_COUNT*3:]) + m_ip_fragment_offset = Signal(intbv(0)[M_COUNT*13:]) + m_ip_ttl = Signal(intbv(0)[M_COUNT*8:]) + m_ip_protocol = Signal(intbv(0)[M_COUNT*8:]) + m_ip_header_checksum = Signal(intbv(0)[M_COUNT*16:]) + m_ip_source_ip = Signal(intbv(0)[M_COUNT*32:]) + m_ip_dest_ip = Signal(intbv(0)[M_COUNT*32:]) + m_udp_source_port = Signal(intbv(0)[M_COUNT*16:]) + m_udp_dest_port = Signal(intbv(0)[M_COUNT*16:]) + m_udp_length = Signal(intbv(0)[M_COUNT*16:]) + m_udp_checksum = Signal(intbv(0)[M_COUNT*16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[M_COUNT*DATA_WIDTH:]) + m_udp_payload_axis_tkeep = Signal(intbv(0xf)[M_COUNT*KEEP_WIDTH:]) + m_udp_payload_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_udp_payload_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_udp_payload_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_udp_payload_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_udp_payload_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_udp_hdr_valid_list = [m_udp_hdr_valid(i) for i in range(M_COUNT)] + m_eth_dest_mac_list = [m_eth_dest_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_src_mac_list = [m_eth_src_mac((i+1)*48, i*48) for i in range(M_COUNT)] + m_eth_type_list = [m_eth_type((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_version_list = [m_ip_version((i+1)*4, i*4) for i in range(M_COUNT)] + m_ip_ihl_list = [m_ip_ihl((i+1)*4, i*4) for i in range(M_COUNT)] + m_ip_dscp_list = [m_ip_dscp((i+1)*6, i*6) for i in range(M_COUNT)] + m_ip_ecn_list = [m_ip_ecn((i+1)*2, i*2) for i in range(M_COUNT)] + m_ip_length_list = [m_ip_length((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_identification_list = [m_ip_identification((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_flags_list = [m_ip_flags((i+1)*3, i*3) for i in range(M_COUNT)] + m_ip_fragment_offset_list = [m_ip_fragment_offset((i+1)*13, i*13) for i in range(M_COUNT)] + m_ip_ttl_list = [m_ip_ttl((i+1)*8, i*8) for i in range(M_COUNT)] + m_ip_protocol_list = [m_ip_protocol((i+1)*8, i*8) for i in range(M_COUNT)] + m_ip_header_checksum_list = [m_ip_header_checksum((i+1)*16, i*16) for i in range(M_COUNT)] + m_ip_source_ip_list = [m_ip_source_ip((i+1)*32, i*32) for i in range(M_COUNT)] + m_ip_dest_ip_list = [m_ip_dest_ip((i+1)*32, i*32) for i in range(M_COUNT)] + m_udp_source_port_list = [m_udp_source_port((i+1)*16, i*16) for i in range(M_COUNT)] + m_udp_dest_port_list = [m_udp_dest_port((i+1)*16, i*16) for i in range(M_COUNT)] + m_udp_length_list = [m_udp_length((i+1)*16, i*16) for i in range(M_COUNT)] + m_udp_checksum_list = [m_udp_checksum((i+1)*16, i*16) for i in range(M_COUNT)] + m_udp_payload_axis_tdata_list = [m_udp_payload_axis_tdata((i+1)*DATA_WIDTH, i*DATA_WIDTH) for i in range(M_COUNT)] + m_udp_payload_axis_tkeep_list = [m_udp_payload_axis_tkeep((i+1)*KEEP_WIDTH, i*KEEP_WIDTH) for i in range(M_COUNT)] + m_udp_payload_axis_tvalid_list = [m_udp_payload_axis_tvalid(i) for i in range(M_COUNT)] + m_udp_payload_axis_tlast_list = [m_udp_payload_axis_tlast(i) for i in range(M_COUNT)] + m_udp_payload_axis_tid_list = [m_udp_payload_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_udp_payload_axis_tdest_list = [m_udp_payload_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_udp_payload_axis_tuser_list = [m_udp_payload_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + source = udp_ep.UDPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready, + udp_hdr_valid=s_udp_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + udp_source_port=s_udp_source_port, + udp_dest_port=s_udp_dest_port, + udp_length=s_udp_length, + udp_checksum=s_udp_checksum, + udp_payload_tdata=s_udp_payload_axis_tdata, + udp_payload_tkeep=s_udp_payload_axis_tkeep, + udp_payload_tvalid=s_udp_payload_axis_tvalid, + udp_payload_tready=s_udp_payload_axis_tready, + udp_payload_tlast=s_udp_payload_axis_tlast, + udp_payload_tuser=s_udp_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + for k in range(M_COUNT): + s = udp_ep.UDPFrameSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready_list[k], + udp_hdr_valid=m_udp_hdr_valid_list[k], + eth_dest_mac=m_eth_dest_mac_list[k], + eth_src_mac=m_eth_src_mac_list[k], + eth_type=m_eth_type_list[k], + ip_version=m_ip_version_list[k], + ip_ihl=m_ip_ihl_list[k], + ip_dscp=m_ip_dscp_list[k], + ip_ecn=m_ip_ecn_list[k], + ip_length=m_ip_length_list[k], + ip_identification=m_ip_identification_list[k], + ip_flags=m_ip_flags_list[k], + ip_fragment_offset=m_ip_fragment_offset_list[k], + ip_ttl=m_ip_ttl_list[k], + ip_protocol=m_ip_protocol_list[k], + ip_header_checksum=m_ip_header_checksum_list[k], + ip_source_ip=m_ip_source_ip_list[k], + ip_dest_ip=m_ip_dest_ip_list[k], + udp_source_port=m_udp_source_port_list[k], + udp_dest_port=m_udp_dest_port_list[k], + udp_length=m_udp_length_list[k], + udp_checksum=m_udp_checksum_list[k], + udp_payload_tdata=m_udp_payload_axis_tdata_list[k], + udp_payload_tkeep=m_udp_payload_axis_tkeep_list[k], + udp_payload_tvalid=m_udp_payload_axis_tvalid_list[k], + udp_payload_tready=m_udp_payload_axis_tready_list[k], + udp_payload_tlast=m_udp_payload_axis_tlast_list[k], + udp_payload_tuser=m_udp_payload_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep=s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tid=s_udp_payload_axis_tid, + s_udp_payload_axis_tdest=s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep=m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tid=m_udp_payload_axis_tid, + m_udp_payload_axis_tdest=m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + enable=enable, + drop=drop, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame1 + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_udp_payload_axis_tvalid or s_udp_hdr_valid: + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_udp_payload_axis_tvalid or s_udp_hdr_valid: + source_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + + while s_udp_payload_axis_tvalid or s_udp_hdr_valid: + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + select.next = 2 + + yield sink_list[1].wait() + rx_frame = sink_list[1].recv() + + assert rx_frame == test_frame1 + + yield sink_list[2].wait() + rx_frame = sink_list[2].recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: enable") + current_test.next = 7 + + enable.next = False + select.next = 0 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + enable.next = True + + yield sink_list[0].wait() + rx_frame = sink_list[0].recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 8: drop") + current_test.next = 8 + + drop.next = True + select.next = 0 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source.send(test_frame) + + yield delay(500) + + assert sink_list[0].empty() + + drop.next = False + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_demux_64_4.v b/fpga/lib/eth/tb/test_udp_demux_64_4.v new file mode 100644 index 000000000..dfab33290 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_demux_64_4.v @@ -0,0 +1,281 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_demux + */ +module test_udp_demux_64_4; + +// Parameters +parameter M_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_udp_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [15:0] s_udp_source_port = 0; +reg [15:0] s_udp_dest_port = 0; +reg [15:0] s_udp_length = 0; +reg [15:0] s_udp_checksum = 0; +reg [DATA_WIDTH-1:0] s_udp_payload_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep = 0; +reg s_udp_payload_axis_tvalid = 0; +reg s_udp_payload_axis_tlast = 0; +reg [ID_WIDTH-1:0] s_udp_payload_axis_tid = 0; +reg [DEST_WIDTH-1:0] s_udp_payload_axis_tdest = 0; +reg [USER_WIDTH-1:0] s_udp_payload_axis_tuser = 0; + +reg [M_COUNT-1:0] m_udp_hdr_ready = 0; +reg [M_COUNT-1:0] m_udp_payload_axis_tready = 0; + +reg enable = 0; +reg drop = 0; +reg [1:0] select = 0; + +// Outputs +wire s_udp_hdr_ready; +wire s_udp_payload_axis_tready; + +wire [M_COUNT-1:0] m_udp_hdr_valid; +wire [M_COUNT*48-1:0] m_eth_dest_mac; +wire [M_COUNT*48-1:0] m_eth_src_mac; +wire [M_COUNT*16-1:0] m_eth_type; +wire [M_COUNT*4-1:0] m_ip_version; +wire [M_COUNT*4-1:0] m_ip_ihl; +wire [M_COUNT*6-1:0] m_ip_dscp; +wire [M_COUNT*2-1:0] m_ip_ecn; +wire [M_COUNT*16-1:0] m_ip_length; +wire [M_COUNT*16-1:0] m_ip_identification; +wire [M_COUNT*3-1:0] m_ip_flags; +wire [M_COUNT*13-1:0] m_ip_fragment_offset; +wire [M_COUNT*8-1:0] m_ip_ttl; +wire [M_COUNT*8-1:0] m_ip_protocol; +wire [M_COUNT*16-1:0] m_ip_header_checksum; +wire [M_COUNT*32-1:0] m_ip_source_ip; +wire [M_COUNT*32-1:0] m_ip_dest_ip; +wire [M_COUNT*16-1:0] m_udp_source_port; +wire [M_COUNT*16-1:0] m_udp_dest_port; +wire [M_COUNT*16-1:0] m_udp_length; +wire [M_COUNT*16-1:0] m_udp_checksum; +wire [M_COUNT*DATA_WIDTH-1:0] m_udp_payload_axis_tdata; +wire [M_COUNT*KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep; +wire [M_COUNT-1:0] m_udp_payload_axis_tvalid; +wire [M_COUNT-1:0] m_udp_payload_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_udp_payload_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_udp_payload_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_udp_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_udp_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tid, + s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser, + m_udp_hdr_ready, + m_udp_payload_axis_tready, + enable, + drop, + select + ); + $to_myhdl( + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_udp_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tid, + m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser + ); + + // dump file + $dumpfile("test_udp_demux_64_4.lxt"); + $dumpvars(0, test_udp_demux_64_4); +end + +udp_demux #( + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tid(s_udp_payload_axis_tid), + .s_udp_payload_axis_tdest(s_udp_payload_axis_tdest), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame outputs + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tid(m_udp_payload_axis_tid), + .m_udp_payload_axis_tdest(m_udp_payload_axis_tdest), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Control + .enable(enable), + .drop(drop), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_ip_rx.py b/fpga/lib/eth/tb/test_udp_ip_rx.py new file mode 100755 index 000000000..c4b1e9c4e --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_ip_rx.py @@ -0,0 +1,1026 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import ip_ep +import udp_ep + +module = 'udp_ip_rx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + m_udp_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[8:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_header_early_termination = Signal(bool(0)) + error_payload_early_termination = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = ip_ep.IPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready, + ip_hdr_valid=s_ip_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = udp_ep.UDPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + busy=busy, + error_header_early_termination=error_header_early_termination, + error_payload_early_termination=error_payload_early_termination + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_header_early_termination_asserted = Signal(bool(0)) + error_payload_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_header_early_termination): + error_header_early_termination_asserted.next = 1 + if (error_payload_early_termination): + error_payload_early_termination_asserted.next = 1 + + def wait_normal(): + while s_ip_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_ip_hdr_valid: + yield clk.posedge + + def wait_pause_source(): + while s_ip_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_ip_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_ip_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_ip_hdr_valid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(payload_len)) + test_frame.build() + ip_frame = test_frame.build_ip() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: trailing bytes (1), length %d" % payload_len) + current_test.next = 4 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data += bytearray(b'\x00') + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: trailing bytes (10), length %d" % payload_len) + current_test.next = 5 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data += bytearray(b'\x00'*10) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 6: trailing bytes with tuser assert (1), length %d" % payload_len) + current_test.next = 6 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data += bytearray(b'\x00') + ip_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 7: trailing bytes with tuser assert (10), length %d" % payload_len) + current_test.next = 7 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data += bytearray(b'\x00'*10) + ip_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 8: truncated payload (1), length %d" % payload_len) + current_test.next = 8 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len+1)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data = ip_frame1.payload.data[:-1] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 9: truncated payload (10), length %d" % payload_len) + current_test.next = 9 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len+10)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data = ip_frame1.payload.data[:-10] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + for length in range(1,9): + yield clk.posedge + print("test 10: truncated header, length %d" % length) + current_test.next = 10 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(16)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(16)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data = ip_frame1.payload.data[:length] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_header_early_termination_asserted.next = 0 + + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert error_header_early_termination_asserted + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_ip_rx.v b/fpga/lib/eth/tb/test_udp_ip_rx.v new file mode 100644 index 000000000..cad10e133 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_ip_rx.v @@ -0,0 +1,225 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_ip_rx + */ +module test_udp_ip_rx; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_ip_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [7:0] s_ip_payload_axis_tdata = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; + +// Outputs +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire m_udp_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [7:0] m_udp_payload_axis_tdata; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire m_udp_payload_axis_tuser; +wire busy; +wire error_header_early_termination; +wire error_payload_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + m_udp_hdr_ready, + m_udp_payload_axis_tready + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_udp_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser, + busy, + error_header_early_termination, + error_payload_early_termination + ); + + // dump file + $dumpfile("test_udp_ip_rx.lxt"); + $dumpvars(0, test_udp_ip_rx); +end + +udp_ip_rx +UUT ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status signals + .busy(busy), + .error_header_early_termination(error_header_early_termination), + .error_payload_early_termination(error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_ip_rx_64.py b/fpga/lib/eth/tb/test_udp_ip_rx_64.py new file mode 100755 index 000000000..6d4ea37a7 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_ip_rx_64.py @@ -0,0 +1,1032 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import ip_ep +import udp_ep + +module = 'udp_ip_rx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_ip_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_length = Signal(intbv(0)[16:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + s_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_ip_payload_axis_tvalid = Signal(bool(0)) + s_ip_payload_axis_tlast = Signal(bool(0)) + s_ip_payload_axis_tuser = Signal(bool(0)) + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + # Outputs + s_ip_hdr_ready = Signal(bool(0)) + s_ip_payload_axis_tready = Signal(bool(0)) + m_udp_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[64:]) + m_udp_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_header_early_termination = Signal(bool(0)) + error_payload_early_termination = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = ip_ep.IPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + ip_hdr_ready=s_ip_hdr_ready, + ip_hdr_valid=s_ip_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_length=s_ip_length, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + ip_payload_tdata=s_ip_payload_axis_tdata, + ip_payload_tkeep=s_ip_payload_axis_tkeep, + ip_payload_tvalid=s_ip_payload_axis_tvalid, + ip_payload_tready=s_ip_payload_axis_tready, + ip_payload_tlast=s_ip_payload_axis_tlast, + ip_payload_tuser=s_ip_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = udp_ep.UDPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tkeep=m_udp_payload_axis_tkeep, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_ip_hdr_valid=s_ip_hdr_valid, + s_ip_hdr_ready=s_ip_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_ip_payload_axis_tdata=s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep=s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid=s_ip_payload_axis_tvalid, + s_ip_payload_axis_tready=s_ip_payload_axis_tready, + s_ip_payload_axis_tlast=s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser=s_ip_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep=m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + busy=busy, + error_header_early_termination=error_header_early_termination, + error_payload_early_termination=error_payload_early_termination + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_header_early_termination_asserted = Signal(bool(0)) + error_payload_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_header_early_termination): + error_header_early_termination_asserted.next = 1 + if (error_payload_early_termination): + error_payload_early_termination_asserted.next = 1 + + def wait_normal(): + while s_ip_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_ip_hdr_valid: + yield clk.posedge + + def wait_pause_source(): + while s_ip_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_ip_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_ip_payload_axis_tvalid or m_udp_payload_axis_tvalid or s_ip_hdr_valid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(payload_len)) + test_frame.build() + ip_frame = test_frame.build_ip() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: trailing bytes (1), length %d" % payload_len) + current_test.next = 4 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data += bytearray(b'\x00') + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: trailing bytes (10), length %d" % payload_len) + current_test.next = 5 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data += bytearray(b'\x00'*10) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 6: trailing bytes with tuser assert (1), length %d" % payload_len) + current_test.next = 6 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data += bytearray(b'\x00') + ip_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 7: trailing bytes with tuser assert (10), length %d" % payload_len) + current_test.next = 7 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data += bytearray(b'\x00'*10) + ip_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 8: truncated payload (1), length %d" % payload_len) + current_test.next = 8 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len+1)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data = ip_frame1.payload.data[:-1] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 9: truncated payload (10), length %d" % payload_len) + current_test.next = 9 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len+10)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data = ip_frame1.payload.data[:-10] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + for length in range(1,9): + yield clk.posedge + print("test 10: truncated header, length %d" % length) + current_test.next = 10 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(16)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(16)) + test_frame2.build() + + ip_frame1 = test_frame1.build_ip() + ip_frame2 = test_frame2.build_ip() + + ip_frame1.payload.data = ip_frame1.payload.data[:length] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_header_early_termination_asserted.next = 0 + + source.send(ip_frame1) + source.send(ip_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + assert error_header_early_termination_asserted + + assert rx_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_ip_rx_64.v b/fpga/lib/eth/tb/test_udp_ip_rx_64.v new file mode 100644 index 000000000..663989b89 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_ip_rx_64.v @@ -0,0 +1,231 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_ip_rx_64 + */ +module test_udp_ip_rx_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_ip_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_length = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [63:0] s_ip_payload_axis_tdata = 0; +reg [7:0] s_ip_payload_axis_tkeep = 0; +reg s_ip_payload_axis_tvalid = 0; +reg s_ip_payload_axis_tlast = 0; +reg s_ip_payload_axis_tuser = 0; +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; + +// Outputs +wire s_ip_hdr_ready; +wire s_ip_payload_axis_tready; +wire m_udp_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [63:0] m_udp_payload_axis_tdata; +wire [7:0] m_udp_payload_axis_tkeep; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire m_udp_payload_axis_tuser; +wire busy; +wire error_header_early_termination; +wire error_payload_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_ip_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_ip_payload_axis_tdata, + s_ip_payload_axis_tkeep, + s_ip_payload_axis_tvalid, + s_ip_payload_axis_tlast, + s_ip_payload_axis_tuser, + m_udp_hdr_ready, + m_udp_payload_axis_tready + ); + $to_myhdl( + s_ip_hdr_ready, + s_ip_payload_axis_tready, + m_udp_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tuser, + busy, + error_header_early_termination, + error_payload_early_termination + ); + + // dump file + $dumpfile("test_udp_ip_rx_64.lxt"); + $dumpvars(0, test_udp_ip_rx_64); +end + +udp_ip_rx_64 +UUT ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status signals + .busy(busy), + .error_header_early_termination(error_header_early_termination), + .error_payload_early_termination(error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_ip_tx.py b/fpga/lib/eth/tb/test_udp_ip_tx.py new file mode 100755 index 000000000..4a34589d7 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_ip_tx.py @@ -0,0 +1,976 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import ip_ep +import udp_ep + +module = 'udp_ip_tx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_udp_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_udp_source_port = Signal(intbv(0)[16:]) + s_udp_dest_port = Signal(intbv(0)[16:]) + s_udp_length = Signal(intbv(0)[16:]) + s_udp_checksum = Signal(intbv(0)[16:]) + s_udp_payload_axis_tdata = Signal(intbv(0)[8:]) + s_udp_payload_axis_tvalid = Signal(bool(0)) + s_udp_payload_axis_tlast = Signal(bool(0)) + s_udp_payload_axis_tuser = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + + # Outputs + s_udp_hdr_ready = Signal(bool(0)) + s_udp_payload_axis_tready = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_payload_early_termination = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = udp_ep.UDPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready, + udp_hdr_valid=s_udp_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + udp_source_port=s_udp_source_port, + udp_dest_port=s_udp_dest_port, + udp_length=s_udp_length, + udp_checksum=s_udp_checksum, + udp_payload_tdata=s_udp_payload_axis_tdata, + udp_payload_tvalid=s_udp_payload_axis_tvalid, + udp_payload_tready=s_udp_payload_axis_tready, + udp_payload_tlast=s_udp_payload_axis_tlast, + udp_payload_tuser=s_udp_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = ip_ep.IPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + busy=busy, + error_payload_early_termination=error_payload_early_termination + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_payload_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_payload_early_termination): + error_payload_early_termination_asserted.next = 1 + + def wait_normal(): + while s_udp_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_udp_hdr_valid: + yield clk.posedge + + def wait_pause_source(): + while s_udp_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_udp_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_udp_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_udp_hdr_valid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(payload_len)) + test_frame.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: trailing bytes (1), length %d" % payload_len) + current_test.next = 4 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00') + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: trailing bytes (10), length %d" % payload_len) + current_test.next = 5 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00'*10) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 6: trailing bytes with tuser assert (1), length %d" % payload_len) + current_test.next = 6 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00') + test_frame1a.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 7: trailing bytes with tuser assert (10), length %d" % payload_len) + current_test.next = 7 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00'*10) + test_frame1a.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 8: truncated payload (1), length %d" % payload_len) + current_test.next = 8 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len+1)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data = test_frame1a.payload.data[:-1] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 9: truncated payload (10), length %d" % payload_len) + current_test.next = 9 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len+10)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data = test_frame1.payload.data[:-10] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_ip_tx.v b/fpga/lib/eth/tb/test_udp_ip_tx.v new file mode 100644 index 000000000..76cf90e39 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_ip_tx.v @@ -0,0 +1,219 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_ip_tx + */ +module test_udp_ip_tx; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_udp_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [15:0] s_udp_source_port = 0; +reg [15:0] s_udp_dest_port = 0; +reg [15:0] s_udp_length = 0; +reg [15:0] s_udp_checksum = 0; +reg [7:0] s_udp_payload_axis_tdata = 0; +reg s_udp_payload_axis_tvalid = 0; +reg s_udp_payload_axis_tlast = 0; +reg s_udp_payload_axis_tuser = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; + +// Outputs +wire s_udp_hdr_ready; +wire s_udp_payload_axis_tready; +wire m_ip_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [7:0] m_ip_payload_axis_tdata; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire busy; +wire error_payload_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_udp_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready + ); + $to_myhdl( + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_ip_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + busy, + error_payload_early_termination + ); + + // dump file + $dumpfile("test_udp_ip_tx.lxt"); + $dumpvars(0, test_udp_ip_tx); +end + +udp_ip_tx +UUT ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(busy), + .error_payload_early_termination(error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_ip_tx_64.py b/fpga/lib/eth/tb/test_udp_ip_tx_64.py new file mode 100755 index 000000000..474adbaea --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_ip_tx_64.py @@ -0,0 +1,982 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import eth_ep +import ip_ep +import udp_ep + +module = 'udp_ip_tx_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_udp_hdr_valid = Signal(bool(0)) + s_eth_dest_mac = Signal(intbv(0)[48:]) + s_eth_src_mac = Signal(intbv(0)[48:]) + s_eth_type = Signal(intbv(0)[16:]) + s_ip_version = Signal(intbv(0)[4:]) + s_ip_ihl = Signal(intbv(0)[4:]) + s_ip_dscp = Signal(intbv(0)[6:]) + s_ip_ecn = Signal(intbv(0)[2:]) + s_ip_identification = Signal(intbv(0)[16:]) + s_ip_flags = Signal(intbv(0)[3:]) + s_ip_fragment_offset = Signal(intbv(0)[13:]) + s_ip_ttl = Signal(intbv(0)[8:]) + s_ip_protocol = Signal(intbv(0)[8:]) + s_ip_header_checksum = Signal(intbv(0)[16:]) + s_ip_source_ip = Signal(intbv(0)[32:]) + s_ip_dest_ip = Signal(intbv(0)[32:]) + s_udp_source_port = Signal(intbv(0)[16:]) + s_udp_dest_port = Signal(intbv(0)[16:]) + s_udp_length = Signal(intbv(0)[16:]) + s_udp_checksum = Signal(intbv(0)[16:]) + s_udp_payload_axis_tdata = Signal(intbv(0)[64:]) + s_udp_payload_axis_tkeep = Signal(intbv(0)[8:]) + s_udp_payload_axis_tvalid = Signal(bool(0)) + s_udp_payload_axis_tlast = Signal(bool(0)) + s_udp_payload_axis_tuser = Signal(bool(0)) + m_ip_payload_axis_tready = Signal(bool(0)) + m_ip_hdr_ready = Signal(bool(0)) + + # Outputs + s_udp_hdr_ready = Signal(bool(0)) + s_udp_payload_axis_tready = Signal(bool(0)) + m_ip_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_ip_payload_axis_tdata = Signal(intbv(0)[64:]) + m_ip_payload_axis_tkeep = Signal(intbv(0)[8:]) + m_ip_payload_axis_tvalid = Signal(bool(0)) + m_ip_payload_axis_tlast = Signal(bool(0)) + m_ip_payload_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + error_payload_early_termination = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + sink_pause = Signal(bool(0)) + + source = udp_ep.UDPFrameSource() + + source_logic = source.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready, + udp_hdr_valid=s_udp_hdr_valid, + eth_dest_mac=s_eth_dest_mac, + eth_src_mac=s_eth_src_mac, + eth_type=s_eth_type, + ip_version=s_ip_version, + ip_ihl=s_ip_ihl, + ip_dscp=s_ip_dscp, + ip_ecn=s_ip_ecn, + ip_identification=s_ip_identification, + ip_flags=s_ip_flags, + ip_fragment_offset=s_ip_fragment_offset, + ip_ttl=s_ip_ttl, + ip_protocol=s_ip_protocol, + ip_header_checksum=s_ip_header_checksum, + ip_source_ip=s_ip_source_ip, + ip_dest_ip=s_ip_dest_ip, + udp_source_port=s_udp_source_port, + udp_dest_port=s_udp_dest_port, + udp_length=s_udp_length, + udp_checksum=s_udp_checksum, + udp_payload_tdata=s_udp_payload_axis_tdata, + udp_payload_tkeep=s_udp_payload_axis_tkeep, + udp_payload_tvalid=s_udp_payload_axis_tvalid, + udp_payload_tready=s_udp_payload_axis_tready, + udp_payload_tlast=s_udp_payload_axis_tlast, + udp_payload_tuser=s_udp_payload_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = ip_ep.IPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + ip_hdr_ready=m_ip_hdr_ready, + ip_hdr_valid=m_ip_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + ip_payload_tdata=m_ip_payload_axis_tdata, + ip_payload_tkeep=m_ip_payload_axis_tkeep, + ip_payload_tvalid=m_ip_payload_axis_tvalid, + ip_payload_tready=m_ip_payload_axis_tready, + ip_payload_tlast=m_ip_payload_axis_tlast, + ip_payload_tuser=m_ip_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep=s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_ip_hdr_valid=m_ip_hdr_valid, + m_ip_hdr_ready=m_ip_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_ip_payload_axis_tdata=m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep=m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid=m_ip_payload_axis_tvalid, + m_ip_payload_axis_tready=m_ip_payload_axis_tready, + m_ip_payload_axis_tlast=m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser=m_ip_payload_axis_tuser, + + busy=busy, + error_payload_early_termination=error_payload_early_termination + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_payload_early_termination_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_payload_early_termination): + error_payload_early_termination_asserted.next = 1 + + def wait_normal(): + while s_udp_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_udp_hdr_valid: + yield clk.posedge + + def wait_pause_source(): + while s_udp_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_udp_hdr_valid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + def wait_pause_sink(): + while s_udp_payload_axis_tvalid or m_ip_payload_axis_tvalid or s_udp_hdr_valid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + for payload_len in range(1,18): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x0800 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80164 + test_frame.ip_dest_ip = 0xc0a80165 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(payload_len)) + test_frame.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: trailing bytes (1), length %d" % payload_len) + current_test.next = 4 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00') + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 5: trailing bytes (10), length %d" % payload_len) + current_test.next = 5 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00'*10) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 6: trailing bytes with tuser assert (1), length %d" % payload_len) + current_test.next = 6 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00') + test_frame1a.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 7: trailing bytes with tuser assert (10), length %d" % payload_len) + current_test.next = 7 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data += bytearray(b'\x00'*10) + test_frame1a.payload.user = 1 + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame1 + assert rx_frame.payload.user[-1] + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 8: truncated payload (1), length %d" % payload_len) + current_test.next = 8 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len+1)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data = test_frame1a.payload.data[:-1] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 9: truncated payload (10), length %d" % payload_len) + current_test.next = 9 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x0800 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80164 + test_frame1.ip_dest_ip = 0xc0a80165 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(payload_len+10)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x0800 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80164 + test_frame2.ip_dest_ip = 0xc0a80166 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.build() + + test_frame1a = udp_ep.UDPFrame(test_frame1) + test_frame1a.payload.data = test_frame1.payload.data[:-10] + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + error_payload_early_termination_asserted.next = 0 + + source.send(test_frame1a) + source.send(test_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert rx_frame.payload.user[-1] + assert error_payload_early_termination_asserted + + yield sink.wait() + rx_frame = sink.recv() + + check_frame = udp_ep.UDPFrame() + check_frame.parse_ip(rx_frame) + + assert check_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_ip_tx_64.v b/fpga/lib/eth/tb/test_udp_ip_tx_64.v new file mode 100644 index 000000000..5f475b425 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_ip_tx_64.v @@ -0,0 +1,225 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_ip_tx_64 + */ +module test_udp_ip_tx_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg s_udp_hdr_valid = 0; +reg [47:0] s_eth_dest_mac = 0; +reg [47:0] s_eth_src_mac = 0; +reg [15:0] s_eth_type = 0; +reg [3:0] s_ip_version = 0; +reg [3:0] s_ip_ihl = 0; +reg [5:0] s_ip_dscp = 0; +reg [1:0] s_ip_ecn = 0; +reg [15:0] s_ip_identification = 0; +reg [2:0] s_ip_flags = 0; +reg [12:0] s_ip_fragment_offset = 0; +reg [7:0] s_ip_ttl = 0; +reg [7:0] s_ip_protocol = 0; +reg [15:0] s_ip_header_checksum = 0; +reg [31:0] s_ip_source_ip = 0; +reg [31:0] s_ip_dest_ip = 0; +reg [15:0] s_udp_source_port = 0; +reg [15:0] s_udp_dest_port = 0; +reg [15:0] s_udp_length = 0; +reg [15:0] s_udp_checksum = 0; +reg [63:0] s_udp_payload_axis_tdata = 0; +reg [7:0] s_udp_payload_axis_tkeep = 0; +reg s_udp_payload_axis_tvalid = 0; +reg s_udp_payload_axis_tlast = 0; +reg s_udp_payload_axis_tuser = 0; +reg m_ip_hdr_ready = 0; +reg m_ip_payload_axis_tready = 0; + +// Outputs +wire s_udp_hdr_ready; +wire s_udp_payload_axis_tready; +wire m_ip_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [63:0] m_ip_payload_axis_tdata; +wire [7:0] m_ip_payload_axis_tkeep; +wire m_ip_payload_axis_tvalid; +wire m_ip_payload_axis_tlast; +wire m_ip_payload_axis_tuser; +wire busy; +wire error_payload_early_termination; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_udp_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tuser, + m_ip_hdr_ready, + m_ip_payload_axis_tready + ); + $to_myhdl( + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_ip_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_ip_payload_axis_tdata, + m_ip_payload_axis_tkeep, + m_ip_payload_axis_tvalid, + m_ip_payload_axis_tlast, + m_ip_payload_axis_tuser, + busy, + error_payload_early_termination + ); + + // dump file + $dumpfile("test_udp_ip_tx_64.lxt"); + $dumpvars(0, test_udp_ip_tx_64); +end + +udp_ip_tx_64 +UUT ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(busy), + .error_payload_early_termination(error_payload_early_termination) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_mux_4.py b/fpga/lib/eth/tb/test_udp_mux_4.py new file mode 100755 index 000000000..e1355b0d6 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_mux_4.py @@ -0,0 +1,736 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import udp_ep + +module = 'udp_mux' +testbench = 'test_%s_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 8 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_udp_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_version_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_ihl_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_dscp_list = [Signal(intbv(0)[6:]) for i in range(S_COUNT)] + s_ip_ecn_list = [Signal(intbv(0)[2:]) for i in range(S_COUNT)] + s_ip_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_identification_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_flags_list = [Signal(intbv(0)[3:]) for i in range(S_COUNT)] + s_ip_fragment_offset_list = [Signal(intbv(0)[13:]) for i in range(S_COUNT)] + s_ip_ttl_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_protocol_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_header_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_source_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_dest_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_udp_source_port_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_dest_port_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_udp_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_udp_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_udp_hdr_valid = ConcatSignal(*reversed(s_udp_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_ip_version = ConcatSignal(*reversed(s_ip_version_list)) + s_ip_ihl = ConcatSignal(*reversed(s_ip_ihl_list)) + s_ip_dscp = ConcatSignal(*reversed(s_ip_dscp_list)) + s_ip_ecn = ConcatSignal(*reversed(s_ip_ecn_list)) + s_ip_length = ConcatSignal(*reversed(s_ip_length_list)) + s_ip_identification = ConcatSignal(*reversed(s_ip_identification_list)) + s_ip_flags = ConcatSignal(*reversed(s_ip_flags_list)) + s_ip_fragment_offset = ConcatSignal(*reversed(s_ip_fragment_offset_list)) + s_ip_ttl = ConcatSignal(*reversed(s_ip_ttl_list)) + s_ip_protocol = ConcatSignal(*reversed(s_ip_protocol_list)) + s_ip_header_checksum = ConcatSignal(*reversed(s_ip_header_checksum_list)) + s_ip_source_ip = ConcatSignal(*reversed(s_ip_source_ip_list)) + s_ip_dest_ip = ConcatSignal(*reversed(s_ip_dest_ip_list)) + s_udp_source_port = ConcatSignal(*reversed(s_udp_source_port_list)) + s_udp_dest_port = ConcatSignal(*reversed(s_udp_dest_port_list)) + s_udp_length = ConcatSignal(*reversed(s_udp_length_list)) + s_udp_checksum = ConcatSignal(*reversed(s_udp_checksum_list)) + s_udp_payload_axis_tdata = ConcatSignal(*reversed(s_udp_payload_axis_tdata_list)) + s_udp_payload_axis_tkeep = ConcatSignal(*reversed(s_udp_payload_axis_tkeep_list)) + s_udp_payload_axis_tvalid = ConcatSignal(*reversed(s_udp_payload_axis_tvalid_list)) + s_udp_payload_axis_tlast = ConcatSignal(*reversed(s_udp_payload_axis_tlast_list)) + s_udp_payload_axis_tid = ConcatSignal(*reversed(s_udp_payload_axis_tid_list)) + s_udp_payload_axis_tdest = ConcatSignal(*reversed(s_udp_payload_axis_tdest_list)) + s_udp_payload_axis_tuser = ConcatSignal(*reversed(s_udp_payload_axis_tuser_list)) + + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + enable = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_udp_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_udp_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_udp_hdr_ready_list = [s_udp_hdr_ready(i) for i in range(S_COUNT)] + s_udp_payload_axis_tready_list = [s_udp_payload_axis_tready(i) for i in range(S_COUNT)] + + m_udp_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_udp_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_udp_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_udp_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = udp_ep.UDPFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready_list[k], + udp_hdr_valid=s_udp_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + ip_version=s_ip_version_list[k], + ip_ihl=s_ip_ihl_list[k], + ip_dscp=s_ip_dscp_list[k], + ip_ecn=s_ip_ecn_list[k], + ip_length=s_ip_length_list[k], + ip_identification=s_ip_identification_list[k], + ip_flags=s_ip_flags_list[k], + ip_fragment_offset=s_ip_fragment_offset_list[k], + ip_ttl=s_ip_ttl_list[k], + ip_protocol=s_ip_protocol_list[k], + ip_header_checksum=s_ip_header_checksum_list[k], + ip_source_ip=s_ip_source_ip_list[k], + ip_dest_ip=s_ip_dest_ip_list[k], + udp_source_port=s_udp_source_port_list[k], + udp_dest_port=s_udp_dest_port_list[k], + udp_length=s_udp_length_list[k], + udp_checksum=s_udp_checksum_list[k], + udp_payload_tdata=s_udp_payload_axis_tdata_list[k], + udp_payload_tkeep=s_udp_payload_axis_tkeep_list[k], + udp_payload_tvalid=s_udp_payload_axis_tvalid_list[k], + udp_payload_tready=s_udp_payload_axis_tready_list[k], + udp_payload_tlast=s_udp_payload_axis_tlast_list[k], + udp_payload_tuser=s_udp_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = udp_ep.UDPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tkeep=m_udp_payload_axis_tkeep, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep=s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tid=s_udp_payload_axis_tid, + s_udp_payload_axis_tdest=s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep=m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tid=m_udp_payload_axis_tid, + m_udp_payload_axis_tdest=m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + enable=enable, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_udp_payload_axis_tvalid: + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_udp_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + select.next = 2 + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_udp_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_mux_4.v b/fpga/lib/eth/tb/test_udp_mux_4.v new file mode 100644 index 000000000..3b655374f --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_mux_4.v @@ -0,0 +1,278 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_mux + */ +module test_udp_mux_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 8; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_udp_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*4-1:0] s_ip_version = 0; +reg [S_COUNT*4-1:0] s_ip_ihl = 0; +reg [S_COUNT*6-1:0] s_ip_dscp = 0; +reg [S_COUNT*2-1:0] s_ip_ecn = 0; +reg [S_COUNT*16-1:0] s_ip_length = 0; +reg [S_COUNT*16-1:0] s_ip_identification = 0; +reg [S_COUNT*3-1:0] s_ip_flags = 0; +reg [S_COUNT*13-1:0] s_ip_fragment_offset = 0; +reg [S_COUNT*8-1:0] s_ip_ttl = 0; +reg [S_COUNT*8-1:0] s_ip_protocol = 0; +reg [S_COUNT*16-1:0] s_ip_header_checksum = 0; +reg [S_COUNT*32-1:0] s_ip_source_ip = 0; +reg [S_COUNT*32-1:0] s_ip_dest_ip = 0; +reg [S_COUNT*16-1:0] s_udp_source_port = 0; +reg [S_COUNT*16-1:0] s_udp_dest_port = 0; +reg [S_COUNT*16-1:0] s_udp_length = 0; +reg [S_COUNT*16-1:0] s_udp_checksum = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_udp_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_udp_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_udp_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_udp_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_udp_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_udp_payload_axis_tuser = 0; + +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; + +reg enable = 0; +reg [1:0] select = 0; + +// Outputs +wire [S_COUNT-1:0] s_udp_hdr_ready; +wire [S_COUNT-1:0] s_udp_payload_axis_tready; + +wire m_udp_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [DATA_WIDTH-1:0] m_udp_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_udp_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_udp_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_udp_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_udp_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tid, + s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser, + m_udp_hdr_ready, + m_udp_payload_axis_tready, + enable, + select + ); + $to_myhdl( + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_udp_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tid, + m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser + ); + + // dump file + $dumpfile("test_udp_mux_4.lxt"); + $dumpvars(0, test_udp_mux_4); +end + +udp_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // UDP frame inputs + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tid(s_udp_payload_axis_tid), + .s_udp_payload_axis_tdest(s_udp_payload_axis_tdest), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tid(m_udp_payload_axis_tid), + .m_udp_payload_axis_tdest(m_udp_payload_axis_tdest), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Control + .enable(enable), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_udp_mux_64_4.py b/fpga/lib/eth/tb/test_udp_mux_64_4.py new file mode 100755 index 000000000..64c5af4c9 --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_mux_64_4.py @@ -0,0 +1,736 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import os + +import udp_ep + +module = 'udp_mux' +testbench = 'test_%s_64_4' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + S_COUNT = 4 + DATA_WIDTH = 64 + KEEP_ENABLE = (DATA_WIDTH>8) + KEEP_WIDTH = (DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_ENABLE = 1 + DEST_WIDTH = 8 + USER_ENABLE = 1 + USER_WIDTH = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_udp_hdr_valid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_eth_dest_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_src_mac_list = [Signal(intbv(0)[48:]) for i in range(S_COUNT)] + s_eth_type_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_version_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_ihl_list = [Signal(intbv(0)[4:]) for i in range(S_COUNT)] + s_ip_dscp_list = [Signal(intbv(0)[6:]) for i in range(S_COUNT)] + s_ip_ecn_list = [Signal(intbv(0)[2:]) for i in range(S_COUNT)] + s_ip_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_identification_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_flags_list = [Signal(intbv(0)[3:]) for i in range(S_COUNT)] + s_ip_fragment_offset_list = [Signal(intbv(0)[13:]) for i in range(S_COUNT)] + s_ip_ttl_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_protocol_list = [Signal(intbv(0)[8:]) for i in range(S_COUNT)] + s_ip_header_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_ip_source_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_ip_dest_ip_list = [Signal(intbv(0)[32:]) for i in range(S_COUNT)] + s_udp_source_port_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_dest_port_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_length_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_checksum_list = [Signal(intbv(0)[16:]) for i in range(S_COUNT)] + s_udp_payload_axis_tdata_list = [Signal(intbv(0)[DATA_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tkeep_list = [Signal(intbv(1)[KEEP_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_udp_payload_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_udp_payload_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_udp_payload_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_udp_hdr_valid = ConcatSignal(*reversed(s_udp_hdr_valid_list)) + s_eth_dest_mac = ConcatSignal(*reversed(s_eth_dest_mac_list)) + s_eth_src_mac = ConcatSignal(*reversed(s_eth_src_mac_list)) + s_eth_type = ConcatSignal(*reversed(s_eth_type_list)) + s_ip_version = ConcatSignal(*reversed(s_ip_version_list)) + s_ip_ihl = ConcatSignal(*reversed(s_ip_ihl_list)) + s_ip_dscp = ConcatSignal(*reversed(s_ip_dscp_list)) + s_ip_ecn = ConcatSignal(*reversed(s_ip_ecn_list)) + s_ip_length = ConcatSignal(*reversed(s_ip_length_list)) + s_ip_identification = ConcatSignal(*reversed(s_ip_identification_list)) + s_ip_flags = ConcatSignal(*reversed(s_ip_flags_list)) + s_ip_fragment_offset = ConcatSignal(*reversed(s_ip_fragment_offset_list)) + s_ip_ttl = ConcatSignal(*reversed(s_ip_ttl_list)) + s_ip_protocol = ConcatSignal(*reversed(s_ip_protocol_list)) + s_ip_header_checksum = ConcatSignal(*reversed(s_ip_header_checksum_list)) + s_ip_source_ip = ConcatSignal(*reversed(s_ip_source_ip_list)) + s_ip_dest_ip = ConcatSignal(*reversed(s_ip_dest_ip_list)) + s_udp_source_port = ConcatSignal(*reversed(s_udp_source_port_list)) + s_udp_dest_port = ConcatSignal(*reversed(s_udp_dest_port_list)) + s_udp_length = ConcatSignal(*reversed(s_udp_length_list)) + s_udp_checksum = ConcatSignal(*reversed(s_udp_checksum_list)) + s_udp_payload_axis_tdata = ConcatSignal(*reversed(s_udp_payload_axis_tdata_list)) + s_udp_payload_axis_tkeep = ConcatSignal(*reversed(s_udp_payload_axis_tkeep_list)) + s_udp_payload_axis_tvalid = ConcatSignal(*reversed(s_udp_payload_axis_tvalid_list)) + s_udp_payload_axis_tlast = ConcatSignal(*reversed(s_udp_payload_axis_tlast_list)) + s_udp_payload_axis_tid = ConcatSignal(*reversed(s_udp_payload_axis_tid_list)) + s_udp_payload_axis_tdest = ConcatSignal(*reversed(s_udp_payload_axis_tdest_list)) + s_udp_payload_axis_tuser = ConcatSignal(*reversed(s_udp_payload_axis_tuser_list)) + + m_udp_hdr_ready = Signal(bool(0)) + m_udp_payload_axis_tready = Signal(bool(0)) + + enable = Signal(bool(0)) + select = Signal(intbv(0)[2:]) + + # Outputs + s_udp_hdr_ready = Signal(intbv(0)[S_COUNT:]) + s_udp_payload_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_udp_hdr_ready_list = [s_udp_hdr_ready(i) for i in range(S_COUNT)] + s_udp_payload_axis_tready_list = [s_udp_payload_axis_tready(i) for i in range(S_COUNT)] + + m_udp_hdr_valid = Signal(bool(0)) + m_eth_dest_mac = Signal(intbv(0)[48:]) + m_eth_src_mac = Signal(intbv(0)[48:]) + m_eth_type = Signal(intbv(0)[16:]) + m_ip_version = Signal(intbv(0)[4:]) + m_ip_ihl = Signal(intbv(0)[4:]) + m_ip_dscp = Signal(intbv(0)[6:]) + m_ip_ecn = Signal(intbv(0)[2:]) + m_ip_length = Signal(intbv(0)[16:]) + m_ip_identification = Signal(intbv(0)[16:]) + m_ip_flags = Signal(intbv(0)[3:]) + m_ip_fragment_offset = Signal(intbv(0)[13:]) + m_ip_ttl = Signal(intbv(0)[8:]) + m_ip_protocol = Signal(intbv(0)[8:]) + m_ip_header_checksum = Signal(intbv(0)[16:]) + m_ip_source_ip = Signal(intbv(0)[32:]) + m_ip_dest_ip = Signal(intbv(0)[32:]) + m_udp_source_port = Signal(intbv(0)[16:]) + m_udp_dest_port = Signal(intbv(0)[16:]) + m_udp_length = Signal(intbv(0)[16:]) + m_udp_checksum = Signal(intbv(0)[16:]) + m_udp_payload_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_udp_payload_axis_tkeep = Signal(intbv(1)[KEEP_WIDTH:]) + m_udp_payload_axis_tvalid = Signal(bool(0)) + m_udp_payload_axis_tlast = Signal(bool(0)) + m_udp_payload_axis_tid = Signal(intbv(0)[ID_WIDTH:]) + m_udp_payload_axis_tdest = Signal(intbv(0)[DEST_WIDTH:]) + m_udp_payload_axis_tuser = Signal(intbv(0)[USER_WIDTH:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause = Signal(bool(0)) + + for k in range(S_COUNT): + s = udp_ep.UDPFrameSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + udp_hdr_ready=s_udp_hdr_ready_list[k], + udp_hdr_valid=s_udp_hdr_valid_list[k], + eth_dest_mac=s_eth_dest_mac_list[k], + eth_src_mac=s_eth_src_mac_list[k], + eth_type=s_eth_type_list[k], + ip_version=s_ip_version_list[k], + ip_ihl=s_ip_ihl_list[k], + ip_dscp=s_ip_dscp_list[k], + ip_ecn=s_ip_ecn_list[k], + ip_length=s_ip_length_list[k], + ip_identification=s_ip_identification_list[k], + ip_flags=s_ip_flags_list[k], + ip_fragment_offset=s_ip_fragment_offset_list[k], + ip_ttl=s_ip_ttl_list[k], + ip_protocol=s_ip_protocol_list[k], + ip_header_checksum=s_ip_header_checksum_list[k], + ip_source_ip=s_ip_source_ip_list[k], + ip_dest_ip=s_ip_dest_ip_list[k], + udp_source_port=s_udp_source_port_list[k], + udp_dest_port=s_udp_dest_port_list[k], + udp_length=s_udp_length_list[k], + udp_checksum=s_udp_checksum_list[k], + udp_payload_tdata=s_udp_payload_axis_tdata_list[k], + udp_payload_tkeep=s_udp_payload_axis_tkeep_list[k], + udp_payload_tvalid=s_udp_payload_axis_tvalid_list[k], + udp_payload_tready=s_udp_payload_axis_tready_list[k], + udp_payload_tlast=s_udp_payload_axis_tlast_list[k], + udp_payload_tuser=s_udp_payload_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + sink = udp_ep.UDPFrameSink() + + sink_logic = sink.create_logic( + clk, + rst, + udp_hdr_ready=m_udp_hdr_ready, + udp_hdr_valid=m_udp_hdr_valid, + eth_dest_mac=m_eth_dest_mac, + eth_src_mac=m_eth_src_mac, + eth_type=m_eth_type, + ip_version=m_ip_version, + ip_ihl=m_ip_ihl, + ip_dscp=m_ip_dscp, + ip_ecn=m_ip_ecn, + ip_length=m_ip_length, + ip_identification=m_ip_identification, + ip_flags=m_ip_flags, + ip_fragment_offset=m_ip_fragment_offset, + ip_ttl=m_ip_ttl, + ip_protocol=m_ip_protocol, + ip_header_checksum=m_ip_header_checksum, + ip_source_ip=m_ip_source_ip, + ip_dest_ip=m_ip_dest_ip, + udp_source_port=m_udp_source_port, + udp_dest_port=m_udp_dest_port, + udp_length=m_udp_length, + udp_checksum=m_udp_checksum, + udp_payload_tdata=m_udp_payload_axis_tdata, + udp_payload_tkeep=m_udp_payload_axis_tkeep, + udp_payload_tvalid=m_udp_payload_axis_tvalid, + udp_payload_tready=m_udp_payload_axis_tready, + udp_payload_tlast=m_udp_payload_axis_tlast, + udp_payload_tuser=m_udp_payload_axis_tuser, + pause=sink_pause, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_udp_hdr_valid=s_udp_hdr_valid, + s_udp_hdr_ready=s_udp_hdr_ready, + s_eth_dest_mac=s_eth_dest_mac, + s_eth_src_mac=s_eth_src_mac, + s_eth_type=s_eth_type, + s_ip_version=s_ip_version, + s_ip_ihl=s_ip_ihl, + s_ip_dscp=s_ip_dscp, + s_ip_ecn=s_ip_ecn, + s_ip_length=s_ip_length, + s_ip_identification=s_ip_identification, + s_ip_flags=s_ip_flags, + s_ip_fragment_offset=s_ip_fragment_offset, + s_ip_ttl=s_ip_ttl, + s_ip_protocol=s_ip_protocol, + s_ip_header_checksum=s_ip_header_checksum, + s_ip_source_ip=s_ip_source_ip, + s_ip_dest_ip=s_ip_dest_ip, + s_udp_source_port=s_udp_source_port, + s_udp_dest_port=s_udp_dest_port, + s_udp_length=s_udp_length, + s_udp_checksum=s_udp_checksum, + s_udp_payload_axis_tdata=s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep=s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid=s_udp_payload_axis_tvalid, + s_udp_payload_axis_tready=s_udp_payload_axis_tready, + s_udp_payload_axis_tlast=s_udp_payload_axis_tlast, + s_udp_payload_axis_tid=s_udp_payload_axis_tid, + s_udp_payload_axis_tdest=s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser=s_udp_payload_axis_tuser, + + m_udp_hdr_valid=m_udp_hdr_valid, + m_udp_hdr_ready=m_udp_hdr_ready, + m_eth_dest_mac=m_eth_dest_mac, + m_eth_src_mac=m_eth_src_mac, + m_eth_type=m_eth_type, + m_ip_version=m_ip_version, + m_ip_ihl=m_ip_ihl, + m_ip_dscp=m_ip_dscp, + m_ip_ecn=m_ip_ecn, + m_ip_length=m_ip_length, + m_ip_identification=m_ip_identification, + m_ip_flags=m_ip_flags, + m_ip_fragment_offset=m_ip_fragment_offset, + m_ip_ttl=m_ip_ttl, + m_ip_protocol=m_ip_protocol, + m_ip_header_checksum=m_ip_header_checksum, + m_ip_source_ip=m_ip_source_ip, + m_ip_dest_ip=m_ip_dest_ip, + m_udp_source_port=m_udp_source_port, + m_udp_dest_port=m_udp_dest_port, + m_udp_length=m_udp_length, + m_udp_checksum=m_udp_checksum, + m_udp_payload_axis_tdata=m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep=m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid=m_udp_payload_axis_tvalid, + m_udp_payload_axis_tready=m_udp_payload_axis_tready, + m_udp_payload_axis_tlast=m_udp_payload_axis_tlast, + m_udp_payload_axis_tid=m_udp_payload_axis_tid, + m_udp_payload_axis_tdest=m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser=m_udp_payload_axis_tuser, + + enable=enable, + select=select + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + enable.next = True + + yield clk.posedge + print("test 1: select port 0") + current_test.next = 1 + + select.next = 0 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[0].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: select port 1") + current_test.next = 2 + + select.next = 1 + + test_frame = udp_ep.UDPFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.ip_version = 4 + test_frame.ip_ihl = 5 + test_frame.ip_dscp = 0 + test_frame.ip_ecn = 0 + test_frame.ip_length = None + test_frame.ip_identification = 0 + test_frame.ip_flags = 2 + test_frame.ip_fragment_offset = 0 + test_frame.ip_ttl = 64 + test_frame.ip_protocol = 0x11 + test_frame.ip_header_checksum = None + test_frame.ip_source_ip = 0xc0a80165 + test_frame.ip_dest_ip = 0xc0a80164 + test_frame.udp_source_port = 1 + test_frame.udp_dest_port = 2 + test_frame.udp_length = None + test_frame.udp_checksum = None + test_frame.payload = bytearray(range(32)) + test_frame.build() + + source_list[1].send(test_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 3: back-to-back packets, same port") + current_test.next = 3 + + select.next = 0 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets, different ports") + current_test.next = 4 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_udp_payload_axis_tvalid: + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alterate pause source") + current_test.next = 5 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_udp_payload_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + select.next = 2 + + for k in range(S_COUNT): + source_pause_list[k].next = False + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alterate pause sink") + current_test.next = 6 + + select.next = 1 + + test_frame1 = udp_ep.UDPFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.ip_version = 4 + test_frame1.ip_ihl = 5 + test_frame1.ip_dscp = 0 + test_frame1.ip_ecn = 0 + test_frame1.ip_length = None + test_frame1.ip_identification = 0 + test_frame1.ip_flags = 2 + test_frame1.ip_fragment_offset = 0 + test_frame1.ip_ttl = 64 + test_frame1.ip_protocol = 0x11 + test_frame1.ip_header_checksum = None + test_frame1.ip_source_ip = 0xc0a80165 + test_frame1.ip_dest_ip = 0xc0a80164 + test_frame1.udp_source_port = 1 + test_frame1.udp_dest_port = 2 + test_frame1.udp_length = None + test_frame1.udp_checksum = None + test_frame1.payload = bytearray(range(32)) + test_frame1.build() + test_frame2 = udp_ep.UDPFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.ip_version = 4 + test_frame2.ip_ihl = 5 + test_frame2.ip_dscp = 0 + test_frame2.ip_ecn = 0 + test_frame2.ip_length = None + test_frame2.ip_identification = 0 + test_frame2.ip_flags = 2 + test_frame2.ip_fragment_offset = 0 + test_frame2.ip_ttl = 64 + test_frame2.ip_protocol = 0x11 + test_frame2.ip_header_checksum = None + test_frame2.ip_source_ip = 0xc0a80165 + test_frame2.ip_dest_ip = 0xc0a80164 + test_frame2.udp_source_port = 1 + test_frame2.udp_dest_port = 2 + test_frame2.udp_length = None + test_frame2.udp_checksum = None + test_frame2.payload = bytearray(range(32)) + test_frame2.build() + + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + yield clk.posedge + yield clk.posedge + + while s_udp_payload_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + select.next = 2 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame1 + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame == test_frame2 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/fpga/lib/eth/tb/test_udp_mux_64_4.v b/fpga/lib/eth/tb/test_udp_mux_64_4.v new file mode 100644 index 000000000..710b765db --- /dev/null +++ b/fpga/lib/eth/tb/test_udp_mux_64_4.v @@ -0,0 +1,278 @@ +/* + +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 1ns / 1ps + +/* + * Testbench for udp_mux + */ +module test_udp_mux_64_4; + +// Parameters +parameter S_COUNT = 4; +parameter DATA_WIDTH = 64; +parameter KEEP_ENABLE = (DATA_WIDTH>8); +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_ENABLE = 1; +parameter DEST_WIDTH = 8; +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT-1:0] s_udp_hdr_valid = 0; +reg [S_COUNT*48-1:0] s_eth_dest_mac = 0; +reg [S_COUNT*48-1:0] s_eth_src_mac = 0; +reg [S_COUNT*16-1:0] s_eth_type = 0; +reg [S_COUNT*4-1:0] s_ip_version = 0; +reg [S_COUNT*4-1:0] s_ip_ihl = 0; +reg [S_COUNT*6-1:0] s_ip_dscp = 0; +reg [S_COUNT*2-1:0] s_ip_ecn = 0; +reg [S_COUNT*16-1:0] s_ip_length = 0; +reg [S_COUNT*16-1:0] s_ip_identification = 0; +reg [S_COUNT*3-1:0] s_ip_flags = 0; +reg [S_COUNT*13-1:0] s_ip_fragment_offset = 0; +reg [S_COUNT*8-1:0] s_ip_ttl = 0; +reg [S_COUNT*8-1:0] s_ip_protocol = 0; +reg [S_COUNT*16-1:0] s_ip_header_checksum = 0; +reg [S_COUNT*32-1:0] s_ip_source_ip = 0; +reg [S_COUNT*32-1:0] s_ip_dest_ip = 0; +reg [S_COUNT*16-1:0] s_udp_source_port = 0; +reg [S_COUNT*16-1:0] s_udp_dest_port = 0; +reg [S_COUNT*16-1:0] s_udp_length = 0; +reg [S_COUNT*16-1:0] s_udp_checksum = 0; +reg [S_COUNT*DATA_WIDTH-1:0] s_udp_payload_axis_tdata = 0; +reg [S_COUNT*KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep = 0; +reg [S_COUNT-1:0] s_udp_payload_axis_tvalid = 0; +reg [S_COUNT-1:0] s_udp_payload_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_udp_payload_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_udp_payload_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_udp_payload_axis_tuser = 0; + +reg m_udp_hdr_ready = 0; +reg m_udp_payload_axis_tready = 0; + +reg enable = 0; +reg [1:0] select = 0; + +// Outputs +wire [S_COUNT-1:0] s_udp_hdr_ready; +wire [S_COUNT-1:0] s_udp_payload_axis_tready; + +wire m_udp_hdr_valid; +wire [47:0] m_eth_dest_mac; +wire [47:0] m_eth_src_mac; +wire [15:0] m_eth_type; +wire [3:0] m_ip_version; +wire [3:0] m_ip_ihl; +wire [5:0] m_ip_dscp; +wire [1:0] m_ip_ecn; +wire [15:0] m_ip_length; +wire [15:0] m_ip_identification; +wire [2:0] m_ip_flags; +wire [12:0] m_ip_fragment_offset; +wire [7:0] m_ip_ttl; +wire [7:0] m_ip_protocol; +wire [15:0] m_ip_header_checksum; +wire [31:0] m_ip_source_ip; +wire [31:0] m_ip_dest_ip; +wire [15:0] m_udp_source_port; +wire [15:0] m_udp_dest_port; +wire [15:0] m_udp_length; +wire [15:0] m_udp_checksum; +wire [DATA_WIDTH-1:0] m_udp_payload_axis_tdata; +wire [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep; +wire m_udp_payload_axis_tvalid; +wire m_udp_payload_axis_tlast; +wire [ID_WIDTH-1:0] m_udp_payload_axis_tid; +wire [DEST_WIDTH-1:0] m_udp_payload_axis_tdest; +wire [USER_WIDTH-1:0] m_udp_payload_axis_tuser; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_udp_hdr_valid, + s_eth_dest_mac, + s_eth_src_mac, + s_eth_type, + s_ip_version, + s_ip_ihl, + s_ip_dscp, + s_ip_ecn, + s_ip_length, + s_ip_identification, + s_ip_flags, + s_ip_fragment_offset, + s_ip_ttl, + s_ip_protocol, + s_ip_header_checksum, + s_ip_source_ip, + s_ip_dest_ip, + s_udp_source_port, + s_udp_dest_port, + s_udp_length, + s_udp_checksum, + s_udp_payload_axis_tdata, + s_udp_payload_axis_tkeep, + s_udp_payload_axis_tvalid, + s_udp_payload_axis_tlast, + s_udp_payload_axis_tid, + s_udp_payload_axis_tdest, + s_udp_payload_axis_tuser, + m_udp_hdr_ready, + m_udp_payload_axis_tready, + enable, + select + ); + $to_myhdl( + s_udp_hdr_ready, + s_udp_payload_axis_tready, + m_udp_hdr_valid, + m_eth_dest_mac, + m_eth_src_mac, + m_eth_type, + m_ip_version, + m_ip_ihl, + m_ip_dscp, + m_ip_ecn, + m_ip_length, + m_ip_identification, + m_ip_flags, + m_ip_fragment_offset, + m_ip_ttl, + m_ip_protocol, + m_ip_header_checksum, + m_ip_source_ip, + m_ip_dest_ip, + m_udp_source_port, + m_udp_dest_port, + m_udp_length, + m_udp_checksum, + m_udp_payload_axis_tdata, + m_udp_payload_axis_tkeep, + m_udp_payload_axis_tvalid, + m_udp_payload_axis_tlast, + m_udp_payload_axis_tid, + m_udp_payload_axis_tdest, + m_udp_payload_axis_tuser + ); + + // dump file + $dumpfile("test_udp_mux_64_4.lxt"); + $dumpvars(0, test_udp_mux_64_4); +end + +udp_mux #( + .S_COUNT(S_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + // UDP frame inputs + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tid(s_udp_payload_axis_tid), + .s_udp_payload_axis_tdest(s_udp_payload_axis_tdest), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tid(m_udp_payload_axis_tid), + .m_udp_payload_axis_tdest(m_udp_payload_axis_tdest), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Control + .enable(enable), + .select(select) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_xgmii_baser_dec_64.py b/fpga/lib/eth/tb/test_xgmii_baser_dec_64.py new file mode 100755 index 000000000..bf6a43e94 --- /dev/null +++ b/fpga/lib/eth/tb/test_xgmii_baser_dec_64.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python +""" + +Copyright (c) 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep +import baser_serdes_ep + +module = 'xgmii_baser_dec_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + CTRL_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + encoded_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + encoded_rx_hdr = Signal(intbv(0)[HDR_WIDTH:]) + + # Outputs + xgmii_rxd = Signal(intbv(0)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0)[CTRL_WIDTH:]) + rx_bad_block = Signal(bool(0)) + + # sources and sinks + source = baser_serdes_ep.BaseRSerdesSource() + + source_logic = source.create_logic( + clk, + tx_data=encoded_rx_data, + tx_header=encoded_rx_hdr, + scramble=False, + name='source' + ) + + sink = xgmii_ep.XGMIISink() + + sink_logic = sink.create_logic( + clk, + rst, + rxd=xgmii_rxd, + rxc=xgmii_rxc, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + encoded_rx_data=encoded_rx_data, + encoded_rx_hdr=encoded_rx_hdr, + rx_bad_block=rx_bad_block + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(16,34)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = bytearray(range(payload_len)) + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: errored frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + #assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_xgmii_baser_dec_64.v b/fpga/lib/eth/tb/test_xgmii_baser_dec_64.v new file mode 100644 index 000000000..1ce426e12 --- /dev/null +++ b/fpga/lib/eth/tb/test_xgmii_baser_dec_64.v @@ -0,0 +1,87 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * Testbench for xgmii_baser_dec_64 + */ +module test_xgmii_baser_dec_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] encoded_rx_data = 0; +reg [HDR_WIDTH-1:0] encoded_rx_hdr = 0; + +// Outputs +wire [DATA_WIDTH-1:0] xgmii_rxd; +wire [CTRL_WIDTH-1:0] xgmii_rxc; +wire rx_bad_block; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + encoded_rx_data, + encoded_rx_hdr + ); + $to_myhdl( + xgmii_rxd, + xgmii_rxc, + rx_bad_block + ); + + // dump file + $dumpfile("test_xgmii_baser_dec_64.lxt"); + $dumpvars(0, test_xgmii_baser_dec_64); +end + +xgmii_baser_dec_64 #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .rx_bad_block(rx_bad_block) +); + +endmodule diff --git a/fpga/lib/eth/tb/test_xgmii_baser_enc_64.py b/fpga/lib/eth/tb/test_xgmii_baser_enc_64.py new file mode 100755 index 000000000..a6c72c17d --- /dev/null +++ b/fpga/lib/eth/tb/test_xgmii_baser_enc_64.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +""" + +Copyright (c) 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. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import xgmii_ep +import baser_serdes_ep + +module = 'xgmii_baser_enc_64' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + CTRL_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + xgmii_txd = Signal(intbv(0)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0)[CTRL_WIDTH:]) + + # Outputs + encoded_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + encoded_tx_hdr = Signal(intbv(0)[HDR_WIDTH:]) + + # sources and sinks + source = xgmii_ep.XGMIISource() + + source_logic = source.create_logic( + clk, + rst, + txd=xgmii_txd, + txc=xgmii_txc, + name='source' + ) + + sink = baser_serdes_ep.BaseRSerdesSink() + + sink_logic = sink.create_logic( + clk, + rx_data=encoded_tx_data, + rx_header=encoded_tx_hdr, + scramble=False, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + encoded_tx_data=encoded_tx_data, + encoded_tx_hdr=encoded_tx_hdr + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(16,34)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = bytearray(range(payload_len)) + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: errored frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + #assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/lib/eth/tb/test_xgmii_baser_enc_64.v b/fpga/lib/eth/tb/test_xgmii_baser_enc_64.v new file mode 100644 index 000000000..dff67b5b1 --- /dev/null +++ b/fpga/lib/eth/tb/test_xgmii_baser_enc_64.v @@ -0,0 +1,84 @@ +/* + +Copyright (c) 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 1ns / 1ps + +/* + * Testbench for xgmii_baser_enc_64 + */ +module test_xgmii_baser_enc_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] xgmii_txd = 0; +reg [CTRL_WIDTH-1:0] xgmii_txc = 0; + +// Outputs +wire [DATA_WIDTH-1:0] encoded_tx_data; +wire [HDR_WIDTH-1:0] encoded_tx_hdr; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + xgmii_txd, + xgmii_txc + ); + $to_myhdl( + encoded_tx_data, + encoded_tx_hdr + ); + + // dump file + $dumpfile("test_xgmii_baser_enc_64.lxt"); + $dumpvars(0, test_xgmii_baser_enc_64); +end + +xgmii_baser_enc_64 #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr) +); + +endmodule diff --git a/fpga/lib/eth/tb/udp_ep.py b/fpga/lib/eth/tb/udp_ep.py new file mode 100644 index 000000000..0a3d156c5 --- /dev/null +++ b/fpga/lib/eth/tb/udp_ep.py @@ -0,0 +1,596 @@ +""" + +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. + +""" + +from myhdl import * +import axis_ep +import eth_ep +import ip_ep +import struct + +class UDPFrame(object): + def __init__(self, + payload=b'', + eth_dest_mac=0, + eth_src_mac=0, + eth_type=0, + ip_version=4, + ip_ihl=5, + ip_dscp=0, + ip_ecn=0, + ip_length=None, + ip_identification=0, + ip_flags=2, + ip_fragment_offset=0, + ip_ttl=64, + ip_protocol=0x11, + ip_header_checksum=None, + ip_source_ip=0xc0a80164, + ip_dest_ip=0xc0a80165, + udp_source_port=1, + udp_dest_port=2, + udp_length=None, + udp_checksum=None + ): + + self._payload = axis_ep.AXIStreamFrame() + self.eth_dest_mac = eth_dest_mac + self.eth_src_mac = eth_src_mac + self.eth_type = eth_type + self.ip_version = ip_version + self.ip_ihl = ip_ihl + self.ip_dscp = ip_dscp + self.ip_ecn = ip_ecn + self.ip_length = ip_length + self.ip_identification = ip_identification + self.ip_flags = ip_flags + self.ip_fragment_offset = ip_fragment_offset + self.ip_ttl = ip_ttl + self.ip_protocol = ip_protocol + self.ip_header_checksum = ip_header_checksum + self.ip_source_ip = ip_source_ip + self.ip_dest_ip = ip_dest_ip + self.udp_source_port = udp_source_port + self.udp_dest_port = udp_dest_port + self.udp_length = udp_length + self.udp_checksum = udp_checksum + + if type(payload) is dict: + self.payload = axis_ep.AXIStreamFrame(payload['udp_payload']) + self.eth_dest_mac = payload['eth_dest_mac'] + self.eth_src_mac = payload['eth_src_mac'] + self.eth_type = payload['eth_type'] + self.ip_version = payload['ip_version'] + self.ip_ihl = payload['ip_ihl'] + self.ip_dscp = payload['ip_dscp'] + self.ip_ecn = payload['ip_ecn'] + self.ip_length = payload['ip_length'] + self.ip_identification = payload['ip_identification'] + self.ip_flags = payload['ip_flags'] + self.ip_fragment_offset = payload['ip_fragment_offset'] + self.ip_ttl = payload['ip_ttl'] + self.ip_protocol = payload['ip_protocol'] + self.ip_header_checksum = payload['ip_header_checksum'] + self.ip_source_ip = payload['ip_source_ip'] + self.ip_dest_ip = payload['ip_dest_ip'] + self.udp_source_port = payload['udp_source_port'] + self.udp_dest_port = payload['udp_dest_port'] + self.udp_length = payload['udp_length'] + self.udp_checksum = payload['udp_checksum'] + if type(payload) is bytes: + payload = bytearray(payload) + if type(payload) is bytearray or type(payload) is axis_ep.AXIStreamFrame: + self.payload = axis_ep.AXIStreamFrame(payload) + if type(payload) is UDPFrame: + self.payload = axis_ep.AXIStreamFrame(payload.payload) + self.eth_dest_mac = payload.eth_dest_mac + self.eth_src_mac = payload.eth_src_mac + self.eth_type = payload.eth_type + self.ip_version = payload.ip_version + self.ip_ihl = payload.ip_ihl + self.ip_dscp = payload.ip_dscp + self.ip_ecn = payload.ip_ecn + self.ip_length = payload.ip_length + self.ip_identification = payload.ip_identification + self.ip_flags = payload.ip_flags + self.ip_fragment_offset = payload.ip_fragment_offset + self.ip_ttl = payload.ip_ttl + self.ip_protocol = payload.ip_protocol + self.ip_header_checksum = payload.ip_header_checksum + self.ip_source_ip = payload.ip_source_ip + self.ip_dest_ip = payload.ip_dest_ip + self.udp_source_port = payload.udp_source_port + self.udp_dest_port = payload.udp_dest_port + self.udp_length = payload.udp_length + self.udp_checksum = payload.udp_checksum + + @property + def payload(self): + return self._payload + + @payload.setter + def payload(self, value): + self._payload = axis_ep.AXIStreamFrame(value) + + def update_ip_length(self): + self.ip_length = self.udp_length + 20 + + def update_udp_length(self): + self.udp_length = len(self.payload.data) + 8 + + def update_length(self): + self.update_udp_length() + self.update_ip_length() + + def calc_ip_checksum(self): + cksum = self.ip_version << 12 | self.ip_ihl << 8 | self.ip_dscp << 2 | self.ip_ecn + cksum += self.ip_length + cksum += self.ip_identification + cksum += self.ip_flags << 13 | self.ip_fragment_offset + cksum += self.ip_ttl << 8 | self.ip_protocol + cksum += self.ip_source_ip & 0xffff + cksum += (self.ip_source_ip >> 16) & 0xffff + cksum += self.ip_dest_ip & 0xffff + cksum += (self.ip_dest_ip >> 16) & 0xffff + cksum = (cksum & 0xffff) + (cksum >> 16) + cksum = (cksum & 0xffff) + (cksum >> 16) + return ~cksum & 0xffff + + def calc_udp_checksum(self): + cksum = self.ip_source_ip & 0xffff + cksum += (self.ip_source_ip >> 16) & 0xffff + cksum += self.ip_dest_ip & 0xffff + cksum += (self.ip_dest_ip >> 16) & 0xffff + cksum += self.ip_protocol + cksum += self.udp_length + cksum += self.udp_source_port + cksum += self.udp_dest_port + cksum += self.udp_length + odd = False + for d in self.payload.data: + if odd: + cksum += d + else: + cksum += d << 8 + odd = not odd + cksum = (cksum & 0xffff) + (cksum >> 16) + cksum = (cksum & 0xffff) + (cksum >> 16) + return ~cksum & 0xffff + + def update_ip_checksum(self): + self.ip_header_checksum = self.calc_ip_checksum() + + def update_udp_checksum(self): + self.udp_checksum = self.calc_udp_checksum() + + def update_checksum(self): + self.update_udp_checksum() + self.update_ip_checksum() + + def build(self): + if self.udp_length is None: + self.update_udp_length() + if self.udp_checksum is None: + self.update_udp_checksum() + if self.ip_length is None: + self.update_ip_length() + if self.ip_header_checksum is None: + self.update_ip_checksum() + + def build_axis(self): + return self.build_eth().build_axis() + + def build_eth(self): + return self.build_ip().build_eth() + + def build_ip(self): + self.build() + data = b'' + + data += struct.pack('>H', self.udp_source_port) + data += struct.pack('>H', self.udp_dest_port) + data += struct.pack('>H', self.udp_length) + data += struct.pack('>H', self.udp_checksum) + + data += self.payload.data + + return ip_ep.IPFrame( + data, + self.eth_dest_mac, + self.eth_src_mac, + self.eth_type, + self.ip_version, + self.ip_ihl, + self.ip_dscp, + self.ip_ecn, + self.ip_length, + self.ip_identification, + self.ip_flags, + self.ip_fragment_offset, + self.ip_ttl, + self.ip_protocol, + self.ip_header_checksum, + self.ip_source_ip, + self.ip_dest_ip + ) + + def parse_axis(self, data): + frame = eth_ep.EthFrame() + frame.parse_axis(data) + self.parse_eth(frame) + + def parse_eth(self, data): + frame = ip_ep.IPFrame() + frame.parse_eth(data) + self.parse_ip(frame) + + def parse_ip(self, data): + self.eth_src_mac = data.eth_src_mac + self.eth_dest_mac = data.eth_dest_mac + self.eth_type = data.eth_type + self.ip_version = data.ip_version + self.ip_ihl = data.ip_ihl + self.ip_dscp = data.ip_dscp + self.ip_ecn = data.ip_ecn + self.ip_length = data.ip_length + self.ip_identification = data.ip_identification + self.ip_flags = data.ip_flags + self.ip_fragment_offset = data.ip_fragment_offset + self.ip_ttl = data.ip_ttl + self.ip_protocol = data.ip_protocol + self.ip_header_checksum = data.ip_header_checksum + self.ip_source_ip = data.ip_source_ip + self.ip_dest_ip = data.ip_dest_ip + + self.udp_source_port = struct.unpack('>H', data.payload.data[0:2])[0] + self.udp_dest_port = struct.unpack('>H', data.payload.data[2:4])[0] + self.udp_length = struct.unpack('>H', data.payload.data[4:6])[0] + self.udp_checksum = struct.unpack('>H', data.payload.data[6:8])[0] + + self.payload = axis_ep.AXIStreamFrame(data.payload.data[8:self.udp_length]) + + def __eq__(self, other): + if type(other) is UDPFrame: + return ( + self.eth_src_mac == other.eth_src_mac and + self.eth_dest_mac == other.eth_dest_mac and + self.eth_type == other.eth_type and + self.ip_version == other.ip_version and + self.ip_ihl == other.ip_ihl and + self.ip_dscp == other.ip_dscp and + self.ip_ecn == other.ip_ecn and + self.ip_length == other.ip_length and + self.ip_identification == other.ip_identification and + self.ip_flags == other.ip_flags and + self.ip_fragment_offset == other.ip_fragment_offset and + self.ip_ttl == other.ip_ttl and + self.ip_protocol == other.ip_protocol and + self.ip_header_checksum == other.ip_header_checksum and + self.ip_source_ip == other.ip_source_ip and + self.ip_dest_ip == other.ip_dest_ip and + self.udp_source_port == other.udp_source_port and + self.udp_dest_port == other.udp_dest_port and + self.udp_length == other.udp_length and + self.udp_checksum == other.udp_checksum and + self.payload == other.payload + ) + return False + + def __repr__(self): + return ( + ('UDPFrame(payload=%s, ' % repr(self.payload)) + + ('eth_dest_mac=0x%012x, ' % self.eth_dest_mac) + + ('eth_src_mac=0x%012x, ' % self.eth_src_mac) + + ('eth_type=0x%04x, ' % self.eth_type) + + ('ip_version=%d, ' % self.ip_version) + + ('ip_ihl=%d, ' % self.ip_ihl) + + ('ip_dscp=%d, ' % self.ip_dscp) + + ('ip_ecn=%d, ' % self.ip_ecn) + + ('ip_length=%d, ' % self.ip_length) + + ('ip_identification=%d, ' % self.ip_identification) + + ('ip_flags=%d, ' % self.ip_flags) + + ('ip_fragment_offset=%d, ' % self.ip_fragment_offset) + + ('ip_ttl=%d, ' % self.ip_ttl) + + ('ip_protocol=0x%02x, ' % self.ip_protocol) + + ('ip_header_checksum=0x%x, ' % self.ip_header_checksum) + + ('ip_source_ip=0x%08x, ' % self.ip_source_ip) + + ('ip_dest_ip=0x%08x, ' % self.ip_dest_ip) + + ('udp_source_port=%d, ' % self.udp_source_port) + + ('udp_dest_port=%d, ' % self.udp_dest_port) + + ('udp_length=%d, ' % self.udp_length) + + ('udp_checksum=0x%04x)' % self.udp_checksum) + ) + + +class UDPFrameSource(): + def __init__(self): + self.active = False + self.has_logic = False + self.queue = [] + self.payload_source = axis_ep.AXIStreamSource() + self.header_queue = [] + self.clk = Signal(bool(0)) + + def send(self, frame): + frame = UDPFrame(frame) + if not self.header_queue: + self.header_queue.append(frame) + self.payload_source.send(frame.payload) + else: + self.queue.append(frame) + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def idle(self): + return not self.queue and not self.active and self.payload_source.idle() + + def wait(self): + while not self.idle(): + yield self.clk.posedge + + def create_logic(self, + clk, + rst, + udp_hdr_valid=None, + udp_hdr_ready=None, + eth_dest_mac=Signal(intbv(0)[48:]), + eth_src_mac=Signal(intbv(0)[48:]), + eth_type=Signal(intbv(0)[16:]), + ip_version=Signal(intbv(4)[4:]), + ip_ihl=Signal(intbv(5)[4:]), + ip_dscp=Signal(intbv(0)[6:]), + ip_ecn=Signal(intbv(0)[2:]), + ip_length=Signal(intbv(0)[16:]), + ip_identification=Signal(intbv(0)[16:]), + ip_flags=Signal(intbv(0)[3:]), + ip_fragment_offset=Signal(intbv(0)[13:]), + ip_ttl=Signal(intbv(0)[8:]), + ip_protocol=Signal(intbv(0)[8:]), + ip_header_checksum=Signal(intbv(0)[16:]), + ip_source_ip=Signal(intbv(0)[32:]), + ip_dest_ip=Signal(intbv(0)[32:]), + udp_source_port=(intbv(0)[16:]), + udp_dest_port=(intbv(0)[16:]), + udp_length=(intbv(0)[16:]), + udp_checksum=(intbv(0)[16:]), + udp_payload_tdata=None, + udp_payload_tkeep=Signal(bool(True)), + udp_payload_tvalid=Signal(bool(False)), + udp_payload_tready=Signal(bool(True)), + udp_payload_tlast=Signal(bool(False)), + udp_payload_tuser=Signal(bool(False)), + pause=0, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + self.clk = clk + + udp_payload_source = self.payload_source.create_logic( + clk=clk, + rst=rst, + tdata=udp_payload_tdata, + tkeep=udp_payload_tkeep, + tvalid=udp_payload_tvalid, + tready=udp_payload_tready, + tlast=udp_payload_tlast, + tuser=udp_payload_tuser, + pause=pause, + ) + + @instance + def logic(): + while True: + yield clk.posedge, rst.posedge + + if rst: + udp_hdr_valid.next = False + self.active = False + else: + udp_hdr_valid.next = self.active and (udp_hdr_valid or not pause) + if udp_hdr_ready and udp_hdr_valid: + udp_hdr_valid.next = False + self.active = False + if not self.active and self.header_queue: + frame = self.header_queue.pop(0) + frame.build() + eth_dest_mac.next = frame.eth_dest_mac + eth_src_mac.next = frame.eth_src_mac + eth_type.next = frame.eth_type + ip_version.next = frame.ip_version + ip_ihl.next = frame.ip_ihl + ip_dscp.next = frame.ip_dscp + ip_ecn.next = frame.ip_ecn + ip_length.next = frame.ip_length + ip_identification.next = frame.ip_identification + ip_flags.next = frame.ip_flags + ip_fragment_offset.next = frame.ip_fragment_offset + ip_ttl.next = frame.ip_ttl + ip_protocol.next = frame.ip_protocol + ip_header_checksum.next = frame.ip_header_checksum + ip_source_ip.next = frame.ip_source_ip + ip_dest_ip.next = frame.ip_dest_ip + udp_source_port.next = frame.udp_source_port + udp_dest_port.next = frame.udp_dest_port + udp_length.next = frame.udp_length + udp_checksum.next = frame.udp_checksum + + if name is not None: + print("[%s] Sending frame %s" % (name, repr(frame))) + + udp_hdr_valid.next = not pause + self.active = True + + if self.queue and not self.header_queue: + frame = self.queue.pop(0) + self.header_queue.append(frame) + self.payload_source.send(frame.payload) + + return instances() + + +class UDPFrameSink(): + def __init__(self): + self.has_logic = False + self.queue = [] + self.payload_sink = axis_ep.AXIStreamSink() + self.header_queue = [] + self.sync = Signal(intbv(0)) + + def recv(self): + if self.queue: + return self.queue.pop(0) + return None + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def wait(self, timeout=0): + if self.queue: + return + if timeout: + yield self.sync, delay(timeout) + else: + yield self.sync + + def create_logic(self, + clk, + rst, + udp_hdr_valid=None, + udp_hdr_ready=None, + eth_dest_mac=Signal(intbv(0)[48:]), + eth_src_mac=Signal(intbv(0)[48:]), + eth_type=Signal(intbv(0)[16:]), + ip_version=Signal(intbv(4)[4:]), + ip_ihl=Signal(intbv(5)[4:]), + ip_dscp=Signal(intbv(0)[6:]), + ip_ecn=Signal(intbv(0)[2:]), + ip_length=Signal(intbv(0)[16:]), + ip_identification=Signal(intbv(0)[16:]), + ip_flags=Signal(intbv(0)[3:]), + ip_fragment_offset=Signal(intbv(0)[13:]), + ip_ttl=Signal(intbv(0)[8:]), + ip_protocol=Signal(intbv(0)[8:]), + ip_header_checksum=Signal(intbv(0)[16:]), + ip_source_ip=Signal(intbv(0)[32:]), + ip_dest_ip=Signal(intbv(0)[32:]), + udp_source_port=(intbv(0)[16:]), + udp_dest_port=(intbv(0)[16:]), + udp_length=(intbv(0)[16:]), + udp_checksum=(intbv(0)[16:]), + udp_payload_tdata=None, + udp_payload_tkeep=Signal(bool(True)), + udp_payload_tvalid=Signal(bool(True)), + udp_payload_tready=Signal(bool(True)), + udp_payload_tlast=Signal(bool(True)), + udp_payload_tuser=Signal(bool(False)), + pause=0, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + udp_hdr_ready_int = Signal(bool(False)) + udp_hdr_valid_int = Signal(bool(False)) + udp_payload_pause = Signal(bool(False)) + + udp_payload_sink = self.payload_sink.create_logic( + clk=clk, + rst=rst, + tdata=udp_payload_tdata, + tkeep=udp_payload_tkeep, + tvalid=udp_payload_tvalid, + tready=udp_payload_tready, + tlast=udp_payload_tlast, + tuser=udp_payload_tuser, + pause=udp_payload_pause + ) + + @always_comb + def pause_logic(): + udp_hdr_ready.next = udp_hdr_ready_int and not pause + udp_hdr_valid_int.next = udp_hdr_valid and not pause + udp_payload_pause.next = pause # or udp_hdr_valid_int + + @instance + def logic(): + while True: + yield clk.posedge, rst.posedge + + if rst: + udp_hdr_ready_int.next = False + frame = UDPFrame() + else: + udp_hdr_ready_int.next = True + + if udp_hdr_ready_int and udp_hdr_valid_int: + frame = UDPFrame() + frame.eth_dest_mac = int(eth_dest_mac) + frame.eth_src_mac = int(eth_src_mac) + frame.eth_type = int(eth_type) + frame.ip_version = int(ip_version) + frame.ip_ihl = int(ip_ihl) + frame.ip_dscp = int(ip_dscp) + frame.ip_ecn = int(ip_ecn) + frame.ip_length = int(ip_length) + frame.ip_identification = int(ip_identification) + frame.ip_flags = int(ip_flags) + frame.ip_fragment_offset = int(ip_fragment_offset) + frame.ip_ttl = int(ip_ttl) + frame.ip_protocol = int(ip_protocol) + frame.ip_header_checksum = int(ip_header_checksum) + frame.ip_source_ip = int(ip_source_ip) + frame.ip_dest_ip = int(ip_dest_ip) + frame.udp_source_port = int(udp_source_port) + frame.udp_dest_port = int(udp_dest_port) + frame.udp_length = int(udp_length) + frame.udp_checksum = int(udp_checksum) + self.header_queue.append(frame) + + if not self.payload_sink.empty() and self.header_queue: + frame = self.header_queue.pop(0) + frame.payload = self.payload_sink.recv() + self.queue.append(frame) + self.sync.next = not self.sync + + if name is not None: + print("[%s] Got frame %s" % (name, repr(frame))) + + # ensure all payloads have been matched to headers + if len(self.header_queue) == 0: + assert self.payload_sink.empty() + + return instances() + diff --git a/fpga/lib/eth/tb/xgmii_ep.py b/fpga/lib/eth/tb/xgmii_ep.py new file mode 100644 index 000000000..6191cb32a --- /dev/null +++ b/fpga/lib/eth/tb/xgmii_ep.py @@ -0,0 +1,339 @@ +""" + +Copyright (c) 2015-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. + +""" + +from myhdl import * + +ETH_PRE = 0x55 +ETH_SFD = 0xD5 + +XGMII_IDLE = 0x07 +XGMII_LPI = 0x06 +XGMII_START = 0xfb +XGMII_TERM = 0xfd +XGMII_ERROR = 0xfe +XGMII_SEQ_OS = 0x9c +XGMII_RES0 = 0x1c +XGMII_RES1 = 0x3c +XGMII_RES2 = 0x7c +XGMII_RES3 = 0xbc +XGMII_RES4 = 0xdc +XGMII_RES5 = 0xf7 +XGMII_SIG_OS = 0x5c + +class XGMIIFrame(object): + def __init__(self, data=b'', error=None, ctrl=None): + self.data = b'' + self.error = None + self.ctrl = None + + if type(data) is XGMIIFrame: + self.data = data.data + self.error = data.error + self.ctrl = data.ctrl + else: + self.data = bytearray(data) + + def build(self): + if self.data is None: + return + + f = list(self.data) + ctrl = [] + error = [] + d = [] + c = [] + i = 0 + + assert_error = False + if (type(self.error) is int or type(self.error) is bool) and self.error: + assert_error = True + error = [0]*len(self.data) + error[-1] = 1 + elif self.error is None: + error = [0]*len(self.data) + else: + error = list(self.error) + + if self.ctrl is None: + ctrl = [0]*len(self.data) + else: + ctrl = list(self.ctrl) + + assert len(ctrl) == len(f) + assert len(ctrl) == len(f) + + for i in range(len(f)): + if error[i]: + f[i] = XGMII_ERROR + ctrl[i] = 1 + + i = 0 + while len(f) > 0: + d.append(f.pop(0)) + c.append(ctrl[i]) + i += 1 + + return d, c + + def parse(self, d, c): + if d is None or c is None: + return + + self.data = bytearray(d) + self.ctrl = c + + self.error = [0]*len(self.data) + + for i in range(len(self.data)): + if c[i] and d[i] == XGMII_ERROR: + self.error[i] = 1 + + def __eq__(self, other): + if type(other) is XGMIIFrame: + return self.data == other.data + + def __repr__(self): + return 'XGMIIFrame(data=%s, error=%s, ctrl=%s)' % (repr(self.data), repr(self.error), repr(self.ctrl)) + + def __iter__(self): + return self.data.__iter__() + + +class XGMIISource(object): + def __init__(self, ifg=12, enable_dic=True): + self.has_logic = False + self.queue = [] + self.ifg = ifg + self.enable_dic = enable_dic + self.force_offset_start = False + + def send(self, frame): + self.queue.append(XGMIIFrame(frame)) + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def create_logic(self, + clk, + rst, + txd, + txc, + enable=True, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + assert len(txd) in [32, 64] + assert len(txd) == len(txc)*8 + + bw = int(len(txd)/8) + + @instance + def logic(): + frame = None + dl = [] + cl = [] + ifg_cnt = 0 + deficit_idle_cnt = 0 + + while True: + yield clk.posedge, rst.posedge + + if rst: + frame = None + txd.next = 0x0707070707070707 if bw == 8 else 0x07070707 + txc.next = 0xff if bw == 8 else 0xf + dl = [] + cl = [] + ifg_cnt = 0 + deficit_idle_cnt = 0 + elif enable: + if ifg_cnt > bw-1 or (not self.enable_dic and ifg_cnt > 0): + ifg_cnt = max(ifg_cnt - bw, 0) + txd.next = 0x0707070707070707 if bw == 8 else 0x07070707 + txc.next = 0xff if bw == 8 else 0xf + elif dl: + d = 0 + c = 0 + + for i in range(bw): + if dl: + d |= dl.pop(0) << (8*i) + c |= cl.pop(0) << i + if not dl: + ifg_cnt = self.ifg - (bw-i) + deficit_idle_cnt + else: + d |= XGMII_IDLE << (8*i) + c |= 1 << i + + txd.next = d + txc.next = c + elif self.queue: + frame = self.queue.pop(0) + dl, cl = frame.build() + if name is not None: + print("[%s] Sending frame %s" % (name, repr(frame))) + + assert len(dl) > 0 + assert dl[0] == ETH_PRE + dl[0] = XGMII_START + cl[0] = 1 + dl.append(XGMII_TERM) + cl.append(1) + + if (bw == 8 and ifg_cnt >= 4) or self.force_offset_start: + ifg_cnt = max(ifg_cnt-4, 0) + dl = [XGMII_IDLE]*4+dl + cl = [1]*4+cl + + deficit_idle_cnt = max(ifg_cnt, 0) + ifg_cnt = 0 + + d = 0 + c = 0 + + for i in range(0,bw): + if dl: + d |= dl.pop(0) << (8*i) + c |= cl.pop(0) << i + if not dl: + ifg_cnt = self.ifg - (bw-i) + deficit_idle_cnt + else: + d |= XGMII_IDLE << (8*i) + c |= 1 << i + + txd.next = d + txc.next = c + else: + ifg_cnt = 0 + deficit_idle_cnt = 0 + txd.next = 0x0707070707070707 if bw == 8 else 0x07070707 + txc.next = 0xff if bw == 8 else 0xf + + return instances() + + +class XGMIISink(object): + def __init__(self): + self.has_logic = False + self.queue = [] + self.sync = Signal(intbv(0)) + + def recv(self): + if self.queue: + return self.queue.pop(0) + return None + + def count(self): + return len(self.queue) + + def empty(self): + return not self.queue + + def wait(self, timeout=0): + if self.queue: + return + if timeout: + yield self.sync, delay(timeout) + else: + yield self.sync + + def create_logic(self, + clk, + rst, + rxd, + rxc, + enable=True, + name=None + ): + + assert not self.has_logic + + self.has_logic = True + + assert len(rxd) in [32, 64] + assert len(rxd) == len(rxc)*8 + + bw = int(len(rxd)/8) + + @instance + def logic(): + frame = None + d = [] + c = [] + + while True: + yield clk.posedge, rst.posedge + + if rst: + frame = None + d = [] + c = [] + elif enable: + if frame is None: + if rxc & 1 and rxd & 0xff == XGMII_START: + # start in lane 0 + frame = XGMIIFrame() + d = [ETH_PRE] + c = [0] + for i in range(1,bw): + d.append((int(rxd) >> (8*i)) & 0xff) + c.append((int(rxc) >> i) & 1) + elif bw == 8 and (rxc >> 4) & 1 and (rxd >> 32) & 0xff == XGMII_START: + # start in lane 4 + frame = XGMIIFrame() + d = [ETH_PRE] + c = [0] + for i in range(5,bw): + d.append((int(rxd) >> (8*i)) & 0xff) + c.append((int(rxc) >> i) & 1) + else: + for i in range(bw): + if (rxc >> i) & 1: + # got a control character; terminate frame reception + if (rxd >> (8*i)) & 0xff != XGMII_TERM: + # store control character if it's not a termination + d.append((int(rxd) >> (8*i)) & 0xff) + c.append((int(rxc) >> i) & 1) + frame.parse(d, c) + self.queue.append(frame) + self.sync.next = not self.sync + if name is not None: + print("[%s] Got frame %s" % (name, repr(frame))) + frame = None + d = [] + c = [] + break + else: + d.append((int(rxd) >> (8*i)) & 0xff) + c.append((int(rxc) >> i) & 1) + + return instances() +