diff --git a/example/VC709/fpga/Makefile b/example/VC709/fpga/Makefile new file mode 100644 index 000000000..3a968e304 --- /dev/null +++ b/example/VC709/fpga/Makefile @@ -0,0 +1,22 @@ +# 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) diff --git a/example/VC709/fpga/README.md b/example/VC709/fpga/README.md new file mode 100644 index 000000000..e6f0cba76 --- /dev/null +++ b/example/VC709/fpga/README.md @@ -0,0 +1,25 @@ +# Verilog Ethernet NetFPGA SUME Example Design + +## Introduction + +This example design targets the NetFPGA SUME 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: xc7vx690tffg1761-3 +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 NetFPGA SUME 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/example/VC709/fpga/common/vivado.mk b/example/VC709/fpga/common/vivado.mk new file mode 100644 index 000000000..ee83637e0 --- /dev/null +++ b/example/VC709/fpga/common/vivado.mk @@ -0,0 +1,123 @@ +################################################################### +# +# Xilinx Vivado FPGA Makefile +# +# Copyright (c) 2016 Alex Forencich +# +################################################################### +# +# Parameters: +# FPGA_TOP - Top module name +# FPGA_FAMILY - FPGA family (e.g. VirtexUltrascale) +# FPGA_DEVICE - FPGA device (e.g. xcvu095-ffva2104-2-e) +# SYN_FILES - space-separated list of source files +# INC_FILES - space-separated list of include files +# XDC_FILES - space-separated list of timing constraint files +# XCI_FILES - space-separated list of IP XCI files +# +# Example: +# +# FPGA_TOP = fpga +# FPGA_FAMILY = VirtexUltrascale +# FPGA_DEVICE = xcvu095-ffva2104-2-e +# SYN_FILES = rtl/fpga.v +# XDC_FILES = fpga.xdc +# XCI_FILES = ip/pcspma.xci +# include ../common/vivado.mk +# +################################################################### + +# phony targets +.PHONY: clean fpga + +# prevent make from deleting intermediate files and reports +.PRECIOUS: %.xpr %.bit %.mcs %.prm +.SECONDARY: + +CONFIG ?= config.mk +-include ../$(CONFIG) + +SYN_FILES_REL = $(patsubst %, ../%, $(SYN_FILES)) +INC_FILES_REL = $(patsubst %, ../%, $(INC_FILES)) +XCI_FILES_REL = $(patsubst %, ../%, $(XCI_FILES)) +IP_TCL_FILES_REL = $(patsubst %, ../%, $(IP_TCL_FILES)) + +ifdef XDC_FILES + XDC_FILES_REL = $(patsubst %, ../%, $(XDC_FILES)) +else + XDC_FILES_REL = $(FPGA_TOP).xdc +endif + +################################################################### +# Main Targets +# +# all: build everything +# clean: remove output files and project files +################################################################### + +all: fpga + +fpga: $(FPGA_TOP).bit + +vivado: $(FPGA_TOP).xpr + vivado $(FPGA_TOP).xpr + +tmpclean: + -rm -rf *.log *.jou *.cache *.hbs *.hw *.ip_user_files *.runs *.xpr *.html *.xml *.sim *.srcs *.str .Xil defines.v + -rm -rf create_project.tcl run_synth.tcl run_impl.tcl generate_bit.tcl + +clean: tmpclean + -rm -rf *.bit program.tcl generate_mcs.tcl *.mcs *.prm flash.tcl + +distclean: clean + -rm -rf rev + +################################################################### +# Target implementations +################################################################### + +# Vivado project file +%.xpr: Makefile $(XCI_FILES_REL) $(IP_TCL_FILES_REL) + rm -rf defines.v + touch defines.v + for x in $(DEFS); do echo '`define' $$x >> defines.v; done + echo "create_project -force -part $(FPGA_PART) $*" > create_project.tcl + echo "add_files -fileset sources_1 defines.v" >> create_project.tcl + for x in $(SYN_FILES_REL); do echo "add_files -fileset sources_1 $$x" >> create_project.tcl; done + for x in $(XDC_FILES_REL); do echo "add_files -fileset constrs_1 $$x" >> create_project.tcl; done + for x in $(XCI_FILES_REL); do echo "import_ip $$x" >> create_project.tcl; done + for x in $(IP_TCL_FILES_REL); do echo "source $$x" >> create_project.tcl; done + echo "exit" >> create_project.tcl + vivado -nojournal -nolog -mode batch -source create_project.tcl + +# synthesis run +%.runs/synth_1/%.dcp: %.xpr $(SYN_FILES_REL) $(INC_FILES_REL) $(XDC_FILES_REL) + echo "open_project $*.xpr" > run_synth.tcl + echo "reset_run synth_1" >> run_synth.tcl + echo "launch_runs synth_1" >> run_synth.tcl + echo "wait_on_run synth_1" >> run_synth.tcl + echo "exit" >> run_synth.tcl + vivado -nojournal -nolog -mode batch -source run_synth.tcl + +# implementation run +%.runs/impl_1/%_routed.dcp: %.runs/synth_1/%.dcp + echo "open_project $*.xpr" > run_impl.tcl + echo "reset_run impl_1" >> run_impl.tcl + echo "launch_runs impl_1" >> run_impl.tcl + echo "wait_on_run impl_1" >> run_impl.tcl + echo "exit" >> run_impl.tcl + vivado -nojournal -nolog -mode batch -source run_impl.tcl + +# bit file +%.bit: %.runs/impl_1/%_routed.dcp + echo "open_project $*.xpr" > generate_bit.tcl + echo "open_run impl_1" >> generate_bit.tcl + echo "write_bitstream -force $*.bit" >> generate_bit.tcl + echo "exit" >> generate_bit.tcl + vivado -nojournal -nolog -mode batch -source generate_bit.tcl + mkdir -p rev + EXT=bit; COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.$$EXT ]; \ + do COUNT=$$((COUNT+1)); done; \ + cp $@ rev/$*_rev$$COUNT.$$EXT; \ + echo "Output: rev/$*_rev$$COUNT.$$EXT"; diff --git a/example/VC709/fpga/fpga.xdc b/example/VC709/fpga/fpga.xdc new file mode 100644 index 000000000..b07af5bf7 --- /dev/null +++ b/example/VC709/fpga/fpga.xdc @@ -0,0 +1,72 @@ +# XDC constraints for the Xilinx VC709 +# part: xc7vx690tffg1761-2 + +# General configuration +set_property CFGBVS GND [current_design] +set_property CONFIG_VOLTAGE 1.8 [current_design] +set_property BITSTREAM.CONFIG.BPI_SYNC_MODE Type1 [current_design] +set_property BITSTREAM.CONFIG.EXTMASTERCCLK_EN div-1 [current_design] +set_property BITSTREAM.GENERAL.COMPRESS true [current_design] +set_property BITSTREAM.CONFIG.UNUSEDPIN Pulldown [current_design] +set_property CONFIG_MODE BPI16 [current_design] + +# 200 MHz system clock +set_property -dict {LOC H19 IOSTANDARD LVDS} [get_ports clk_200mhz_p] +set_property -dict {LOC G18 IOSTANDARD LVDS} [get_ports clk_200mhz_n] +create_clock -period 5 -name clk_200mhz [get_ports clk_200mhz_p] + +# LEDs 0-7 +set_property -dict {LOC AM39 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 4} [get_ports {led[0]}] +set_property -dict {LOC AN39 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 4} [get_ports {led[1]}] +set_property -dict {LOC AR37 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 4} [get_ports {led[2]}] +set_property -dict {LOC AT37 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 4} [get_ports {led[3]}] +set_property -dict {LOC AR35 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 4} [get_ports {led[4]}] +set_property -dict {LOC AP41 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 4} [get_ports {led[5]}] +set_property -dict {LOC AP42 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 4} [get_ports {led[6]}] +set_property -dict {LOC AU39 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 4} [get_ports {led[7]}] + +# Push buttons +set_property -dict {LOC AU38 IOSTANDARD LVCMOS18} [get_ports {btn[0]}] +set_property -dict {LOC AW40 IOSTANDARD LVCMOS18} [get_ports {btn[1]}] + +# SFP+ Interfaces +set_property -dict {LOC AM8 } [get_ports sfp_1_rx_p] +set_property -dict {LOC AN2 } [get_ports sfp_1_tx_p] +set_property -dict {LOC AN6 } [get_ports sfp_2_rx_p] +set_property -dict {LOC AP4 } [get_ports sfp_2_tx_p] +set_property -dict {LOC AL6 } [get_ports sfp_3_rx_p] +set_property -dict {LOC AM4 } [get_ports sfp_3_tx_p] +set_property -dict {LOC AJ6 } [get_ports sfp_4_rx_p] +set_property -dict {LOC AL2 } [get_ports sfp_4_tx_p] +set_property -dict {LOC AH8 } [get_ports sfp_mgt_refclk_p] +set_property -dict {LOC AH7 } [get_ports sfp_mgt_refclk_n] +set_property -dict {LOC AT36 IOSTANDARD LVCMOS18} [get_ports sfp_clk_rst_n] +set_property -dict {LOC AA42 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_1_mod_detect] +set_property -dict {LOC AB42 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_2_mod_detect] +set_property -dict {LOC AC39 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_3_mod_detect] +set_property -dict {LOC AC41 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_4_mod_detect] +set_property -dict {LOC W40 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_1_rs[0]}] +set_property -dict {LOC Y40 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_1_rs[1]}] +set_property -dict {LOC AB38 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_2_rs[0]}] +set_property -dict {LOC AB39 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_2_rs[1]}] +set_property -dict {LOC AD42 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_3_rs[0]}] +set_property -dict {LOC AE42 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_3_rs[1]}] +set_property -dict {LOC AE39 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_4_rs[0]}] +set_property -dict {LOC AE40 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports {sfp_4_rs[1]}] +set_property -dict {LOC AA40 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_1_los] +set_property -dict {LOC Y39 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_2_los] +set_property -dict {LOC AD38 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_3_los] +set_property -dict {LOC AD40 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_4_los] +set_property -dict {LOC Y42 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports sfp_1_tx_disable] +set_property -dict {LOC AB41 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports sfp_2_tx_disable] +set_property -dict {LOC AC38 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports sfp_3_tx_disable] +set_property -dict {LOC AC40 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports sfp_4_tx_disable] +set_property -dict {LOC AA39 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_1_tx_fault] +set_property -dict {LOC Y38 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_2_tx_fault] +set_property -dict {LOC AA41 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_3_tx_fault] +set_property -dict {LOC AE38 IOSTANDARD LVCMOS18 PULLUP true} [get_ports sfp_4_tx_fault] + +# I2C interface +set_property -dict {LOC AT35 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports i2c_scl] +set_property -dict {LOC AU32 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12 PULLUP true} [get_ports i2c_sda] +set_property -dict {LOC AY42 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 12} [get_ports i2c_mux_reset_n] diff --git a/example/VC709/fpga/fpga/Makefile b/example/VC709/fpga/fpga/Makefile new file mode 100644 index 000000000..a63f25adb --- /dev/null +++ b/example/VC709/fpga/fpga/Makefile @@ -0,0 +1,72 @@ +# FPGA settings +FPGA_PART = xc7vx690tffg1761-2 +FPGA_TOP = fpga +FPGA_ARCH = virtex7 + +# Files for synthesis +SYN_FILES = rtl/fpga.v +SYN_FILES += rtl/fpga_core.v +SYN_FILES += rtl/debounce_switch.v +SYN_FILES += rtl/i2c_master.v +SYN_FILES += rtl/si5324_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/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.v +SYN_FILES += lib/eth/rtl/eth_axis_tx.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.v +SYN_FILES += lib/eth/rtl/arp_cache.v +SYN_FILES += lib/eth/rtl/arp_eth_rx.v +SYN_FILES += lib/eth/rtl/arp_eth_tx.v +SYN_FILES += lib/eth/rtl/eth_arb_mux.v +SYN_FILES += lib/eth/lib/axis/rtl/arbiter.v +SYN_FILES += lib/eth/lib/axis/rtl/priority_encoder.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_fifo.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_async_fifo.v +SYN_FILES += lib/eth/lib/axis/rtl/axis_async_fifo_adapter.v +SYN_FILES += lib/eth/lib/axis/rtl/sync_reset.v + +# XDC files +XDC_FILES = fpga.xdc +XDC_FILES += lib/eth/syn/eth_mac_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/axis_async_fifo.tcl +XDC_FILES += lib/eth/lib/axis/syn/sync_reset.tcl + +# IP +IP_TCL_FILES = ip/ten_gig_eth_pcs_pma_0.tcl +IP_TCL_FILES += ip/ten_gig_eth_pcs_pma_1.tcl + +include ../common/vivado.mk + +program: $(FPGA_TOP).bit + echo "open_hw" > program.tcl + echo "connect_hw_server" >> program.tcl + echo "open_hw_target" >> program.tcl + echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl + echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl + echo "set_property PROGRAM.FILE {$(FPGA_TOP).bit} [current_hw_device]" >> program.tcl + echo "program_hw_devices [current_hw_device]" >> program.tcl + echo "exit" >> program.tcl + vivado -nojournal -nolog -mode batch -source program.tcl + diff --git a/example/VC709/fpga/ip/ten_gig_eth_pcs_pma_0.tcl b/example/VC709/fpga/ip/ten_gig_eth_pcs_pma_0.tcl new file mode 100644 index 000000000..d8359592e --- /dev/null +++ b/example/VC709/fpga/ip/ten_gig_eth_pcs_pma_0.tcl @@ -0,0 +1,9 @@ + +create_ip -name ten_gig_eth_pcs_pma -vendor xilinx.com -library ip -module_name ten_gig_eth_pcs_pma_0 + +set_property -dict [list \ + CONFIG.MDIO_Management {false} \ + CONFIG.base_kr {BASE-R} \ + CONFIG.SupportLevel {1} \ + CONFIG.DClkRate {125} \ +] [get_ips ten_gig_eth_pcs_pma_0] diff --git a/example/VC709/fpga/ip/ten_gig_eth_pcs_pma_1.tcl b/example/VC709/fpga/ip/ten_gig_eth_pcs_pma_1.tcl new file mode 100644 index 000000000..448e42ac4 --- /dev/null +++ b/example/VC709/fpga/ip/ten_gig_eth_pcs_pma_1.tcl @@ -0,0 +1,9 @@ + +create_ip -name ten_gig_eth_pcs_pma -vendor xilinx.com -library ip -module_name ten_gig_eth_pcs_pma_1 + +set_property -dict [list \ + CONFIG.MDIO_Management {false} \ + CONFIG.base_kr {BASE-R} \ + CONFIG.SupportLevel {0} \ + CONFIG.DClkRate {125} \ +] [get_ips ten_gig_eth_pcs_pma_1] diff --git a/example/VC709/fpga/lib/eth b/example/VC709/fpga/lib/eth new file mode 120000 index 000000000..11a54ed36 --- /dev/null +++ b/example/VC709/fpga/lib/eth @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/example/VC709/fpga/rtl/debounce_switch.v b/example/VC709/fpga/rtl/debounce_switch.v new file mode 100644 index 000000000..bb631cc35 --- /dev/null +++ b/example/VC709/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/example/VC709/fpga/rtl/fpga.v b/example/VC709/fpga/rtl/fpga.v new file mode 100644 index 000000000..359f8e3a8 --- /dev/null +++ b/example/VC709/fpga/rtl/fpga.v @@ -0,0 +1,687 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps +`default_nettype none + +/* + * FPGA top-level module + */ +module fpga ( + /* + * Clock: 200MHz LVDS + */ + input wire clk_200mhz_p, + input wire clk_200mhz_n, + + /* + * GPIO + */ + output wire [7:0] led, + + /* + * I2C + */ + inout wire i2c_scl, + inout wire i2c_sda, + output wire i2c_mux_reset_n, + + /* + * 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_3_rx_p, + input wire sfp_3_rx_n, + output wire sfp_3_tx_p, + output wire sfp_3_tx_n, + input wire sfp_4_rx_p, + input wire sfp_4_rx_n, + output wire sfp_4_tx_p, + output wire sfp_4_tx_n, + input wire sfp_mgt_refclk_p, + input wire sfp_mgt_refclk_n, + output wire sfp_clk_rst_n, + input wire sfp_1_mod_detect, + input wire sfp_2_mod_detect, + input wire sfp_3_mod_detect, + input wire sfp_4_mod_detect, + output wire [1:0] sfp_1_rs, + output wire [1:0] sfp_2_rs, + output wire [1:0] sfp_3_rs, + output wire [1:0] sfp_4_rs, + input wire sfp_1_los, + input wire sfp_2_los, + input wire sfp_3_los, + input wire sfp_4_los, + output wire sfp_1_tx_disable, + output wire sfp_2_tx_disable, + output wire sfp_3_tx_disable, + output wire sfp_4_tx_disable, + input wire sfp_1_tx_fault, + input wire sfp_2_tx_fault, + input wire sfp_3_tx_fault, + input wire sfp_4_tx_fault +); + +// Clock and reset + +wire clk_200mhz_ibufg; + +// Internal 125 MHz clock +wire clk_125mhz_mmcm_out; +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_200mhz_ibufg_inst ( + .O (clk_200mhz_ibufg), + .I (clk_200mhz_p), + .IB (clk_200mhz_n) +); + +// 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(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(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_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), + .out(rst_125mhz_int) +); + +// I2C +wire i2c_scl_i, i2c_scl_o, i2c_scl_t; +wire i2c_sda_i, i2c_sda_o, i2c_sda_t; + +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; + +wire [6:0] si5324_i2c_cmd_address; +wire si5324_i2c_cmd_start; +wire si5324_i2c_cmd_read; +wire si5324_i2c_cmd_write; +wire si5324_i2c_cmd_write_multiple; +wire si5324_i2c_cmd_stop; +wire si5324_i2c_cmd_valid; +wire si5324_i2c_cmd_ready; + +wire [7:0] si5324_i2c_data; +wire si5324_i2c_data_valid; +wire si5324_i2c_data_ready; +wire si5324_i2c_data_last; + +wire si5324_i2c_init_busy; + +assign i2c_mux_reset_n = !rst_125mhz_int; +assign sfp_clk_rst_n = !rst_125mhz_int; + +// delay start by ~10 ms +reg [20:0] si5324_i2c_init_start_delay = 21'd0; + +always @(posedge clk_125mhz_int) begin + if (rst_125mhz_int) begin + si5324_i2c_init_start_delay <= 21'd0; + end else begin + if (!si5324_i2c_init_start_delay[20]) begin + si5324_i2c_init_start_delay <= si5324_i2c_init_start_delay + 21'd1; + end + end +end + +si5324_i2c_init +si5324_i2c_init_inst ( + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + .cmd_address(si5324_i2c_cmd_address), + .cmd_start(si5324_i2c_cmd_start), + .cmd_read(si5324_i2c_cmd_read), + .cmd_write(si5324_i2c_cmd_write), + .cmd_write_multiple(si5324_i2c_cmd_write_multiple), + .cmd_stop(si5324_i2c_cmd_stop), + .cmd_valid(si5324_i2c_cmd_valid), + .cmd_ready(si5324_i2c_cmd_ready), + .data_out(si5324_i2c_data), + .data_out_valid(si5324_i2c_data_valid), + .data_out_ready(si5324_i2c_data_ready), + .data_out_last(si5324_i2c_data_last), + .busy(si5324_i2c_init_busy), + .start(si5324_i2c_init_start_delay[20]) +); + +i2c_master +si5324_i2c_master_inst ( + .clk(clk_125mhz_int), + .rst(rst_125mhz_int), + .cmd_address(si5324_i2c_cmd_address), + .cmd_start(si5324_i2c_cmd_start), + .cmd_read(si5324_i2c_cmd_read), + .cmd_write(si5324_i2c_cmd_write), + .cmd_write_multiple(si5324_i2c_cmd_write_multiple), + .cmd_stop(si5324_i2c_cmd_stop), + .cmd_valid(si5324_i2c_cmd_valid), + .cmd_ready(si5324_i2c_cmd_ready), + .data_in(si5324_i2c_data), + .data_in_valid(si5324_i2c_data_valid), + .data_in_ready(si5324_i2c_data_ready), + .data_in_last(si5324_i2c_data_last), + .data_out(), + .data_out_valid(), + .data_out_ready(1), + .data_out_last(), + .scl_i(i2c_scl_i), + .scl_o(i2c_scl_o), + .scl_t(i2c_scl_t), + .sda_i(i2c_sda_i), + .sda_o(i2c_sda_o), + .sda_t(i2c_sda_t), + .busy(), + .bus_control(), + .bus_active(), + .missed_ack(), + .prescale(312), + .stop_on_idle(1) +); + +// XGMII 10G PHY + +assign sfp_1_tx_disable = 1'b0; +assign sfp_2_tx_disable = 1'b0; +assign sfp_3_tx_disable = 1'b0; +assign sfp_4_tx_disable = 1'b0; +assign sfp_1_rs = 1'b1; +assign sfp_2_rs = 1'b1; +assign sfp_3_rs = 1'b1; +assign sfp_4_rs = 1'b1; + +wire sfp_1_tx_clk_int = clk_156mhz_int; +wire sfp_1_tx_rst_int = rst_156mhz_int; +wire [63:0] sfp_1_txd_int; +wire [7:0] sfp_1_txc_int; +wire sfp_1_rx_clk_int = clk_156mhz_int; +wire sfp_1_rx_rst_int = rst_156mhz_int; +wire [63:0] sfp_1_rxd_int; +wire [7:0] sfp_1_rxc_int; +wire sfp_2_tx_clk_int = clk_156mhz_int; +wire sfp_2_tx_rst_int = rst_156mhz_int; +wire [63:0] sfp_2_txd_int; +wire [7:0] sfp_2_txc_int; +wire sfp_2_rx_clk_int = clk_156mhz_int; +wire sfp_2_rx_rst_int = rst_156mhz_int; +wire [63:0] sfp_2_rxd_int; +wire [7:0] sfp_2_rxc_int; +wire sfp_3_tx_clk_int = clk_156mhz_int; +wire sfp_3_tx_rst_int = rst_156mhz_int; +wire [63:0] sfp_3_txd_int; +wire [7:0] sfp_3_txc_int; +wire sfp_3_rx_clk_int = clk_156mhz_int; +wire sfp_3_rx_rst_int = rst_156mhz_int; +wire [63:0] sfp_3_rxd_int; +wire [7:0] sfp_3_rxc_int; +wire sfp_4_tx_clk_int = clk_156mhz_int; +wire sfp_4_tx_rst_int = rst_156mhz_int; +wire [63:0] sfp_4_txd_int; +wire [7:0] sfp_4_txc_int; +wire sfp_4_rx_clk_int = clk_156mhz_int; +wire sfp_4_rx_rst_int = rst_156mhz_int; +wire [63:0] sfp_4_rxd_int; +wire [7:0] sfp_4_rxc_int; + +wire sfp_reset_in; +wire sfp_txusrclk; +wire sfp_txusrclk2; +wire sfp_coreclk; +wire sfp_qplloutclk; +wire sfp_qplloutrefclk; +wire sfp_qplllock; +wire sfp_gttxreset; +wire sfp_gtrxreset; +wire sfp_txuserrdy; +wire sfp_areset_datapathclk; +wire sfp_resetdone; +wire sfp_reset_counter_done; + +sync_reset #( + .N(4) +) +sync_reset_sfp_inst ( + .clk(sfp_coreclk), + .rst(rst_125mhz_int || si5324_i2c_init_busy), + .out(sfp_reset_in) +); + +//assign sfp_reset_in = rst_125mhz_int || si5324_i2c_init_busy; + +assign clk_156mhz_int = sfp_coreclk; + +sync_reset #( + .N(4) +) +sync_reset_156mhz_inst ( + .clk(clk_156mhz_int), + .rst(!sfp_resetdone), + .out(rst_156mhz_int) +); + +wire [535:0] sfp_config_vector; + +assign sfp_config_vector[14:1] = 0; +assign sfp_config_vector[79:17] = 0; +assign sfp_config_vector[109:84] = 0; +assign sfp_config_vector[175:170] = 0; +assign sfp_config_vector[239:234] = 0; +assign sfp_config_vector[269:246] = 0; +assign sfp_config_vector[511:272] = 0; +assign sfp_config_vector[515:513] = 0; +assign sfp_config_vector[517:517] = 0; +assign sfp_config_vector[0] = 0; // pma_loopback; +assign sfp_config_vector[15] = 0; // pma_reset; +assign sfp_config_vector[16] = 0; // global_tx_disable; +assign sfp_config_vector[83:80] = 0; // pma_vs_loopback; +assign sfp_config_vector[110] = 0; // pcs_loopback; +assign sfp_config_vector[111] = 0; // pcs_reset; +assign sfp_config_vector[169:112] = 0; // test_patt_a; +assign sfp_config_vector[233:176] = 0; // test_patt_b; +assign sfp_config_vector[240] = 0; // data_patt_sel; +assign sfp_config_vector[241] = 0; // test_patt_sel; +assign sfp_config_vector[242] = 0; // rx_test_patt_en; +assign sfp_config_vector[243] = 0; // tx_test_patt_en; +assign sfp_config_vector[244] = 0; // prbs31_tx_en; +assign sfp_config_vector[245] = 0; // prbs31_rx_en; +assign sfp_config_vector[271:270] = 0; // pcs_vs_loopback; +assign sfp_config_vector[512] = 0; // set_pma_link_status; +assign sfp_config_vector[516] = 0; // set_pcs_link_status; +assign sfp_config_vector[518] = 0; // clear_pcs_status2; +assign sfp_config_vector[519] = 0; // clear_test_patt_err_count; +assign sfp_config_vector[535:520] = 0; + +wire [447:0] sfp_1_status_vector; +wire [447:0] sfp_2_status_vector; +wire [447:0] sfp_3_status_vector; +wire [447:0] sfp_4_status_vector; + +wire sfp_1_rx_block_lock = sfp_1_status_vector[256]; +wire sfp_2_rx_block_lock = sfp_2_status_vector[256]; +wire sfp_3_rx_block_lock = sfp_3_status_vector[256]; +wire sfp_4_rx_block_lock = sfp_4_status_vector[256]; + +wire [7:0] sfp_1_core_status; +wire [7:0] sfp_2_core_status; +wire [7:0] sfp_3_core_status; +wire [7:0] sfp_4_core_status; + +ten_gig_eth_pcs_pma_0 +sfp_1_pcs_pma_inst ( + .dclk(clk_125mhz_int), + .rxrecclk_out(), + .refclk_p(sfp_mgt_refclk_p), + .refclk_n(sfp_mgt_refclk_n), + .sim_speedup_control(1'b0), + .coreclk_out(sfp_coreclk), + .qplloutclk_out(sfp_qplloutclk), + .qplloutrefclk_out(sfp_qplloutrefclk), + .qplllock_out(sfp_qplllock), + .txusrclk_out(sfp_txusrclk), + .txusrclk2_out(sfp_txusrclk2), + .areset_datapathclk_out(sfp_areset_datapathclk), + .gttxreset_out(sfp_gttxreset), + .gtrxreset_out(sfp_gtrxreset), + .txuserrdy_out(sfp_txuserrdy), + .reset_counter_done_out(sfp_reset_counter_done), + .reset(sfp_reset_in), + .xgmii_txd(sfp_1_txd_int), + .xgmii_txc(sfp_1_txc_int), + .xgmii_rxd(sfp_1_rxd_int), + .xgmii_rxc(sfp_1_rxc_int), + .txp(sfp_1_tx_p), + .txn(sfp_1_tx_n), + .rxp(sfp_1_rx_p), + .rxn(sfp_1_rx_n), + .configuration_vector(sfp_config_vector), + .status_vector(sfp_1_status_vector), + .core_status(sfp_1_core_status), + .resetdone_out(sfp_resetdone), + .signal_detect(1'b1), + .tx_fault(1'b0), + .drp_req(), + .drp_gnt(1'b1), + .drp_den_o(), + .drp_dwe_o(), + .drp_daddr_o(), + .drp_di_o(), + .drp_drdy_o(), + .drp_drpdo_o(), + .drp_den_i(1'b0), + .drp_dwe_i(1'b0), + .drp_daddr_i(16'd0), + .drp_di_i(16'd0), + .drp_drdy_i(1'b0), + .drp_drpdo_i(16'd0), + .pma_pmd_type(3'd0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_1 +sfp_2_pcs_pma_inst ( + .dclk(clk_125mhz_int), + .rxrecclk_out(), + .coreclk(sfp_coreclk), + .txusrclk(sfp_txusrclk), + .txusrclk2(sfp_txusrclk2), + .txoutclk(), + .areset(sfp_reset_in), + .areset_coreclk(sfp_areset_datapathclk), + .gttxreset(sfp_gttxreset), + .gtrxreset(sfp_gtrxreset), + .sim_speedup_control(1'b0), + .txuserrdy(sfp_txuserrdy), + .qplllock(sfp_qplllock), + .qplloutclk(sfp_qplloutclk), + .qplloutrefclk(sfp_qplloutrefclk), + .reset_counter_done(sfp_reset_counter_done), + .xgmii_txd(sfp_2_txd_int), + .xgmii_txc(sfp_2_txc_int), + .xgmii_rxd(sfp_2_rxd_int), + .xgmii_rxc(sfp_2_rxc_int), + .txp(sfp_2_tx_p), + .txn(sfp_2_tx_n), + .rxp(sfp_2_rx_p), + .rxn(sfp_2_rx_n), + .configuration_vector(sfp_config_vector), + .status_vector(sfp_2_status_vector), + .core_status(sfp_2_core_status), + .tx_resetdone(), + .rx_resetdone(), + .signal_detect(1'b1), + .tx_fault(1'b0), + .drp_req(), + .drp_gnt(1'b1), + .drp_den_o(), + .drp_dwe_o(), + .drp_daddr_o(), + .drp_di_o(), + .drp_drdy_o(), + .drp_drpdo_o(), + .drp_den_i(1'b0), + .drp_dwe_i(1'b0), + .drp_daddr_i(16'd0), + .drp_di_i(16'd0), + .drp_drdy_i(1'b0), + .drp_drpdo_i(16'd0), + .pma_pmd_type(3'd0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_1 +sfp_3_pcs_pma_inst ( + .dclk(clk_125mhz_int), + .rxrecclk_out(), + .coreclk(sfp_coreclk), + .txusrclk(sfp_txusrclk), + .txusrclk2(sfp_txusrclk2), + .txoutclk(), + .areset(sfp_reset_in), + .areset_coreclk(sfp_areset_datapathclk), + .gttxreset(sfp_gttxreset), + .gtrxreset(sfp_gtrxreset), + .sim_speedup_control(1'b0), + .txuserrdy(sfp_txuserrdy), + .qplllock(sfp_qplllock), + .qplloutclk(sfp_qplloutclk), + .qplloutrefclk(sfp_qplloutrefclk), + .reset_counter_done(sfp_reset_counter_done), + .xgmii_txd(sfp_3_txd_int), + .xgmii_txc(sfp_3_txc_int), + .xgmii_rxd(sfp_3_rxd_int), + .xgmii_rxc(sfp_3_rxc_int), + .txp(sfp_3_tx_p), + .txn(sfp_3_tx_n), + .rxp(sfp_3_rx_p), + .rxn(sfp_3_rx_n), + .configuration_vector(sfp_config_vector), + .status_vector(sfp_3_status_vector), + .core_status(sfp_3_core_status), + .tx_resetdone(), + .rx_resetdone(), + .signal_detect(1'b1), + .tx_fault(1'b0), + .drp_req(), + .drp_gnt(1'b1), + .drp_den_o(), + .drp_dwe_o(), + .drp_daddr_o(), + .drp_di_o(), + .drp_drdy_o(), + .drp_drpdo_o(), + .drp_den_i(1'b0), + .drp_dwe_i(1'b0), + .drp_daddr_i(16'd0), + .drp_di_i(16'd0), + .drp_drdy_i(1'b0), + .drp_drpdo_i(16'd0), + .pma_pmd_type(3'd0), + .tx_disable() +); + +ten_gig_eth_pcs_pma_1 +sfp_4_pcs_pma_inst ( + .dclk(clk_125mhz_int), + .rxrecclk_out(), + .coreclk(sfp_coreclk), + .txusrclk(sfp_txusrclk), + .txusrclk2(sfp_txusrclk2), + .txoutclk(), + .areset(sfp_reset_in), + .areset_coreclk(sfp_areset_datapathclk), + .gttxreset(sfp_gttxreset), + .gtrxreset(sfp_gtrxreset), + .sim_speedup_control(1'b0), + .txuserrdy(sfp_txuserrdy), + .qplllock(sfp_qplllock), + .qplloutclk(sfp_qplloutclk), + .qplloutrefclk(sfp_qplloutrefclk), + .reset_counter_done(sfp_reset_counter_done), + .xgmii_txd(sfp_4_txd_int), + .xgmii_txc(sfp_4_txc_int), + .xgmii_rxd(sfp_4_rxd_int), + .xgmii_rxc(sfp_4_rxc_int), + .txp(sfp_4_tx_p), + .txn(sfp_4_tx_n), + .rxp(sfp_4_rx_p), + .rxn(sfp_4_rx_n), + .configuration_vector(sfp_config_vector), + .status_vector(sfp_4_status_vector), + .core_status(sfp_4_core_status), + .tx_resetdone(), + .rx_resetdone(), + .signal_detect(1'b1), + .tx_fault(1'b0), + .drp_req(), + .drp_gnt(1'b1), + .drp_den_o(), + .drp_dwe_o(), + .drp_daddr_o(), + .drp_di_o(), + .drp_drdy_o(), + .drp_drpdo_o(), + .drp_den_i(1'b0), + .drp_dwe_i(1'b0), + .drp_daddr_i(16'd0), + .drp_di_i(16'd0), + .drp_drdy_i(1'b0), + .drp_drpdo_i(16'd0), + .pma_pmd_type(3'd0), + .tx_disable() +); + +assign led[0] = sfp_1_rx_block_lock; +assign led[1] = 1'b0; +assign led[2] = sfp_2_rx_block_lock; +assign led[3] = 1'b0; +assign led[4] = sfp_3_rx_block_lock; +assign led[5] = 1'b0; +assign led[6] = sfp_4_rx_block_lock; +assign led[7] = 1'b0; + +fpga_core +core_inst ( + /* + * Clock: 156.25 MHz + * Synchronous reset + */ + .clk(clk_156mhz_int), + .rst(rst_156mhz_int), + /* + * GPIO + */ + .btn(), + .sfp_1_led(), + .sfp_2_led(), + .sfp_3_led(), + .sfp_4_led(), + .led(), + /* + * 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), + .sfp_3_tx_clk(sfp_3_tx_clk_int), + .sfp_3_tx_rst(sfp_3_tx_rst_int), + .sfp_3_txd(sfp_3_txd_int), + .sfp_3_txc(sfp_3_txc_int), + .sfp_3_rx_clk(sfp_3_rx_clk_int), + .sfp_3_rx_rst(sfp_3_rx_rst_int), + .sfp_3_rxd(sfp_3_rxd_int), + .sfp_3_rxc(sfp_3_rxc_int), + .sfp_4_tx_clk(sfp_4_tx_clk_int), + .sfp_4_tx_rst(sfp_4_tx_rst_int), + .sfp_4_txd(sfp_4_txd_int), + .sfp_4_txc(sfp_4_txc_int), + .sfp_4_rx_clk(sfp_4_rx_clk_int), + .sfp_4_rx_rst(sfp_4_rx_rst_int), + .sfp_4_rxd(sfp_4_rxd_int), + .sfp_4_rxc(sfp_4_rxc_int) +); + +endmodule + +`default_nettype wire diff --git a/example/VC709/fpga/rtl/fpga_core.v b/example/VC709/fpga/rtl/fpga_core.v new file mode 100644 index 000000000..c72ccecd6 --- /dev/null +++ b/example/VC709/fpga/rtl/fpga_core.v @@ -0,0 +1,627 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 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 [1:0] btn, + output wire [1:0] sfp_1_led, + output wire [1:0] sfp_2_led, + output wire [1:0] sfp_3_led, + output wire [1:0] sfp_4_led, + output wire [1:0] led, + + /* + * I2C + */ + input wire i2c_scl_i, + output wire i2c_scl_o, + output wire i2c_scl_t, + input wire i2c_sda_i, + output wire i2c_sda_o, + output wire i2c_sda_t, + + /* + * 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, + input wire sfp_3_tx_clk, + input wire sfp_3_tx_rst, + output wire [63:0] sfp_3_txd, + output wire [7:0] sfp_3_txc, + input wire sfp_3_rx_clk, + input wire sfp_3_rx_rst, + input wire [63:0] sfp_3_rxd, + input wire [7:0] sfp_3_rxc, + input wire sfp_4_tx_clk, + input wire sfp_4_tx_rst, + output wire [63:0] sfp_4_txd, + output wire [7:0] sfp_4_txc, + input wire sfp_4_rx_clk, + input wire sfp_4_rx_rst, + input wire [63:0] sfp_4_rxd, + input wire [7:0] sfp_4_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; +assign sfp_3_txd = 64'h0707070707070707; +assign sfp_3_txc = 8'hff; +assign sfp_4_txd = 64'h0707070707070707; +assign sfp_4_txc = 8'hff; + +eth_mac_10g_fifo #( + .ENABLE_PADDING(1), + .ENABLE_DIC(1), + .MIN_FRAME_LENGTH(64), + .TX_FIFO_DEPTH(4096), + .TX_FRAME_FIFO(1), + .RX_FIFO_DEPTH(4096), + .RX_FRAME_FIFO(1) +) +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 #( + .DATA_WIDTH(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 #( + .DATA_WIDTH(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 #( + .DEPTH(8192), + .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/example/VC709/fpga/rtl/i2c_master.v b/example/VC709/fpga/rtl/i2c_master.v new file mode 100644 index 000000000..95d3a5212 --- /dev/null +++ b/example/VC709/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/example/VC709/fpga/rtl/si5324_i2c_init.v b/example/VC709/fpga/rtl/si5324_i2c_init.v new file mode 100644 index 000000000..0d01e721e --- /dev/null +++ b/example/VC709/fpga/rtl/si5324_i2c_init.v @@ -0,0 +1,494 @@ +/* + +Copyright (c) 2015-2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * si5324_i2c_init + */ +module si5324_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 +00 1000001 : send I2C stop +01 aaaaaaa : start write to address +1 dddddddd : write 8-bit data + +Examples + +write 0x11223344 to register 0x0004 on device at 0x50 + +01 1010000 start write to 0x50 +1 00000000 write address 0x0004 +1 00000100 +1 00010001 write data 0x11223344 +1 00100010 +1 00110011 +1 01000100 +0 00000000 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 = 37; + +reg [8:0] init_data [INIT_DATA_LEN-1:0]; + +initial begin + // init Si5324 registers + init_data[0] = {2'b01, 7'h74}; // start write to 0x74 (I2C mux) + init_data[1] = {1'b1, 8'h80}; // select Si5324 + init_data[2] = {2'b00, 7'b1000001}; // I2C stop + init_data[3] = {2'b01, 7'h68}; // start write to 0x68 (Si5324) + init_data[4] = {1'b1, 8'd0}; // register 0 + init_data[5] = {1'b1, 8'h54}; // Reg 0: Free run, Clock off before ICAL, Bypass off (normal operation) + init_data[6] = {1'b1, 8'hE4}; // Reg 1: CKIN2 second priority, CKIN1 first priority + init_data[7] = {1'b1, 8'h12}; // Reg 2: BWSEL = 1 + init_data[8] = {1'b1, 8'h15}; // Reg 3: CKIN1 selected, Digital Hold off, Output clocks disabled during ICAL + init_data[9] = {1'b1, 8'h92}; // Reg 4: Automatic Revertive, HIST_DEL = 0x12 + init_data[10] = {2'b01, 7'h68}; // start write to 0x68 (Si5324) + init_data[11] = {1'b1, 8'd10}; // register 10 + init_data[12] = {1'b1, 8'h08}; // Reg 10: CKOUT2 disabled, CKOUT1 enabled + init_data[13] = {1'b1, 8'h40}; // Reg 11: CKIN2 enabled, CKIN1 enabled + init_data[14] = {2'b01, 7'h68}; // start write to 0x68 (Si5324) + init_data[15] = {1'b1, 8'd25}; // register 25 + init_data[16] = {1'b1, 8'hA0}; // Reg 25: N1_HS = 9 + init_data[17] = {2'b01, 7'h68}; // start write to 0x68 (Si5324) + init_data[18] = {1'b1, 8'd31}; // register 31 + init_data[19] = {1'b1, 8'h00}; // Regs 31,32,33: NC1_LS = 4 + init_data[20] = {1'b1, 8'h00}; + init_data[21] = {1'b1, 8'h03}; + init_data[22] = {2'b01, 7'h68}; // start write to 0x68 (Si5324) + init_data[23] = {1'b1, 8'd40}; // register 40 + init_data[24] = {1'b1, 8'hC2}; // Regs 40,41,42: N2_HS = 10, N2_LS = 150000 + init_data[25] = {1'b1, 8'h49}; + init_data[26] = {1'b1, 8'hEF}; + init_data[27] = {1'b1, 8'h00}; // Regs 43,44,45: N31 = 30475 + init_data[28] = {1'b1, 8'h77}; + init_data[29] = {1'b1, 8'h0B}; + init_data[30] = {1'b1, 8'h00}; // Regs 46,47,48: N32 = 30475 + init_data[31] = {1'b1, 8'h77}; + init_data[32] = {1'b1, 8'h0B}; + init_data[33] = {2'b01, 7'h68}; // start write to 0x68 (Si5324) + init_data[34] = {1'b1, 8'd136}; // register 136 + init_data[35] = {1'b1, 8'h40}; // Reg 136: ICAL = 1 + init_data[36] = 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'b001000001) begin + // send stop + cmd_write_next = 1'b0; + cmd_start_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_RUN; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'd0) begin + // stop + 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'b001000001) begin + // send stop + cmd_write_next = 1'b0; + cmd_start_next = 1'b0; + cmd_stop_next = 1'b1; + cmd_valid_next = 1'b1; + + address_next = address_reg + 1; + + state_next = STATE_TABLE_3; + end else if (init_data_reg == 9'b000001001) begin + // data table start + data_ptr_next = address_reg + 1; + address_next = address_reg + 1; + state_next = STATE_TABLE_1; + end else if (init_data_reg == 9'b000001000) begin + // address table start + address_next = address_ptr_reg; + state_next = STATE_TABLE_2; + end else if (init_data_reg == 9'd1) begin + // exit mode + address_next = address_reg + 1; + state_next = STATE_RUN; + end else if (init_data_reg == 9'd0) begin + // stop + 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/example/VC709/fpga/tb/fpga_core/Makefile b/example/VC709/fpga/tb/fpga_core/Makefile new file mode 100644 index 000000000..87e6cf660 --- /dev/null +++ b/example/VC709/fpga/tb/fpga_core/Makefile @@ -0,0 +1,95 @@ +# Copyright (c) 2020 Alex Forencich +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +TOPLEVEL_LANG = verilog + +SIM ?= icarus +WAVES ?= 0 + +COCOTB_HDL_TIMEUNIT = 1ns +COCOTB_HDL_TIMEPRECISION = 1ps + +DUT = fpga_core +TOPLEVEL = $(DUT) +MODULE = test_$(DUT) +VERILOG_SOURCES += ../../rtl/$(DUT).v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_mac_10g_fifo.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_mac_10g.v +VERILOG_SOURCES += ../../lib/eth/rtl/axis_xgmii_rx_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/axis_xgmii_tx_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/lfsr.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_axis_rx.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_axis_tx.v +VERILOG_SOURCES += ../../lib/eth/rtl/udp_complete_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/udp_checksum_gen_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/udp_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/udp_ip_rx_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/udp_ip_tx_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/ip_complete_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/ip_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/ip_eth_rx_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/ip_eth_tx_64.v +VERILOG_SOURCES += ../../lib/eth/rtl/ip_arb_mux.v +VERILOG_SOURCES += ../../lib/eth/rtl/arp.v +VERILOG_SOURCES += ../../lib/eth/rtl/arp_cache.v +VERILOG_SOURCES += ../../lib/eth/rtl/arp_eth_rx.v +VERILOG_SOURCES += ../../lib/eth/rtl/arp_eth_tx.v +VERILOG_SOURCES += ../../lib/eth/rtl/eth_arb_mux.v +VERILOG_SOURCES += ../../lib/eth/lib/axis/rtl/arbiter.v +VERILOG_SOURCES += ../../lib/eth/lib/axis/rtl/priority_encoder.v +VERILOG_SOURCES += ../../lib/eth/lib/axis/rtl/axis_fifo.v +VERILOG_SOURCES += ../../lib/eth/lib/axis/rtl/axis_async_fifo.v +VERILOG_SOURCES += ../../lib/eth/lib/axis/rtl/axis_async_fifo_adapter.v + +# module parameters +#export PARAM_A ?= value + +ifeq ($(SIM), icarus) + PLUSARGS += -fst + +# COMPILE_ARGS += -P $(TOPLEVEL).A=$(PARAM_A) + + ifeq ($(WAVES), 1) + VERILOG_SOURCES += iverilog_dump.v + COMPILE_ARGS += -s iverilog_dump + endif +else ifeq ($(SIM), verilator) + COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH + +# COMPILE_ARGS += -GA=$(PARAM_A) + + ifeq ($(WAVES), 1) + COMPILE_ARGS += --trace-fst + endif +endif + +include $(shell cocotb-config --makefiles)/Makefile.sim + +iverilog_dump.v: + echo 'module iverilog_dump();' > $@ + echo 'initial begin' >> $@ + echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@ + echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@ + echo 'end' >> $@ + echo 'endmodule' >> $@ + +clean:: + @rm -rf iverilog_dump.v + @rm -rf dump.fst $(TOPLEVEL).fst diff --git a/example/VC709/fpga/tb/fpga_core/test_fpga_core.py b/example/VC709/fpga/tb/fpga_core/test_fpga_core.py new file mode 100644 index 000000000..b6ec23c1d --- /dev/null +++ b/example/VC709/fpga/tb/fpga_core/test_fpga_core.py @@ -0,0 +1,245 @@ +""" + +Copyright (c) 2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +import logging +import os + +from scapy.layers.l2 import Ether, ARP +from scapy.layers.inet import IP, UDP + +import cocotb_test.simulator + +import cocotb +from cocotb.log import SimLog +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge + +from cocotbext.eth import XgmiiFrame, XgmiiSource, XgmiiSink + + +class TB: + def __init__(self, dut): + self.dut = dut + + self.log = SimLog("cocotb.tb") + self.log.setLevel(logging.DEBUG) + + cocotb.fork(Clock(dut.clk, 6.4, units="ns").start()) + + # Ethernet + cocotb.fork(Clock(dut.sfp_1_rx_clk, 6.4, units="ns").start()) + self.sfp_1_source = XgmiiSource(dut.sfp_1_rxd, dut.sfp_1_rxc, dut.sfp_1_rx_clk, dut.sfp_1_rx_rst) + cocotb.fork(Clock(dut.sfp_1_tx_clk, 6.4, units="ns").start()) + self.sfp_1_sink = XgmiiSink(dut.sfp_1_txd, dut.sfp_1_txc, dut.sfp_1_tx_clk, dut.sfp_1_tx_rst) + + cocotb.fork(Clock(dut.sfp_2_rx_clk, 6.4, units="ns").start()) + self.sfp_2_source = XgmiiSource(dut.sfp_2_rxd, dut.sfp_2_rxc, dut.sfp_2_rx_clk, dut.sfp_2_rx_rst) + cocotb.fork(Clock(dut.sfp_2_tx_clk, 6.4, units="ns").start()) + self.sfp_2_sink = XgmiiSink(dut.sfp_2_txd, dut.sfp_2_txc, dut.sfp_2_tx_clk, dut.sfp_2_tx_rst) + + cocotb.fork(Clock(dut.sfp_3_rx_clk, 6.4, units="ns").start()) + self.sfp_3_source = XgmiiSource(dut.sfp_3_rxd, dut.sfp_3_rxc, dut.sfp_3_rx_clk, dut.sfp_3_rx_rst) + cocotb.fork(Clock(dut.sfp_3_tx_clk, 6.4, units="ns").start()) + self.sfp_3_sink = XgmiiSink(dut.sfp_3_txd, dut.sfp_3_txc, dut.sfp_3_tx_clk, dut.sfp_3_tx_rst) + + cocotb.fork(Clock(dut.sfp_4_rx_clk, 6.4, units="ns").start()) + self.sfp_4_source = XgmiiSource(dut.sfp_4_rxd, dut.sfp_4_rxc, dut.sfp_4_rx_clk, dut.sfp_4_rx_rst) + cocotb.fork(Clock(dut.sfp_4_tx_clk, 6.4, units="ns").start()) + self.sfp_4_sink = XgmiiSink(dut.sfp_4_txd, dut.sfp_4_txc, dut.sfp_4_tx_clk, dut.sfp_4_tx_rst) + + dut.btn.setimmediatevalue(0) + + async def init(self): + + self.dut.rst.setimmediatevalue(0) + self.dut.sfp_1_rx_rst.setimmediatevalue(0) + self.dut.sfp_1_tx_rst.setimmediatevalue(0) + self.dut.sfp_2_rx_rst.setimmediatevalue(0) + self.dut.sfp_2_tx_rst.setimmediatevalue(0) + self.dut.sfp_3_rx_rst.setimmediatevalue(0) + self.dut.sfp_3_tx_rst.setimmediatevalue(0) + self.dut.sfp_4_rx_rst.setimmediatevalue(0) + self.dut.sfp_4_tx_rst.setimmediatevalue(0) + + for k in range(10): + await RisingEdge(self.dut.clk) + + self.dut.rst <= 1 + self.dut.sfp_1_rx_rst <= 1 + self.dut.sfp_1_tx_rst <= 1 + self.dut.sfp_2_rx_rst <= 1 + self.dut.sfp_2_tx_rst <= 1 + self.dut.sfp_3_rx_rst <= 1 + self.dut.sfp_3_tx_rst <= 1 + self.dut.sfp_4_rx_rst <= 1 + self.dut.sfp_4_tx_rst <= 1 + + for k in range(10): + await RisingEdge(self.dut.clk) + + self.dut.rst <= 0 + self.dut.sfp_1_rx_rst <= 0 + self.dut.sfp_1_tx_rst <= 0 + self.dut.sfp_2_rx_rst <= 0 + self.dut.sfp_2_tx_rst <= 0 + self.dut.sfp_3_rx_rst <= 0 + self.dut.sfp_3_tx_rst <= 0 + self.dut.sfp_4_rx_rst <= 0 + self.dut.sfp_4_tx_rst <= 0 + + +@cocotb.test() +async def run_test(dut): + + tb = TB(dut) + + await tb.init() + + tb.log.info("test UDP RX packet") + + payload = bytes([x % 256 for x in range(256)]) + eth = Ether(src='5a:51:52:53:54:55', dst='02:00:00:00:00:00') + ip = IP(src='192.168.1.100', dst='192.168.1.128') + udp = UDP(sport=5678, dport=1234) + test_pkt = eth / ip / udp / payload + + test_frame = XgmiiFrame.from_payload(test_pkt.build()) + + await tb.sfp_1_source.send(test_frame) + + tb.log.info("receive ARP request") + + rx_frame = await tb.sfp_1_sink.recv() + + rx_pkt = Ether(bytes(rx_frame.get_payload())) + + tb.log.info("RX packet: %s", repr(rx_pkt)) + + assert rx_pkt.dst == 'ff:ff:ff:ff:ff:ff' + assert rx_pkt.src == test_pkt.dst + assert rx_pkt[ARP].hwtype == 1 + assert rx_pkt[ARP].ptype == 0x0800 + assert rx_pkt[ARP].hwlen == 6 + assert rx_pkt[ARP].plen == 4 + assert rx_pkt[ARP].op == 1 + assert rx_pkt[ARP].hwsrc == test_pkt.dst + assert rx_pkt[ARP].psrc == test_pkt[IP].dst + assert rx_pkt[ARP].hwdst == '00:00:00:00:00:00' + assert rx_pkt[ARP].pdst == test_pkt[IP].src + + tb.log.info("send ARP response") + + eth = Ether(src=test_pkt.src, dst=test_pkt.dst) + arp = ARP(hwtype=1, ptype=0x0800, hwlen=6, plen=4, op=2, + hwsrc=test_pkt.src, psrc=test_pkt[IP].src, + hwdst=test_pkt.dst, pdst=test_pkt[IP].dst) + resp_pkt = eth / arp + + resp_frame = XgmiiFrame.from_payload(resp_pkt.build()) + + await tb.sfp_1_source.send(resp_frame) + + tb.log.info("receive UDP packet") + + rx_frame = await tb.sfp_1_sink.recv() + + rx_pkt = Ether(bytes(rx_frame.get_payload())) + + tb.log.info("RX packet: %s", repr(rx_pkt)) + + assert rx_pkt.dst == test_pkt.src + assert rx_pkt.src == test_pkt.dst + assert rx_pkt[IP].dst == test_pkt[IP].src + assert rx_pkt[IP].src == test_pkt[IP].dst + assert rx_pkt[UDP].dport == test_pkt[UDP].sport + assert rx_pkt[UDP].sport == test_pkt[UDP].dport + assert rx_pkt[UDP].payload == test_pkt[UDP].payload + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +# cocotb-test + +tests_dir = os.path.abspath(os.path.dirname(__file__)) +rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl')) +lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib')) +axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'eth', 'lib', 'axis', 'rtl')) +eth_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'eth', 'rtl')) + + +def test_fpga_core(request): + dut = "fpga_core" + module = os.path.splitext(os.path.basename(__file__))[0] + toplevel = dut + + verilog_sources = [ + os.path.join(rtl_dir, f"{dut}.v"), + os.path.join(eth_rtl_dir, "eth_mac_10g_fifo.v"), + os.path.join(eth_rtl_dir, "eth_mac_10g.v"), + os.path.join(eth_rtl_dir, "axis_xgmii_rx_64.v"), + os.path.join(eth_rtl_dir, "axis_xgmii_tx_64.v"), + os.path.join(eth_rtl_dir, "lfsr.v"), + os.path.join(eth_rtl_dir, "eth_axis_rx.v"), + os.path.join(eth_rtl_dir, "eth_axis_tx.v"), + os.path.join(eth_rtl_dir, "udp_complete_64.v"), + os.path.join(eth_rtl_dir, "udp_checksum_gen_64.v"), + os.path.join(eth_rtl_dir, "udp_64.v"), + os.path.join(eth_rtl_dir, "udp_ip_rx_64.v"), + os.path.join(eth_rtl_dir, "udp_ip_tx_64.v"), + os.path.join(eth_rtl_dir, "ip_complete_64.v"), + os.path.join(eth_rtl_dir, "ip_64.v"), + os.path.join(eth_rtl_dir, "ip_eth_rx_64.v"), + os.path.join(eth_rtl_dir, "ip_eth_tx_64.v"), + os.path.join(eth_rtl_dir, "ip_arb_mux.v"), + os.path.join(eth_rtl_dir, "arp.v"), + os.path.join(eth_rtl_dir, "arp_cache.v"), + os.path.join(eth_rtl_dir, "arp_eth_rx.v"), + os.path.join(eth_rtl_dir, "arp_eth_tx.v"), + os.path.join(eth_rtl_dir, "eth_arb_mux.v"), + os.path.join(axis_rtl_dir, "arbiter.v"), + os.path.join(axis_rtl_dir, "priority_encoder.v"), + os.path.join(axis_rtl_dir, "axis_fifo.v"), + os.path.join(axis_rtl_dir, "axis_async_fifo.v"), + os.path.join(axis_rtl_dir, "axis_async_fifo_adapter.v"), + ] + + parameters = {} + + # parameters['A'] = val + + extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()} + + sim_build = os.path.join(tests_dir, "sim_build", + request.node.name.replace('[', '-').replace(']', '')) + + cocotb_test.simulator.run( + python_search=[tests_dir], + verilog_sources=verilog_sources, + toplevel=toplevel, + module=module, + parameters=parameters, + sim_build=sim_build, + extra_env=extra_env, + )