diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..38bdb8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +**/DVEfiles/ +**/csrc/ +**/dump.* +**/flist.vcs +**/simv* +**/ucli.key +**/vc_hdrs.h +**/*.swp +**/*.swo +rv_plic/v/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..60ea3c7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "rv_plic/import/opentitan"] + path = rv_plic/import/opentitan + url = https://github.com/lowRISC/opentitan.git diff --git a/ethernet_controller/.gitignore b/ethernet_controller/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/ethernet_controller/LICENSE b/ethernet_controller/LICENSE new file mode 100644 index 0000000..dc29846 --- /dev/null +++ b/ethernet_controller/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ethernet_controller/README b/ethernet_controller/README new file mode 100644 index 0000000..689e412 --- /dev/null +++ b/ethernet_controller/README @@ -0,0 +1,5 @@ +This folder contains an Ethternet controller implementation +based on the MAC module 'eth_mac_1g_rgmii_fifo' from +https://github.com/alexforencich/verilog-ethernet. + +Several changes have been made to allow synthesis on both FPGA/ASIC. diff --git a/ethernet_controller/syn/gf14/gf14.tcl b/ethernet_controller/syn/gf14/gf14.tcl new file mode 100644 index 0000000..8342504 --- /dev/null +++ b/ethernet_controller/syn/gf14/gf14.tcl @@ -0,0 +1,226 @@ +puts "BSG-info: Running script [info script]\n" + +#set_app_var sh_continue_on_error false +#error "Let's stop here for a while" + +######################################## +## Source common scripts +source -echo -verbose $::env(BSG_DESIGNS_TARGET_TCL_DIR)/common/bsg_chip_misc.tcl + +######################################## +## App Var Setup + +# Needed for automatic clock-gate insertions +set_app_var case_analysis_propagate_through_icg true + + +###################### Clocks ###################### + +set usr_clk_period_ps 1000 +set usr_clk_uncertainty_per 3.0 +set usr_clk_uncertainty_ps [expr min([expr ${usr_clk_period_ps}*(${usr_clk_uncertainty_per}/100.0)], 20)] + +set clk250_uncertainty_per 3.0 +set clk250_uncertainty_ps [expr min([expr 4000*(${clk250_uncertainty_per}/100.0)], 20)] + +set rgmii_rx_clk_uncertainty_per 3.0 +set rgmii_rx_clk_uncertainty_ps [expr min([expr 8000*(${rgmii_rx_clk_uncertainty_per}/100.0)], 20)] + +set gtx_clk_uncertainty_per 3.0 +set gtx_clk_uncertainty_ps [expr min([expr 8000*(${gtx_clk_uncertainty_per}/100.0)], 20)] + +# clk250, usr_clk, rgmii_rx_clk, rgmii_tx_clk, gtx_clk +# CLK250 (250 MHZ) +create_clock -period 4000 -name clk250 [get_ports clk250_i] +# User logic (1 GHZ) +create_clock -period ${usr_clk_period_ps} -name usr_clk [get_ports clk_i] +# RGMII RX CLK (125 MHZ) +create_clock -period 8000 -name rgmii_rx_clk [get_ports rgmii_rx_clk_i] + +# Generated clocks from clk250: gtx_clk (for DDR data) +create_generated_clock -name gtx_clk -source [get_ports clk250_i] -divide_by 2 [get_pins mac/eth_mac_1g_rgmii_inst/rgmii_phy_if_inst/tx_clks_gen/gtx_clk_gen/clk_r_o_reg/Q] +# Generated clocks from clk250: rgmii_tx_clk (for DDR clock) +create_generated_clock -name rgmii_tx_clk -source [get_ports clk250_i] -edges {2 4 6} -edge_shift {0.000 0.000 0.000} [get_ports rgmii_tx_clk_o] + +# Clock Uncertainty +#set_clock_uncertainty ${usr_clk_uncertainty_ps} [get_clocks usr_clk] +#set_clock_uncertainty ${clk250_uncertainty_ps} [get_clocks clk250] +#set_clock_uncertainty ${rgmii_rx_clk_uncertainty_ps} [get_clocks rgmii_rx_clk] +#set_clock_uncertainty ${gtx_clk_uncertainty_ps} [get_clocks gtx_clk] + +set clock_jitter 20.0 +set extra_margin 20.0 + +# The values are based on the clock qor report from pnr +set_clock_uncertainty [expr 30 + ${clock_jitter} + ${extra_margin}] -setup [get_clocks usr_clk] +set_clock_uncertainty [expr 30 + ${extra_margin}] -hold [get_clocks usr_clk] +set_clock_uncertainty [expr 60 + ${clock_jitter} + ${extra_margin}] -setup [get_clocks clk250] +set_clock_uncertainty [expr 40 + ${extra_margin}] -hold [get_clocks clk250] +set_clock_uncertainty [expr 30 + ${clock_jitter} + ${extra_margin}] -setup [get_clocks rgmii_rx_clk] +set_clock_uncertainty [expr 40 + ${extra_margin}] -hold [get_clocks rgmii_rx_clk] +set_clock_uncertainty [expr 40 + ${clock_jitter} + ${extra_margin}] -setup [get_clocks gtx_clk] +set_clock_uncertainty [expr 40 + ${extra_margin}] -hold [get_clocks gtx_clk] +#set_clock_uncertainty [expr ${clock_jitter} + ${extra_margin}] -setup [get_clocks usr_clk] +#set_clock_uncertainty [expr ${extra_margin}] -hold [get_clocks usr_clk] +#set_clock_uncertainty [expr ${clock_jitter} + ${extra_margin}] -setup [get_clocks clk250] +#set_clock_uncertainty [expr ${extra_margin}] -hold [get_clocks clk250] +#set_clock_uncertainty [expr ${clock_jitter} + ${extra_margin}] -setup [get_clocks rgmii_rx_clk] +#set_clock_uncertainty [expr ${extra_margin}] -hold [get_clocks rgmii_rx_clk] +#set_clock_uncertainty [expr ${clock_jitter} + ${extra_margin}] -setup [get_clocks gtx_clk] +#set_clock_uncertainty [expr ${extra_margin}] -hold [get_clocks gtx_clk] + +# No clock uncertainty for rgmii_tx_clk + +###################### Set Input Delay ###################### + +# Make input min delay so large that even with zero network propagation delay at input ports is fine +set input_delay_min_per 30.0 +set input_delay_max_per 70.0 +set usr_clk_input_delay_min_ps [expr ${usr_clk_period_ps}*(${input_delay_min_per}/100.0)] +set usr_clk_input_delay_max_ps [expr ${usr_clk_period_ps}*(${input_delay_max_per}/100.0)] +set clk250_input_delay_min_ps [expr 4000*(${input_delay_min_per}/100.0)] +set clk250_input_delay_max_ps [expr 4000*(${input_delay_max_per}/100.0)] +set tx_clk_input_delay_min_ps [expr 8000*(${input_delay_min_per}/100.0)] +set tx_clk_input_delay_max_ps [expr 8000*(${input_delay_max_per}/100.0)] +set rx_clk_input_delay_min_ps [expr 8000*(${input_delay_min_per}/100.0)] +set rx_clk_input_delay_max_ps [expr 8000*(${input_delay_max_per}/100.0)] + + +set usr_inputs {reset_i addr_i write_en_i read_en_i write_mask_i write_data_i} +set_input_delay -network_latency_included -min ${usr_clk_input_delay_min_ps} -clock usr_clk ${usr_inputs} +set_input_delay -network_latency_included -max ${usr_clk_input_delay_max_ps} -clock usr_clk ${usr_inputs} +set_input_delay -network_latency_included -min ${clk250_input_delay_min_ps} -clock clk250 {clk250_reset_i tx_clk_gen_reset_i} +set_input_delay -network_latency_included -max ${clk250_input_delay_max_ps} -clock clk250 {clk250_reset_i tx_clk_gen_reset_i} +set_input_delay -network_latency_included -min ${tx_clk_input_delay_min_ps} -clock gtx_clk {tx_reset_i} +set_input_delay -network_latency_included -max ${tx_clk_input_delay_max_ps} -clock gtx_clk {tx_reset_i} +set_input_delay -network_latency_included -min ${rx_clk_input_delay_min_ps} -clock rgmii_rx_clk {rx_reset_i} +set_input_delay -network_latency_included -max ${rx_clk_input_delay_max_ps} -clock rgmii_rx_clk {rx_reset_i} + +set_driving_cell -min -no_design_rule -lib_cell $LIB_CELLS(invx2) [all_inputs] +set_driving_cell -max -no_design_rule -lib_cell $LIB_CELLS(invx8) [all_inputs] + + +###################### Set Output Delay ###################### + +set output_delay_min_per 2.0 +set output_delay_max_per 20.0 +set output_delay_min_ps [expr (-1)*${usr_clk_period_ps}*(${output_delay_min_per}/100.0)] +set output_delay_max_ps [expr ${usr_clk_period_ps}*(${output_delay_max_per}/100.0)] + +set usr_outputs {read_data_o rx_interrupt_pending_o tx_interrupt_pending_o} +set_output_delay -network_latency_included -min ${output_delay_min_ps} -clock usr_clk ${usr_outputs} +set_output_delay -network_latency_included -max ${output_delay_max_ps} -clock usr_clk ${usr_outputs} + +set_load -min [load_of [get_lib_pin */$LIB_CELLS(invx2,load_pin)]] [all_outputs] +set_load -max [load_of [get_lib_pin */$LIB_CELLS(invx8,load_pin)]] [all_outputs] + + +###################### Default max delay ###################### +# Set default max delay between each async clock groups to 0 in order to catch unnoticed paths +# set_max_delay is used instead of set_clock_groups in order to have safer constraints +# Async groups: +# 1. usr_clk +# 2. clk250 (which generates gtx_clk, rgmii_tx_clk) +# 3. rgmii_rx_clk (from Ethernet PHY) +set_clock_groups -asynchronous -allow_paths -name g1 -group {clk250 gtx_clk rgmii_tx_clk} -group {rgmii_rx_clk} -group {usr_clk} + +set_max_delay -from [get_clocks usr_clk] -to [get_clocks {gtx_clk rgmii_tx_clk clk250 rgmii_rx_clk}] -ignore_clock_latency 0.0 +set_max_delay -from [get_clocks gtx_clk] -to [get_clocks {usr_clk rgmii_rx_clk}] -ignore_clock_latency 0.0 +set_max_delay -from [get_clocks rgmii_tx_clk] -to [get_clocks {usr_clk rgmii_rx_clk}] -ignore_clock_latency 0.0 +set_max_delay -from [get_clocks clk250] -to [get_clocks {usr_clk rgmii_rx_clk}] -ignore_clock_latency 0.0 +set_max_delay -from [get_clocks rgmii_rx_clk] -to [get_clocks {usr_clk gtx_clk rgmii_tx_clk clk250}] -ignore_clock_latency 0.0 + +################# bsg_launch_sync_sync ################# + +foreach inst [get_object_name [get_cells -hier -filter {hdl_template == bsg_launch_sync_sync}]] { + set_boundary_optimization [get_cells $inst] false + foreach launch_reg [get_object_name [get_cells -regexp [format {%s/.*/bsg_SYNC_LNCH_r_reg\\[.*]} $inst]]] { + regexp {([\w/.\[\]]+)/[\w]+\[([0-9]+)\]} $launch_reg -> path index + set source_cell [get_cells $path/bsg_SYNC_LNCH_r_reg[$index]] + set dest_cell [get_cells $path/genblk1[$index].hard_sync_int] + # max delay between launch flop and sync_1 flop + # Some random small limit is applied. As long as the RP groups work, this constraint is not really necessary + # TODO: use set_max_delay +# set_max_delay -from $source_cell -to $dest_cell -ignore_clock_latency 200.0 + set_false_path -from $source_cell -to $dest_cell +# set_false_path -hold -from $source_cell -to $dest_cell + } +} + +################# bsg_async_fifo ################# +# Set false path from write clock port to read data in bsg_mem_1r1w in bsg_async_fifo +#foreach fifo_inst [get_object_name [get_cells -hier -filter {hdl_template == bsg_async_fifo}]] { +# set_false_path -from $fifo_inst/MSYNC_1r1w/synth/w_clk_i -to $fifo_inst/MSYNC_1r1w/synth/r_data_o +#} +set_false_path -from usr_clk -through mac/tx_fifo/fifo_inst/cdc/MSYNC_1r1w/synth/r_data_o +set_false_path -from rgmii_rx_clk -through mac/rx_fifo/fifo_inst/cdc/MSYNC_1r1w/synth/r_data_o + +################# Input/Ouput delay ################# +# Constant: 8ns +set CLK125_PERIOD 8000 + +# +# +--------------+ +--------------+ +# | | | | RGMII RX CLK +# ---+ +--------------+ +----- +# _________ _________ +# XXXX__Data___XXXXXX___Data__XXXXX RGMII RX Data/Control +# <--|----> <--|----> +# BFE AFE BFE AFE +# +# NOTE: According to RGMII timing spec, +# the typical values of both RX_BFE_DELAY and RX_AFE_DELAY +# are 2.0, but both can be as low as 1.2, which means both +# of them should <= 1.2. However, it is hard to meet timing +# on Zedboard if RX_AFE_DELAY is too low. Fortunately, the +# Avnet Ethernet FMC module offers larger RX_AFE_DELAY. +# Set input delay for RX RGMII +set RX_BFE_DELAY 500 +set RX_AFE_DELAY 1200 + +set RX_MAX_DELAY [expr {$CLK125_PERIOD/2 - $RX_BFE_DELAY}] +set RX_MIN_DELAY $RX_AFE_DELAY + +set_input_delay -network_latency_included -clock [get_clocks rgmii_rx_clk] -max $RX_MAX_DELAY [get_ports rgmii_rxd*] +set_input_delay -network_latency_included -clock [get_clocks rgmii_rx_clk] -min $RX_MIN_DELAY [get_ports rgmii_rxd*] +set_input_delay -network_latency_included -clock [get_clocks rgmii_rx_clk] -clock_fall -max -add_delay $RX_MAX_DELAY [get_ports rgmii_rxd*] +set_input_delay -network_latency_included -clock [get_clocks rgmii_rx_clk] -clock_fall -min -add_delay $RX_MIN_DELAY [get_ports rgmii_rxd*] + +set_input_delay -network_latency_included -clock [get_clocks rgmii_rx_clk] -max $RX_MAX_DELAY [get_ports rgmii_rx_ctl_i] +set_input_delay -network_latency_included -clock [get_clocks rgmii_rx_clk] -min $RX_MIN_DELAY [get_ports rgmii_rx_ctl_i] +set_input_delay -network_latency_included -clock [get_clocks rgmii_rx_clk] -clock_fall -max -add_delay $RX_MAX_DELAY [get_ports rgmii_rx_ctl_i] +set_input_delay -network_latency_included -clock [get_clocks rgmii_rx_clk] -clock_fall -min -add_delay $RX_MIN_DELAY [get_ports rgmii_rx_ctl_i] + +# +--------------+ +--------------+ +# | | | | CLK +# ---+ +--------------+ +----- +# _________ _________ +# XXXX__Data___XXXXXX___Data__XXXXX +# <--|----> <--|----> +# BFE AFE BFE AFE +# Set output delay for TX RGMII +# According to RGMII timing spec, both TX_BFE_DELAY and +# TX_AFE_DELAY should >= 1.2 +set TX_BFE_DELAY 1600 +set TX_AFE_DELAY 1600 + +set TX_MAX_DELAY $TX_BFE_DELAY +set TX_MIN_DELAY [expr -$TX_AFE_DELAY] +# TODO: Add -source_latency_included? +set_output_delay -network_latency_included -clock [get_clocks rgmii_tx_clk] -max $TX_MAX_DELAY [get_ports rgmii_txd*] +set_output_delay -network_latency_included -clock [get_clocks rgmii_tx_clk] -min $TX_MIN_DELAY [get_ports rgmii_txd*] +set_output_delay -network_latency_included -clock [get_clocks rgmii_tx_clk] -clock_fall -max -add_delay $TX_MAX_DELAY [get_ports rgmii_txd*] +set_output_delay -network_latency_included -clock [get_clocks rgmii_tx_clk] -clock_fall -min -add_delay $TX_MIN_DELAY [get_ports rgmii_txd*] + +set_output_delay -network_latency_included -clock [get_clocks rgmii_tx_clk] -max $TX_MAX_DELAY [get_ports rgmii_tx_ctl_o] +set_output_delay -network_latency_included -clock [get_clocks rgmii_tx_clk] -min $TX_MIN_DELAY [get_ports rgmii_tx_ctl_o] +set_output_delay -network_latency_included -clock [get_clocks rgmii_tx_clk] -clock_fall -max -add_delay $TX_MAX_DELAY [get_ports rgmii_tx_ctl_o] +set_output_delay -network_latency_included -clock [get_clocks rgmii_tx_clk] -clock_fall -min -add_delay $TX_MIN_DELAY [get_ports rgmii_tx_ctl_o] + +#set_timing_derate -cell_delay -early 0.95 +#set_timing_derate -cell_delay -late 1.05 + +set_max_transition -data_path 75 [all_clocks] + +puts "BSG-info: Completed script [info script]\n" + diff --git a/ethernet_controller/syn/zedboard/zedboard.tcl b/ethernet_controller/syn/zedboard/zedboard.tcl new file mode 100644 index 0000000..95e5508 --- /dev/null +++ b/ethernet_controller/syn/zedboard/zedboard.tcl @@ -0,0 +1,138 @@ + +# ** Assign the path of the clk250 source pin to clk250_source_pin before sourcing this tcl ** + +# Currently this contraint file is highly coupled with Zynq-parrot +###################### Clocks ###################### +# clk250 -> clk125 +set inst [get_cells -hier -filter {(ORIG_REF_NAME == tx_clks_generator || REF_NAME == tx_clks_generator)}] +create_generated_clock -name gtx_clk -source [get_pins $clk250_source_pin] -divide_by 2 [get_pins $inst/gtx_clk_gen/clk_r_o_reg/Q] +# clk250 -> 90-degree shifted clk125 for rgmii TX clk source +create_generated_clock -name rgmii_tx_clk -source [get_pins $clk250_source_pin] -edges {2 4 6} -edge_shift {0.000 0.000 0.000} [get_ports rgmii_tx_clk_o] +# RX clk source (125M) +create_clock -period 8.000 -name rgmii_rx_clk -waveform {0.000 4.000} [get_ports rgmii_rx_clk_i] + +################# bsg_launch_sync_sync ################# +foreach blss_inst [get_cells -hier -filter {(ORIG_REF_NAME == bsg_launch_sync_sync || REF_NAME == bsg_launch_sync_sync)}] { + puts "blss_inst: $blss_inst" + foreach launch_reg [get_cells $blss_inst/*/bsg_SYNC_LNCH_r_reg[*]] { + # ASYNC_REG should have been applied in RTL + regexp {([\w/.\[\]]+)/[\w]+\[([0-9]+)\]} $launch_reg -> path index + + set source_cell [get_cells $path/bsg_SYNC_LNCH_r_reg[$index]] + set dest_cell [get_cells $path/bsg_SYNC_1_r_reg[$index]] + set write_clk [get_clocks -of_objects [get_pins $source_cell/C]] + set read_clk [get_clocks -of_objects [get_pins $dest_cell/C]] + set read_clk_period [get_property -min PERIOD $read_clk] + set write_clk_period [get_property -min PERIOD $write_clk] + set min_clk_period [expr $read_clk_period < $write_clk_period ? $read_clk_period : $write_clk_period] + # max delay between launch flop and sync_1 flop + set_max_delay -from $source_cell -to $dest_cell -datapath_only $min_clk_period + } +} + +################# bsg_tag_client ################# +foreach inst [get_cells -hier -filter {(ORIG_REF_NAME == bsg_tag_client || REF_NAME == bsg_tag_client)}] { + set source_cell [get_cells $inst/tag_data_reg/data_r_reg[0]] + set dest_cell [get_cells $inst/recv/data_r_reg[0]] + set write_clk [get_clocks -of_objects [get_pins $source_cell/C]] + set read_clk [get_clocks -of_objects [get_pins $dest_cell/C]] + set read_clk_period [get_property -min PERIOD $read_clk] + set write_clk_period [get_property -min PERIOD $write_clk] + set min_clk_period [expr $read_clk_period < $write_clk_period ? $read_clk_period : $write_clk_period] + set_max_delay -from $source_cell -to $dest_cell -datapath_only $min_clk_period +} + +################# iodelay reset ################# +# ASYNC_REG should have been applied in RTL +set inst [get_cells -hier -filter {(ORIG_REF_NAME == iodelay_control || REF_NAME == iodelay_control)}] +set dest_cell [get_cells $inst/nosim.reset_iodelay_sync/bsg_SYNC_1_r_reg[0]] +set_false_path -to [get_pins -of_objects $dest_cell -filter {IS_PRESET || IS_RESET}] +set dest_cell [get_cells $inst/nosim.reset_iodelay_sync/bsg_SYNC_2_r_reg[0]] +set_false_path -to [get_pins -of_objects $dest_cell -filter {IS_PRESET || IS_RESET}] + +################# bsg_async_fifo ################# +# Set max delay from write clock to read port in bsg_mem_1r1w in bsg_async_fifo +foreach fifo_inst [get_cells -hier -filter {(ORIG_REF_NAME == bsg_async_fifo || REF_NAME == bsg_async_fifo)}] { + set write_clk [get_clocks -of_objects [get_pins $fifo_inst/bapg_wr/*/*/bsg_SYNC_LNCH_r_reg[0]/C]] + set read_clk [get_clocks -of_objects [get_pins $fifo_inst/bapg_rd/*/*/bsg_SYNC_LNCH_r_reg[0]/C]] + set write_clk_period [get_property -min PERIOD $write_clk] + set read_clk_period [get_property -min PERIOD $read_clk] + set min_clk_period [expr $read_clk_period < $write_clk_period ? $read_clk_period : $write_clk_period] + foreach ram_inst [get_cells $fifo_inst/MSYNC_1r1w/synth/nz.mem*/RAM*] { + set_max_delay -from $write_clk -through [get_pins $ram_inst/O] $min_clk_period -datapath_only + } +} + +################# Input/Ouput delay ################# +# Constant: 8ns +set CLK125_PERIOD 8 + +# +# +--------------+ +--------------+ +# | | | | RGMII RX CLK +# ---+ +--------------+ +----- +# _________ _________ +# XXXX__Data___XXXXXX___Data__XXXXX RGMII RX Data/Control +# <--|----> <--|----> +# BFE AFE BFE AFE +# +# NOTE: According to RGMII timing spec, +# the typical values of both RX_BFE_DELAY and RX_AFE_DELAY +# are 2.0, but both can be as low as 1.2, which means both +# of them should <= 1.2. However, it is hard to meet timing +# on Zedboard if RX_AFE_DELAY is too low. Fortunately, the +# Avnet Ethernet FMC module offers larger RX_AFE_DELAY. +# Set input delay for RX RGMII +set RX_BFE_DELAY 0.5 +set RX_AFE_DELAY 1.8 + +set RX_MAX_DELAY [expr {$CLK125_PERIOD/2 - $RX_BFE_DELAY}] +set RX_MIN_DELAY $RX_AFE_DELAY + +set_input_delay -clock [get_clocks rgmii_rx_clk] -max $RX_MAX_DELAY [get_ports rgmii_rxd*] +set_input_delay -clock [get_clocks rgmii_rx_clk] -min $RX_MIN_DELAY [get_ports rgmii_rxd*] +set_input_delay -clock [get_clocks rgmii_rx_clk] -clock_fall -max -add_delay $RX_MAX_DELAY [get_ports rgmii_rxd*] +set_input_delay -clock [get_clocks rgmii_rx_clk] -clock_fall -min -add_delay $RX_MIN_DELAY [get_ports rgmii_rxd*] + +set_input_delay -clock [get_clocks rgmii_rx_clk] -max $RX_MAX_DELAY [get_ports rgmii_rx_ctl_i] +set_input_delay -clock [get_clocks rgmii_rx_clk] -min $RX_MIN_DELAY [get_ports rgmii_rx_ctl_i] +set_input_delay -clock [get_clocks rgmii_rx_clk] -clock_fall -max -add_delay $RX_MAX_DELAY [get_ports rgmii_rx_ctl_i] +set_input_delay -clock [get_clocks rgmii_rx_clk] -clock_fall -min -add_delay $RX_MIN_DELAY [get_ports rgmii_rx_ctl_i] + +# +--------------+ +--------------+ +# | | | | CLK +# ---+ +--------------+ +----- +# _________ _________ +# XXXX__Data___XXXXXX___Data__XXXXX +# <--|----> <--|----> +# BFE AFE BFE AFE +# Set output delay for TX RGMII +# According to RGMII timing spec, both TX_BFE_DELAY and +# TX_AFE_DELAY should >= 1.2 +set TX_BFE_DELAY 1.6 +set TX_AFE_DELAY 1.6 + +set TX_MAX_DELAY $TX_BFE_DELAY +set TX_MIN_DELAY [expr -$TX_AFE_DELAY] + +set_output_delay -clock [get_clocks rgmii_tx_clk] -max $TX_MAX_DELAY [get_ports rgmii_txd*] +set_output_delay -clock [get_clocks rgmii_tx_clk] -min $TX_MIN_DELAY [get_ports rgmii_txd*] +set_output_delay -clock [get_clocks rgmii_tx_clk] -clock_fall -max -add_delay $TX_MAX_DELAY [get_ports rgmii_txd*] +set_output_delay -clock [get_clocks rgmii_tx_clk] -clock_fall -min -add_delay $TX_MIN_DELAY [get_ports rgmii_txd*] + +set_output_delay -clock [get_clocks rgmii_tx_clk] -max $TX_MAX_DELAY [get_ports rgmii_tx_ctl_o] +set_output_delay -clock [get_clocks rgmii_tx_clk] -min $TX_MIN_DELAY [get_ports rgmii_tx_ctl_o] +set_output_delay -clock [get_clocks rgmii_tx_clk] -clock_fall -max -add_delay $TX_MAX_DELAY [get_ports rgmii_tx_ctl_o] +set_output_delay -clock [get_clocks rgmii_tx_clk] -clock_fall -min -add_delay $TX_MIN_DELAY [get_ports rgmii_tx_ctl_o] + +################# IOB packing ################# +# Set IOB packing for TX RGMII outputs in order to help meet timing +set_property IOB TRUE [get_ports rgmii_tx_clk_o] +set_property IOB TRUE [get_ports rgmii_tx_ctl_o] +set_property IOB TRUE [get_ports rgmii_txd_o[0]] +set_property IOB TRUE [get_ports rgmii_txd_o[1]] +set_property IOB TRUE [get_ports rgmii_txd_o[2]] +set_property IOB TRUE [get_ports rgmii_txd_o[3]] + +############# Ethernet reset path ############# +set_false_path -to [get_ports eth_phy_resetn_o[0]] diff --git a/ethernet_controller/test/Makefile b/ethernet_controller/test/Makefile new file mode 100644 index 0000000..c23b7e9 --- /dev/null +++ b/ethernet_controller/test/Makefile @@ -0,0 +1,35 @@ +.PHONY: all clean test view + + +TOP := $(shell git rev-parse --show-toplevel) +export TEST_DIR := $(TOP)/ethernet_controller/test +export ETH_SRC_DIR := $(TOP)/ethernet_controller/rtl/ +export BASEJUMP_STL_DIR := $(TOP)/ethernet_controller/import/basejump_stl + +TOP_MODULE := wrapper + +DVE ?= dve +VCS ?= vcs + +DVE_OPTS := -full64 +DVE_OPTS += -vpd dump.vpd + +VCS_OPTS := -full64 +VCS_OPTS += -f flist.vcs +VCS_OPTS += -sverilog +VCS_OPTS += +lint=all +lint=noVCDE +lint=noNS +lint=noSVA-UA +VCS_OPTS += -top $(TOP_MODULE) +VCS_OPTS += +incdir+$(BASEJUMP_STL_DIR)/bsg_misc +VCS_OPTS += -assert svaext +VCS_OPTS += -debug_all + + +all: flist.vcs + $(VCS) $(VCS_OPTS) -CFLAGS "$(CFLAGS)" + +flist.vcs: flist.template + cat $^ | envsubst > $@ +view: + $(DVE) $(DVE_OPTS) +clean: + rm -rf csrc simv simv.daidir dump.vcd dump.fst flist.vcs ucli.key tx.txt rx.txt vc_hdrs.h diff --git a/ethernet_controller/test/flist.template b/ethernet_controller/test/flist.template new file mode 100644 index 0000000..9a49dfa --- /dev/null +++ b/ethernet_controller/test/flist.template @@ -0,0 +1,65 @@ + ++incdir+$BASEJUMP_STL_DIR/bsg_misc + +$TEST_DIR/testbench.sv +$TEST_DIR/phy_nonsynth.sv + +$ETH_SRC_DIR/ethernet_receiver.sv +$ETH_SRC_DIR/ethernet_sender.sv +$ETH_SRC_DIR/packet_buffer.sv +$ETH_SRC_DIR/ethernet_controller.sv +$ETH_SRC_DIR/ethernet_control_unit.sv +$ETH_SRC_DIR/interrupt_control_unit.sv +$ETH_SRC_DIR/eth_mac_1g_rgmii_fifo.v +$ETH_SRC_DIR/eth_mac_1g_rgmii.v +$ETH_SRC_DIR/eth_mac_1g.v +$ETH_SRC_DIR/axis_adapter.v +$ETH_SRC_DIR/axis_async_fifo_adapter.v +$ETH_SRC_DIR/axis_async_fifo.v +$ETH_SRC_DIR/axis_fifo.v +$ETH_SRC_DIR/axis_fifo_mem.sv +$ETH_SRC_DIR/axis_gmii_rx.v +$ETH_SRC_DIR/axis_gmii_tx.v +$ETH_SRC_DIR/lfsr.v +$ETH_SRC_DIR/tx_clks_generator.sv +$ETH_SRC_DIR/oddr_clock_downsample_and_right_shift.sv +$ETH_SRC_DIR/rgmii_phy_if.v +$ETH_SRC_DIR/ssio_ddr_in.v + +$BASEJUMP_STL_DIR/bsg_link/bsg_link_oddr_phy.v +$BASEJUMP_STL_DIR/bsg_link/bsg_link_iddr_phy.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_defines.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_counter_clock_downsample.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_strobe.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_buf.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_reduce.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_nor3.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_nand.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_xnor.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_muxi2_gatestack.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_dff.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_dff_en.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_dff_en_bypass.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_dff_reset.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_dff_reset_set_clear.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_dff_reset_en.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_dff_async_reset.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_counter_up_down.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_decode.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_mux_one_hot.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_circular_ptr.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_counter_clear_up.v +$BASEJUMP_STL_DIR/bsg_misc/bsg_edge_detect.v +$BASEJUMP_STL_DIR/bsg_dataflow/bsg_fifo_tracker.v +$BASEJUMP_STL_DIR/bsg_dataflow/bsg_flow_counter.v +$BASEJUMP_STL_DIR/bsg_mem/bsg_mem_1rw_sync_mask_write_byte.v +$BASEJUMP_STL_DIR/bsg_mem/bsg_mem_1rw_sync_mask_write_byte_synth.v +$BASEJUMP_STL_DIR/bsg_mem/bsg_mem_1rw_sync.v +$BASEJUMP_STL_DIR/bsg_mem/bsg_mem_1rw_sync_synth.v +$BASEJUMP_STL_DIR/bsg_mem/bsg_mem_1r1w.v +$BASEJUMP_STL_DIR/bsg_mem/bsg_mem_1r1w_synth.v +$BASEJUMP_STL_DIR/bsg_mem/bsg_mem_1r1w_sync.v +$BASEJUMP_STL_DIR/bsg_mem/bsg_mem_1r1w_sync_synth.v +$BASEJUMP_STL_DIR/bsg_async/bsg_async_fifo.v +$BASEJUMP_STL_DIR/bsg_async/bsg_launch_sync_sync.v +$BASEJUMP_STL_DIR/bsg_async/bsg_async_ptr_gray.v diff --git a/ethernet_controller/test/phy_nonsynth.sv b/ethernet_controller/test/phy_nonsynth.sv new file mode 100644 index 0000000..463a470 --- /dev/null +++ b/ethernet_controller/test/phy_nonsynth.sv @@ -0,0 +1,287 @@ +`default_nettype none + + +// Change the PHY speed here: SPEED_10/100/1000 +`define SPEED_1000 // MHZ + +// MAC dest + SRC dest + EtherType + Payload: +// 7 + 1 + 6 + 6 + 2 + 1500 + 4 +`define PACKET_MAX_SIZE 1526 + +// Currently phy_nonsynth only sends 1 RGMII packet +module phy_nonsynth ( + output bit rgmii_rx_clk_o + , input bit rgmii_rx_rst_i + , output logic [3:0] rgmii_rxd_o + , output logic rgmii_rx_ctl_o + , input bit rgmii_tx_clk_i + , input logic [3:0] rgmii_txd_i + , input logic rgmii_tx_ctl_i + , input logic [1:0] speed_i +); + int unsigned crctable[256] = { + 32'h00000000, 32'h77073096, 32'hEE0E612C, 32'h990951BA, + 32'h076DC419, 32'h706AF48F, 32'hE963A535, 32'h9E6495A3, + 32'h0EDB8832, 32'h79DCB8A4, 32'hE0D5E91E, 32'h97D2D988, + 32'h09B64C2B, 32'h7EB17CBD, 32'hE7B82D07, 32'h90BF1D91, + 32'h1DB71064, 32'h6AB020F2, 32'hF3B97148, 32'h84BE41DE, + 32'h1ADAD47D, 32'h6DDDE4EB, 32'hF4D4B551, 32'h83D385C7, + 32'h136C9856, 32'h646BA8C0, 32'hFD62F97A, 32'h8A65C9EC, + 32'h14015C4F, 32'h63066CD9, 32'hFA0F3D63, 32'h8D080DF5, + 32'h3B6E20C8, 32'h4C69105E, 32'hD56041E4, 32'hA2677172, + 32'h3C03E4D1, 32'h4B04D447, 32'hD20D85FD, 32'hA50AB56B, + 32'h35B5A8FA, 32'h42B2986C, 32'hDBBBC9D6, 32'hACBCF940, + 32'h32D86CE3, 32'h45DF5C75, 32'hDCD60DCF, 32'hABD13D59, + 32'h26D930AC, 32'h51DE003A, 32'hC8D75180, 32'hBFD06116, + 32'h21B4F4B5, 32'h56B3C423, 32'hCFBA9599, 32'hB8BDA50F, + 32'h2802B89E, 32'h5F058808, 32'hC60CD9B2, 32'hB10BE924, + 32'h2F6F7C87, 32'h58684C11, 32'hC1611DAB, 32'hB6662D3D, + 32'h76DC4190, 32'h01DB7106, 32'h98D220BC, 32'hEFD5102A, + 32'h71B18589, 32'h06B6B51F, 32'h9FBFE4A5, 32'hE8B8D433, + 32'h7807C9A2, 32'h0F00F934, 32'h9609A88E, 32'hE10E9818, + 32'h7F6A0DBB, 32'h086D3D2D, 32'h91646C97, 32'hE6635C01, + 32'h6B6B51F4, 32'h1C6C6162, 32'h856530D8, 32'hF262004E, + 32'h6C0695ED, 32'h1B01A57B, 32'h8208F4C1, 32'hF50FC457, + 32'h65B0D9C6, 32'h12B7E950, 32'h8BBEB8EA, 32'hFCB9887C, + 32'h62DD1DDF, 32'h15DA2D49, 32'h8CD37CF3, 32'hFBD44C65, + 32'h4DB26158, 32'h3AB551CE, 32'hA3BC0074, 32'hD4BB30E2, + 32'h4ADFA541, 32'h3DD895D7, 32'hA4D1C46D, 32'hD3D6F4FB, + 32'h4369E96A, 32'h346ED9FC, 32'hAD678846, 32'hDA60B8D0, + 32'h44042D73, 32'h33031DE5, 32'hAA0A4C5F, 32'hDD0D7CC9, + 32'h5005713C, 32'h270241AA, 32'hBE0B1010, 32'hC90C2086, + 32'h5768B525, 32'h206F85B3, 32'hB966D409, 32'hCE61E49F, + 32'h5EDEF90E, 32'h29D9C998, 32'hB0D09822, 32'hC7D7A8B4, + 32'h59B33D17, 32'h2EB40D81, 32'hB7BD5C3B, 32'hC0BA6CAD, + 32'hEDB88320, 32'h9ABFB3B6, 32'h03B6E20C, 32'h74B1D29A, + 32'hEAD54739, 32'h9DD277AF, 32'h04DB2615, 32'h73DC1683, + 32'hE3630B12, 32'h94643B84, 32'h0D6D6A3E, 32'h7A6A5AA8, + 32'hE40ECF0B, 32'h9309FF9D, 32'h0A00AE27, 32'h7D079EB1, + 32'hF00F9344, 32'h8708A3D2, 32'h1E01F268, 32'h6906C2FE, + 32'hF762575D, 32'h806567CB, 32'h196C3671, 32'h6E6B06E7, + 32'hFED41B76, 32'h89D32BE0, 32'h10DA7A5A, 32'h67DD4ACC, + 32'hF9B9DF6F, 32'h8EBEEFF9, 32'h17B7BE43, 32'h60B08ED5, + 32'hD6D6A3E8, 32'hA1D1937E, 32'h38D8C2C4, 32'h4FDFF252, + 32'hD1BB67F1, 32'hA6BC5767, 32'h3FB506DD, 32'h48B2364B, + 32'hD80D2BDA, 32'hAF0A1B4C, 32'h36034AF6, 32'h41047A60, + 32'hDF60EFC3, 32'hA867DF55, 32'h316E8EEF, 32'h4669BE79, + 32'hCB61B38C, 32'hBC66831A, 32'h256FD2A0, 32'h5268E236, + 32'hCC0C7795, 32'hBB0B4703, 32'h220216B9, 32'h5505262F, + 32'hC5BA3BBE, 32'hB2BD0B28, 32'h2BB45A92, 32'h5CB36A04, + 32'hC2D7FFA7, 32'hB5D0CF31, 32'h2CD99E8B, 32'h5BDEAE1D, + 32'h9B64C2B0, 32'hEC63F226, 32'h756AA39C, 32'h026D930A, + 32'h9C0906A9, 32'hEB0E363F, 32'h72076785, 32'h05005713, + 32'h95BF4A82, 32'hE2B87A14, 32'h7BB12BAE, 32'h0CB61B38, + 32'h92D28E9B, 32'hE5D5BE0D, 32'h7CDCEFB7, 32'h0BDBDF21, + 32'h86D3D2D4, 32'hF1D4E242, 32'h68DDB3F8, 32'h1FDA836E, + 32'h81BE16CD, 32'hF6B9265B, 32'h6FB077E1, 32'h18B74777, + 32'h88085AE6, 32'hFF0F6A70, 32'h66063BCA, 32'h11010B5C, + 32'h8F659EFF, 32'hF862AE69, 32'h616BFFD3, 32'h166CCF45, + 32'hA00AE278, 32'hD70DD2EE, 32'h4E048354, 32'h3903B3C2, + 32'hA7672661, 32'hD06016F7, 32'h4969474D, 32'h3E6E77DB, + 32'hAED16A4A, 32'hD9D65ADC, 32'h40DF0B66, 32'h37D83BF0, + 32'hA9BCAE53, 32'hDEBB9EC5, 32'h47B2CF7F, 32'h30B5FFE9, + 32'hBDBDF21C, 32'hCABAC28A, 32'h53B39330, 32'h24B4A3A6, + 32'hBAD03605, 32'hCDD70693, 32'h54DE5729, 32'h23D967BF, + 32'hB3667A2E, 32'hC4614AB8, 32'h5D681B02, 32'h2A6F2B94, + 32'hB40BBE37, 32'hC30C8EA1, 32'h5A05DF1B, 32'h2D02EF8D + }; + + function automatic int unsigned CalcCRC32( + byte unsigned packet[1513:0], + int unsigned len); + int unsigned crc = 32'hffffffff; + + for (int j = 0; j < len; j++) begin + crc = crctable[(crc ^ packet[j]) & 32'hFF] ^ (crc >> 8); + end + return crc ^ 32'hffffffff; + endfunction + + bit clk250_internal; + bit clk250_reset_internal; + always #2 clk250_internal = ~clk250_internal; + + initial begin + rgmii_rxd_o = '0; + rgmii_rx_ctl_o = 1'b0; + clk250_reset_internal = 1'b1; + @(negedge clk250_internal) + clk250_reset_internal = 1'b0; + end + +`ifdef SPEED_1000 + wire [6:0] val_li = 7'd0; +`elsif SPEED_100 + wire [6:0] val_li = 7'd4; +`elsif SPEED_10 + wire [6:0] val_li = 7'd49; +`endif + bit rgmii_rxd_reference_clk; + // X / 2 - 1 + bsg_counter_clock_downsample #( + .width_p(7) + ) rgmii_rxd_reference_clk_downsample ( + .clk_i(clk250_internal) + ,.reset_i(clk250_reset_internal) + ,.val_i(val_li) + ,.clk_r_o(rgmii_rxd_reference_clk) + ); + + task automatic rx_clk_generator(); + forever begin + @(posedge rgmii_rxd_reference_clk); + @(negedge clk250_internal); + rgmii_rx_clk_o = ~rgmii_rx_clk_o; + @(negedge rgmii_rxd_reference_clk) + @(negedge clk250_internal); + rgmii_rx_clk_o = ~rgmii_rx_clk_o; + end + endtask + + task automatic rx_signals_generator( + byte unsigned packet [`PACKET_MAX_SIZE - 1:0], + int unsigned packet_size); + int unsigned packet_idx = 0; + bit [7:0] next_byte; + bit second_half = 1'b0; + + forever begin + @(posedge rgmii_rxd_reference_clk); + if(packet_idx == packet_size + 12) begin + rgmii_rx_ctl_o = 1'b0; + break; + end + next_byte = packet[packet_idx]; +`ifdef SPEED_1000 + rgmii_rxd_o = next_byte[3:0]; +`else + if(second_half) + rgmii_rxd_o = next_byte[7:4]; + else + rgmii_rxd_o = next_byte[3:0]; + +`endif + rgmii_rx_ctl_o = 1'b1; + @(negedge rgmii_rxd_reference_clk) +`ifdef SPEED_1000 + rgmii_rxd_o = next_byte[7:4]; +`endif + rgmii_rx_ctl_o = 1'b1; +`ifndef SPEED_1000 + if(second_half) begin +`endif + // sent out a byte + packet_idx++; +`ifndef SPEED_1000 + end + second_half = ~second_half; +`endif + end + + endtask + + // Send procedure + initial begin + byte unsigned next_char = 0; + // Change the RGMII packet size here: + int unsigned packet_size = 64; + byte unsigned packet [`PACKET_MAX_SIZE - 1:0]; + int unsigned crc; + assert(packet_size <= 1514); + rgmii_rx_ctl_o = 1'b0; + + @(negedge rgmii_rx_rst_i); + // Start sending + // Sending preamble & SFD + for(int i = 0;i < 7;i++) begin + packet[i] = 8'h55; + end + packet[7] = 8'hd5; + // Sending packet content + for(int i = 0;i < packet_size;i++) + packet[i + 8] = next_char++; + // Sending FCS + crc = CalcCRC32(packet[`PACKET_MAX_SIZE - 5:8], packet_size); + for(int i = 0;i < 4;i++) + packet[packet_size + 8 + i] = (crc >> (i * 8)) & 32'hff; + // wait until the speed indicator has been changed accordingly +`ifdef SPEED_1000 + wait(speed_i == 2'b10); +`elsif SPEED_100 + wait(speed_i == 2'b01); +`elsif SPEED_10 + wait(speed_i == 2'b00); +`endif + rx_signals_generator(packet, packet_size); + end + initial begin + @(negedge clk250_reset_internal); + rx_clk_generator(); + end + + // Recv procedure + initial begin + int receiving_flag = 0; + bit second_half = 1'b0; + byte unsigned packet [`PACKET_MAX_SIZE - 1:0]; + logic [7:0] txd; + logic tx_en, tx_er; + int unsigned packet_idx = 0, crc_dut, crc; + forever begin + @(posedge rgmii_tx_clk_i); + tx_en = rgmii_tx_ctl_i; +`ifdef SPEED_1000 + txd[3:0] = rgmii_txd_i; +`else + if(second_half) + txd[7:4] = rgmii_txd_i; + else + txd[3:0] = rgmii_txd_i; +`endif + @(negedge rgmii_tx_clk_i) + tx_er = tx_en ^ rgmii_tx_ctl_i; +`ifdef SPEED_1000 + txd[7:4] = rgmii_txd_i; +`endif + if(tx_er == 1'b0 && tx_en == 1'b1) begin +`ifdef SPEED_1000 + packet[packet_idx++] = txd; + receiving_flag = 1; +`else + if(second_half) begin + packet[packet_idx++] = txd; + receiving_flag = 1; + end + second_half = ~second_half; +`endif + end + else begin + // receive a packet + if(receiving_flag) begin + assert(packet_idx >= 12); + // Check Preamble + for(int i = 0;i < 7;i++) + assert(packet[i] == 8'h55); + // Check SFD + assert(packet[7] == 8'hd5); + crc_dut = 0; + for(int i = 0;i < 4;i++) begin + crc_dut <<= 8; + crc_dut += packet[packet_idx-1-i]; + end + // Check FCS + crc = CalcCRC32(packet[`PACKET_MAX_SIZE - 5:8], packet_idx - 12); + assert(crc == crc_dut); + $display("PHY received a packet without corruption:"); + $display("size(including preamble and CRC): 0x%x", packet_idx); + for(int i = 0;i < packet_idx;i++) + $display("%x", packet[i]); + second_half = 1'b0; + packet_idx = 0; + receiving_flag = 0; + end + end + end + end + +endmodule diff --git a/ethernet_controller/test/testbench.sv b/ethernet_controller/test/testbench.sv new file mode 100644 index 0000000..6055c2b --- /dev/null +++ b/ethernet_controller/test/testbench.sv @@ -0,0 +1,340 @@ +`default_nettype none + +`include "bsg_defines.v" + +// This testbench contains two parts: user_signals and phy_nonsynth. +// user_signals sends and receives packets at user level while phy_nonsynth +// sends and receives packets at RGMII level. + +// Currently user_signals sends only one packet. +// Therefore the expected output should be that both +// MAC and PHY side receive a packet with payload +// 00, 01, ... + +// The write op size can be set to different value +// 2'b00: 1 byte, 2'b01: 2 bytes, 2'b10: 4 bytes +`define WRITE_OP_SIZE 2'b00 +program user_signals #( + parameter data_width_p + , localparam addr_width_lp = 14 +) +( + input bit clk_i + , output bit reset_o + , input bit clk250_i + , output bit clk250_reset_o + , output bit tx_clk_gen_reset_o + , input bit tx_clk_i + , output bit tx_reset_o + , input bit rx_clk_i + , output bit rx_reset_o + + , output logic [addr_width_lp-1:0] addr_o + , output logic write_en_o + , output logic read_en_o + , output logic [data_width_p/8-1:0] write_mask_o + , output logic [data_width_p-1:0] write_data_o + , input logic [data_width_p-1:0] read_data_i + + , input logic rx_interrupt_pending_i + , input logic tx_interrupt_pending_i +); + + function automatic int unsigned op_size_to_mask( + int unsigned op_size_i + , int unsigned addr_i); + int unsigned mask_tmp, mask_o; + mask_tmp = '0; + mask_o = '0; + case (op_size_i) + 'b00: + mask_tmp = (8'h1 << addr_i[2:0]); + 'b01: + mask_tmp = (8'h3 << {addr_i[2:1], 1'b0}); + 'b10: + mask_tmp = (8'hF << {addr_i[2], 2'b0}); + 'b11: + mask_tmp = 8'hFF; + default: begin $display("unknown op_size"); $finish; end + endcase + case (data_width_p / 8) + 4: mask_o = mask_tmp[3:0] | mask_tmp[7:4]; + 8: mask_o = mask_tmp; + endcase + return mask_o; + endfunction + + task automatic write_addr ( + input int unsigned address + ,input int unsigned data + ,input int unsigned op_size); + assert((address % ('b1 << op_size)) == 0); + write_mask_o = op_size_to_mask(op_size, address); + addr_o = address; + write_data_o = data; + write_en_o = 1'b1; + @(posedge clk_i) + write_en_o = 1'b0; + endtask + + task automatic read_addr ( + input int unsigned address + ,output int unsigned read_data + ,input int unsigned op_size); + assert((address % ('b1 << op_size)) == 0); + write_mask_o = op_size_to_mask(op_size, address); + addr_o = address; + read_en_o = 1'b1; + @(posedge clk_i) + read_data = read_data_i; + read_en_o = 1'b0; + endtask + + task automatic send_packet ( + input int unsigned size + ,input int unsigned op_size); + bit [7:0] next_char = '0; + int unsigned word, wait_cnt, read_data; + bit [data_width_p-1:0] write_data; + assert((size % ('b1 << op_size)) == 0); + assert((data_width_p / 8) >= ('b1 << op_size)); + word = size / ('b1 << op_size); + // Sending packet with size 'size' + // wait until TX is ready + wait_cnt = 0; + forever begin + read_addr(32'h101C, read_data, 32'b10); + if(read_data == 32'd1) + break; + if(wait_cnt > 10000) begin + $display("MAC TX Timeout"); + $finish; + end + wait_cnt = wait_cnt + 1; + end + for(int unsigned i = 0;i < word;i++) begin + write_data = '0; + for(int j = 0;j < (1 << op_size);j++) + write_data[j * 8+:8] = next_char++; + write_data = write_data << (i * ('d1 << op_size) * 8 % data_width_p); + $display("DEBUG: %0x %0x", 'h0800 + (i * ('d1 << op_size)), write_data); + write_addr('h0800 + (i * ('d1 << op_size)), write_data, op_size); + end + + // write size + write_addr(32'h1028, size, 32'b10); + // send + write_addr(32'h1018, 32'b0, 32'b10); + endtask + + task automatic receive_packet (); + int unsigned read_data, size; + int unsigned word; + int unsigned op_size = $clog2(data_width_p/8); + + int unsigned ret, i, wait_cnt = 0; + // wait, read packets + forever begin + read_addr(32'h1010, read_data, 32'b10); + if(read_data == 32'd1) + break; + if(wait_cnt > 10000) begin + $display("MAC RX Timeout"); + $finish; + end + wait_cnt = wait_cnt + 1; + end + read_addr(32'h1004, size, 32'b10); + assert((size % ('b1 << op_size)) == 0); + $display("MAC received a packet:"); + $display("size: 0x%x", size); + $display("payload:"); + + word = size / ('b1 << op_size); + for(i = 0;i < word;i = i + 1) begin + read_addr(32'h0000 + i * (1 << op_size), read_data, op_size); + $display("%x", read_data); + end + // clear RX pending + write_addr(32'h1010, 32'd1, 32'b10); + endtask + +/* + // Set timeout + initial begin + #1000000 + $fclose(tx_fd); + $fclose(rx_fd); + $display("Timeout"); + $finish; + end +*/ + bit [1:0] write_op_size; + initial begin + write_en_o = 1'b0; + read_en_o = 1'b0; + clk250_reset_o = 1'b1; + reset_o = 1'b1; + rx_reset_o = 1'b1; + tx_reset_o = 1'b1; + tx_clk_gen_reset_o = 1'b1; + @(posedge clk250_i) + // Now tx_clk is toggling + tx_clk_gen_reset_o = 1'b0; + // Assuming rx_clk can be the slowest clock + // Wait for 3 * 2.5MHZ cycle starting from a rx_clk posedge clk + // This should be long enough for flushing all the unknown values + // in bsg_launch_sync_sync + @(posedge rx_clk_i); + #1200; // ((1/2.5) * 1000) * 3 + @(posedge clk250_i); + clk250_reset_o = 1'b0; + @(posedge tx_clk_i); + tx_reset_o = 1'b0; + @(posedge rx_clk_i) + rx_reset_o = 1'b0; + @(posedge clk_i) +/* + // Assuming rx_clk can be the slowest clock + // Wait for 3 * 2.5MHZ cycle starting from a rx_clk posedge clk + // This should be long enough for flushing all the unknown values + // in bsg_launch_sync_sync + @(posedge rx_clk_i); + #1200; // ((1/2.5) * 1000) * 3 + // Reset Deassertion order: + // clk250_reset_o -> tx_reset_o -+-> reset_o + // ^ + // -------- rx_reset_o -----------+ + @(posedge clk250_i); + clk250_reset_o = 1'b0; + @(posedge tx_clk_i); + @(posedge tx_clk_i); + @(posedge tx_clk_i); + tx_reset_o = 1'b0; + @(posedge rx_clk_i) + @(posedge rx_clk_i) + @(posedge rx_clk_i) + rx_reset_o = 1'b0; + @(posedge clk_i) +*/ + reset_o = 1'b0; + // test starts + // Set the write op size here: + write_op_size = `WRITE_OP_SIZE; + send_packet(32'd128, write_op_size); + receive_packet(); + // Wait for some period of time + for(int i = 0;i < 4096;i++) + @(posedge clk_i); + $display("Test completed"); + $finish; + end +endprogram + + +module wrapper(); + parameter data_width_p = 32; + localparam addr_width_lp = 14; + initial begin + $vcdplusfile("dump.vpd"); + $vcdpluson(); + end + bit clk_li; + bit reset_li; + bit clk250_li; + bit clk250_reset_li; + bit tx_clk_gen_reset_li; + bit tx_clk_lo; + bit tx_reset_li; + bit rx_clk_lo; + bit rx_reset_li; + + logic [addr_width_lp-1:0] addr_li; + logic write_en_li; + logic read_en_li; + logic [data_width_p/8-1:0] write_mask_li; + logic [data_width_p-1:0] write_data_li; + logic [data_width_p-1:0] read_data_lo; + + logic rx_interrupt_pending_lo; + logic tx_interrupt_pending_lo; + + logic rgmii_rx_clk_li; + logic [3:0] rgmii_rxd_li; + logic rgmii_rx_ctl_li; + logic rgmii_tx_clk_lo; + logic [3:0] rgmii_txd_lo; + logic rgmii_tx_ctl_lo; + + always #25 clk_li = ~clk_li; // 20 MHZ + always #2 clk250_li = ~clk250_li; // 250 MHZ + + user_signals #( + .data_width_p(data_width_p) + ) user_signals ( + .clk_i(clk_li) + ,.reset_o(reset_li) + ,.clk250_i(clk250_li) + ,.clk250_reset_o(clk250_reset_li) + ,.tx_clk_gen_reset_o(tx_clk_gen_reset_li) + ,.tx_clk_i(tx_clk_lo) + ,.tx_reset_o(tx_reset_li) + ,.rx_clk_i(rx_clk_lo) + ,.rx_reset_o(rx_reset_li) + + ,.addr_o(addr_li) + ,.write_en_o(write_en_li) + ,.read_en_o(read_en_li) + ,.write_mask_o(write_mask_li) + ,.write_data_o(write_data_li) + ,.read_data_i(read_data_lo) + + ,.rx_interrupt_pending_i(rx_interrupt_pending_lo) + ,.tx_interrupt_pending_i(tx_interrupt_pending_lo) + + ); + + phy_nonsynth phy ( + .rgmii_rx_clk_o(rgmii_rx_clk_li) + ,.rgmii_rx_rst_i(rx_reset_li) + ,.rgmii_rxd_o(rgmii_rxd_li) + ,.rgmii_rx_ctl_o(rgmii_rx_ctl_li) + ,.rgmii_tx_clk_i(rgmii_tx_clk_lo) + ,.rgmii_txd_i(rgmii_txd_lo) + ,.rgmii_tx_ctl_i(rgmii_tx_ctl_lo) + ,.speed_i(dut.mac.speed) + ); + + ethernet_controller #( + .data_width_p(data_width_p) + ) dut ( + .clk_i(clk_li) + ,.reset_i(reset_li) + ,.clk250_i(clk250_li) + ,.clk250_reset_i(clk250_reset_li) + ,.tx_clk_gen_reset_i(tx_clk_gen_reset_li) + ,.tx_clk_o(tx_clk_lo) + ,.tx_reset_i(tx_reset_li) + ,.rx_clk_o(rx_clk_lo) + ,.rx_reset_i(rx_reset_li) + + ,.addr_i(addr_li) + ,.write_en_i(write_en_li) + ,.read_en_i(read_en_li) + ,.write_mask_i(write_mask_li) + ,.write_data_i(write_data_li) + ,.read_data_o(read_data_lo) + + ,.rx_interrupt_pending_o(rx_interrupt_pending_lo) + ,.tx_interrupt_pending_o(tx_interrupt_pending_lo) + + ,.rgmii_rx_clk_i(rgmii_rx_clk_li) + ,.rgmii_rxd_i(rgmii_rxd_li) + ,.rgmii_rx_ctl_i(rgmii_rx_ctl_li) + ,.rgmii_tx_clk_o(rgmii_tx_clk_lo) + ,.rgmii_txd_o(rgmii_txd_lo) + ,.rgmii_tx_ctl_o(rgmii_tx_ctl_lo) + ); + +endmodule + diff --git a/ethernet_controller/v/axis_adapter.v b/ethernet_controller/v/axis_adapter.v new file mode 100644 index 0000000..235baf6 --- /dev/null +++ b/ethernet_controller/v/axis_adapter.v @@ -0,0 +1,554 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +/* + * AXI4-Stream bus width adapter + */ +module axis_adapter # +( + // Width of input AXI stream interface in bits + parameter S_DATA_WIDTH = 8, + // Propagate tkeep signal on input interface + // If disabled, tkeep assumed to be 1'b1 + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + // tkeep signal width (words per cycle) on input interface + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + // Width of output AXI stream interface in bits + parameter M_DATA_WIDTH = 8, + // Propagate tkeep signal on output interface + // If disabled, tkeep assumed to be 1'b1 + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + // tkeep signal width (words per cycle) on output interface + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [S_DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [M_DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +// force keep width to 1 when disabled +parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; +parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; + +// bus word sizes (must be identical) +parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT; +parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT; +// output bus is wider +parameter EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT; +// total data and keep widths +parameter DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH; +parameter KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT; +// required number of segments in wider bus +parameter SEGMENT_COUNT = EXPAND_BUS ? (M_KEEP_WIDTH_INT / S_KEEP_WIDTH_INT) : (S_KEEP_WIDTH_INT / M_KEEP_WIDTH_INT); +parameter SEGMENT_COUNT_WIDTH = SEGMENT_COUNT == 1 ? 1 : $clog2(SEGMENT_COUNT); +// data width and keep width per segment +parameter SEGMENT_DATA_WIDTH = DATA_WIDTH / SEGMENT_COUNT; +parameter SEGMENT_KEEP_WIDTH = KEEP_WIDTH / SEGMENT_COUNT; + +// bus width assertions +initial begin + if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end +end + +// state register +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_TRANSFER_IN = 3'd1, + STATE_TRANSFER_OUT = 3'd2; + +reg [2:0] state_reg, state_next; + +reg [SEGMENT_COUNT_WIDTH-1:0] segment_count_reg, segment_count_next; + +reg last_segment; + +reg [DATA_WIDTH-1:0] temp_tdata_reg, temp_tdata_next; +reg [KEEP_WIDTH-1:0] temp_tkeep_reg, temp_tkeep_next; +reg temp_tlast_reg, temp_tlast_next; +reg [ID_WIDTH-1:0] temp_tid_reg, temp_tid_next; +reg [DEST_WIDTH-1:0] temp_tdest_reg, temp_tdest_next; +reg [USER_WIDTH-1:0] temp_tuser_reg, temp_tuser_next; + +// internal datapath +reg [M_DATA_WIDTH-1:0] m_axis_tdata_int; +reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg s_axis_tready_reg, s_axis_tready_next; + +assign s_axis_tready = s_axis_tready_reg; + +always @* begin + state_next = STATE_IDLE; + + segment_count_next = segment_count_reg; + + last_segment = 0; + + temp_tdata_next = temp_tdata_reg; + temp_tkeep_next = temp_tkeep_reg; + temp_tlast_next = temp_tlast_reg; + temp_tid_next = temp_tid_reg; + temp_tdest_next = temp_tdest_reg; + temp_tuser_next = temp_tuser_reg; + + if (EXPAND_BUS) begin + m_axis_tdata_int = temp_tdata_reg; + m_axis_tkeep_int = temp_tkeep_reg; + m_axis_tlast_int = temp_tlast_reg; + end else begin + m_axis_tdata_int = {M_DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {M_KEEP_WIDTH{1'b0}}; + m_axis_tlast_int = 1'b0; + end + m_axis_tvalid_int = 1'b0; + m_axis_tid_int = temp_tid_reg; + m_axis_tdest_int = temp_tdest_reg; + m_axis_tuser_int = temp_tuser_reg; + + s_axis_tready_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - no data in registers + if (SEGMENT_COUNT == 1) begin + // output and input same width - just act like a register + + // accept data next cycle if output register ready next cycle + s_axis_tready_next = m_axis_tready_int_early; + + // transfer through + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + state_next = STATE_IDLE; + end else if (EXPAND_BUS) begin + // output bus is wider + + // accept new data + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store it in data register + + // pass complete input word, zero-extended to temp register + temp_tdata_next = s_axis_tdata; + temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + // first input segment complete + segment_count_next = 1; + + if (s_axis_tlast) begin + // got last signal on first segment, so output it + s_axis_tready_next = 1'b0; + state_next = STATE_TRANSFER_OUT; + end else begin + // otherwise, transfer in the rest of the words + s_axis_tready_next = 1'b1; + state_next = STATE_TRANSFER_IN; + end + end else begin + state_next = STATE_IDLE; + end + end else begin + // output bus is narrower + + // accept new data + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store it in data register + segment_count_next = 0; + + // is this the last segment? + if (SEGMENT_COUNT == 1) begin + // last segment by counter value + last_segment = 1'b1; + end else if (S_KEEP_ENABLE && s_axis_tkeep[SEGMENT_KEEP_WIDTH-1:0] != {SEGMENT_KEEP_WIDTH{1'b1}}) begin + // last segment by tkeep fall in current segment + last_segment = 1'b1; + end else if (S_KEEP_ENABLE && s_axis_tkeep[(SEGMENT_KEEP_WIDTH*2)-1:SEGMENT_KEEP_WIDTH] == {SEGMENT_KEEP_WIDTH{1'b0}}) begin + // last segment by tkeep fall at end of current segment + last_segment = 1'b1; + end else begin + last_segment = 1'b0; + end + + // pass complete input word, zero-extended to temp register + temp_tdata_next = s_axis_tdata; + temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + // short-circuit and get first word out the door + m_axis_tdata_int = s_axis_tdata[SEGMENT_DATA_WIDTH-1:0]; + m_axis_tkeep_int = s_axis_tkeep[SEGMENT_KEEP_WIDTH-1:0]; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = s_axis_tlast & last_segment; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + if (m_axis_tready_int_reg) begin + // if output register is ready for first word, then move on to the next one + segment_count_next = 1; + end + + if (!last_segment || !m_axis_tready_int_reg) begin + // continue outputting words + s_axis_tready_next = 1'b0; + state_next = STATE_TRANSFER_OUT; + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_IDLE; + end + end + end + STATE_TRANSFER_IN: begin + // transfer word to temp registers + // only used when output is wider + + // accept new data + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store in data register + + temp_tdata_next[segment_count_reg*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH] = s_axis_tdata; + temp_tkeep_next[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + segment_count_next = segment_count_reg + 1; + + if ((segment_count_reg == SEGMENT_COUNT-1) || s_axis_tlast) begin + // terminated by counter or tlast signal, output complete word + // read input word next cycle if output will be ready + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_TRANSFER_OUT; + end else begin + // more words to read + s_axis_tready_next = 1'b1; + state_next = STATE_TRANSFER_IN; + end + end else begin + state_next = STATE_TRANSFER_IN; + end + end + STATE_TRANSFER_OUT: begin + // transfer word to output registers + + if (EXPAND_BUS) begin + // output bus is wider + + // do not accept new data + s_axis_tready_next = 1'b0; + + // single-cycle output of entire stored word (output wider) + m_axis_tdata_int = temp_tdata_reg; + m_axis_tkeep_int = temp_tkeep_reg; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = temp_tlast_reg; + m_axis_tid_int = temp_tid_reg; + m_axis_tdest_int = temp_tdest_reg; + m_axis_tuser_int = temp_tuser_reg; + + if (m_axis_tready_int_reg) begin + // word transfer out + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in + + // pass complete input word, zero-extended to temp register + temp_tdata_next = s_axis_tdata; + temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + // first input segment complete + segment_count_next = 1; + + if (s_axis_tlast) begin + // got last signal on first segment, so output it + s_axis_tready_next = 1'b0; + state_next = STATE_TRANSFER_OUT; + end else begin + // otherwise, transfer in the rest of the words + s_axis_tready_next = 1'b1; + state_next = STATE_TRANSFER_IN; + end + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_TRANSFER_OUT; + end + end else begin + // output bus is narrower + + // do not accept new data + s_axis_tready_next = 1'b0; + + // is this the last segment? + if (segment_count_reg == SEGMENT_COUNT-1) begin + // last segment by counter value + last_segment = 1'b1; + end else if (temp_tkeep_reg[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] != {SEGMENT_KEEP_WIDTH{1'b1}}) begin + // last segment by tkeep fall in current segment + last_segment = 1'b1; + end else if (temp_tkeep_reg[(segment_count_reg+1)*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] == {SEGMENT_KEEP_WIDTH{1'b0}}) begin + // last segment by tkeep fall at end of current segment + last_segment = 1'b1; + end else begin + last_segment = 1'b0; + end + + // output current part of stored word (output narrower) + m_axis_tdata_int = temp_tdata_reg[segment_count_reg*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH]; + m_axis_tkeep_int = temp_tkeep_reg[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH]; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = temp_tlast_reg && last_segment; + m_axis_tid_int = temp_tid_reg; + m_axis_tdest_int = temp_tdest_reg; + m_axis_tuser_int = temp_tuser_reg; + + if (m_axis_tready_int_reg) begin + // word transfer out + + segment_count_next = segment_count_reg + 1; + + if (last_segment) begin + // terminated by counter or tlast signal + + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + // more words to write + state_next = STATE_TRANSFER_OUT; + end + end else begin + state_next = STATE_TRANSFER_OUT; + end + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + segment_count_reg <= '0; + + state_reg <= STATE_IDLE; + s_axis_tready_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_axis_tready_reg <= s_axis_tready_next; + end + + segment_count_reg <= segment_count_next; + + temp_tdata_reg <= temp_tdata_next; + temp_tkeep_reg <= temp_tkeep_next; + temp_tlast_reg <= temp_tlast_next; + temp_tid_reg <= temp_tid_next; + temp_tdest_reg <= temp_tdest_next; + temp_tuser_reg <= temp_tuser_next; +end + +// output datapath logic +reg [M_DATA_WIDTH-1:0] m_axis_tdata_reg; +reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_reg; +reg m_axis_tvalid_reg, m_axis_tvalid_next; +reg m_axis_tlast_reg; +reg [ID_WIDTH-1:0] m_axis_tid_reg; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg; +reg [USER_WIDTH-1:0] m_axis_tuser_reg; + +reg [M_DATA_WIDTH-1:0] temp_m_axis_tdata_reg; +reg [M_KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg; +reg temp_m_axis_tvalid_reg, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = M_KEEP_ENABLE ? m_axis_tkeep_reg : {M_KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/ethernet_controller/v/axis_async_fifo.v b/ethernet_controller/v/axis_async_fifo.v new file mode 100644 index 0000000..51aef89 --- /dev/null +++ b/ethernet_controller/v/axis_async_fifo.v @@ -0,0 +1,400 @@ +/* + +Copyright (c) 2014-2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +/* + * AXI4-Stream asynchronous FIFO + */ +module axis_async_fifo # +( + // FIFO depth in words + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter DEPTH = 4096, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // number of output pipeline registers + parameter PIPELINE_OUTPUT = 2, + // Frame FIFO mode - operate on frames instead of cycles + // When set, m_axis_tvalid will not be deasserted within a frame + // Requires LAST_ENABLE set + parameter FRAME_FIFO = 0, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames larger than FIFO + // Requires FRAME_FIFO set + parameter DROP_OVERSIZE_FRAME = FRAME_FIFO, + // Drop frames marked bad + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_WHEN_FULL = 0, + // If 1, put a bsg_async_fifo at input side + // If 0, put a bsg_async_fifo at output side + parameter upstream_async_fifo_p +) +( + /* + * AXI input + */ + input wire s_clk, + input wire s_rst, + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + input wire m_clk, + input wire m_rst, + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire s_status_overflow, + output wire s_status_bad_frame, + output wire s_status_good_frame, + output wire m_status_overflow, + output wire m_status_bad_frame, + output wire m_status_good_frame +); + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +// axis_fifo +logic fifo_clk; +logic fifo_rst; +logic [DATA_WIDTH-1:0] fifo_m_axis_tdata; +logic [KEEP_WIDTH-1:0] fifo_m_axis_tkeep; +logic fifo_m_axis_tvalid; +logic fifo_m_axis_tready; +logic fifo_m_axis_tlast; +logic [ID_WIDTH-1:0] fifo_m_axis_tid; +logic [DEST_WIDTH-1:0] fifo_m_axis_tdest; +logic [USER_WIDTH-1:0] fifo_m_axis_tuser; + +logic [DATA_WIDTH-1:0] fifo_s_axis_tdata; +logic [KEEP_WIDTH-1:0] fifo_s_axis_tkeep; +logic fifo_s_axis_tvalid; +logic fifo_s_axis_tready; +logic fifo_s_axis_tlast; +logic [ID_WIDTH-1:0] fifo_s_axis_tid; +logic [DEST_WIDTH-1:0] fifo_s_axis_tdest; +logic [USER_WIDTH-1:0] fifo_s_axis_tuser; + +// Status +logic fifo_status_overflow; +logic fifo_status_bad_frame; +logic fifo_status_good_frame; +logic fifo_status_overflow_synced; +logic fifo_status_bad_frame_synced; +logic fifo_status_good_frame_synced; + +// bsg_async_fifo +logic w_enq_li; +logic [WIDTH-1:0] w_data_li; +logic w_full_lo; +logic r_deq_li; +logic [WIDTH-1:0] r_data_lo; +logic r_valid_lo; + +// bsg_launch_sync_sync +logic iclk_li; +logic iclk_reset_li; +logic oclk_li; +logic oclk_reset_li; + +if(upstream_async_fifo_p) begin: up + + // fifo clock, reset + assign fifo_clk = m_clk; + assign fifo_rst = m_rst; + // bsg_launch_sync_sync clock, reset + assign iclk_li = m_clk; + assign iclk_reset_li = m_rst; + assign oclk_li = s_clk; + assign oclk_reset_li = s_rst; + + // AXIS input -> bsg_async_fifo + assign w_data_li[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign w_data_li[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign w_data_li[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign w_data_li[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign w_data_li[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign w_data_li[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; + + assign w_enq_li = s_axis_tvalid & s_axis_tready; + assign s_axis_tready = ~w_full_lo; + + // bsg_async_fifo -> axis_fifo + assign fifo_s_axis_tvalid = r_valid_lo; + assign r_deq_li = fifo_s_axis_tvalid & fifo_s_axis_tready; + + assign fifo_s_axis_tdata = r_data_lo[DATA_WIDTH-1:0]; + if(KEEP_ENABLE) + assign fifo_s_axis_tkeep = r_data_lo[KEEP_OFFSET +: KEEP_WIDTH]; + else + assign fifo_s_axis_tkeep = {KEEP_WIDTH{1'b1}}; + if(LAST_ENABLE) + assign fifo_s_axis_tlast = r_data_lo[LAST_OFFSET]; + else + assign fifo_s_axis_tlast = 1'b1; + if(ID_ENABLE) + assign fifo_s_axis_tid = r_data_lo[ID_OFFSET +: ID_WIDTH]; + else + assign fifo_s_axis_tid = {ID_WIDTH{1'b0}}; + if(DEST_ENABLE) + assign fifo_s_axis_tdest = r_data_lo[DEST_OFFSET +: DEST_WIDTH]; + else + assign fifo_s_axis_tdest = {DEST_WIDTH{1'b0}}; + if(USER_ENABLE) + assign fifo_s_axis_tuser = r_data_lo[USER_OFFSET +: USER_WIDTH]; + else + assign fifo_s_axis_tuser = {USER_WIDTH{1'b0}}; + + // axis_fifo -> AXIS output + assign m_axis_tdata = fifo_m_axis_tdata; + assign m_axis_tkeep = fifo_m_axis_tkeep; + assign m_axis_tvalid = fifo_m_axis_tvalid; + assign m_axis_tlast = fifo_m_axis_tlast; + assign m_axis_tid = fifo_m_axis_tid; + assign m_axis_tdest = fifo_m_axis_tdest; + assign m_axis_tuser = fifo_m_axis_tuser; + assign fifo_m_axis_tready = m_axis_tready; + // Status + assign s_status_overflow = fifo_status_overflow_synced; + assign s_status_bad_frame = fifo_status_bad_frame_synced; + assign s_status_good_frame = fifo_status_good_frame_synced; + assign m_status_overflow = fifo_status_overflow; + assign m_status_bad_frame = fifo_status_bad_frame; + assign m_status_good_frame = fifo_status_good_frame; + +end else begin: down + + // fifo clock, reset + assign fifo_clk = s_clk; + assign fifo_rst = s_rst; + // bsg_launch_sync_sync clock, reset + assign iclk_li = s_clk; + assign iclk_reset_li = s_rst; + assign oclk_li = m_clk; + assign oclk_reset_li = m_rst; + + // AXIS input -> axis_fifo + + assign fifo_s_axis_tdata = s_axis_tdata; + assign fifo_s_axis_tkeep = s_axis_tkeep; + assign fifo_s_axis_tvalid = s_axis_tvalid; + assign fifo_s_axis_tlast = s_axis_tlast; + assign fifo_s_axis_tid = s_axis_tid; + assign fifo_s_axis_tdest = s_axis_tdest; + assign fifo_s_axis_tuser = s_axis_tuser; + assign s_axis_tready = fifo_s_axis_tready; + + // axis_fifo -> bsg_async_fifo + + assign w_data_li[DATA_WIDTH-1:0] = fifo_m_axis_tdata; + if (KEEP_ENABLE) assign w_data_li[KEEP_OFFSET +: KEEP_WIDTH] = fifo_m_axis_tkeep; + if (LAST_ENABLE) assign w_data_li[LAST_OFFSET] = fifo_m_axis_tlast; + if (ID_ENABLE) assign w_data_li[ID_OFFSET +: ID_WIDTH] = fifo_m_axis_tid; + if (DEST_ENABLE) assign w_data_li[DEST_OFFSET +: DEST_WIDTH] = fifo_m_axis_tdest; + if (USER_ENABLE) assign w_data_li[USER_OFFSET +: USER_WIDTH] = fifo_m_axis_tuser; + assign w_enq_li = fifo_m_axis_tvalid & fifo_m_axis_tready; + assign fifo_m_axis_tready = ~w_full_lo; + + // bsg_async_fifo -> AXIS output + assign r_deq_li = m_axis_tvalid & m_axis_tready; + assign m_axis_tvalid = r_valid_lo; + + assign m_axis_tdata = r_data_lo[DATA_WIDTH-1:0]; + if(KEEP_ENABLE) + assign m_axis_tkeep = r_data_lo[KEEP_OFFSET +: KEEP_WIDTH]; + else + assign m_axis_tkeep = {KEEP_WIDTH{1'b1}}; + if(LAST_ENABLE) + assign m_axis_tlast = r_data_lo[LAST_OFFSET]; + else + assign m_axis_tlast = 1'b1; + if(ID_ENABLE) + assign m_axis_tid = r_data_lo[ID_OFFSET +: ID_WIDTH]; + else + assign m_axis_tid = {ID_WIDTH{1'b0}}; + if(DEST_ENABLE) + assign m_axis_tdest = r_data_lo[DEST_OFFSET +: DEST_WIDTH]; + else + assign m_axis_tdest = {DEST_WIDTH{1'b0}}; + if(USER_ENABLE) + assign m_axis_tuser = r_data_lo[USER_OFFSET +: USER_WIDTH]; + else + assign m_axis_tuser = {USER_WIDTH{1'b0}}; + + // Status + assign s_status_overflow = fifo_status_overflow; + assign s_status_bad_frame = fifo_status_bad_frame; + assign s_status_good_frame = fifo_status_good_frame; + assign m_status_overflow = fifo_status_overflow_synced; + assign m_status_bad_frame = fifo_status_bad_frame_synced; + assign m_status_good_frame = fifo_status_good_frame_synced; + +end + +bsg_async_fifo #( + .lg_size_p(3) + ,.width_p(WIDTH) +) cdc ( + .w_clk_i(s_clk) + ,.w_reset_i(s_rst) + ,.w_enq_i(w_enq_li) + ,.w_data_i(w_data_li) + ,.w_full_o(w_full_lo) + ,.r_clk_i(m_clk) + ,.r_reset_i(m_rst) + ,.r_deq_i(r_deq_li) + ,.r_data_o(r_data_lo) + ,.r_valid_o(r_valid_lo) +); + +axis_fifo #( + .DEPTH(DEPTH) + ,.DATA_WIDTH(DATA_WIDTH) + ,.KEEP_ENABLE(KEEP_ENABLE) + ,.KEEP_WIDTH(KEEP_WIDTH) + ,.LAST_ENABLE(LAST_ENABLE) + ,.ID_ENABLE(ID_ENABLE) + ,.ID_WIDTH(ID_WIDTH) + ,.DEST_ENABLE(DEST_ENABLE) + ,.DEST_WIDTH(DEST_WIDTH) + ,.USER_ENABLE(USER_ENABLE) + ,.USER_WIDTH(USER_WIDTH) + ,.PIPELINE_OUTPUT(PIPELINE_OUTPUT) + ,.FRAME_FIFO(FRAME_FIFO) + ,.USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE) + ,.USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK) + ,.DROP_OVERSIZE_FRAME(DROP_OVERSIZE_FRAME) + ,.DROP_BAD_FRAME(DROP_BAD_FRAME) + ,.DROP_WHEN_FULL(DROP_WHEN_FULL) +) fifo ( + .clk(fifo_clk) + ,.rst(fifo_rst) + + ,.s_axis_tdata (fifo_s_axis_tdata) + ,.s_axis_tkeep (fifo_s_axis_tkeep) + ,.s_axis_tvalid(fifo_s_axis_tvalid) + ,.s_axis_tready(fifo_s_axis_tready) + ,.s_axis_tlast (fifo_s_axis_tlast) + ,.s_axis_tid (fifo_s_axis_tid) + ,.s_axis_tdest (fifo_s_axis_tdest) + ,.s_axis_tuser (fifo_s_axis_tuser) + + ,.m_axis_tdata (fifo_m_axis_tdata) + ,.m_axis_tkeep (fifo_m_axis_tkeep) + ,.m_axis_tvalid(fifo_m_axis_tvalid) + ,.m_axis_tready(fifo_m_axis_tready) + ,.m_axis_tlast (fifo_m_axis_tlast) + ,.m_axis_tid (fifo_m_axis_tid) + ,.m_axis_tdest (fifo_m_axis_tdest) + ,.m_axis_tuser (fifo_m_axis_tuser) + + ,.status_overflow (fifo_status_overflow) + ,.status_bad_frame (fifo_status_bad_frame) + ,.status_good_frame(fifo_status_good_frame) +); +logic overflow_sync1_reg, overflow_sync3_reg, overflow_sync4_reg; +logic bad_frame_sync1_reg, bad_frame_sync3_reg, bad_frame_sync4_reg; +logic good_frame_sync1_reg, good_frame_sync3_reg, good_frame_sync4_reg; + +wire overflow_sync1_next = overflow_sync1_reg ^ fifo_status_overflow; +wire bad_frame_sync1_next = bad_frame_sync1_reg ^ fifo_status_bad_frame; +wire good_frame_sync1_next = good_frame_sync1_reg ^ fifo_status_good_frame; + +assign fifo_status_overflow_synced = overflow_sync3_reg ^ overflow_sync4_reg; +assign fifo_status_bad_frame_synced = bad_frame_sync3_reg ^ bad_frame_sync4_reg; +assign fifo_status_good_frame_synced = good_frame_sync3_reg ^ good_frame_sync4_reg; + +bsg_dff_reset #( + .width_p(3) +) status ( + .clk_i(oclk_li) + ,.reset_i(oclk_reset_li) + ,.data_i({overflow_sync3_reg, bad_frame_sync3_reg, good_frame_sync3_reg}) + ,.data_o({overflow_sync4_reg, bad_frame_sync4_reg, good_frame_sync4_reg}) +); + +bsg_launch_sync_sync #( + .width_p(3), + .use_negedge_for_launch_p(0), + .use_async_reset_p(0) +) status_synchronizer ( + .iclk_i(iclk_li), + .iclk_reset_i(iclk_reset_li), + .oclk_i(oclk_li), + .iclk_data_i({overflow_sync1_next, bad_frame_sync1_next, good_frame_sync1_next}), + .iclk_data_o({overflow_sync1_reg, bad_frame_sync1_reg, good_frame_sync1_reg}), + .oclk_data_o({overflow_sync3_reg, bad_frame_sync3_reg, good_frame_sync3_reg}) +); + +endmodule diff --git a/ethernet_controller/v/axis_async_fifo_adapter.v b/ethernet_controller/v/axis_async_fifo_adapter.v new file mode 100644 index 0000000..d2943de --- /dev/null +++ b/ethernet_controller/v/axis_async_fifo_adapter.v @@ -0,0 +1,360 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`include "bsg_defines.v" + +/* + * AXI4-Stream asynchronous FIFO with width converter + */ +module axis_async_fifo_adapter # +( + // FIFO depth in words + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter DEPTH = 4096, + // Width of input AXI stream interface in bits + parameter S_DATA_WIDTH = 8, + // Propagate tkeep signal on input interface + // If disabled, tkeep assumed to be 1'b1 + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + // tkeep signal width (words per cycle) on input interface + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + // Width of output AXI stream interface in bits + parameter M_DATA_WIDTH = 8, + // Propagate tkeep signal on output interface + // If disabled, tkeep assumed to be 1'b1 + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + // tkeep signal width (words per cycle) on output interface + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // number of output pipeline registers + parameter PIPELINE_OUTPUT = 2, + // Frame FIFO mode - operate on frames instead of cycles + // When set, m_axis_tvalid will not be deasserted within a frame + // Requires LAST_ENABLE set + parameter FRAME_FIFO = 0, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames larger than FIFO + // Requires FRAME_FIFO set + parameter DROP_OVERSIZE_FRAME = FRAME_FIFO, + // Drop frames marked bad + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_WHEN_FULL = 0, + // put an async FIFO either on upstream or downstream + parameter `BSG_INV_PARAM(upstream_async_fifo_p) +) +( + /* + * AXI input + */ + input wire s_clk, + input wire s_rst, + input wire [S_DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + input wire m_clk, + input wire m_rst, + output wire [M_DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire s_status_overflow, + output wire s_status_bad_frame, + output wire s_status_good_frame, + output wire m_status_overflow, + output wire m_status_bad_frame, + output wire m_status_good_frame +); + +// force keep width to 1 when disabled +parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; +parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; + +// bus word sizes (must be identical) +parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT; +parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT; +// output bus is wider +parameter EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT; +// total data and keep widths +parameter DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH; +parameter KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT; + +// bus width assertions +initial begin + if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] pre_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] pre_fifo_axis_tkeep; +wire pre_fifo_axis_tvalid; +wire pre_fifo_axis_tready; +wire pre_fifo_axis_tlast; +wire [ID_WIDTH-1:0] pre_fifo_axis_tid; +wire [DEST_WIDTH-1:0] pre_fifo_axis_tdest; +wire [USER_WIDTH-1:0] pre_fifo_axis_tuser; + +wire [DATA_WIDTH-1:0] post_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] post_fifo_axis_tkeep; +wire post_fifo_axis_tvalid; +wire post_fifo_axis_tready; +wire post_fifo_axis_tlast; +wire [ID_WIDTH-1:0] post_fifo_axis_tid; +wire [DEST_WIDTH-1:0] post_fifo_axis_tdest; +wire [USER_WIDTH-1:0] post_fifo_axis_tuser; + +generate + +if (M_KEEP_WIDTH == S_KEEP_WIDTH) begin + + // same width, no adapter needed + + assign pre_fifo_axis_tdata = s_axis_tdata; + assign pre_fifo_axis_tkeep = s_axis_tkeep; + assign pre_fifo_axis_tvalid = s_axis_tvalid; + assign s_axis_tready = pre_fifo_axis_tready; + assign pre_fifo_axis_tlast = s_axis_tlast; + assign pre_fifo_axis_tid = s_axis_tid; + assign pre_fifo_axis_tdest = s_axis_tdest; + assign pre_fifo_axis_tuser = s_axis_tuser; + + assign m_axis_tdata = post_fifo_axis_tdata; + assign m_axis_tkeep = post_fifo_axis_tkeep; + assign m_axis_tvalid = post_fifo_axis_tvalid; + assign post_fifo_axis_tready = m_axis_tready; + assign m_axis_tlast = post_fifo_axis_tlast; + assign m_axis_tid = post_fifo_axis_tid; + assign m_axis_tdest = post_fifo_axis_tdest; + assign m_axis_tuser = post_fifo_axis_tuser; + + +end else if (EXPAND_BUS) begin + + // output wider, adapt width before FIFO + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(s_clk), + .rst(s_rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(pre_fifo_axis_tdata), + .m_axis_tkeep(pre_fifo_axis_tkeep), + .m_axis_tvalid(pre_fifo_axis_tvalid), + .m_axis_tready(pre_fifo_axis_tready), + .m_axis_tlast(pre_fifo_axis_tlast), + .m_axis_tid(pre_fifo_axis_tid), + .m_axis_tdest(pre_fifo_axis_tdest), + .m_axis_tuser(pre_fifo_axis_tuser) + ); + + assign m_axis_tdata = post_fifo_axis_tdata; + assign m_axis_tkeep = post_fifo_axis_tkeep; + assign m_axis_tvalid = post_fifo_axis_tvalid; + assign post_fifo_axis_tready = m_axis_tready; + assign m_axis_tlast = post_fifo_axis_tlast; + assign m_axis_tid = post_fifo_axis_tid; + assign m_axis_tdest = post_fifo_axis_tdest; + assign m_axis_tuser = post_fifo_axis_tuser; + +end else begin + + // input wider, adapt width after FIFO + + assign pre_fifo_axis_tdata = s_axis_tdata; + assign pre_fifo_axis_tkeep = s_axis_tkeep; + assign pre_fifo_axis_tvalid = s_axis_tvalid; + assign s_axis_tready = pre_fifo_axis_tready; + assign pre_fifo_axis_tlast = s_axis_tlast; + assign pre_fifo_axis_tid = s_axis_tid; + assign pre_fifo_axis_tdest = s_axis_tdest; + assign pre_fifo_axis_tuser = s_axis_tuser; + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(m_clk), + .rst(m_rst), + // AXI input + .s_axis_tdata(post_fifo_axis_tdata), + .s_axis_tkeep(post_fifo_axis_tkeep), + .s_axis_tvalid(post_fifo_axis_tvalid), + .s_axis_tready(post_fifo_axis_tready), + .s_axis_tlast(post_fifo_axis_tlast), + .s_axis_tid(post_fifo_axis_tid), + .s_axis_tdest(post_fifo_axis_tdest), + .s_axis_tuser(post_fifo_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) + ); + +end + +endgenerate + +axis_async_fifo #( + .DEPTH(DEPTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(EXPAND_BUS ? M_KEEP_ENABLE : S_KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .PIPELINE_OUTPUT(PIPELINE_OUTPUT), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_OVERSIZE_FRAME(DROP_OVERSIZE_FRAME), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL), + .upstream_async_fifo_p(upstream_async_fifo_p) +) +fifo_inst ( + // AXI input + .s_clk(s_clk), + .s_rst(s_rst), + .s_axis_tdata(pre_fifo_axis_tdata), + .s_axis_tkeep(pre_fifo_axis_tkeep), + .s_axis_tvalid(pre_fifo_axis_tvalid), + .s_axis_tready(pre_fifo_axis_tready), + .s_axis_tlast(pre_fifo_axis_tlast), + .s_axis_tid(pre_fifo_axis_tid), + .s_axis_tdest(pre_fifo_axis_tdest), + .s_axis_tuser(pre_fifo_axis_tuser), + // AXI output + .m_clk(m_clk), + .m_rst(m_rst), + .m_axis_tdata(post_fifo_axis_tdata), + .m_axis_tkeep(post_fifo_axis_tkeep), + .m_axis_tvalid(post_fifo_axis_tvalid), + .m_axis_tready(post_fifo_axis_tready), + .m_axis_tlast(post_fifo_axis_tlast), + .m_axis_tid(post_fifo_axis_tid), + .m_axis_tdest(post_fifo_axis_tdest), + .m_axis_tuser(post_fifo_axis_tuser), + // Status + .s_status_overflow(s_status_overflow), + .s_status_bad_frame(s_status_bad_frame), + .s_status_good_frame(s_status_good_frame), + .m_status_overflow(m_status_overflow), + .m_status_bad_frame(m_status_bad_frame), + .m_status_good_frame(m_status_good_frame) +); + +endmodule + +`BSG_ABSTRACT_MODULE(axis_async_fifo_adapter) diff --git a/ethernet_controller/v/axis_fifo.v b/ethernet_controller/v/axis_fifo.v new file mode 100644 index 0000000..c9f233d --- /dev/null +++ b/ethernet_controller/v/axis_fifo.v @@ -0,0 +1,353 @@ +/* + +Copyright (c) 2013-2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall + +/* + * AXI4-Stream FIFO + */ +module axis_fifo # +( + // FIFO depth in words + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter DEPTH = 4096, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = ((DATA_WIDTH+7)/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // number of output pipeline registers + parameter PIPELINE_OUTPUT = 2, + // Frame FIFO mode - operate on frames instead of cycles + // When set, m_axis_tvalid will not be deasserted within a frame + // Requires LAST_ENABLE set + parameter FRAME_FIFO = 0, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames larger than FIFO + // Requires FRAME_FIFO set + parameter DROP_OVERSIZE_FRAME = FRAME_FIFO, + // Drop frames marked bad + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_WHEN_FULL = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire status_overflow, + output wire status_bad_frame, + output wire status_good_frame +); + +parameter ADDR_WIDTH = (KEEP_ENABLE && KEEP_WIDTH > 1) ? $clog2(DEPTH/KEEP_WIDTH) : $clog2(DEPTH); + +// check configuration +initial begin + if (PIPELINE_OUTPUT < 1) begin + $error("Error: PIPELINE_OUTPUT must be at least 1 (instance %m)"); + $finish; + end + + if (FRAME_FIFO && !LAST_ENABLE) begin + $error("Error: FRAME_FIFO set requires LAST_ENABLE set (instance %m)"); + $finish; + end + + if (DROP_OVERSIZE_FRAME && !FRAME_FIFO) begin + $error("Error: DROP_OVERSIZE_FRAME set requires FRAME_FIFO set (instance %m)"); + $finish; + end + + if (DROP_BAD_FRAME && !(FRAME_FIFO && DROP_OVERSIZE_FRAME)) begin + $error("Error: DROP_BAD_FRAME set requires FRAME_FIFO and DROP_OVERSIZE_FRAME set (instance %m)"); + $finish; + end + + if (DROP_WHEN_FULL && !(FRAME_FIFO && DROP_OVERSIZE_FRAME)) begin + $error("Error: DROP_WHEN_FULL set requires FRAME_FIFO and DROP_OVERSIZE_FRAME set (instance %m)"); + $finish; + end + + if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin + $error("Error: Invalid USER_BAD_FRAME_MASK value (instance %m)"); + $finish; + end +end + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + + +reg [ADDR_WIDTH:0] wr_ptr_reg; +reg [ADDR_WIDTH:0] wr_ptr_cur_reg; +reg [ADDR_WIDTH:0] rd_ptr_reg; + +wire [WIDTH-1:0] s_axis; + +logic mem_w_v_li; +logic [ADDR_WIDTH-1:0] mem_w_addr_li; +wire [WIDTH-1:0] mem_w_data_li = s_axis; +logic mem_r_v_li; +wire [ADDR_WIDTH-1:0] mem_r_addr_li = rd_ptr_reg[ADDR_WIDTH-1:0]; +logic [WIDTH-1:0] mem_r_data_lo; + +reg [PIPELINE_OUTPUT-1:0] m_axis_tvalid_pipe_reg; + +// full when first MSB different but rest same +wire full = wr_ptr_reg == (rd_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); +wire full_cur = wr_ptr_cur_reg == (rd_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); +// empty when pointers match exactly +wire empty = wr_ptr_reg == rd_ptr_reg; +// overflow within packet +wire full_wr = wr_ptr_reg == (wr_ptr_cur_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); + +reg drop_frame_reg; +reg send_frame_reg; +reg overflow_reg; +reg bad_frame_reg; +reg good_frame_reg; + +assign s_axis_tready = FRAME_FIFO ? (!full_cur || (full_wr && DROP_OVERSIZE_FRAME) || DROP_WHEN_FULL) : !full; + +generate + assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; +endgenerate + +assign m_axis_tvalid = m_axis_tvalid_pipe_reg[PIPELINE_OUTPUT-1]; + +assign m_axis_tdata = mem_r_data_lo[DATA_WIDTH-1:0]; +assign m_axis_tkeep = KEEP_ENABLE ? mem_r_data_lo[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; +assign m_axis_tlast = LAST_ENABLE ? mem_r_data_lo[LAST_OFFSET] : 1'b1; +assign m_axis_tid = ID_ENABLE ? mem_r_data_lo[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? mem_r_data_lo[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? mem_r_data_lo[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}}; + +assign status_overflow = overflow_reg; +assign status_bad_frame = bad_frame_reg; +assign status_good_frame = good_frame_reg; + +// Write logic +always @(posedge clk) begin + overflow_reg <= 1'b0; + bad_frame_reg <= 1'b0; + good_frame_reg <= 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + // transfer in + if (!FRAME_FIFO) begin + // normal FIFO mode + wr_ptr_reg <= wr_ptr_reg + 1; + end else if ((full_cur && DROP_WHEN_FULL) || (full_wr && DROP_OVERSIZE_FRAME) || drop_frame_reg) begin + // full, packet overflow, or currently dropping frame + // drop frame + drop_frame_reg <= 1'b1; + if (s_axis_tlast) begin + // end of frame, reset write pointer + wr_ptr_cur_reg <= wr_ptr_reg; + drop_frame_reg <= 1'b0; + overflow_reg <= 1'b1; + end + end else begin + // store it + wr_ptr_cur_reg <= wr_ptr_cur_reg + 1; + if (s_axis_tlast || (!DROP_OVERSIZE_FRAME && (full_wr || send_frame_reg))) begin + // end of frame or send frame + send_frame_reg <= !s_axis_tlast; + if (s_axis_tlast && DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(s_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin + // bad packet, reset write pointer + wr_ptr_cur_reg <= wr_ptr_reg; + bad_frame_reg <= 1'b1; + end else begin + // good packet or packet overflow, update write pointer + wr_ptr_reg <= wr_ptr_cur_reg + 1; + good_frame_reg <= s_axis_tlast; + end + end + end + end else if (s_axis_tvalid && full_wr && FRAME_FIFO && !DROP_OVERSIZE_FRAME) begin + // data valid with packet overflow + // update write pointer + send_frame_reg <= 1'b1; + wr_ptr_reg <= wr_ptr_cur_reg; + end + + if (rst) begin + wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}}; + + drop_frame_reg <= 1'b0; + send_frame_reg <= 1'b0; + overflow_reg <= 1'b0; + bad_frame_reg <= 1'b0; + good_frame_reg <= 1'b0; + end +end + +// Read logic +integer j; + +always @(posedge clk) begin + if (m_axis_tready) begin + // output ready; invalidate stage + m_axis_tvalid_pipe_reg[PIPELINE_OUTPUT-1] <= 1'b0; + end + + for (j = PIPELINE_OUTPUT-1; j > 0; j = j - 1) begin + if (m_axis_tready || ((~m_axis_tvalid_pipe_reg) >> j)) begin + // output ready or bubble in pipeline; transfer down pipeline + m_axis_tvalid_pipe_reg[j] <= m_axis_tvalid_pipe_reg[j-1]; + m_axis_tvalid_pipe_reg[j-1] <= 1'b0; + end + end + + if (m_axis_tready || ~m_axis_tvalid_pipe_reg) begin + // output ready or bubble in pipeline; read new data from FIFO + m_axis_tvalid_pipe_reg[0] <= 1'b0; + if (!empty) begin + // not empty, increment pointer + m_axis_tvalid_pipe_reg[0] <= 1'b1; + rd_ptr_reg <= rd_ptr_reg + 1; + end + end + + if (rst) begin + rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + m_axis_tvalid_pipe_reg <= {PIPELINE_OUTPUT{1'b0}}; + end +end + +//Write logic +always @(*) begin + mem_w_addr_li = '0; + mem_w_v_li = 1'b0; + if (s_axis_tready && s_axis_tvalid) begin + // transfer in + if (!FRAME_FIFO) begin + // normal FIFO mode + mem_w_addr_li = wr_ptr_reg[ADDR_WIDTH-1:0]; + mem_w_v_li = 1'b1; + end else if ((full_cur && DROP_WHEN_FULL) || (full_wr && DROP_OVERSIZE_FRAME) || drop_frame_reg) begin + end else begin + // store it + mem_w_addr_li = wr_ptr_cur_reg[ADDR_WIDTH-1:0]; + mem_w_v_li = 1'b1; + end + end +end + +// Read logic +always @(*) begin + mem_r_v_li = 1'b0; + if (m_axis_tready || ~m_axis_tvalid_pipe_reg) begin + // output ready or bubble in pipeline; read new data from FIFO + if (!empty) begin + mem_r_v_li = 1'b1; + end + end +end + + +axis_fifo_mem #( + .width_p(WIDTH) + ,.els_p(2**ADDR_WIDTH) + ,.pipeline_output_p(PIPELINE_OUTPUT) +) mem ( + .clk_i(clk) + ,.reset_i(rst) + ,.w_v_i (mem_w_v_li) + ,.w_addr_i(mem_w_addr_li) + ,.w_data_i(mem_w_data_li) + ,.r_v_i (mem_r_v_li) + ,.r_addr_i(mem_r_addr_li) + ,.r_data_o(mem_r_data_lo) + + ,.output_ready_i(m_axis_tready) + ,.valid_pipe_reg_i(m_axis_tvalid_pipe_reg) +); + +endmodule + +`resetall diff --git a/ethernet_controller/v/axis_fifo_mem.sv b/ethernet_controller/v/axis_fifo_mem.sv new file mode 100644 index 0000000..141d27f --- /dev/null +++ b/ethernet_controller/v/axis_fifo_mem.sv @@ -0,0 +1,95 @@ +`include "bsg_defines.v" + +module axis_fifo_mem #( + parameter `BSG_INV_PARAM(width_p) + , parameter `BSG_INV_PARAM(els_p) + , parameter `BSG_INV_PARAM(pipeline_output_p) + , localparam addr_width_lp=`BSG_SAFE_CLOG2(els_p) +) +( + input clk_i + , input reset_i + , input w_v_i + , input [addr_width_lp-1:0] w_addr_i + , input [`BSG_SAFE_MINUS(width_p, 1):0] w_data_i + + , input r_v_i + , input [addr_width_lp-1:0] r_addr_i + , output logic [`BSG_SAFE_MINUS(width_p, 1):0] r_data_o + + , input output_ready_i + , input [pipeline_output_p-1:0] valid_pipe_reg_i +); + +if (pipeline_output_p > 1) begin + // This block of code is extracted from Alex's axis_fifo. It can work + // even if pipeline_output_p == 1, but with that setting we might as + // well use bsg_mem so that this module will also be suitable for ASIC. + logic [width_p-1:0] pipe_reg [pipeline_output_p-1:0]; + + integer j; + always @(posedge clk_i) begin + for (j = pipeline_output_p - 1; j > 0; j = j - 1) begin + if (output_ready_i || ((~valid_pipe_reg_i) >> j)) begin + // output ready or bubble in pipeline; transfer down pipeline + pipe_reg[j] <= pipe_reg[j-1]; + end + end + end + + logic [width_p-1:0] mem [els_p-1:0]; + always @(posedge clk_i) begin + if(w_v_i) + mem[w_addr_i] <= w_data_i; + end + + always @(posedge clk_i) begin + if(r_v_i) + pipe_reg[0] <= mem[r_addr_i]; + end + + assign r_data_o = pipe_reg[pipeline_output_p-1]; + +end else begin + // Equivalent code when pipeline_output_p == 1 but suitable for ASIC + wire unused = | {output_ready_i, valid_pipe_reg_i}; + + logic [`BSG_SAFE_MINUS(width_p, 1):0] r_data_lo; + // latch last read + logic read_en_r; + bsg_dff #( + .width_p(1) + ) read_en_dff ( + .clk_i(clk_i) + ,.data_i(r_v_i) + ,.data_o(read_en_r) + ); + + bsg_dff_en_bypass #( + .width_p(width_p) + ) dff_bypass ( + .clk_i(clk_i) + ,.en_i(read_en_r) + ,.data_i(r_data_lo) + ,.data_o(r_data_o) + ); + + bsg_mem_1r1w_sync #( + .width_p(width_p) + ,.els_p(els_p) + ) mem ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.w_v_i(w_v_i) + ,.w_addr_i(w_addr_i) + ,.w_data_i(w_data_i) + + ,.r_v_i(r_v_i) + ,.r_addr_i(r_addr_i) + ,.r_data_o(r_data_lo) + ); +end + +endmodule + +`BSG_ABSTRACT_MODULE(axis_fifo_mem) diff --git a/ethernet_controller/v/axis_gmii_rx.v b/ethernet_controller/v/axis_gmii_rx.v new file mode 100644 index 0000000..795ae8c --- /dev/null +++ b/ethernet_controller/v/axis_gmii_rx.v @@ -0,0 +1,350 @@ +/* + +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 + +/* + * AXI4-Stream GMII frame receiver (GMII in, AXI out) + */ +module axis_gmii_rx # +( + parameter DATA_WIDTH = 8, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * GMII input + */ + input wire [DATA_WIDTH-1:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Control + */ + input wire clk_enable, + input wire mii_select, + + /* + * Status + */ + output wire start_packet, + output wire error_bad_frame, + output wire error_bad_fcs +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 8) begin + $error("Error: Interface width must be 8"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PAYLOAD = 3'd1, + STATE_WAIT_LAST = 3'd2; + +reg [2:0] state_reg, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg mii_odd; +reg mii_locked; + +reg [DATA_WIDTH-1:0] gmii_rxd_d0; +reg [DATA_WIDTH-1:0] gmii_rxd_d1; +reg [DATA_WIDTH-1:0] gmii_rxd_d2; +reg [DATA_WIDTH-1:0] gmii_rxd_d3; +reg [DATA_WIDTH-1:0] gmii_rxd_d4; + +reg gmii_rx_dv_d0; +reg gmii_rx_dv_d1; +reg gmii_rx_dv_d2; +reg gmii_rx_dv_d3; +reg gmii_rx_dv_d4; + +reg gmii_rx_er_d0; +reg gmii_rx_er_d1; +reg gmii_rx_er_d2; +reg gmii_rx_er_d3; +reg gmii_rx_er_d4; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg, m_axis_tdata_next; +reg m_axis_tvalid_reg, m_axis_tvalid_next; +reg m_axis_tlast_reg, m_axis_tlast_next; +reg m_axis_tuser_reg, m_axis_tuser_next; + +reg start_packet_reg, start_packet_next; +reg error_bad_frame_reg, error_bad_frame_next; +reg error_bad_fcs_reg, error_bad_fcs_next; + +reg [PTP_TS_WIDTH-1:0] ptp_ts_reg, ptp_ts_next; + +reg [31:0] crc_state; +wire [31:0] crc_next; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; + +if(PTP_TS_ENABLE) + assign m_axis_tuser = {ptp_ts_reg, m_axis_tuser_reg}; +else + assign m_axis_tuser = m_axis_tuser_reg; + +assign start_packet = start_packet_reg; +assign error_bad_frame = error_bad_frame_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(gmii_rxd_d4), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tvalid_next = 1'b0; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + start_packet_next = 1'b0; + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + ptp_ts_next = ptp_ts_reg; + + if (!clk_enable) begin + // clock disabled - hold state + state_next = state_reg; + end else if (mii_select && !mii_odd) begin + // MII even cycle - hold state + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (gmii_rx_dv_d4 && !gmii_rx_er_d4 && gmii_rxd_d4 == ETH_SFD) begin + ptp_ts_next = ptp_ts; + start_packet_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + update_crc = 1'b1; + + m_axis_tdata_next = gmii_rxd_d4; + m_axis_tvalid_next = 1'b1; + + if (gmii_rx_dv_d4 && gmii_rx_er_d4) begin + // error + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_WAIT_LAST; + end else if (!gmii_rx_dv) begin + // end of packet + m_axis_tlast_next = 1'b1; + if (gmii_rx_er_d0 || gmii_rx_er_d1 || gmii_rx_er_d2 || gmii_rx_er_d3) begin + // error received in FCS bytes + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + end else if ({gmii_rxd_d0, gmii_rxd_d1, gmii_rxd_d2, gmii_rxd_d3} == ~crc_next) begin + // FCS good + m_axis_tuser_next = 1'b0; + end else begin + // FCS bad + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_WAIT_LAST: begin + // wait for end of packet + + if (~gmii_rx_dv) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase + end +end + +always @(posedge clk) begin + state_reg <= state_next; + + ptp_ts_reg <= ptp_ts_next; + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + if (clk_enable) begin + if (mii_select) begin + mii_odd <= !mii_odd; + + if (mii_locked) begin + mii_locked <= gmii_rx_dv; + end else if (gmii_rx_dv && {gmii_rxd[3:0], gmii_rxd_d0[7:4]} == ETH_SFD) begin + mii_locked <= 1'b1; + mii_odd <= 1'b1; + end + + gmii_rxd_d0 <= {gmii_rxd[3:0], gmii_rxd_d0[7:4]}; + + if (mii_odd) begin + gmii_rxd_d1 <= gmii_rxd_d0; + gmii_rxd_d2 <= gmii_rxd_d1; + gmii_rxd_d3 <= gmii_rxd_d2; + gmii_rxd_d4 <= gmii_rxd_d3; + + gmii_rx_dv_d0 <= gmii_rx_dv & gmii_rx_dv_d0; + gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv; + gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv; + gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv; + gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv; + + gmii_rx_er_d0 <= gmii_rx_er | gmii_rx_er_d0; + gmii_rx_er_d1 <= gmii_rx_er_d0; + gmii_rx_er_d2 <= gmii_rx_er_d1; + gmii_rx_er_d3 <= gmii_rx_er_d2; + gmii_rx_er_d4 <= gmii_rx_er_d3; + end else begin + gmii_rx_dv_d0 <= gmii_rx_dv; + gmii_rx_er_d0 <= gmii_rx_er; + end + end else begin + gmii_rxd_d0 <= gmii_rxd; + gmii_rxd_d1 <= gmii_rxd_d0; + gmii_rxd_d2 <= gmii_rxd_d1; + gmii_rxd_d3 <= gmii_rxd_d2; + gmii_rxd_d4 <= gmii_rxd_d3; + + gmii_rx_dv_d0 <= gmii_rx_dv; + gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv; + gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv; + gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv; + gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv; + + gmii_rx_er_d0 <= gmii_rx_er; + gmii_rx_er_d1 <= gmii_rx_er_d0; + gmii_rx_er_d2 <= gmii_rx_er_d1; + gmii_rx_er_d3 <= gmii_rx_er_d2; + gmii_rx_er_d4 <= gmii_rx_er_d3; + end + end + + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + + start_packet_reg <= start_packet_next; + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + + if (rst) begin + gmii_rxd_d0 <= {DATA_WIDTH{1'b0}}; + gmii_rxd_d1 <= {DATA_WIDTH{1'b0}}; + gmii_rxd_d2 <= {DATA_WIDTH{1'b0}}; + gmii_rxd_d3 <= {DATA_WIDTH{1'b0}}; + gmii_rxd_d4 <= {DATA_WIDTH{1'b0}}; + ptp_ts_reg <= '0; + + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_reg <= 1'b0; + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + mii_locked <= 1'b0; + mii_odd <= 1'b0; + + gmii_rx_dv_d0 <= 1'b0; + gmii_rx_dv_d1 <= 1'b0; + gmii_rx_dv_d2 <= 1'b0; + gmii_rx_dv_d3 <= 1'b0; + gmii_rx_dv_d4 <= 1'b0; + end +end + +endmodule diff --git a/ethernet_controller/v/axis_gmii_tx.v b/ethernet_controller/v/axis_gmii_tx.v new file mode 100644 index 0000000..25336d0 --- /dev/null +++ b/ethernet_controller/v/axis_gmii_tx.v @@ -0,0 +1,449 @@ +/* + +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 + +/* + * AXI4-Stream GMII frame transmitter (AXI in, GMII out) + */ +module axis_gmii_tx # +( + parameter DATA_WIDTH = 8, + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * GMII output + */ + output wire [DATA_WIDTH-1:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Control + */ + input wire clk_enable, + input wire mii_select, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire start_packet, + output wire error_underflow +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 8) begin + $error("Error: Interface width must be 8"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PREAMBLE = 3'd1, + STATE_PAYLOAD = 3'd2, + STATE_LAST = 3'd3, + STATE_PAD = 3'd4, + STATE_FCS = 3'd5, + STATE_WAIT_END = 3'd6, + STATE_IFG = 3'd7; + +reg [2:0] state_reg, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [7:0] s_tdata_reg, s_tdata_next; + +reg mii_odd_reg, mii_odd_next; +reg [3:0] mii_msn_reg, mii_msn_next; + +reg [15:0] frame_ptr_reg, frame_ptr_next; + +reg [7:0] gmii_txd_reg, gmii_txd_next; +reg gmii_tx_en_reg, gmii_tx_en_next; +reg gmii_tx_er_reg, gmii_tx_er_next; + +reg s_axis_tready_reg, s_axis_tready_next; + +reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg, m_axis_ptp_ts_next; +reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg, m_axis_ptp_ts_tag_next; +reg m_axis_ptp_ts_valid_reg, m_axis_ptp_ts_valid_next; + +reg start_packet_reg, start_packet_next; +reg error_underflow_reg, error_underflow_next; + +reg [31:0] crc_state; +wire [31:0] crc_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign gmii_txd = gmii_txd_reg; +assign gmii_tx_en = gmii_tx_en_reg; +assign gmii_tx_er = gmii_tx_er_reg; + +assign m_axis_ptp_ts = PTP_TS_ENABLE ? m_axis_ptp_ts_reg : {PTP_TS_WIDTH{1'b0}}; +assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : {PTP_TAG_WIDTH{1'b0}}; +assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0; + +assign start_packet = start_packet_reg; +assign error_underflow = error_underflow_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_tdata_reg), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + mii_odd_next = mii_odd_reg; + mii_msn_next = mii_msn_reg; + + frame_ptr_next = frame_ptr_reg; + + s_axis_tready_next = 1'b0; + + s_tdata_next = s_tdata_reg; + + m_axis_ptp_ts_next = m_axis_ptp_ts_reg; + m_axis_ptp_ts_tag_next = m_axis_ptp_ts_tag_reg; + m_axis_ptp_ts_valid_next = 1'b0; + + gmii_txd_next = {DATA_WIDTH{1'b0}}; + gmii_tx_en_next = 1'b0; + gmii_tx_er_next = 1'b0; + + start_packet_next = 1'b0; + error_underflow_next = 1'b0; + + if (!clk_enable) begin + // clock disabled - hold state and outputs + gmii_txd_next = gmii_txd_reg; + gmii_tx_en_next = gmii_tx_en_reg; + gmii_tx_er_next = gmii_tx_er_reg; + state_next = state_reg; + end else if (mii_select && mii_odd_reg) begin + // MII odd cycle - hold state, output MSN + mii_odd_next = 1'b0; + gmii_txd_next = {4'd0, mii_msn_reg}; + gmii_tx_en_next = gmii_tx_en_reg; + gmii_tx_er_next = gmii_tx_er_reg; + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + mii_odd_next = 1'b0; + + if (s_axis_tvalid) begin + mii_odd_next = 1'b1; + frame_ptr_next = 16'd1; + gmii_txd_next = ETH_PRE; + gmii_tx_en_next = 1'b1; + state_next = STATE_PREAMBLE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + // send preamble + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = ETH_PRE; + gmii_tx_en_next = 1'b1; + + if (frame_ptr_reg == 16'd6) begin + s_axis_tready_next = 1'b1; + s_tdata_next = s_axis_tdata; + state_next = STATE_PREAMBLE; + end else if (frame_ptr_reg == 16'd7) begin + // end of preamble; start payload + frame_ptr_next = 16'd0; + if (s_axis_tready_reg) begin + s_axis_tready_next = 1'b1; + s_tdata_next = s_axis_tdata; + end + gmii_txd_next = ETH_SFD; + m_axis_ptp_ts_next = ptp_ts; + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_next = 1'b1; + start_packet_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_PREAMBLE; + end + end + STATE_PAYLOAD: begin + // send payload + + update_crc = 1'b1; + s_axis_tready_next = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = s_tdata_reg; + gmii_tx_en_next = 1'b1; + + s_tdata_next = s_axis_tdata; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = !s_axis_tready_reg; + if (s_axis_tuser[0]) begin + gmii_tx_er_next = 1'b1; + frame_ptr_next = 1'b0; + state_next = STATE_IFG; + end else begin + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + gmii_tx_er_next = 1'b1; + frame_ptr_next = 16'd0; + error_underflow_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + STATE_LAST: begin + // last payload word + + update_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = s_tdata_reg; + gmii_tx_en_next = 1'b1; + + if (ENABLE_PADDING && frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + s_tdata_next = 8'd0; + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + STATE_PAD: begin + // send padding + + update_crc = 1'b1; + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = 8'd0; + gmii_tx_en_next = 1'b1; + + s_tdata_next = 8'd0; + + if (frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + STATE_FCS: begin + // send FCS + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + case (frame_ptr_reg) + 2'd0: gmii_txd_next = ~crc_state[7:0]; + 2'd1: gmii_txd_next = ~crc_state[15:8]; + 2'd2: gmii_txd_next = ~crc_state[23:16]; + 2'd3: gmii_txd_next = ~crc_state[31:24]; + endcase + gmii_tx_en_next = 1'b1; + + if (frame_ptr_reg < 3) begin + state_next = STATE_FCS; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_IFG; + end + end + STATE_WAIT_END: begin + // wait for end of frame + + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + s_axis_tready_next = 1'b1; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = 1'b0; + if (frame_ptr_reg < ifg_delay-1) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + STATE_IFG: begin + // send IFG + + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + if (frame_ptr_reg < ifg_delay-1) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + endcase + + if (mii_select) begin + mii_msn_next = gmii_txd_next[7:4]; + gmii_txd_next[7:4] = 4'd0; + end + end +end + +always @(posedge clk) begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + m_axis_ptp_ts_reg <= m_axis_ptp_ts_next; + m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next; + m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next; + + mii_odd_reg <= mii_odd_next; + mii_msn_reg <= mii_msn_next; + + s_tdata_reg <= s_tdata_next; + + s_axis_tready_reg <= s_axis_tready_next; + + gmii_txd_reg <= gmii_txd_next; + gmii_tx_en_reg <= gmii_tx_en_next; + gmii_tx_er_reg <= gmii_tx_er_next; + + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + + start_packet_reg <= start_packet_next; + error_underflow_reg <= error_underflow_next; + + if (rst) begin + s_tdata_reg <= 8'd0; + mii_odd_reg <= 1'b0; + mii_msn_reg <= 4'b0; + m_axis_ptp_ts_reg <= '0; + m_axis_ptp_ts_tag_reg <= '0; + + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + s_axis_tready_reg <= 1'b0; + + m_axis_ptp_ts_valid_reg <= 1'b0; + + gmii_tx_en_reg <= 1'b0; + gmii_tx_er_reg <= 1'b0; + + start_packet_reg <= 1'b0; + error_underflow_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end +end + +endmodule diff --git a/ethernet_controller/v/eth_mac_1g.v b/ethernet_controller/v/eth_mac_1g.v new file mode 100644 index 0000000..98a7b95 --- /dev/null +++ b/ethernet_controller/v/eth_mac_1g.v @@ -0,0 +1,165 @@ +/* + +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 + +/* + * 1G Ethernet MAC + */ +module eth_mac_1g # +( + parameter DATA_WIDTH = 8, + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_PTP_TS_ENABLE = 0, + parameter TX_PTP_TS_WIDTH = 96, + parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE, + parameter TX_PTP_TAG_WIDTH = 16, + parameter RX_PTP_TS_ENABLE = 0, + parameter RX_PTP_TS_WIDTH = 96, + parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1, + parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire [TX_USER_WIDTH-1:0] tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire [RX_USER_WIDTH-1:0] rx_axis_tuser, + + /* + * GMII interface + */ + input wire [DATA_WIDTH-1:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + output wire [DATA_WIDTH-1:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * PTP + */ + input wire [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts, + input wire [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts, + output wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts, + output wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag, + output wire tx_axis_ptp_ts_valid, + + /* + * Control + */ + input wire rx_clk_enable, + input wire tx_clk_enable, + input wire rx_mii_select, + input wire tx_mii_select, + + /* + * Status + */ + output wire tx_start_packet, + output wire tx_error_underflow, + output wire rx_start_packet, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +axis_gmii_rx #( + .DATA_WIDTH(DATA_WIDTH), + .PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .USER_WIDTH(RX_USER_WIDTH) +) +axis_gmii_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tuser(rx_axis_tuser), + .ptp_ts(rx_ptp_ts), + .clk_enable(rx_clk_enable), + .mii_select(rx_mii_select), + .start_packet(rx_start_packet), + .error_bad_frame(rx_error_bad_frame), + .error_bad_fcs(rx_error_bad_fcs) +); + +axis_gmii_tx #( + .DATA_WIDTH(DATA_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .USER_WIDTH(TX_USER_WIDTH) +) +axis_gmii_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .ptp_ts(tx_ptp_ts), + .m_axis_ptp_ts(tx_axis_ptp_ts), + .m_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .clk_enable(tx_clk_enable), + .mii_select(tx_mii_select), + .ifg_delay(ifg_delay), + .start_packet(tx_start_packet), + .error_underflow(tx_error_underflow) +); + +endmodule diff --git a/ethernet_controller/v/eth_mac_1g_rgmii.v b/ethernet_controller/v/eth_mac_1g_rgmii.v new file mode 100644 index 0000000..0ee67a6 --- /dev/null +++ b/ethernet_controller/v/eth_mac_1g_rgmii.v @@ -0,0 +1,284 @@ +/* + +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 + +/* + * 1G Ethernet MAC with RGMII interface + */ +module eth_mac_1g_rgmii # +( + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire clk250, + input wire clk250_rst, + input wire tx_clk_gen_rst, + + output wire rx_clk, + input wire rx_rst, + output wire tx_clk, + input wire tx_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * RGMII interface + */ + input wire rgmii_rx_clk, + input wire [3:0] rgmii_rxd, + input wire rgmii_rx_ctl, + output wire rgmii_tx_clk, + output wire [3:0] rgmii_txd, + output wire rgmii_tx_ctl, + + /* + * Status + */ + output wire tx_error_underflow, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [7:0] mac_gmii_rxd; +wire mac_gmii_rx_dv; +wire mac_gmii_rx_er; +wire mac_gmii_tx_clk_en; +wire [7:0] mac_gmii_txd; +wire mac_gmii_tx_en; +wire mac_gmii_tx_er; + +reg [1:0] speed_reg; +reg mii_select_reg; + +wire tx_mii_select_sync; +wire rx_mii_select_sync; +/* +reg [1:0] tx_mii_select_sync; + +always @(posedge tx_clk) begin + tx_mii_select_sync <= {tx_mii_select_sync[0], mii_select_reg}; +end + +reg [1:0] rx_mii_select_sync; + +always @(posedge rx_clk) begin + rx_mii_select_sync <= {rx_mii_select_sync[0], mii_select_reg}; +end +*/ +/* +// UNUSED as gtx_clk == tx_clk +bsg_launch_sync_sync #( + .width_p(1) + ,.use_negedge_for_launch_p(0) + ,.use_async_reset_p(0) +) tx_mii_select_reg_sync ( + .iclk_i(gtx_clk) + ,.iclk_reset_i(gtx_rst) + ,.oclk_i(tx_clk) + ,.iclk_data_i(mii_select_reg) + ,.iclk_data_o() // UNUSED + ,.oclk_data_o(tx_mii_select_sync) +);*/ +assign tx_mii_select_sync = mii_select_reg; + +bsg_launch_sync_sync #( + .width_p(1) + ,.use_negedge_for_launch_p(0) + ,.use_async_reset_p(0) +) rx_mii_select_reg_sync ( + .iclk_i(tx_clk) + ,.iclk_reset_i(tx_rst) + ,.oclk_i(rx_clk) + ,.iclk_data_i(mii_select_reg) + ,.iclk_data_o() // UNUSED + ,.oclk_data_o(rx_mii_select_sync) +); + +// PHY speed detection + + +reg [2:0] rx_prescale; + +always @(posedge rx_clk) begin + if(rx_rst) + rx_prescale <= 3'd0; + else + rx_prescale <= rx_prescale + 3'd1; +end + +reg rx_prescale_sync_2; +reg rx_prescale_sync_3; +/* +reg [2:0] rx_prescale_sync; + +always @(posedge gtx_clk) begin + rx_prescale_sync <= {rx_prescale_sync[1:0], rx_prescale[2]}; +end +*/ + +always @(posedge tx_clk) begin + rx_prescale_sync_3 <= rx_prescale_sync_2; +end +bsg_launch_sync_sync #( + .width_p(1) + ,.use_negedge_for_launch_p(0) + ,.use_async_reset_p(0) +) rx_prescale_sync ( + .iclk_i(rx_clk) + ,.iclk_reset_i(rx_rst) + ,.oclk_i(tx_clk) + ,.iclk_data_i(rx_prescale[2]) + ,.iclk_data_o() // UNUSED + ,.oclk_data_o(rx_prescale_sync_2) +); + + +reg [6:0] rx_speed_count_1; +reg [1:0] rx_speed_count_2; + +always @(posedge tx_clk) begin + if (tx_rst) begin + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b10; + mii_select_reg <= 1'b0; + end else begin + rx_speed_count_1 <= rx_speed_count_1 + 1; + + if (rx_prescale_sync_2 ^ rx_prescale_sync_3) begin + rx_speed_count_2 <= rx_speed_count_2 + 1; + end + + if (&rx_speed_count_1) begin + // reference count overflow - 10M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b00; + mii_select_reg <= 1'b1; + end + + if (&rx_speed_count_2) begin + // prescaled count overflow - 100M or 1000M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + if (rx_speed_count_1[6:5]) begin + // large reference count - 100M + speed_reg <= 2'b01; + mii_select_reg <= 1'b1; + end else begin + // small reference count - 1000M + speed_reg <= 2'b10; + mii_select_reg <= 1'b0; + end + end + end +end + +assign speed = speed_reg; + +rgmii_phy_if rgmii_phy_if_inst ( + .clk250(clk250), + .clk250_rst(clk250_rst), + .tx_clk_gen_rst(tx_clk_gen_rst), + + .mac_gmii_rx_clk(rx_clk), + .mac_gmii_rx_rst(rx_rst), + .mac_gmii_rxd(mac_gmii_rxd), + .mac_gmii_rx_dv(mac_gmii_rx_dv), + .mac_gmii_rx_er(mac_gmii_rx_er), + .mac_gmii_tx_clk(tx_clk), + .mac_gmii_tx_rst(tx_rst), + .mac_gmii_tx_clk_en(mac_gmii_tx_clk_en), + .mac_gmii_txd(mac_gmii_txd), + .mac_gmii_tx_en(mac_gmii_tx_en), + .mac_gmii_tx_er(mac_gmii_tx_er), + + .phy_rgmii_rx_clk(rgmii_rx_clk), + .phy_rgmii_rxd(rgmii_rxd), + .phy_rgmii_rx_ctl(rgmii_rx_ctl), + .phy_rgmii_tx_clk(rgmii_tx_clk), + .phy_rgmii_txd(rgmii_txd), + .phy_rgmii_tx_ctl(rgmii_tx_ctl), + + .speed(speed) +); + +eth_mac_1g #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tvalid(tx_axis_tvalid), + .tx_axis_tready(tx_axis_tready), + .tx_axis_tlast(tx_axis_tlast), + .tx_axis_tuser(tx_axis_tuser), + .rx_axis_tdata(rx_axis_tdata), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rxd(mac_gmii_rxd), + .gmii_rx_dv(mac_gmii_rx_dv), + .gmii_rx_er(mac_gmii_rx_er), + .gmii_txd(mac_gmii_txd), + .gmii_tx_en(mac_gmii_tx_en), + .gmii_tx_er(mac_gmii_tx_er), + .rx_clk_enable(1'b1), + .tx_clk_enable(mac_gmii_tx_clk_en), + .rx_mii_select(rx_mii_select_sync), + .tx_mii_select(tx_mii_select_sync), + .tx_error_underflow(tx_error_underflow), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/ethernet_controller/v/eth_mac_1g_rgmii_fifo.v b/ethernet_controller/v/eth_mac_1g_rgmii_fifo.v new file mode 100644 index 0000000..34d6b6f --- /dev/null +++ b/ethernet_controller/v/eth_mac_1g_rgmii_fifo.v @@ -0,0 +1,398 @@ +/* + +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 + +/* + * 1G Ethernet MAC with RGMII interface and TX and RX FIFOs + */ +module eth_mac_1g_rgmii_fifo # +( + parameter AXIS_DATA_WIDTH = 8, + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_DEPTH = 4096, + parameter TX_FIFO_PIPELINE_OUTPUT = 2, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_OVERSIZE_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_BAD_FRAME = TX_DROP_OVERSIZE_FRAME, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_DEPTH = 4096, + parameter RX_FIFO_PIPELINE_OUTPUT = 2, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_OVERSIZE_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_BAD_FRAME = RX_DROP_OVERSIZE_FRAME, + parameter RX_DROP_WHEN_FULL = RX_DROP_OVERSIZE_FRAME +) +( + input wire logic_clk, + input wire logic_rst, + + input wire clk250, + input wire clk250_rst, + input wire tx_clk_gen_rst, + + output wire tx_clk, + input wire tx_rst, + + output wire rx_clk, + input wire rx_rst, + + /* + * AXI input + */ + input wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * RGMII interface + */ + input wire rgmii_rx_clk, + input wire [3:0] rgmii_rxd, + input wire rgmii_rx_ctl, + output wire rgmii_tx_clk, + output wire [3:0] rgmii_txd, + output wire rgmii_tx_ctl, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [7:0] tx_fifo_axis_tdata; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [7:0] rx_fifo_axis_tdata; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +wire [0:0] tx_sync_reg_1_n; +reg [0:0] tx_sync_reg_1; +reg [0:0] tx_sync_reg_2; +reg [0:0] tx_sync_reg_3; +reg [0:0] tx_sync_reg_4; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; +/* +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end +*/ +always @(posedge logic_clk) begin + if(logic_rst) + tx_sync_reg_4 <= '0; + else + tx_sync_reg_4 <= tx_sync_reg_3; +end + +assign tx_sync_reg_1_n[0] = tx_sync_reg_1[0] ^ {tx_error_underflow_int}; +bsg_launch_sync_sync #( + .width_p(1) + ,.use_negedge_for_launch_p(0) + ,.use_async_reset_p(0) +) tx_error_sync ( + .iclk_i(tx_clk) + ,.iclk_reset_i(tx_rst) + ,.oclk_i(logic_clk) + ,.iclk_data_i(tx_sync_reg_1_n[0]) + ,.iclk_data_o(tx_sync_reg_1[0]) + ,.oclk_data_o(tx_sync_reg_3[0]) +); + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +wire [1:0] rx_sync_reg_1_n; +reg [1:0] rx_sync_reg_1; +reg [1:0] rx_sync_reg_2; +reg [1:0] rx_sync_reg_3; +reg [1:0] rx_sync_reg_4; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +assign rx_sync_reg_1_n = rx_sync_reg_1 ^ {rx_error_bad_fcs_int, rx_error_bad_frame_int}; +/* +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_fcs_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end +*/ + +always @(posedge logic_clk) begin + if(logic_rst) + rx_sync_reg_4 <= '0; + else + rx_sync_reg_4 <= rx_sync_reg_3; +end + +bsg_launch_sync_sync #( + .width_p(2) + ,.use_negedge_for_launch_p(0) + ,.use_async_reset_p(0) +) rx_error_sync ( + .iclk_i(rx_clk) + ,.iclk_reset_i(rx_rst) + ,.oclk_i(logic_clk) + ,.iclk_data_i(rx_sync_reg_1_n) + ,.iclk_data_o(rx_sync_reg_1) + ,.oclk_data_o(rx_sync_reg_3) +); + +wire [1:0] speed_int; + +reg [1:0] speed_sync_reg_1; +reg [1:0] speed_sync_reg_2; + +assign speed = speed_sync_reg_2; + +/* +always @(posedge logic_clk) begin + speed_sync_reg_1 <= speed_int; + speed_sync_reg_2 <= speed_sync_reg_1; +end +*/ + +bsg_launch_sync_sync #( + .width_p(2) + ,.use_negedge_for_launch_p(0) + ,.use_async_reset_p(0) +) speed_sync ( + .iclk_i(tx_clk) + ,.iclk_reset_i(tx_rst) + ,.oclk_i(logic_clk) + ,.iclk_data_i(speed_int) + ,.iclk_data_o() // UNUSED + ,.oclk_data_o(speed_sync_reg_2) +); + + +eth_mac_1g_rgmii #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_rgmii_inst ( + .clk250(clk250), + .clk250_rst(clk250_rst), + .tx_clk_gen_rst(tx_clk_gen_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .rgmii_rx_clk(rgmii_rx_clk), + .rgmii_rxd(rgmii_rxd), + .rgmii_rx_ctl(rgmii_rx_ctl), + .rgmii_tx_clk(rgmii_tx_clk), + .rgmii_txd(rgmii_txd), + .rgmii_tx_ctl(rgmii_tx_ctl), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .speed(speed_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo_adapter #( + .DEPTH(TX_FIFO_DEPTH), + .S_DATA_WIDTH(AXIS_DATA_WIDTH), + .S_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .S_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .M_DATA_WIDTH(8), + .M_KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .PIPELINE_OUTPUT(TX_FIFO_PIPELINE_OUTPUT), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_OVERSIZE_FRAME(TX_DROP_OVERSIZE_FRAME), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL), + .upstream_async_fifo_p(1) +) +tx_fifo ( + // AXI input + .s_clk(logic_clk), + .s_rst(logic_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(8'b0), + .s_axis_tdest(8'b0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_rst(tx_rst), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo_adapter #( + .DEPTH(RX_FIFO_DEPTH), + .S_DATA_WIDTH(8), + .S_KEEP_ENABLE(0), + .M_DATA_WIDTH(AXIS_DATA_WIDTH), + .M_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .M_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .PIPELINE_OUTPUT(RX_FIFO_PIPELINE_OUTPUT), + .FRAME_FIFO(RX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_OVERSIZE_FRAME(RX_DROP_OVERSIZE_FRAME), + .DROP_BAD_FRAME(RX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(RX_DROP_WHEN_FULL), + .upstream_async_fifo_p(0) +) +rx_fifo ( + // AXI input + .s_clk(rx_clk), + .s_rst(rx_rst), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(1'b0), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(8'b0), + .s_axis_tdest(8'b0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_rst(logic_rst), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/ethernet_controller/v/ethernet_control_unit.sv b/ethernet_controller/v/ethernet_control_unit.sv new file mode 100644 index 0000000..b9a56a5 --- /dev/null +++ b/ethernet_controller/v/ethernet_control_unit.sv @@ -0,0 +1,273 @@ + +/* + * Memory map (compatible with the Litex Ethernet driver in Linux kernel 5.15): + * 1. RX/TX Buffers (when eth_mtu_p == 0x800): + * + * RX Buffer: + * 0x0000-0x0800 + * TX Buffer: + * 0x0800-0x1000 + * + * 2. Register Map: + * + * Readable Register: + * 0x1000: Index of the Received Packet (a.k.a LITEETH_WRITER_SLOT) + * 0x1004: Length of the Received Packet (a.k.a LITEETH_WRITER_LENGTH) + * 0x1010: RX Event Pending Bit (a.k.a LITEETH_WRITER_EV_PENDING) + * 0x101C: TX Ready Bit (a.k.a LITEETH_READER_READY) + * 0x1030: TX Event Pending Bit (a.k.a LITEETH_READER_EV_PENDING) + * 0x1050: Debug Info (not compatible with Liteeth) + * + * Writable Register: + * 0x1010: RX Event Pending Bit (a.k.a LITEETH_WRITER_EV_PENDING) + * 0x1014: RX Event Enable Bit (a.k.a LITEETH_WRITER_EV_ENABLE) + * 0x1018: TX Send Bit (a.k.a LITEETH_READER_START) + * 0x1024: Index of the Transmitting Packet (a.k.a LITEETH_READER_SLOT) + * 0x1028: Length of the Transmitting Packet (a.k.a LITEETH_READER_LENGTH) + * 0x1030: TX Event Pending Bit (a.k.a LITEETH_READER_EV_PENDING) + * 0x1034: TX Event Enable Bit (a.k.a LITEETH_READER_EV_ENABLE) + * + * Link: + * https://elixir.bootlin.com/linux/v5.15/source/drivers/net/ethernet/litex/litex_liteeth.c + * + */ + +`include "bsg_defines.v" + + +module ethernet_control_unit # +( + parameter eth_mtu_p = 2048 // byte + , parameter data_width_p = 32 + , localparam packet_size_width_lp = $clog2(eth_mtu_p+1) + , localparam packet_addr_width_lp = $clog2(eth_mtu_p) + , localparam addr_width_lp = 14 +) +( + input bit clk_i + , input logic reset_i + + , input logic [addr_width_lp-1:0] addr_i + , input logic write_en_i + , input logic read_en_i + , input logic [(data_width_p/8)-1:0] write_mask_i + , input logic [data_width_p-1:0] write_data_i + , output logic [data_width_p-1:0] read_data_o // sync read + + , input logic [15:0] debug_info_i + + , output logic packet_send_o + , input logic packet_req_i + , output logic packet_wsize_valid_o + , output logic [packet_size_width_lp-1:0] packet_wsize_o + , output logic packet_wvalid_o + , output logic [packet_addr_width_lp-1:0] packet_waddr_o + , output logic [data_width_p-1:0] packet_wdata_o + , output logic [(data_width_p/8)-1:0] packet_wmask_o + + , output logic packet_ack_o + , input logic packet_avail_i + , output logic packet_rvalid_o + , output logic [packet_addr_width_lp-1:0] packet_raddr_o + , input logic [data_width_p-1:0] packet_rdata_i + , input logic [packet_size_width_lp-1:0] packet_rsize_i + + , output logic tx_interrupt_clear_o + + , input logic rx_interrupt_pending_i + , input logic tx_interrupt_pending_i + , output logic rx_interrupt_enable_o + , output logic rx_interrupt_enable_v_o + , output logic tx_interrupt_enable_o + , output logic tx_interrupt_enable_v_o +); + + logic buffer_read_v_r; + logic [data_width_p-1:0] readable_reg_r, readable_reg_n; + logic rx_interrupt_clear, tx_interrupt_clear; + // Not used in this Ethernet controller, always points to 0 + logic tx_idx_r, tx_idx_n; + logic io_decode_error; + + bsg_dff_reset_en + #(.width_p(data_width_p + 2)) + registers + (.clk_i(clk_i) + ,.reset_i(reset_i) + ,.en_i(read_en_i | write_en_i) + ,.data_i({readable_reg_n, packet_rvalid_o, tx_idx_n}) + ,.data_o({readable_reg_r, buffer_read_v_r, tx_idx_r}) + ); + + always_comb begin + io_decode_error = 1'b0; + packet_raddr_o = '0; + packet_rvalid_o = 1'b0; + + packet_waddr_o = '0; + packet_wmask_o = '0; + packet_wdata_o = '0; + packet_wvalid_o = 1'b0; + + readable_reg_n = '0; + packet_send_o = 1'b0; + tx_idx_n = tx_idx_r; + rx_interrupt_clear = 1'b0; + tx_interrupt_clear = 1'b0; + + rx_interrupt_enable_o = 1'b0; + rx_interrupt_enable_v_o = 1'b0; + tx_interrupt_enable_o = 1'b0; + tx_interrupt_enable_v_o = 1'b0; + + packet_wsize_o = '0; + packet_wsize_valid_o = 1'b0; + casez(addr_i) + 16'h0???: begin + if(addr_i < 16'h0800) begin + // RX buffer; R + if(read_en_i) begin + packet_raddr_o = addr_i[packet_addr_width_lp-1:0]; + packet_rvalid_o = 1'b1; + io_decode_error = (write_mask_i != (data_width_p/8)'('1)); + end + if(write_en_i) + io_decode_error = 1'b1; + end + else begin + // TX buffer; W + if(read_en_i) + io_decode_error = 1'b1; + if(write_en_i) begin + packet_waddr_o = addr_i[packet_addr_width_lp-1:0]; + packet_wmask_o = write_mask_i; + packet_wdata_o = write_data_i; + packet_wvalid_o = 1'b1; + end + end + end + 16'h1000: begin + // RX current slot index; R + if(read_en_i) + readable_reg_n = '0; // always 0 + if(write_en_i) + io_decode_error = 1'b1; + end + 16'h1004: begin + // RX received size; R + if(read_en_i) begin + if(packet_avail_i) + readable_reg_n = packet_rsize_i; + end + if(write_en_i) + io_decode_error = 1'b1; + end + 16'h1010: begin + // RX EV Pending; RW + if(read_en_i) + readable_reg_n = rx_interrupt_pending_i; + if(write_en_i) begin + if(write_data_i[0] == 'b1) begin + rx_interrupt_clear = 1'b1; + end + end + end + 16'h1014: begin + // RX EV Enable; W + if(read_en_i) + io_decode_error = 1'b1; + if(write_en_i) begin + rx_interrupt_enable_o = write_data_i[0]; + rx_interrupt_enable_v_o = 1'b1; + end + end + 16'h1018: begin + // TX Send Bit; W + if(read_en_i) + io_decode_error = 1'b1; + if(write_en_i) + packet_send_o = 1'b1; + end + 16'h101C: begin + // TX Ready bit; R + if(read_en_i) + readable_reg_n = packet_req_i; + if(write_en_i) + io_decode_error = 1'b1; + end + 16'h1024: begin + // TX current slot index; W + if(read_en_i) + io_decode_error = 1'b1; + if(write_en_i) + tx_idx_n = write_data_i[0]; + end + 16'h1028: begin + // TX size; W + if(read_en_i) + io_decode_error = 1'b1; + if(write_en_i) begin + packet_wsize_o = write_data_i; + packet_wsize_valid_o = 1'b1; + end + end + 16'h1030: begin + // TX Pending Bit; RW + if(read_en_i) + readable_reg_n = tx_interrupt_pending_i; + if(write_en_i) begin + if(write_data_i[0] == 'b1) begin + // Generate a pulse signal for clear + tx_interrupt_clear = 1'b1; + end + end + end + 16'h1034: begin + // TX Enable Bit; W + if(read_en_i) + io_decode_error = 1'b1; + if(write_en_i) begin + tx_interrupt_enable_o = write_data_i[0]; + tx_interrupt_enable_v_o = 1'b1; + end + end + 16'h1050: begin + // Debug Info; R + if(read_en_i) + readable_reg_n = debug_info_i; + if(write_en_i) + io_decode_error = 1'b1; + end + + default: begin + // Unsupported MMIO + if(read_en_i || write_en_i) + io_decode_error = 1'b1; + end + endcase + if(read_en_i & write_en_i) + io_decode_error = 1'b1; + end + + // Output can either come from RX buffer or registers + assign read_data_o = buffer_read_v_r ? packet_rdata_i : readable_reg_r; + + assign packet_ack_o = rx_interrupt_clear; + assign tx_interrupt_clear_o = tx_interrupt_clear; + + // synopsys translate_off + always_ff @(posedge clk_i) begin + assert(reset_i !== 1'b0 || io_decode_error === 1'b0) else begin + $error("%m: decode error at %t\n", $time); + $finish; + end + end + initial begin + assert(eth_mtu_p <= 2048) + else $error("%m: eth_mtu_p should be <= 2048"); + assert(data_width_p == 32) + else $error("%m: unsupported data_width_p"); + end + // synopsys translate_on + +endmodule diff --git a/ethernet_controller/v/ethernet_controller.sv b/ethernet_controller/v/ethernet_controller.sv new file mode 100644 index 0000000..f3c6c16 --- /dev/null +++ b/ethernet_controller/v/ethernet_controller.sv @@ -0,0 +1,279 @@ + +`include "bsg_defines.v" + +module ethernet_controller # +( + parameter data_width_p = 32 + , parameter ifg_delay_p = 8'd12 + , localparam addr_width_lp = 14 +) +( + // For user logic + input logic clk_i + , input logic reset_i + // For 2X clock for generating RGMII signals (DDR) + , input logic clk250_i + , input logic clk250_reset_i + // For tx_clk clock generator + , input logic tx_clk_gen_reset_i + + // MAC TX clock downsampled from clk250_i + , output logic tx_clk_o + , input logic tx_reset_i + // MAC RX clock from rgmii_rx_clk_i + , output logic rx_clk_o + , input logic rx_reset_i + + , input logic [addr_width_lp-1:0] addr_i + , input logic write_en_i + , input logic read_en_i + , input logic [data_width_p/8-1:0] write_mask_i + , input logic [data_width_p-1:0] write_data_i + , output logic [data_width_p-1:0] read_data_o // sync read + + , output logic rx_interrupt_pending_o + , output logic tx_interrupt_pending_o + + , input logic rgmii_rx_clk_i + , input logic [3:0] rgmii_rxd_i + , input logic rgmii_rx_ctl_i + , output logic rgmii_tx_clk_o + , output logic [3:0] rgmii_txd_o + , output logic rgmii_tx_ctl_o +); + + localparam eth_mtu_lp = 2048; // byte + localparam packet_size_width_lp = $clog2(eth_mtu_lp+1); + localparam packet_addr_width_lp = $clog2(eth_mtu_lp); + + logic packet_send_lo; + logic packet_avail_lo; + logic packet_ack_lo; + logic packet_req_lo; + + logic packet_wsize_valid_lo; + logic [packet_size_width_lp-1:0] packet_wsize_lo; + + logic [packet_addr_width_lp-1:0] packet_waddr_lo; + logic [data_width_p/8-1:0] packet_wmask_lo; + logic [data_width_p-1:0] packet_wdata_lo; + logic packet_wvalid_lo; + + logic [packet_addr_width_lp-1:0] packet_raddr_lo; + logic [data_width_p-1:0] packet_rdata_lo; + logic packet_rvalid_lo; + + logic [packet_size_width_lp-1:0] packet_rsize_lo; + + logic tx_error_underflow_lo; + logic tx_fifo_overflow_lo; + logic tx_fifo_bad_frame_lo; + logic tx_fifo_good_frame_lo; + logic rx_error_bad_frame_lo; + logic rx_error_bad_fcs_lo; + logic rx_fifo_overflow_lo; + logic rx_fifo_bad_frame_lo; + logic rx_fifo_good_frame_lo; + logic [1:0] speed_lo; + + logic tx_interrupt_clear_lo; + logic rx_interrupt_enable_lo, rx_interrupt_enable_v_lo; + logic tx_interrupt_enable_lo, tx_interrupt_enable_v_lo; + logic rx_interrupt_pending_lo, tx_interrupt_pending_lo; + + + logic [data_width_p-1:0] tx_axis_tdata_lo; + logic [data_width_p/8-1:0] tx_axis_tkeep_lo; + logic tx_axis_tvalid_lo; + logic tx_axis_tlast_lo; + logic tx_axis_tready_li; + logic tx_axis_tuser_lo; + + logic [data_width_p-1:0] rx_axis_tdata_li; + logic [data_width_p/8-1:0] rx_axis_tkeep_li; + logic rx_axis_tvalid_li; + logic rx_axis_tready_lo; + logic rx_axis_tlast_li; + logic rx_axis_tuser_li; + + + wire [15:0] debug_info_li = { + tx_error_underflow_lo + ,tx_fifo_overflow_lo + ,tx_fifo_bad_frame_lo + ,tx_fifo_good_frame_lo + ,rx_error_bad_frame_lo + ,rx_error_bad_fcs_lo + ,rx_fifo_overflow_lo + ,rx_fifo_bad_frame_lo + ,rx_fifo_good_frame_lo + ,speed_lo + }; + + ethernet_control_unit #( + .eth_mtu_p(eth_mtu_lp) + ,.data_width_p(data_width_p) + ) ethernet_control_unit ( + .clk_i + ,.reset_i + + ,.addr_i + ,.write_en_i + ,.read_en_i + ,.write_mask_i + ,.write_data_i + ,.read_data_o + + ,.debug_info_i(debug_info_li) + + ,.packet_send_o(packet_send_lo) + ,.packet_req_i(packet_req_lo) + ,.packet_wsize_valid_o(packet_wsize_valid_lo) + ,.packet_wsize_o(packet_wsize_lo) + ,.packet_wvalid_o(packet_wvalid_lo) + ,.packet_waddr_o(packet_waddr_lo) + ,.packet_wdata_o(packet_wdata_lo) + ,.packet_wmask_o(packet_wmask_lo) + + ,.packet_ack_o(packet_ack_lo) + ,.packet_avail_i(packet_avail_lo) + ,.packet_rvalid_o(packet_rvalid_lo) + ,.packet_raddr_o(packet_raddr_lo) + ,.packet_rdata_i(packet_rdata_lo) + ,.packet_rsize_i(packet_rsize_lo) + + ,.tx_interrupt_clear_o(tx_interrupt_clear_lo) + + ,.rx_interrupt_pending_i(rx_interrupt_pending_lo) + ,.tx_interrupt_pending_i(tx_interrupt_pending_lo) + ,.rx_interrupt_enable_o(rx_interrupt_enable_lo) + ,.rx_interrupt_enable_v_o(rx_interrupt_enable_v_lo) + ,.tx_interrupt_enable_o(tx_interrupt_enable_lo) + ,.tx_interrupt_enable_v_o(tx_interrupt_enable_v_lo) + ); + + + ethernet_sender #( + .data_width_p(data_width_p) + ,.eth_mtu_p(eth_mtu_lp)) + sender ( + .clk_i(clk_i) + ,.reset_i(reset_i) + + ,.packet_send_i(packet_send_lo) + ,.packet_req_o(packet_req_lo) + ,.packet_wsize_valid_i(packet_wsize_valid_lo) + ,.packet_wsize_i(packet_wsize_lo) + ,.packet_wvalid_i(packet_wvalid_lo) + ,.packet_waddr_i(packet_waddr_lo) + ,.packet_wdata_i(packet_wdata_lo) + ,.packet_wmask_i(packet_wmask_lo) + + ,.tx_axis_tdata_o(tx_axis_tdata_lo) + ,.tx_axis_tkeep_o(tx_axis_tkeep_lo) + ,.tx_axis_tvalid_o(tx_axis_tvalid_lo) + ,.tx_axis_tlast_o(tx_axis_tlast_lo) + ,.tx_axis_tready_i(tx_axis_tready_li) + ,.tx_axis_tuser_o(tx_axis_tuser_lo) + + ,.send_count_o(/* UNUSED */) + ); + + ethernet_receiver #( + .data_width_p(data_width_p) + ,.eth_mtu_p(eth_mtu_lp)) + receiver ( + .clk_i(clk_i) + ,.reset_i(reset_i) + + ,.packet_ack_i(packet_ack_lo) + ,.packet_avail_o(packet_avail_lo) + ,.packet_rvalid_i(packet_rvalid_lo) + ,.packet_raddr_i(packet_raddr_lo) + ,.packet_rdata_o(packet_rdata_lo) + ,.packet_rsize_o(packet_rsize_lo) + + ,.rx_axis_tdata_i(rx_axis_tdata_li) + ,.rx_axis_tkeep_i(rx_axis_tkeep_li) + ,.rx_axis_tvalid_i(rx_axis_tvalid_li) + ,.rx_axis_tready_o(rx_axis_tready_lo) + ,.rx_axis_tlast_i(rx_axis_tlast_li) + ,.rx_axis_tuser_i(rx_axis_tuser_li) + + ,.receive_count_o(/* UNUSED */) + ); + + eth_mac_1g_rgmii_fifo #( + .AXIS_DATA_WIDTH(data_width_p) + ,.TX_FIFO_PIPELINE_OUTPUT(1) + ,.RX_FIFO_PIPELINE_OUTPUT(1)) + mac ( + .logic_clk(clk_i) + ,.logic_rst(reset_i) + ,.clk250(clk250_i) + ,.clk250_rst(clk250_reset_i) + ,.tx_clk_gen_rst(tx_clk_gen_reset_i) + ,.tx_clk(tx_clk_o) + ,.tx_rst(tx_reset_i) + ,.rx_clk(rx_clk_o) + ,.rx_rst(rx_reset_i) + + ,.tx_axis_tdata(tx_axis_tdata_lo) + ,.tx_axis_tkeep(tx_axis_tkeep_lo) + ,.tx_axis_tvalid(tx_axis_tvalid_lo) + ,.tx_axis_tready(tx_axis_tready_li) + ,.tx_axis_tlast(tx_axis_tlast_lo) + ,.tx_axis_tuser(tx_axis_tuser_lo) + + ,.rx_axis_tdata(rx_axis_tdata_li) + ,.rx_axis_tkeep(rx_axis_tkeep_li) + ,.rx_axis_tvalid(rx_axis_tvalid_li) + ,.rx_axis_tready(rx_axis_tready_lo) + ,.rx_axis_tlast(rx_axis_tlast_li) + ,.rx_axis_tuser(rx_axis_tuser_li) + + ,.rgmii_rx_clk(rgmii_rx_clk_i) + ,.rgmii_rxd(rgmii_rxd_i) + ,.rgmii_rx_ctl(rgmii_rx_ctl_i) + ,.rgmii_tx_clk(rgmii_tx_clk_o) + ,.rgmii_txd(rgmii_txd_o) + ,.rgmii_tx_ctl(rgmii_tx_ctl_o) + + ,.tx_error_underflow(tx_error_underflow_lo) + ,.tx_fifo_overflow(tx_fifo_overflow_lo) + ,.tx_fifo_bad_frame(tx_fifo_bad_frame_lo) + ,.tx_fifo_good_frame(tx_fifo_good_frame_lo) + ,.rx_error_bad_frame(rx_error_bad_frame_lo) + ,.rx_error_bad_fcs(rx_error_bad_fcs_lo) + ,.rx_fifo_overflow(rx_fifo_overflow_lo) + ,.rx_fifo_bad_frame(rx_fifo_bad_frame_lo) + ,.rx_fifo_good_frame(rx_fifo_good_frame_lo) + ,.speed(speed_lo) + + ,.ifg_delay(ifg_delay_p) + ); + wire rx_interrupt_lo; + wire tx_interrupt_lo; + interrupt_control_unit interrupt_control_unit ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.packet_avail_i(packet_avail_lo) + ,.packet_req_i(packet_req_lo) + + ,.tx_interrupt_clear_i(tx_interrupt_clear_lo) + + ,.rx_interrupt_enable_i(rx_interrupt_enable_lo) + ,.rx_interrupt_enable_v_i(rx_interrupt_enable_v_lo) + ,.tx_interrupt_enable_i(tx_interrupt_enable_lo) + ,.tx_interrupt_enable_v_i(tx_interrupt_enable_v_lo) + + ,.rx_interrupt_pending_o(rx_interrupt_pending_lo) + ,.tx_interrupt_pending_o(tx_interrupt_pending_lo) + ,.rx_interrupt_o(rx_interrupt_lo) + ,.tx_interrupt_o(tx_interrupt_lo) + ); + + assign rx_interrupt_pending_o = rx_interrupt_lo; + assign tx_interrupt_pending_o = tx_interrupt_lo; + +endmodule diff --git a/ethernet_controller/v/ethernet_receiver.sv b/ethernet_controller/v/ethernet_receiver.sv new file mode 100644 index 0000000..8620fdf --- /dev/null +++ b/ethernet_controller/v/ethernet_receiver.sv @@ -0,0 +1,217 @@ + +/* + * This module receives packets from axis bus and stores them in its buffer. + * Received packets can be read through packet_* signals. + * + */ + +`include "bsg_defines.v" + +module ethernet_receiver +#( + parameter data_width_p = 32 + // maximum size of an Ethernet packet + , parameter eth_mtu_p = 2048 // byte + , parameter recv_count_p = (32'b1 << 16 - 1) + , localparam addr_width_lp = $clog2(eth_mtu_p) + , localparam size_width_lp = `BSG_WIDTH(`BSG_SAFE_CLOG2(data_width_p/8)) + , localparam packet_size_width_lp = $clog2(eth_mtu_p+1) +) +( + input logic clk_i + , input logic reset_i + + /* Host <- Packet */ + // clear packet + , input logic packet_ack_i + // packet is ready to read + , output logic packet_avail_o + , input logic packet_rvalid_i + , input logic [addr_width_lp-1:0] packet_raddr_i + // sync read + , output logic [data_width_p-1:0] packet_rdata_o + , output logic [packet_size_width_lp-1:0] packet_rsize_o + + /* Packet <- AXIS */ + , input logic [data_width_p-1:0] rx_axis_tdata_i + , input logic [data_width_p/8-1:0] rx_axis_tkeep_i + , input logic rx_axis_tvalid_i + , output logic rx_axis_tready_o + , input logic rx_axis_tlast_i + , input logic rx_axis_tuser_i + + /* stat */ + , output logic [$clog2(recv_count_p+1)-1:0] receive_count_o +); + localparam recv_ptr_width_lp = $clog2(eth_mtu_p/(data_width_p/8)); + + logic packet_avail_lo; + logic packet_ack_li; + logic [packet_size_width_lp-1:0] packet_rsize_lo; + logic packet_rvalid_li; + logic [addr_width_lp-1:0] packet_raddr_li; + logic [data_width_p-1:0] packet_rdata_lo; + + logic packet_send_li; + logic packet_req_lo; + + logic packet_wsize_valid_li; + logic [packet_size_width_lp-1:0] packet_wsize_li; + + logic packet_wvalid_li; + logic [addr_width_lp-1:0] packet_waddr_li; + logic [data_width_p-1:0] packet_wdata_li; + + logic recv_ptr_unwind; + logic recv_ptr_increment; + logic [recv_ptr_width_lp-1:0] recv_ptr_r; + logic [packet_size_width_lp-1:0] packet_size_remaining; + + logic receive_complete; + bsg_flow_counter #(.els_p(recv_count_p)) + receive_count ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.v_i(receive_complete) + ,.ready_i(1'b1) + ,.yumi_i(1'b0) + + ,.count_o(receive_count_o) + ); + + +if(data_width_p == 64) begin + always_comb begin + packet_size_remaining = 'h0; + case(rx_axis_tkeep_i) + 8'b1111_1111: + packet_size_remaining = 'h8; + 8'b0111_1111: + packet_size_remaining = 'h7; + 8'b0011_1111: + packet_size_remaining = 'h6; + 8'b0001_1111: + packet_size_remaining = 'h5; + 8'b0000_1111: + packet_size_remaining = 'h4; + 8'b0000_0111: + packet_size_remaining = 'h3; + 8'b0000_0011: + packet_size_remaining = 'h2; + 8'b0000_0001: + packet_size_remaining = 'h1; + endcase + end +end +else if(data_width_p == 32) begin + always_comb begin + packet_size_remaining = 'h0; + case(rx_axis_tkeep_i) + 4'b1111: + packet_size_remaining = 'h4; + 4'b0111: + packet_size_remaining = 'h3; + 4'b0011: + packet_size_remaining = 'h2; + 4'b0001: + packet_size_remaining = 'h1; + endcase + end +end + + bsg_counter_clear_up #( // unit: 'data_width_p/8' byte + .max_val_p(eth_mtu_p/(data_width_p/8)-1) + ,.init_val_p(0) + ) recv_counter ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.clear_i(recv_ptr_unwind) + ,.up_i(recv_ptr_increment) + ,.count_o(recv_ptr_r) + ); + + assign packet_avail_o = packet_avail_lo; + + packet_buffer #(.slot_p(2) + ,.data_width_p(data_width_p) + ,.els_p(eth_mtu_p)) + rx_buffer ( + .clk_i(clk_i) + ,.reset_i(reset_i) + + ,.packet_avail_o(packet_avail_lo) + ,.packet_ack_i(packet_ack_li) + ,.packet_rvalid_i(packet_rvalid_li) + ,.packet_raddr_i(packet_raddr_li) + ,.packet_rdata_o(packet_rdata_lo) + ,.packet_rsize_o(packet_rsize_lo) + + ,.packet_send_i(packet_send_li) + ,.packet_req_o(packet_req_lo) + ,.packet_wsize_valid_i(packet_wsize_valid_li) + ,.packet_wsize_i(packet_wsize_li) + ,.packet_wvalid_i(packet_wvalid_li) + ,.packet_waddr_i(packet_waddr_li) + ,.packet_wdata_i(packet_wdata_li) + ,.packet_wmask_i((data_width_p/8)'('1)) + ); + + always_comb begin + packet_rsize_o = '0; + packet_ack_li = 1'b0; + packet_rvalid_li = 1'b0; + packet_raddr_li = packet_raddr_i; + packet_rdata_o = packet_rdata_lo; + if(packet_avail_lo) begin + packet_rsize_o = packet_rsize_lo; + packet_ack_li = packet_ack_i; + packet_rvalid_li = packet_rvalid_i; + end + end + + always_comb begin + rx_axis_tready_o = 1'b0; + recv_ptr_unwind = 1'b0; + recv_ptr_increment = 1'b0; + packet_send_li = 1'b0; + packet_wsize_li = '0; + packet_wsize_valid_li = 1'b0; + packet_wvalid_li = 1'b0; + packet_waddr_li = '0; + packet_wdata_li = '0; + receive_complete = 1'b0; + if(packet_req_lo) begin + rx_axis_tready_o = 1'b1; + if(rx_axis_tvalid_i) begin + packet_wvalid_li = 1'b1; + packet_waddr_li = addr_width_lp'(recv_ptr_r*(data_width_p/8)); + packet_wdata_li = rx_axis_tdata_i; + if(rx_axis_tlast_i) begin + recv_ptr_unwind = 1'b1; + if(~rx_axis_tuser_i) begin + // end of good frame + packet_send_li = 1'b1; + packet_wsize_li = (recv_ptr_r*(data_width_p/8)) + packet_size_remaining; + packet_wsize_valid_li = 1'b1; + receive_complete = 1'b1; + end + end + else begin + recv_ptr_increment = 1'b1; + end + end + end + end + + // synopsys translate_off + always_ff @(posedge clk_i) begin + if(~reset_i) begin + assert(~(~packet_avail_lo & packet_rvalid_i)) + else $error("%m: reading data when rx not ready at time %t", $time); + assert(~(~packet_avail_lo & packet_ack_i)) + else $error("%m: receiving packet when rx not ready at time %t", $time); + end + end + // synopsys translate_on + +endmodule diff --git a/ethernet_controller/v/ethernet_sender.sv b/ethernet_controller/v/ethernet_sender.sv new file mode 100644 index 0000000..d1b89f2 --- /dev/null +++ b/ethernet_controller/v/ethernet_sender.sv @@ -0,0 +1,253 @@ + +/* + * This module receives packets from packet_* signals and stores them + * in its buffer. Received packets are then sent to axis bus. + * + */ + +`include "bsg_defines.v" + +module ethernet_sender # +( + parameter data_width_p = 32 + // maximum size of an Ethernet packet + , parameter eth_mtu_p = 2048 // byte + , parameter send_count_p = (32'b1 << 16 - 1) + , localparam addr_width_lp = $clog2(eth_mtu_p) + , localparam packet_size_width_lp = $clog2(eth_mtu_p+1) +) +( + input logic clk_i + , input logic reset_i + + // send out packet + , input logic packet_send_i + // space available for packet + , output logic packet_req_o + + /* Host -> Packet */ + , input logic packet_wsize_valid_i + , input logic [packet_size_width_lp-1:0] packet_wsize_i + , input logic packet_wvalid_i + , input logic [addr_width_lp-1:0] packet_waddr_i + , input logic [data_width_p-1:0] packet_wdata_i + , input logic [(data_width_p/8)-1:0] packet_wmask_i + + /* Packet -> AXIS */ + , output logic [data_width_p-1:0] tx_axis_tdata_o + , output logic [data_width_p/8-1:0] tx_axis_tkeep_o + , output logic tx_axis_tvalid_o + , output logic tx_axis_tlast_o + , input logic tx_axis_tready_i + , output logic tx_axis_tuser_o + + , output logic [$clog2(send_count_p+1)-1:0] send_count_o +); + localparam send_ptr_width_lp = $clog2(eth_mtu_p/(data_width_p/8)); + localparam send_ptr_offset_width_lp = $clog2(data_width_p/8); + + logic [send_ptr_width_lp - 1:0] send_ptr_r; + + logic [data_width_p/8-1:0] tx_axis_tkeep_li; + logic tx_axis_tlast_li; + logic tx_axis_tuser_li; + + logic packet_avail_lo; + logic packet_ack_li; + logic [packet_size_width_lp-1:0] packet_rsize_lo; + logic packet_rvalid_li; + logic [addr_width_lp-1:0] packet_raddr_li; + logic [data_width_p-1:0] packet_rdata_lo; + + logic send_ptr_increment; + + + logic [send_ptr_width_lp - 1:0] send_ptr_end; + logic [send_ptr_offset_width_lp - 1 :0] send_remaining; + logic last_send_f; + + logic packet_req_lo; + + logic packet_wsize_valid_li; + logic packet_wvalid_li; + logic packet_send_li; + + logic send_complete; + bsg_flow_counter #(.els_p(send_count_p)) + send_count ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.v_i(send_complete) + ,.ready_i(1'b1) + ,.yumi_i(1'b0) + ,.count_o(send_count_o) + ); + + packet_buffer #(.slot_p(2) + ,.data_width_p(data_width_p) + ,.els_p(eth_mtu_p)) + tx_buffer ( + .clk_i(clk_i) + ,.reset_i(reset_i) + + ,.packet_avail_o(packet_avail_lo) + ,.packet_ack_i(packet_ack_li) + ,.packet_rvalid_i(packet_rvalid_li) + ,.packet_raddr_i(packet_raddr_li) + ,.packet_rdata_o(packet_rdata_lo) + ,.packet_rsize_o(packet_rsize_lo) + + ,.packet_send_i(packet_send_li) + ,.packet_req_o(packet_req_lo) + ,.packet_wsize_valid_i(packet_wsize_valid_li) + ,.packet_wsize_i(packet_wsize_i) + ,.packet_wvalid_i(packet_wvalid_li) + ,.packet_waddr_i(packet_waddr_i) + ,.packet_wdata_i(packet_wdata_i) + ,.packet_wmask_i(packet_wmask_i) + ); + + + // used for aligning the control signals with the sycn read value + bsg_dff_reset_en #( + .width_p(data_width_p/8+2) + ) tx_dff ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.en_i(packet_rvalid_li) + ,.data_i({tx_axis_tkeep_li, tx_axis_tlast_li, tx_axis_tuser_li}) + ,.data_o({tx_axis_tkeep_o, tx_axis_tlast_o, tx_axis_tuser_o}) + ); + logic packet_rvalid_lo, packet_rready_li; + bsg_dff_reset_set_clear #(.width_p(1) + ) packet_rvalid_reg ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.set_i(packet_rvalid_li) + ,.clear_i(packet_rready_li) + ,.data_o(packet_rvalid_lo) + ); + assign packet_rready_li = tx_axis_tready_i; + assign tx_axis_tvalid_o = packet_rvalid_lo; + + logic send_ptr_unwind; + bsg_counter_clear_up #( // unit: 'data_width_p/8' byte + .max_val_p(eth_mtu_p/(data_width_p/8)-1) + ,.init_val_p(0) + ) send_counter ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.clear_i(send_ptr_unwind) + ,.up_i(send_ptr_increment) + ,.count_o(send_ptr_r) + ); + + assign tx_axis_tdata_o = packet_rdata_lo; + assign packet_raddr_li = (addr_width_lp)'(send_ptr_r*(data_width_p/8)); + + assign send_ptr_end = (send_ptr_width_lp)'((packet_rsize_lo - 1) >> $clog2(data_width_p/8)); + assign send_remaining = packet_rsize_lo[$clog2(data_width_p/8)-1:0]; + assign last_send_f = (send_ptr_r == send_ptr_end); + wire packet_rdata_sending = ~(packet_rvalid_lo & ~packet_rready_li); + always_comb begin + send_ptr_increment = 1'b0; + send_ptr_unwind = 1'b0; + packet_rvalid_li = 1'b0; + packet_ack_li = 1'b0; + send_complete = 1'b0; + if(packet_avail_lo) begin + if(packet_rdata_sending) begin + packet_rvalid_li = 1'b1; + if(~last_send_f) begin + send_ptr_increment = 1'b1; + end + else begin + send_ptr_unwind = 1'b1; + packet_ack_li = 1'b1; // switch to next packet + send_complete = 1'b1; + end + end + end + end + assign tx_axis_tlast_li = last_send_f; + assign tx_axis_tuser_li = 1'b0; + +if(data_width_p == 64) begin + always_comb begin + if(!last_send_f) + tx_axis_tkeep_li = '1; + else begin + tx_axis_tkeep_li = '0; + case(send_remaining) + 3'd0: + tx_axis_tkeep_li = 8'b1111_1111; + 3'd1: + tx_axis_tkeep_li = 8'b0000_0001; + 3'd2: + tx_axis_tkeep_li = 8'b0000_0011; + 3'd3: + tx_axis_tkeep_li = 8'b0000_0111; + 3'd4: + tx_axis_tkeep_li = 8'b0000_1111; + 3'd5: + tx_axis_tkeep_li = 8'b0001_1111; + 3'd6: + tx_axis_tkeep_li = 8'b0011_1111; + 3'd7: + tx_axis_tkeep_li = 8'b0111_1111; + endcase + end + end +end +else if(data_width_p == 32)begin + always_comb begin + if(!last_send_f) + tx_axis_tkeep_li = '1; + else begin + tx_axis_tkeep_li = '0; + case(send_remaining) + 2'd0: + tx_axis_tkeep_li = 4'b1111; + 2'd1: + tx_axis_tkeep_li = 4'b0001; + 2'd2: + tx_axis_tkeep_li = 4'b0011; + 2'd3: + tx_axis_tkeep_li = 4'b0111; + endcase + end + end +end + + + assign packet_req_o = packet_req_lo; + always_comb begin + packet_wsize_valid_li = 1'b0; + packet_wvalid_li = 1'b0; + packet_send_li = 1'b0; + if(packet_req_lo) begin + packet_wsize_valid_li = packet_wsize_valid_i; + packet_wvalid_li = packet_wvalid_i; + packet_send_li = packet_send_i; + end + end + + // synopsys translate_off + always_ff @(posedge clk_i) begin + if(reset_i == 1'b0) begin + assert(~(~packet_req_lo & packet_wsize_valid_i)) + else $error("%m: writing size when tx not ready at time %t", $time); + assert(~(~packet_req_lo & packet_wvalid_i)) + else $error("%m: writing data when tx not ready at time %t", $time); + assert(~(~packet_req_lo & packet_send_i)) + else $error("%m: sending packet when tx not ready at time %t", $time); + end + end + initial begin + assert(data_width_p == 32 || data_width_p == 64) + else $error("%m: unsupported data_width_p"); + + end + // synopsys translate_on + +endmodule diff --git a/ethernet_controller/v/interrupt_control_unit.sv b/ethernet_controller/v/interrupt_control_unit.sv new file mode 100644 index 0000000..d2ad978 --- /dev/null +++ b/ethernet_controller/v/interrupt_control_unit.sv @@ -0,0 +1,78 @@ + +module interrupt_control_unit ( + + input bit clk_i + , input logic reset_i + , input logic packet_avail_i + , input logic packet_req_i + + , input logic tx_interrupt_clear_i + + , input logic rx_interrupt_enable_i + , input logic rx_interrupt_enable_v_i + , input logic tx_interrupt_enable_i + , input logic tx_interrupt_enable_v_i + + , output logic rx_interrupt_pending_o + , output logic tx_interrupt_pending_o + , output logic rx_interrupt_o + , output logic tx_interrupt_o +); + + logic rx_interrupt_enable_r; + logic tx_interrupt_enable_r; + wire tx_interrupt_set_li; + + bsg_dff_reset_en #(.width_p(1)) + rx_interrupt_enable_reg ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.en_i(rx_interrupt_enable_v_i) + ,.data_i(rx_interrupt_enable_i) + ,.data_o(rx_interrupt_enable_r) + ); + + bsg_dff_reset_en #(.width_p(1)) + tx_interrupt_enable_reg ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.en_i(tx_interrupt_enable_v_i) + ,.data_i(tx_interrupt_enable_i) + ,.data_o(tx_interrupt_enable_r) + ); + + // Set tx interupt when packet_req_i goes from 0 -> 1 + // Since the init value of packet_req_i is 1, and + // bsg_edge_detect can only set init value to 0, + // ~packet_req_i and falling edge are used. + bsg_edge_detect #(.falling_not_rising_p(1)) + edge_detect ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.sig_i(~packet_req_i) + ,.detect_o(tx_interrupt_set_li) + ); + + logic tx_interrupt_pending_r_lo; + + bsg_dff_reset_set_clear #(.width_p(1)) + tx_interrupt_pending_reg ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.set_i(tx_interrupt_set_li) + ,.clear_i(tx_interrupt_clear_i) + ,.data_o(tx_interrupt_pending_r_lo) + ); + + // We don't really have an RX interrupt pending reg. The clear pending op + // will just go directly to the receive side of the Ethernet module and + // remove one received packet in the RX buffer. If after that the buffer happens + // to become empty, the RX pending bit will goes to 0, otherwise it keeps being + // 1. + assign rx_interrupt_pending_o = packet_avail_i; + assign tx_interrupt_pending_o = tx_interrupt_pending_r_lo; + + assign rx_interrupt_o = rx_interrupt_pending_o & rx_interrupt_enable_r; + assign tx_interrupt_o = tx_interrupt_pending_o & tx_interrupt_enable_r; + +endmodule diff --git a/ethernet_controller/v/lfsr.v b/ethernet_controller/v/lfsr.v new file mode 100644 index 0000000..d7662b6 --- /dev/null +++ b/ethernet_controller/v/lfsr.v @@ -0,0 +1,226 @@ +/*** This file is auto-generated from lfsr_template.v ***/ + +/* + * Parametrizable combinatorial parallel LFSR/CRC + */ +module lfsr # +( + // width of LFSR + parameter LFSR_WIDTH = 31, + // LFSR polynomial + parameter LFSR_POLY = 31'h10000001, + // LFSR configuration: "GALOIS", "FIBONACCI" + parameter LFSR_CONFIG = "FIBONACCI", + // LFSR feed forward enable + parameter LFSR_FEED_FORWARD = 0, + // bit-reverse input and output + parameter REVERSE = 0, + // width of data input + parameter DATA_WIDTH = 8, + // implementation style: "AUTO", "LOOP", "REDUCTION" + parameter STYLE = "AUTO" +) +( + input wire [DATA_WIDTH-1:0] data_in, + input wire [LFSR_WIDTH-1:0] state_in, + output wire [DATA_WIDTH-1:0] data_out, + output wire [LFSR_WIDTH-1:0] state_out +); +/* === Settings === + * LFSR_WIDTH: 32 + * LFSR_POLY: 0x04c11db7 + * LFSR_CONFIG: "GALOIS" + * LFSR_FEED_FORWARD: 0 + * REVERSE: 1 + * DATA_WIDTH: 8 + */ +initial begin + if(LFSR_WIDTH != 32 || + LFSR_POLY != 32'h04c11db7 || + LFSR_CONFIG != "GALOIS" || + LFSR_FEED_FORWARD != 0 || + REVERSE != 1 || + DATA_WIDTH != 8) begin + $error("Error: unsupported lfsr settings"); + $finish; + end +end +wire [31:0][31:0] lfsr_mask_state = { +32'b00000000000000000000000010000010, +32'b00000000000000000000000011000011, +32'b00000000000000000000000011100011, +32'b00000000000000000000000001110001, +32'b00000000000000000000000010111010, +32'b00000000000000000000000011011111, +32'b00000000000000000000000001101111, +32'b00000000000000000000000010110101, +32'b10000000000000000000000011011000, +32'b01000000000000000000000001101100, +32'b00100000000000000000000010110100, +32'b00010000000000000000000011011000, +32'b00001000000000000000000011101110, +32'b00000100000000000000000001110111, +32'b00000010000000000000000000111011, +32'b00000001000000000000000000011101, +32'b00000000100000000000000010001100, +32'b00000000010000000000000001000110, +32'b00000000001000000000000000100011, +32'b00000000000100000000000000010001, +32'b00000000000010000000000000001000, +32'b00000000000001000000000000000100, +32'b00000000000000100000000010000000, +32'b00000000000000010000000011000010, +32'b00000000000000001000000001100001, +32'b00000000000000000100000000110000, +32'b00000000000000000010000010011010, +32'b00000000000000000001000001001101, +32'b00000000000000000000100000100110, +32'b00000000000000000000010000010011, +32'b00000000000000000000001000001001, +32'b00000000000000000000000100000100 +}; + +wire [31:0][7:0] lfsr_mask_data = { +8'b10000010, +8'b11000011, +8'b11100011, +8'b01110001, +8'b10111010, +8'b11011111, +8'b01101111, +8'b10110101, +8'b11011000, +8'b01101100, +8'b10110100, +8'b11011000, +8'b11101110, +8'b01110111, +8'b00111011, +8'b00011101, +8'b10001100, +8'b01000110, +8'b00100011, +8'b00010001, +8'b00001000, +8'b00000100, +8'b10000000, +8'b11000010, +8'b01100001, +8'b00110000, +8'b10011010, +8'b01001101, +8'b00100110, +8'b00010011, +8'b00001001, +8'b00000100 +}; + +wire [7:0][31:0] output_mask_state = { +32'b00000000000000000000000010000010, +32'b00000000000000000000000001000001, +32'b00000000000000000000000000100000, +32'b00000000000000000000000000010000, +32'b00000000000000000000000000001000, +32'b00000000000000000000000000000100, +32'b00000000000000000000000000000010, +32'b00000000000000000000000000000001 +}; + +wire [7:0][7:0] output_mask_data = { +8'b10000010, +8'b01000001, +8'b00100000, +8'b00010000, +8'b00001000, +8'b00000100, +8'b00000010, +8'b00000001 +}; + +integer i, j, k; +// synthesis translate_off +`define SIMULATION +// synthesis translate_on + +`ifdef SIMULATION +// "AUTO" style is "REDUCTION" for faster simulation +parameter STYLE_INT = (STYLE == "AUTO") ? "REDUCTION" : STYLE; +`else +// "AUTO" style is "LOOP" for better synthesis result +parameter STYLE_INT = (STYLE == "AUTO") ? "LOOP" : STYLE; +`endif + +genvar n; + +generate + +if (STYLE_INT == "REDUCTION") begin + + // use Verilog reduction operator + // fast in iverilog + // significantly larger than generated code with ISE (inferred wide XORs may be tripping up optimizer) + // slightly smaller than generated code with Quartus + // --> better for simulation + + for (n = 0; n < LFSR_WIDTH; n = n + 1) begin : loop1 + assign state_out[n] = ^{(state_in & lfsr_mask_state[n]), (data_in & lfsr_mask_data[n])}; + end + for (n = 0; n < DATA_WIDTH; n = n + 1) begin : loop2 + assign data_out[n] = ^{(state_in & output_mask_state[n]), (data_in & output_mask_data[n])}; + end + +end else if (STYLE_INT == "LOOP") begin + + // use nested loops + // very slow in iverilog + // slightly smaller than generated code with ISE + // same size as generated code with Quartus + // --> better for synthesis + + reg [LFSR_WIDTH-1:0] state_out_reg; + reg [DATA_WIDTH-1:0] data_out_reg; + + assign state_out = state_out_reg; + assign data_out = data_out_reg; + + always @* begin + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + state_out_reg[i] = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + if (lfsr_mask_state[i][j]) begin + state_out_reg[i] = state_out_reg[i] ^ state_in[j]; + end + end + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + if (lfsr_mask_data[i][j]) begin + state_out_reg[i] = state_out_reg[i] ^ data_in[j]; + end + end + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + data_out_reg[i] = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + if (output_mask_state[i][j]) begin + data_out_reg[i] = data_out_reg[i] ^ state_in[j]; + end + end + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + if (output_mask_data[i][j]) begin + data_out_reg[i] = data_out_reg[i] ^ data_in[j]; + end + end + end + end + +end else begin + + initial begin + $error("Error: unknown style setting!"); + $finish; + end + +end + +endgenerate + +endmodule diff --git a/ethernet_controller/v/lfsr_gen_dir/Makefile b/ethernet_controller/v/lfsr_gen_dir/Makefile new file mode 100644 index 0000000..3c619af --- /dev/null +++ b/ethernet_controller/v/lfsr_gen_dir/Makefile @@ -0,0 +1,28 @@ +.PHONY: all clean + +export CUR_DIR := $(shell pwd) + +TOP_MODULE := lfsr_gen + +TEMPLATE_LFSR := $(CUR_DIR)/lfsr_template.v +TARGET_LFSR := $(CUR_DIR)/../lfsr.v +LFSR_GENERATOR := $(CUR_DIR)/lfsr_gen.sv + +VCS ?= vcs +VCS_OPTS := -full64 +VCS_OPTS += -sverilog +VCS_OPTS += +lint=all +VCS_OPTS += -top $(TOP_MODULE) +VCS_OPTS += -debug_all +VCS_OPTS += $(TEMPLATE_LFSR) $(LFSR_GENERATOR) + + +all: + $(VCS) $(VCS_OPTS) + echo "/*** This file is auto-generated from lfsr_template.v ***/" > $(TARGET_LFSR) + sed -n 29,56p $(TEMPLATE_LFSR) >> $(TARGET_LFSR) + $(CUR_DIR)/simv | head -n -4 | tail -n +4 >> $(TARGET_LFSR) + sed -n 359,444p $(TEMPLATE_LFSR) >> $(TARGET_LFSR) + +clean: + rm -rf simv.daidir/ simv csrc/ flist.vcs ucli.key vc_hdrs.h $(TARGET_LFSR) diff --git a/ethernet_controller/v/lfsr_gen_dir/lfsr_gen.sv b/ethernet_controller/v/lfsr_gen_dir/lfsr_gen.sv new file mode 100644 index 0000000..101217f --- /dev/null +++ b/ethernet_controller/v/lfsr_gen_dir/lfsr_gen.sv @@ -0,0 +1,87 @@ + +`timescale 1ns / 1ps + +module lfsr_gen(); + + parameter LFSR_WIDTH = 32; + parameter LFSR_POLY = 32'h4c11db7; + parameter LFSR_CONFIG = "GALOIS"; + parameter LFSR_FEED_FORWARD = 0; + parameter REVERSE = 1; + parameter DATA_WIDTH = 8; + parameter STYLE = "AUTO"; + + + lfsr #( + .LFSR_WIDTH(LFSR_WIDTH) + ,.LFSR_POLY(LFSR_POLY) + ,.LFSR_CONFIG(LFSR_CONFIG) + ,.LFSR_FEED_FORWARD(LFSR_FEED_FORWARD) + ,.REVERSE(REVERSE) + ,.DATA_WIDTH(DATA_WIDTH) + ,.STYLE(STYLE) + ) dut ( + .data_in() // UNUSED + ,.state_in() // UNUSED + ,.data_out() // UNUSED + ,.state_out() // UNUSED + ); + + initial begin + #1; + $write("/* === Settings ===\n"); + $write(" * LFSR_WIDTH: %0d\n", LFSR_WIDTH); + $write(" * LFSR_POLY: 0x%x\n", LFSR_POLY); + $write(" * LFSR_CONFIG: \"%s\"\n", LFSR_CONFIG); + $write(" * LFSR_FEED_FORWARD: %0d\n", LFSR_FEED_FORWARD); + $write(" * REVERSE: %0d\n", REVERSE); + $write(" * DATA_WIDTH: %0d\n", DATA_WIDTH); + $write(" */\n"); + $write("initial begin\n"); + $write(" if(LFSR_WIDTH != %0d ||\n", LFSR_WIDTH); + $write(" LFSR_POLY != %0d'h%x ||\n", LFSR_WIDTH, LFSR_POLY); + $write(" LFSR_CONFIG != \"%s\" ||\n", LFSR_CONFIG); + $write(" LFSR_FEED_FORWARD != %0d ||\n", LFSR_FEED_FORWARD); + $write(" REVERSE != %0d ||\n", REVERSE); + $write(" DATA_WIDTH != %0d) begin\n", DATA_WIDTH); + $write(" $error(\"Error: unsupported lfsr settings\");\n"); + $write(" $finish;\n"); + $write(" end\n"); + $write("end\n"); + + $write("wire [%0d:0][%0d:0] lfsr_mask_state = {\n", LFSR_WIDTH - 1, LFSR_WIDTH - 1); + for (int i = 0; i < LFSR_WIDTH; i = i + 1) begin + $write("%0d'b%b", LFSR_WIDTH, dut.lfsr_mask_state[LFSR_WIDTH - 1 - i]); + if(i != LFSR_WIDTH - 1) + $write(","); + $write("\n"); + end + $write("};\n\n"); + $write("wire [%0d:0][%0d:0] lfsr_mask_data = {\n", LFSR_WIDTH - 1, DATA_WIDTH - 1); + for (int i = 0; i < LFSR_WIDTH; i = i + 1) begin + $write("%0d'b%b", DATA_WIDTH, dut.lfsr_mask_data[LFSR_WIDTH - 1 - i]); + if(i != LFSR_WIDTH - 1) + $write(","); + $write("\n"); + end + $write("};\n\n"); + $write("wire [%0d:0][%0d:0] output_mask_state = {\n", DATA_WIDTH - 1, LFSR_WIDTH - 1); + for (int i = 0; i < DATA_WIDTH; i = i + 1) begin + $write("%0d'b%b", LFSR_WIDTH, dut.output_mask_state[DATA_WIDTH - 1 - i]); + if(i != DATA_WIDTH - 1) + $write(","); + $write("\n"); + end + $write("};\n\n"); + $write("wire [%0d:0][%0d:0] output_mask_data = {\n", DATA_WIDTH - 1, DATA_WIDTH - 1); + for (int i = 0; i < DATA_WIDTH; i = i + 1) begin + $write("%0d'b%b", DATA_WIDTH, dut.output_mask_data[DATA_WIDTH - 1 - i]); + if(i != DATA_WIDTH - 1) + $write(","); + $write("\n"); + end + $write("};\n\n"); + $write("integer i, j, k;\n"); + end + +endmodule diff --git a/ethernet_controller/v/lfsr_gen_dir/lfsr_template.v b/ethernet_controller/v/lfsr_gen_dir/lfsr_template.v new file mode 100644 index 0000000..f0b36ae --- /dev/null +++ b/ethernet_controller/v/lfsr_gen_dir/lfsr_template.v @@ -0,0 +1,445 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps + +/* + * Parametrizable combinatorial parallel LFSR/CRC + */ +module lfsr # +( + // width of LFSR + parameter LFSR_WIDTH = 31, + // LFSR polynomial + parameter LFSR_POLY = 31'h10000001, + // LFSR configuration: "GALOIS", "FIBONACCI" + parameter LFSR_CONFIG = "FIBONACCI", + // LFSR feed forward enable + parameter LFSR_FEED_FORWARD = 0, + // bit-reverse input and output + parameter REVERSE = 0, + // width of data input + parameter DATA_WIDTH = 8, + // implementation style: "AUTO", "LOOP", "REDUCTION" + parameter STYLE = "AUTO" +) +( + input wire [DATA_WIDTH-1:0] data_in, + input wire [LFSR_WIDTH-1:0] state_in, + output wire [DATA_WIDTH-1:0] data_out, + output wire [LFSR_WIDTH-1:0] state_out +); + +/* + +Fully parametrizable combinatorial parallel LFSR/CRC module. Implements an unrolled LFSR +next state computation, shifting DATA_WIDTH bits per pass through the module. Input data +is XORed with LFSR feedback path, tie data_in to zero if this is not required. + +Works in two parts: statically computes a set of bit masks, then uses these bit masks to +select bits for XORing to compute the next state. + +Ports: + +data_in + +Data bits to be shifted through the LFSR (DATA_WIDTH bits) + +state_in + +LFSR/CRC current state input (LFSR_WIDTH bits) + +data_out + +Data bits shifted out of LFSR (DATA_WIDTH bits) + +state_out + +LFSR/CRC next state output (LFSR_WIDTH bits) + +Parameters: + +LFSR_WIDTH + +Specify width of LFSR/CRC register + +LFSR_POLY + +Specify the LFSR/CRC polynomial in hex format. For example, the polynomial + +x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 + +would be represented as + +32'h04c11db7 + +Note that the largest term (x^32) is suppressed. This term is generated automatically based +on LFSR_WIDTH. + +LFSR_CONFIG + +Specify the LFSR configuration, either Fibonacci or Galois. Fibonacci is generally used +for linear-feedback shift registers (LFSR) for pseudorandom binary sequence (PRBS) generators, +scramblers, and descrambers, while Galois is generally used for cyclic redundancy check +generators and checkers. + +Fibonacci style (example for 64b66b scrambler, 0x8000000001) + + DIN (LSB first) + | + V + (+)<---------------------------(+)<-----------------------------. + | ^ | + | .----. .----. .----. | .----. .----. .----. | + +->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--' + | '----' '----' '----' '----' '----' '----' + V + DOUT + +Galois style (example for CRC16, 0x8005) + + ,-------------------+-------------------------+----------(+)<-- DIN (MSB first) + | | | ^ + | .----. .----. V .----. .----. V .----. | + `->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |--+---> DOUT + '----' '----' '----' '----' '----' + +LFSR_FEED_FORWARD + +Generate feed forward instead of feed back LFSR. Enable this for PRBS checking and self- +synchronous descrambling. + +Fibonacci feed-forward style (example for 64b66b descrambler, 0x8000000001) + + DIN (LSB first) + | + | .----. .----. .----. .----. .----. .----. + +->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--. + | '----' '----' '----' | '----' '----' '----' | + | V | + (+)<---------------------------(+)------------------------------' + | + V + DOUT + +Galois feed-forward style + + ,-------------------+-------------------------+------------+--- DIN (MSB first) + | | | | + | .----. .----. V .----. .----. V .----. V + `->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |->(+)-> DOUT + '----' '----' '----' '----' '----' + +REVERSE + +Bit-reverse LFSR input and output. Shifts MSB first by default, set REVERSE for LSB first. + +DATA_WIDTH + +Specify width of input and output data bus. The module will perform one shift per input +data bit, so if the input data bus is not required tie data_in to zero and set DATA_WIDTH +to the required number of shifts per clock cycle. + +STYLE + +Specify implementation style. Can be "AUTO", "LOOP", or "REDUCTION". When "AUTO" +is selected, implemenation will be "LOOP" or "REDUCTION" based on synthesis translate +directives. "REDUCTION" and "LOOP" are functionally identical, however they simulate +and synthesize differently. "REDUCTION" is implemented with a loop over a Verilog +reduction operator. "LOOP" is implemented as a doubly-nested loop with no reduction +operator. "REDUCTION" is very fast for simulation in iverilog and synthesizes well in +Quartus but synthesizes poorly in ISE, likely due to large inferred XOR gates causing +problems with the optimizer. "LOOP" synthesizes will in both ISE and Quartus. "AUTO" +will default to "REDUCTION" when simulating and "LOOP" for synthesizers that obey +synthesis translate directives. + +Settings for common LFSR/CRC implementations: + +Name Configuration Length Polynomial Initial value Notes +CRC16-IBM Galois, bit-reverse 16 16'h8005 16'hffff +CRC16-CCITT Galois 16 16'h1021 16'h1d0f +CRC32 Galois, bit-reverse 32 32'h04c11db7 32'hffffffff Ethernet FCS; invert final output +PRBS6 Fibonacci 6 6'h21 any +PRBS7 Fibonacci 7 7'h41 any +PRBS9 Fibonacci 9 9'h021 any ITU V.52 +PRBS10 Fibonacci 10 10'h081 any ITU +PRBS11 Fibonacci 11 11'h201 any ITU O.152 +PRBS15 Fibonacci, inverted 15 15'h4001 any ITU O.152 +PRBS17 Fibonacci 17 17'h04001 any +PRBS20 Fibonacci 20 20'h00009 any ITU V.57 +PRBS23 Fibonacci, inverted 23 23'h040001 any ITU O.151 +PRBS29 Fibonacci, inverted 29 29'h08000001 any +PRBS31 Fibonacci, inverted 31 31'h10000001 any +64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet +128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3 + +*/ + +reg [LFSR_WIDTH-1:0] lfsr_mask_state[LFSR_WIDTH-1:0]; +reg [DATA_WIDTH-1:0] lfsr_mask_data[LFSR_WIDTH-1:0]; +reg [LFSR_WIDTH-1:0] output_mask_state[DATA_WIDTH-1:0]; +reg [DATA_WIDTH-1:0] output_mask_data[DATA_WIDTH-1:0]; + +reg [LFSR_WIDTH-1:0] state_val = 0; +reg [DATA_WIDTH-1:0] data_val = 0; + +integer i, j, k; + +initial begin + // init bit masks + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + lfsr_mask_state[i] = {LFSR_WIDTH{1'b0}}; + lfsr_mask_state[i][i] = 1'b1; + lfsr_mask_data[i] = {DATA_WIDTH{1'b0}}; + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + output_mask_state[i] = {LFSR_WIDTH{1'b0}}; + if (i < LFSR_WIDTH) begin + output_mask_state[i][i] = 1'b1; + end + output_mask_data[i] = {DATA_WIDTH{1'b0}}; + end + + // simulate shift register + if (LFSR_CONFIG == "FIBONACCI") begin + // Fibonacci configuration + for (i = DATA_WIDTH-1; i >= 0; i = i - 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_WIDTH-1]; + data_val = lfsr_mask_data[LFSR_WIDTH-1]; + data_val = data_val ^ (1 << i); + + // add XOR inputs from correct indicies + for (j = 1; j < LFSR_WIDTH; j = j + 1) begin + if (LFSR_POLY & (1 << j)) begin + state_val = lfsr_mask_state[j-1] ^ state_val; + data_val = lfsr_mask_data[j-1] ^ data_val; + end + end + + // shift + for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin + output_mask_state[j] = output_mask_state[j-1]; + output_mask_data[j] = output_mask_data[j-1]; + end + output_mask_state[0] = state_val; + output_mask_data[0] = data_val; + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = {LFSR_WIDTH{1'b0}}; + data_val = 1 << i; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + end + end else if (LFSR_CONFIG == "GALOIS") begin + // Galois configuration + for (i = DATA_WIDTH-1; i >= 0; i = i - 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_WIDTH-1]; + data_val = lfsr_mask_data[LFSR_WIDTH-1]; + data_val = data_val ^ (1 << i); + + // shift + for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin + output_mask_state[j] = output_mask_state[j-1]; + output_mask_data[j] = output_mask_data[j-1]; + end + output_mask_state[0] = state_val; + output_mask_data[0] = data_val; + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = {LFSR_WIDTH{1'b0}}; + data_val = 1 << i; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + + // add XOR inputs at correct indicies + for (j = 1; j < LFSR_WIDTH; j = j + 1) begin + if (LFSR_POLY & (1 << j)) begin + lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; + lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; + end + end + end + end else begin + $error("Error: unknown configuration setting!"); + $finish; + end + + // reverse bits if selected + if (REVERSE) begin + // reverse order + for (i = 0; i < LFSR_WIDTH/2; i = i + 1) begin + state_val = lfsr_mask_state[i]; + data_val = lfsr_mask_data[i]; + lfsr_mask_state[i] = lfsr_mask_state[LFSR_WIDTH-i-1]; + lfsr_mask_data[i] = lfsr_mask_data[LFSR_WIDTH-i-1]; + lfsr_mask_state[LFSR_WIDTH-i-1] = state_val; + lfsr_mask_data[LFSR_WIDTH-i-1] = data_val; + end + for (i = 0; i < DATA_WIDTH/2; i = i + 1) begin + state_val = output_mask_state[i]; + data_val = output_mask_data[i]; + output_mask_state[i] = output_mask_state[DATA_WIDTH-i-1]; + output_mask_data[i] = output_mask_data[DATA_WIDTH-i-1]; + output_mask_state[DATA_WIDTH-i-1] = state_val; + output_mask_data[DATA_WIDTH-i-1] = data_val; + end + // reverse bits + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + state_val = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + state_val[j] = lfsr_mask_state[i][LFSR_WIDTH-j-1]; + end + lfsr_mask_state[i] = state_val; + + data_val = 0; + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + data_val[j] = lfsr_mask_data[i][DATA_WIDTH-j-1]; + end + lfsr_mask_data[i] = data_val; + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + state_val = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + state_val[j] = output_mask_state[i][LFSR_WIDTH-j-1]; + end + output_mask_state[i] = state_val; + + data_val = 0; + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + data_val[j] = output_mask_data[i][DATA_WIDTH-j-1]; + end + output_mask_data[i] = data_val; + end + end + + // for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + // $display("%b %b", lfsr_mask_state[i], lfsr_mask_data[i]); + // end +end + +// synthesis translate_off +`define SIMULATION +// synthesis translate_on + +`ifdef SIMULATION +// "AUTO" style is "REDUCTION" for faster simulation +parameter STYLE_INT = (STYLE == "AUTO") ? "REDUCTION" : STYLE; +`else +// "AUTO" style is "LOOP" for better synthesis result +parameter STYLE_INT = (STYLE == "AUTO") ? "LOOP" : STYLE; +`endif + +genvar n; + +generate + +if (STYLE_INT == "REDUCTION") begin + + // use Verilog reduction operator + // fast in iverilog + // significantly larger than generated code with ISE (inferred wide XORs may be tripping up optimizer) + // slightly smaller than generated code with Quartus + // --> better for simulation + + for (n = 0; n < LFSR_WIDTH; n = n + 1) begin : loop1 + assign state_out[n] = ^{(state_in & lfsr_mask_state[n]), (data_in & lfsr_mask_data[n])}; + end + for (n = 0; n < DATA_WIDTH; n = n + 1) begin : loop2 + assign data_out[n] = ^{(state_in & output_mask_state[n]), (data_in & output_mask_data[n])}; + end + +end else if (STYLE_INT == "LOOP") begin + + // use nested loops + // very slow in iverilog + // slightly smaller than generated code with ISE + // same size as generated code with Quartus + // --> better for synthesis + + reg [LFSR_WIDTH-1:0] state_out_reg; + reg [DATA_WIDTH-1:0] data_out_reg; + + assign state_out = state_out_reg; + assign data_out = data_out_reg; + + always @* begin + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + state_out_reg[i] = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + if (lfsr_mask_state[i][j]) begin + state_out_reg[i] = state_out_reg[i] ^ state_in[j]; + end + end + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + if (lfsr_mask_data[i][j]) begin + state_out_reg[i] = state_out_reg[i] ^ data_in[j]; + end + end + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + data_out_reg[i] = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + if (output_mask_state[i][j]) begin + data_out_reg[i] = data_out_reg[i] ^ state_in[j]; + end + end + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + if (output_mask_data[i][j]) begin + data_out_reg[i] = data_out_reg[i] ^ data_in[j]; + end + end + end + end + +end else begin + + initial begin + $error("Error: unknown style setting!"); + $finish; + end + +end + +endgenerate + +endmodule + +`resetall diff --git a/ethernet_controller/v/oddr_clock_downsample_and_right_shift.sv b/ethernet_controller/v/oddr_clock_downsample_and_right_shift.sv new file mode 100644 index 0000000..d435259 --- /dev/null +++ b/ethernet_controller/v/oddr_clock_downsample_and_right_shift.sv @@ -0,0 +1,48 @@ + +/* + * This module downsamples and right shifts the input clock. + * In our specific use case, the clk_i is 250 MHZ, and by having + * differnet clk_setting_i, we can get 2.5 MHZ, 25 MHZ and 125 MHZ + * right shifted generated clock at runtime. + * To see how Alex's Ethernet module achieves this, please see + * rgmii_tx_clk_1, rgmii_tx_clk_2 in rgmii_phy_if.v. + * + */ + +module oddr_clock_downsample_and_right_shift + (input clk_i + ,input reset_i + ,input [1:0] clk_setting_i + ,output ready_o + // output clock and data + ,output logic clk_r_o + ); + + logic odd_r; + logic [1:0] clk_setting_r; + logic clk_setting_r_2; + + // ready to accept new data every two cycles + assign ready_o = ~odd_r; + + always_ff @(posedge clk_i) + if (~odd_r) + clk_setting_r <= clk_setting_i; + + // odd_r signal (mux select bit) + always_ff @(posedge clk_i) + if (reset_i) + odd_r <= 1'b0; + else + odd_r <= ~odd_r; + + always_ff @(negedge clk_i) + if (odd_r) + clk_setting_r_2 <= clk_setting_r[0]; + else + clk_setting_r_2 <= clk_setting_r[1]; + + always_ff @(negedge clk_i) + clk_r_o <= clk_setting_r_2; + +endmodule diff --git a/ethernet_controller/v/packet_buffer.sv b/ethernet_controller/v/packet_buffer.sv new file mode 100644 index 0000000..12ca511 --- /dev/null +++ b/ethernet_controller/v/packet_buffer.sv @@ -0,0 +1,280 @@ + +/* + * + * This modules implements a FIFO used for buffering Ethernet packets. It can achieve up to 1r1w per + * cycle. The FIFO consists of slots, and each slot contains an Ethernet packet. + * When packet_avail_o == 1'b1, the read slot is valid; the size and content of the + * received packet can be read through the corresponding read signals. When the received packet in the + * read slot is no longer needed, set packet_ack_i to 1'b1 to free the slot. + * When packet_req_o == 1'b1, the write slot is valid and the size and content of the + * incoming packet can be written through the corresponding write signals. After finishing writing it, + * set packet_send_i to 1'b1 to reserve the slot. + * + * + * When slot_p == 2, and a packet has been written in slot 0: + * + * .____________. -> read slot ptr + * |____word____| + * | . | + * | . | + * | . | + * | | + * | slot 0 | + * | | + * |____________| + * |____word____| + * + * write slot ptr -> .____________. + * |____word____| + * | . | + * | . | + * | . | + * | | + * | slot 1 | + * | | + * |____________| + * |____word____| + * + * + */ + +`include "bsg_defines.v" + +module packet_buffer #( + parameter `BSG_INV_PARAM(slot_p) + , parameter `BSG_INV_PARAM(data_width_p) + , parameter `BSG_INV_PARAM(els_p) + , localparam addr_width_lp = $clog2(els_p) + , localparam packet_size_width_lp = $clog2(els_p+1) +) +( + input logic clk_i + , input logic reset_i + + //============================= RX ============================= + /* Control signals for RX packet (both helpfull) */ + , output logic packet_avail_o + , input logic packet_ack_i + + /* Read signals for RX packet (valid when packet_avail_o == 1) */ + // (read data size is always equal to data_width_p) + , input logic packet_rvalid_i + , input logic [addr_width_lp-1:0] packet_raddr_i + , output logic [data_width_p-1:0] packet_rdata_o + // packet size; valid as long as packet_avail_o == 1'b1 + , output logic [packet_size_width_lp-1:0] packet_rsize_o + + //============================= TX ============================= + /* Control signals for TX packet (both helpfull) */ + , input logic packet_send_i + , output logic packet_req_o + + /* Write signals for TX packet (valid when packet_req_o == 1) */ + // packet size + , input logic packet_wsize_valid_i + , input logic [packet_size_width_lp-1:0] packet_wsize_i + + , input logic packet_wvalid_i + , input logic [addr_width_lp-1:0] packet_waddr_i + , input logic [data_width_p-1:0] packet_wdata_i + , input logic [(data_width_p/8)-1:0] packet_wmask_i +); + + logic misaligned_access; + logic full_o; + logic empty_o; + + logic [7:0] write_mask; + + localparam lsb_lp = $clog2(data_width_p >> 3); + + always_comb begin + write_mask = '0; + misaligned_access = 1'b0; + // write + if(packet_wvalid_i) begin + case(packet_wmask_i) + (data_width_p/8)'('h3): begin // 2 + if(packet_waddr_i[0]) + misaligned_access = 1'b1; + end + (data_width_p/8)'('hF): begin // 4 + if(packet_waddr_i[1:0] != '0) + misaligned_access = 1'b1; + end + (data_width_p/8)'('hFF): begin // 8 + if(packet_waddr_i[2:0] != '0) + misaligned_access = 1'b1; + end + endcase + end + + // read + if(packet_rvalid_i) begin + if(data_width_p == 64) begin + if(packet_raddr_i[2:0] != '0) + misaligned_access = 1'b1; + end + else if(data_width_p == 32) begin + if(packet_raddr_i[1:0] != '0) + misaligned_access = 1'b1; + end + end + end + + wire readable = ~empty_o; + wire writable = ~full_o; + assign packet_avail_o = readable; + assign packet_req_o = writable; + wire enq_li = packet_send_i & packet_req_o; + wire deq_li = packet_avail_o & packet_ack_i; + + logic [`BSG_SAFE_CLOG2(slot_p)-1:0] wptr_r_lo; + logic [`BSG_SAFE_CLOG2(slot_p)-1:0] rptr_r_lo; + logic [slot_p-1:0] rptr_one_hot_lo; + logic [slot_p-1:0] wptr_one_hot_lo; + + bsg_fifo_tracker #(.els_p(slot_p)) + slot_ptr ( + .clk_i(clk_i) + ,.reset_i(reset_i) + + ,.enq_i(enq_li) + ,.deq_i(deq_li) + + ,.wptr_r_o(wptr_r_lo) + ,.rptr_r_o(rptr_r_lo) + ,.rptr_n_o(/* UNUSED */) + + ,.full_o(full_o) + ,.empty_o(empty_o) + ); + + bsg_decode #(.num_out_p(slot_p)) + rptr_one_hot ( + .i(rptr_r_lo) + ,.o(rptr_one_hot_lo) + ); + + bsg_decode #(.num_out_p(slot_p)) + wptr_one_hot ( + .i(wptr_r_lo) + ,.o(wptr_one_hot_lo) + ); + + logic [slot_p-1:0] prev_rptr_one_hot_lo; + wire data_reading = packet_rvalid_i & readable; + bsg_dff_reset_en #(.width_p(slot_p) + ) prev_rptr_one_hot_reg ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.en_i(data_reading) + ,.data_i(rptr_one_hot_lo) + ,.data_o(prev_rptr_one_hot_lo) + ); + + logic [slot_p-1:0][packet_size_width_lp-1:0] read_size_r_lo; + logic [slot_p-1:0][data_width_p-1:0] read_data_r_lo; + + localparam buffer_mem_els_lp = els_p / (data_width_p >> 3); + localparam buffer_mem_addr_width_lp = $clog2(buffer_mem_els_lp); +genvar i; +generate + for(i = 0;i < slot_p;i = i + 1) begin: slot + + wire per_slot_data_reading = rptr_one_hot_lo[i] & packet_rvalid_i & readable; + wire per_slot_data_writing = wptr_one_hot_lo[i] & packet_wvalid_i & writable; + + logic [buffer_mem_addr_width_lp-1:0] selected_addr_lo; + wire v_li = per_slot_data_reading | per_slot_data_writing; + wire w_li = per_slot_data_writing; + wire read_en = v_li & ~w_li; + logic read_en_r; + logic [data_width_p-1:0] data_out; + + bsg_mux_one_hot #( + .width_p(buffer_mem_addr_width_lp) + ,.els_p(2) + ) addr_mux ( + .data_i({packet_raddr_i[addr_width_lp-1:lsb_lp], packet_waddr_i[addr_width_lp-1:lsb_lp]}) + ,.sel_one_hot_i({per_slot_data_reading, per_slot_data_writing}) + ,.data_o(selected_addr_lo) + ); + + bsg_mem_1rw_sync_mask_write_byte #( + .els_p(buffer_mem_els_lp) + ,.data_width_p(data_width_p) + ,.latch_last_read_p(0) + ) buffer_mem ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.v_i(v_li) + ,.w_i(w_li) + ,.addr_i(selected_addr_lo) + ,.data_i(packet_wdata_i) + ,.write_mask_i(packet_wmask_i) + ,.data_o(data_out) + ); + + bsg_dff #( + .width_p(1) + ) read_en_dff ( + .clk_i(clk_i) + ,.data_i(read_en) + ,.data_o(read_en_r) + ); + bsg_dff_en_bypass #( + .width_p(data_width_p) + ) dff_bypass ( + .clk_i(clk_i) + ,.en_i(read_en_r) + ,.data_i(data_out) + ,.data_o(read_data_r_lo[i]) + ); + + wire size_writing = wptr_one_hot_lo[i] & packet_wsize_valid_i & writable; + bsg_dff_reset_en #(.width_p(packet_size_width_lp) + ) size_dff ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.en_i(size_writing) + ,.data_i(packet_wsize_i) + ,.data_o(read_size_r_lo[i]) + ); + end +endgenerate + + bsg_mux_one_hot #( + .width_p(packet_size_width_lp) + ,.els_p(slot_p)) + size_mux ( + .data_i(read_size_r_lo) + ,.sel_one_hot_i(rptr_one_hot_lo) + ,.data_o(packet_rsize_o) + ); + + bsg_mux_one_hot #( + .width_p(data_width_p) + ,.els_p(slot_p)) + data_mux ( + .data_i(read_data_r_lo) + ,.sel_one_hot_i(prev_rptr_one_hot_lo) + ,.data_o(packet_rdata_o) + ); + + // synopsys translate_off + always_ff @(posedge clk_i) begin + assert(reset_i !== 1'b0 || (misaligned_access === 1'b0)) + else $error("%m: packet_buffer: misaligned access"); + end + initial begin + assert(data_width_p == 32 || data_width_p == 64) + else begin + $error("%m: packet_buffer: unsupported data width"); + end + end + // synopsys translate_on + +endmodule + +`BSG_ABSTRACT_MODULE(packet_buffer) diff --git a/ethernet_controller/v/rgmii_phy_if.v b/ethernet_controller/v/rgmii_phy_if.v new file mode 100644 index 0000000..2e80873 --- /dev/null +++ b/ethernet_controller/v/rgmii_phy_if.v @@ -0,0 +1,249 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +/************************************************************************************* + +clk250_i: + +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +- + | | | | | | | | | | | | | | | | | | | | + +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ + + +clk250_rst_i: + +--------+ + | + +-----------------------------------------------------------------------+ + + +phy_rgmii_tx_clk: + +-------+ +-------+ +---+ + | | | | | + +--------------------------------------------+ +-------+ +-------+ + + +mac_gmii_tx_clk: + +-------+ +-------+ +-------+ +-------+ + | | | | | | | | + +----------------+ +-------+ +-------+ +-------+ +-------+ + + +gmii_txd, gmii_tx_en, gmii_tx_er: + +---------------------------------+---------------+---------------+--------------+ + | d1d0 | d1d0 | d1d0 + +---------------------------------+---------------+---------------+--------------+ + + +rgmii_txd, rgmii_tx_ctl: + +--------------------------------------------------------+-------+-------+-------+ + | d0 | d1 | d0 | + +--------------------------------------------------------+-------+-------+-------+ + +*************************************************************************************/ + + +/* + * RGMII PHY interface + */ +module rgmii_phy_if ( + input wire clk250, + input wire clk250_rst, + input wire tx_clk_gen_rst, + + /* + * GMII interface to MAC + */ + output wire mac_gmii_rx_clk, + input wire mac_gmii_rx_rst, + output wire [7:0] mac_gmii_rxd, + output wire mac_gmii_rx_dv, + output wire mac_gmii_rx_er, + output wire mac_gmii_tx_clk, + input wire mac_gmii_tx_rst, + output wire mac_gmii_tx_clk_en, + input wire [7:0] mac_gmii_txd, + input wire mac_gmii_tx_en, + input wire mac_gmii_tx_er, + + /* + * RGMII interface to PHY + */ + input wire phy_rgmii_rx_clk, + input wire [3:0] phy_rgmii_rxd, + input wire phy_rgmii_rx_ctl, + output wire phy_rgmii_tx_clk, + output wire [3:0] phy_rgmii_txd, + output wire phy_rgmii_tx_ctl, + + /* + * Control + */ + input wire [1:0] speed +); + +// receive + +wire rgmii_rx_ctl_1; +wire rgmii_rx_ctl_2; + +ssio_ddr_in # +( + .WIDTH(5) +) +rx_ssio_ddr_inst ( + .input_clk(phy_rgmii_rx_clk), + .input_d({phy_rgmii_rxd, phy_rgmii_rx_ctl}), + .output_clk(mac_gmii_rx_clk), + .output_q1({mac_gmii_rxd[3:0], rgmii_rx_ctl_1}), + .output_q2({mac_gmii_rxd[7:4], rgmii_rx_ctl_2}) +); + +assign mac_gmii_rx_dv = rgmii_rx_ctl_1; +assign mac_gmii_rx_er = rgmii_rx_ctl_1 ^ rgmii_rx_ctl_2; + +// transmit + +reg rgmii_tx_clk_1; +reg rgmii_tx_clk_2; +reg rgmii_tx_clk_rise; +reg rgmii_tx_clk_fall; + +reg [5:0] count_reg; + +always @(posedge mac_gmii_tx_clk) begin + if (mac_gmii_tx_rst) begin + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_rise <= 1'b1; + rgmii_tx_clk_fall <= 1'b1; + count_reg <= 0; + end else begin + rgmii_tx_clk_1 <= rgmii_tx_clk_2; + + if (speed == 2'b00) begin + // 10M + count_reg <= count_reg + 1; + rgmii_tx_clk_rise <= 1'b0; + rgmii_tx_clk_fall <= 1'b0; + if (count_reg == 24) begin + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b1; + rgmii_tx_clk_rise <= 1'b1; + end else if (count_reg >= 49) begin + rgmii_tx_clk_1 <= 1'b0; + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_fall <= 1'b1; + count_reg <= 0; + end + end else if (speed == 2'b01) begin + // 100M + count_reg <= count_reg + 1; + rgmii_tx_clk_rise <= 1'b0; + rgmii_tx_clk_fall <= 1'b0; + if (count_reg == 2) begin + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b1; + rgmii_tx_clk_rise <= 1'b1; + end else if (count_reg >= 4) begin + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_fall <= 1'b1; + count_reg <= 0; + end + end else begin + // 1000M + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_rise <= 1'b1; + rgmii_tx_clk_fall <= 1'b1; + end + end +end + +reg [3:0] rgmii_txd_1; +reg [3:0] rgmii_txd_2; +reg rgmii_tx_ctl_1; +reg rgmii_tx_ctl_2; + +reg gmii_clk_en; + +always @* begin + if (speed == 2'b00) begin + // 10M + rgmii_txd_1 = mac_gmii_txd[3:0]; + rgmii_txd_2 = mac_gmii_txd[3:0]; + if (rgmii_tx_clk_2) begin + rgmii_tx_ctl_1 = mac_gmii_tx_en; + rgmii_tx_ctl_2 = mac_gmii_tx_en; + end else begin + rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er; + rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er; + end + gmii_clk_en = rgmii_tx_clk_fall; + end else if (speed == 2'b01) begin + // 100M + rgmii_txd_1 = mac_gmii_txd[3:0]; + rgmii_txd_2 = mac_gmii_txd[3:0]; + if (rgmii_tx_clk_2) begin + rgmii_tx_ctl_1 = mac_gmii_tx_en; + rgmii_tx_ctl_2 = mac_gmii_tx_en; + end else begin + rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er; + rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er; + end + gmii_clk_en = rgmii_tx_clk_fall; + end else begin + // 1000M + rgmii_txd_1 = mac_gmii_txd[3:0]; + rgmii_txd_2 = mac_gmii_txd[7:4]; + rgmii_tx_ctl_1 = mac_gmii_tx_en; + rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er; + gmii_clk_en = 1; + end +end + +tx_clks_generator + tx_clks_gen + (.clk250_i(clk250) + ,.clk250_rst_i(clk250_rst) + ,.tx_clk_gen_rst_i(tx_clk_gen_rst) + + ,.phy_rgmii_tx_clk_setting_i({rgmii_tx_clk_2, rgmii_tx_clk_1}) + ,.phy_rgmii_tx_clk_o(phy_rgmii_tx_clk) + ,.mac_gmii_tx_clk_o(mac_gmii_tx_clk)); + +bsg_link_oddr_phy #(.width_p(5)) + data_oddr_inst + (.reset_i(clk250_rst) + ,.clk_i(clk250) + ,.data_i({{rgmii_txd_2, rgmii_tx_ctl_2}, {rgmii_txd_1, rgmii_tx_ctl_1}}) + ,.ready_o(/* UNUSED */) + + ,.data_r_o({phy_rgmii_txd, phy_rgmii_tx_ctl}) + ,.clk_r_o(/* UNUSED */) + ); + +assign mac_gmii_tx_clk_en = gmii_clk_en; + +endmodule diff --git a/ethernet_controller/v/ssio_ddr_in.v b/ethernet_controller/v/ssio_ddr_in.v new file mode 100644 index 0000000..683ff12 --- /dev/null +++ b/ethernet_controller/v/ssio_ddr_in.v @@ -0,0 +1,60 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +/* + * Generic source synchronous DDR input + */ +module ssio_ddr_in # +( + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q1, + output wire [WIDTH-1:0] output_q2 +); + +genvar n; +generate +for (n = 0; n < WIDTH; n = n + 1) begin : iddr + bsg_link_iddr_phy #(.width_p(1)) + iddr_inst ( + .clk_i(input_clk) + ,.data_i(input_d[n]) + ,.data_r_o({output_q2[n], output_q1[n]}) + ); +end +endgenerate + +assign output_clk = input_clk; + +endmodule diff --git a/ethernet_controller/v/tx_clks_generator.sv b/ethernet_controller/v/tx_clks_generator.sv new file mode 100644 index 0000000..e4e7d0a --- /dev/null +++ b/ethernet_controller/v/tx_clks_generator.sv @@ -0,0 +1,75 @@ + +`include "bsg_defines.v" + + +/************************************************************************************* + +clk250_i: + +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +- + | | | | | | | | | | | | | | | | | | | | + +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ + + +clk250_rst_i: + +--------+ + | + +-----------------------------------------------------------------------+ + + +phy_rgmii_tx_clk_o(when ETH operating in 1000M mode): + +-------+ +-------+ +---+ + | | | | | + +--------------------------------------------+ +-------+ +-------+ + + +mac_gmii_tx_clk_o: + +-------+ +-------+ +-------+ +-------+ + | | | | | | | | + +----------------+ +-------+ +-------+ +-------+ +-------+ + +*************************************************************************************/ + + + +module tx_clks_generator +( + input logic clk250_i + , input logic clk250_rst_i + , input logic tx_clk_gen_rst_i + + // phy_rgmii_tx_clk_setting_i: + // It is used for determining the frequency of + // phy_rgmii_tx_clk_o. With this, we can support + // various modes: 10M, 100M, 1000M Ethernet. + , input logic [1:0] phy_rgmii_tx_clk_setting_i + , output logic phy_rgmii_tx_clk_o + , output logic mac_gmii_tx_clk_o +); + +/* + * phy_rgmii_tx_clk_gen and gtx_clk_gen should use the same + * reset_i signal. Otherwise the PHY TX clk might not be + * logically in sync with the gtx clk, i.e. the first data + * sent in TX RGMII must be followed by a pos edge of RGMII + * TX clk instead of a neg edge. + */ + +/** phy_rgmii_tx_clk generation **/ +oddr_clock_downsample_and_right_shift + phy_rgmii_tx_clk_gen ( + .clk_i(clk250_i) + ,.reset_i(clk250_rst_i) + ,.clk_setting_i(phy_rgmii_tx_clk_setting_i) + ,.ready_o(/* UNUSED */) + ,.clk_r_o(phy_rgmii_tx_clk_o)); + + +/** gtx_clk generation **/ +bsg_counter_clock_downsample #(.width_p(2)) + gtx_clk_gen ( + .clk_i(clk250_i) + ,.reset_i(tx_clk_gen_rst_i) + ,.val_i(2'b0) // divided by 2 + ,.clk_r_o(mac_gmii_tx_clk_o)); + +endmodule diff --git a/ethernet_controller/v/zedboard/arst_sync.sv b/ethernet_controller/v/zedboard/arst_sync.sv new file mode 100644 index 0000000..2ff1e5f --- /dev/null +++ b/ethernet_controller/v/zedboard/arst_sync.sv @@ -0,0 +1,23 @@ + +module arst_sync ( + input arst_i + , input bclk_i + , output logic brst_o +); + + (* ASYNC_REG = "TRUE" *) + logic [0:0] bsg_SYNC_1_r, bsg_SYNC_2_r; + always @(posedge bclk_i or posedge arst_i) begin + if(arst_i) + bsg_SYNC_1_r <= 1'b1; + else + bsg_SYNC_1_r <= 1'b0; + end + always @(posedge bclk_i or posedge arst_i) begin + if(arst_i) + bsg_SYNC_2_r <= 1'b1; + else + bsg_SYNC_2_r <= bsg_SYNC_1_r; + end + assign brst_o = bsg_SYNC_2_r[0]; +endmodule diff --git a/ethernet_controller/v/zedboard/axis_async_fifo.v b/ethernet_controller/v/zedboard/axis_async_fifo.v new file mode 100644 index 0000000..402c632 --- /dev/null +++ b/ethernet_controller/v/zedboard/axis_async_fifo.v @@ -0,0 +1,401 @@ +/* + +Copyright (c) 2014-2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +/* + * AXI4-Stream asynchronous FIFO + */ +module axis_async_fifo # +( + // FIFO depth in words + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter DEPTH = 4096, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // number of output pipeline registers + parameter PIPELINE_OUTPUT = 2, + // Frame FIFO mode - operate on frames instead of cycles + // When set, m_axis_tvalid will not be deasserted within a frame + // Requires LAST_ENABLE set + parameter FRAME_FIFO = 0, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames larger than FIFO + // Requires FRAME_FIFO set + parameter DROP_OVERSIZE_FRAME = FRAME_FIFO, + // Drop frames marked bad + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_WHEN_FULL = 0, + // If 1, put a bsg_async_fifo at input side + // If 0, put a bsg_async_fifo at output side + parameter upstream_async_fifo_p +) +( + /* + * AXI input + */ + input wire s_clk, + input wire s_rst, + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + input wire m_clk, + input wire m_rst, + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire s_status_overflow, + output wire s_status_bad_frame, + output wire s_status_good_frame, + output wire m_status_overflow, + output wire m_status_bad_frame, + output wire m_status_good_frame +); + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +// axis_fifo +logic fifo_clk; +logic fifo_rst; +logic [DATA_WIDTH-1:0] fifo_m_axis_tdata; +logic [KEEP_WIDTH-1:0] fifo_m_axis_tkeep; +logic fifo_m_axis_tvalid; +logic fifo_m_axis_tready; +logic fifo_m_axis_tlast; +logic [ID_WIDTH-1:0] fifo_m_axis_tid; +logic [DEST_WIDTH-1:0] fifo_m_axis_tdest; +logic [USER_WIDTH-1:0] fifo_m_axis_tuser; + +logic [DATA_WIDTH-1:0] fifo_s_axis_tdata; +logic [KEEP_WIDTH-1:0] fifo_s_axis_tkeep; +logic fifo_s_axis_tvalid; +logic fifo_s_axis_tready; +logic fifo_s_axis_tlast; +logic [ID_WIDTH-1:0] fifo_s_axis_tid; +logic [DEST_WIDTH-1:0] fifo_s_axis_tdest; +logic [USER_WIDTH-1:0] fifo_s_axis_tuser; + +// Status +logic fifo_status_overflow; +logic fifo_status_bad_frame; +logic fifo_status_good_frame; +logic fifo_status_overflow_synced; +logic fifo_status_bad_frame_synced; +logic fifo_status_good_frame_synced; + +// bsg_async_fifo +logic w_enq_li; +logic [WIDTH-1:0] w_data_li; +logic w_full_lo; +logic r_deq_li; +logic [WIDTH-1:0] r_data_lo; +logic r_valid_lo; + +// bsg_launch_sync_sync +logic iclk_li; +logic iclk_reset_li; +logic oclk_li; +logic oclk_reset_li; + +if(upstream_async_fifo_p) begin: up + + // fifo clock, reset + assign fifo_clk = m_clk; + assign fifo_rst = m_rst; + // bsg_launch_sync_sync clock, reset + assign iclk_li = m_clk; + assign iclk_reset_li = m_rst; + assign oclk_li = s_clk; + assign oclk_reset_li = s_rst; + + // AXIS input -> bsg_async_fifo + assign w_data_li[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign w_data_li[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign w_data_li[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign w_data_li[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign w_data_li[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign w_data_li[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; + + assign w_enq_li = s_axis_tvalid & s_axis_tready; + assign s_axis_tready = ~w_full_lo; + + // bsg_async_fifo -> axis_fifo + assign fifo_s_axis_tvalid = r_valid_lo; + assign r_deq_li = fifo_s_axis_tvalid & fifo_s_axis_tready; + + assign fifo_s_axis_tdata = r_data_lo[DATA_WIDTH-1:0]; + if(KEEP_ENABLE) + assign fifo_s_axis_tkeep = r_data_lo[KEEP_OFFSET +: KEEP_WIDTH]; + else + assign fifo_s_axis_tkeep = {KEEP_WIDTH{1'b1}}; + if(LAST_ENABLE) + assign fifo_s_axis_tlast = r_data_lo[LAST_OFFSET]; + else + assign fifo_s_axis_tlast = 1'b1; + if(ID_ENABLE) + assign fifo_s_axis_tid = r_data_lo[ID_OFFSET +: ID_WIDTH]; + else + assign fifo_s_axis_tid = {ID_WIDTH{1'b0}}; + if(DEST_ENABLE) + assign fifo_s_axis_tdest = r_data_lo[DEST_OFFSET +: DEST_WIDTH]; + else + assign fifo_s_axis_tdest = {DEST_WIDTH{1'b0}}; + if(USER_ENABLE) + assign fifo_s_axis_tuser = r_data_lo[USER_OFFSET +: USER_WIDTH]; + else + assign fifo_s_axis_tuser = {USER_WIDTH{1'b0}}; + + // axis_fifo -> AXIS output + assign m_axis_tdata = fifo_m_axis_tdata; + assign m_axis_tkeep = fifo_m_axis_tkeep; + assign m_axis_tvalid = fifo_m_axis_tvalid; + assign m_axis_tlast = fifo_m_axis_tlast; + assign m_axis_tid = fifo_m_axis_tid; + assign m_axis_tdest = fifo_m_axis_tdest; + assign m_axis_tuser = fifo_m_axis_tuser; + assign fifo_m_axis_tready = m_axis_tready; + // Status + assign s_status_overflow = fifo_status_overflow_synced; + assign s_status_bad_frame = fifo_status_bad_frame_synced; + assign s_status_good_frame = fifo_status_good_frame_synced; + assign m_status_overflow = fifo_status_overflow; + assign m_status_bad_frame = fifo_status_bad_frame; + assign m_status_good_frame = fifo_status_good_frame; + +end else begin: down + + // fifo clock, reset + assign fifo_clk = s_clk; + assign fifo_rst = s_rst; + // bsg_launch_sync_sync clock, reset + assign iclk_li = s_clk; + assign iclk_reset_li = s_rst; + assign oclk_li = m_clk; + assign oclk_reset_li = m_rst; + + // AXIS input -> axis_fifo + + assign fifo_s_axis_tdata = s_axis_tdata; + assign fifo_s_axis_tkeep = s_axis_tkeep; + assign fifo_s_axis_tvalid = s_axis_tvalid; + assign fifo_s_axis_tlast = s_axis_tlast; + assign fifo_s_axis_tid = s_axis_tid; + assign fifo_s_axis_tdest = s_axis_tdest; + assign fifo_s_axis_tuser = s_axis_tuser; + assign s_axis_tready = fifo_s_axis_tready; + + // axis_fifo -> bsg_async_fifo + + assign w_data_li[DATA_WIDTH-1:0] = fifo_m_axis_tdata; + if (KEEP_ENABLE) assign w_data_li[KEEP_OFFSET +: KEEP_WIDTH] = fifo_m_axis_tkeep; + if (LAST_ENABLE) assign w_data_li[LAST_OFFSET] = fifo_m_axis_tlast; + if (ID_ENABLE) assign w_data_li[ID_OFFSET +: ID_WIDTH] = fifo_m_axis_tid; + if (DEST_ENABLE) assign w_data_li[DEST_OFFSET +: DEST_WIDTH] = fifo_m_axis_tdest; + if (USER_ENABLE) assign w_data_li[USER_OFFSET +: USER_WIDTH] = fifo_m_axis_tuser; + assign w_enq_li = fifo_m_axis_tvalid & fifo_m_axis_tready; + assign fifo_m_axis_tready = ~w_full_lo; + + // bsg_async_fifo -> AXIS output + assign r_deq_li = m_axis_tvalid & m_axis_tready; + assign m_axis_tvalid = r_valid_lo; + + assign m_axis_tdata = r_data_lo[DATA_WIDTH-1:0]; + if(KEEP_ENABLE) + assign m_axis_tkeep = r_data_lo[KEEP_OFFSET +: KEEP_WIDTH]; + else + assign m_axis_tkeep = {KEEP_WIDTH{1'b1}}; + if(LAST_ENABLE) + assign m_axis_tlast = r_data_lo[LAST_OFFSET]; + else + assign m_axis_tlast = 1'b1; + if(ID_ENABLE) + assign m_axis_tid = r_data_lo[ID_OFFSET +: ID_WIDTH]; + else + assign m_axis_tid = {ID_WIDTH{1'b0}}; + if(DEST_ENABLE) + assign m_axis_tdest = r_data_lo[DEST_OFFSET +: DEST_WIDTH]; + else + assign m_axis_tdest = {DEST_WIDTH{1'b0}}; + if(USER_ENABLE) + assign m_axis_tuser = r_data_lo[USER_OFFSET +: USER_WIDTH]; + else + assign m_axis_tuser = {USER_WIDTH{1'b0}}; + + // Status + assign s_status_overflow = fifo_status_overflow; + assign s_status_bad_frame = fifo_status_bad_frame; + assign s_status_good_frame = fifo_status_good_frame; + assign m_status_overflow = fifo_status_overflow_synced; + assign m_status_bad_frame = fifo_status_bad_frame_synced; + assign m_status_good_frame = fifo_status_good_frame_synced; + +end + +(* KEEP_HIERARCHY = "TRUE" *) +bsg_async_fifo #( + .lg_size_p(3) + ,.width_p(WIDTH) +) cdc ( + .w_clk_i(s_clk) + ,.w_reset_i(s_rst) + ,.w_enq_i(w_enq_li) + ,.w_data_i(w_data_li) + ,.w_full_o(w_full_lo) + ,.r_clk_i(m_clk) + ,.r_reset_i(m_rst) + ,.r_deq_i(r_deq_li) + ,.r_data_o(r_data_lo) + ,.r_valid_o(r_valid_lo) +); + +axis_fifo #( + .DEPTH(DEPTH) + ,.DATA_WIDTH(DATA_WIDTH) + ,.KEEP_ENABLE(KEEP_ENABLE) + ,.KEEP_WIDTH(KEEP_WIDTH) + ,.LAST_ENABLE(LAST_ENABLE) + ,.ID_ENABLE(ID_ENABLE) + ,.ID_WIDTH(ID_WIDTH) + ,.DEST_ENABLE(DEST_ENABLE) + ,.DEST_WIDTH(DEST_WIDTH) + ,.USER_ENABLE(USER_ENABLE) + ,.USER_WIDTH(USER_WIDTH) + ,.PIPELINE_OUTPUT(PIPELINE_OUTPUT) + ,.FRAME_FIFO(FRAME_FIFO) + ,.USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE) + ,.USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK) + ,.DROP_OVERSIZE_FRAME(DROP_OVERSIZE_FRAME) + ,.DROP_BAD_FRAME(DROP_BAD_FRAME) + ,.DROP_WHEN_FULL(DROP_WHEN_FULL) +) fifo ( + .clk(fifo_clk) + ,.rst(fifo_rst) + + ,.s_axis_tdata (fifo_s_axis_tdata) + ,.s_axis_tkeep (fifo_s_axis_tkeep) + ,.s_axis_tvalid(fifo_s_axis_tvalid) + ,.s_axis_tready(fifo_s_axis_tready) + ,.s_axis_tlast (fifo_s_axis_tlast) + ,.s_axis_tid (fifo_s_axis_tid) + ,.s_axis_tdest (fifo_s_axis_tdest) + ,.s_axis_tuser (fifo_s_axis_tuser) + + ,.m_axis_tdata (fifo_m_axis_tdata) + ,.m_axis_tkeep (fifo_m_axis_tkeep) + ,.m_axis_tvalid(fifo_m_axis_tvalid) + ,.m_axis_tready(fifo_m_axis_tready) + ,.m_axis_tlast (fifo_m_axis_tlast) + ,.m_axis_tid (fifo_m_axis_tid) + ,.m_axis_tdest (fifo_m_axis_tdest) + ,.m_axis_tuser (fifo_m_axis_tuser) + + ,.status_overflow (fifo_status_overflow) + ,.status_bad_frame (fifo_status_bad_frame) + ,.status_good_frame(fifo_status_good_frame) +); +logic overflow_sync1_reg, overflow_sync3_reg, overflow_sync4_reg; +logic bad_frame_sync1_reg, bad_frame_sync3_reg, bad_frame_sync4_reg; +logic good_frame_sync1_reg, good_frame_sync3_reg, good_frame_sync4_reg; + +wire overflow_sync1_next = overflow_sync1_reg ^ fifo_status_overflow; +wire bad_frame_sync1_next = bad_frame_sync1_reg ^ fifo_status_bad_frame; +wire good_frame_sync1_next = good_frame_sync1_reg ^ fifo_status_good_frame; + +assign fifo_status_overflow_synced = overflow_sync3_reg ^ overflow_sync4_reg; +assign fifo_status_bad_frame_synced = bad_frame_sync3_reg ^ bad_frame_sync4_reg; +assign fifo_status_good_frame_synced = good_frame_sync3_reg ^ good_frame_sync4_reg; + +bsg_dff_reset #( + .width_p(3) +) status ( + .clk_i(oclk_li) + ,.reset_i(oclk_reset_li) + ,.data_i({overflow_sync3_reg, bad_frame_sync3_reg, good_frame_sync3_reg}) + ,.data_o({overflow_sync4_reg, bad_frame_sync4_reg, good_frame_sync4_reg}) +); + +bsg_launch_sync_sync #( + .width_p(3), + .use_negedge_for_launch_p(0), + .use_async_reset_p(0) +) status_synchronizer ( + .iclk_i(iclk_li), + .iclk_reset_i(iclk_reset_li), + .oclk_i(oclk_li), + .iclk_data_i({overflow_sync1_next, bad_frame_sync1_next, good_frame_sync1_next}), + .iclk_data_o({overflow_sync1_reg, bad_frame_sync1_reg, good_frame_sync1_reg}), + .oclk_data_o({overflow_sync3_reg, bad_frame_sync3_reg, good_frame_sync3_reg}) +); + +endmodule diff --git a/ethernet_controller/v/zedboard/axis_fifo_mem.sv b/ethernet_controller/v/zedboard/axis_fifo_mem.sv new file mode 100644 index 0000000..33fd05f --- /dev/null +++ b/ethernet_controller/v/zedboard/axis_fifo_mem.sv @@ -0,0 +1,96 @@ +`include "bsg_defines.v" + +module axis_fifo_mem #( + parameter `BSG_INV_PARAM(width_p) + , parameter `BSG_INV_PARAM(els_p) + , parameter `BSG_INV_PARAM(pipeline_output_p) + , localparam addr_width_lp=`BSG_SAFE_CLOG2(els_p) +) +( + input clk_i + , input reset_i + , input w_v_i + , input [addr_width_lp-1:0] w_addr_i + , input [`BSG_SAFE_MINUS(width_p, 1):0] w_data_i + + , input r_v_i + , input [addr_width_lp-1:0] r_addr_i + , output logic [`BSG_SAFE_MINUS(width_p, 1):0] r_data_o + + , input output_ready_i + , input [pipeline_output_p-1:0] valid_pipe_reg_i +); + +if (pipeline_output_p > 1) begin + // This block of code is extracted from Alex's axis_fifo. It can work + // even if pipeline_output_p == 1, but with that setting we might as + // well use bsg_mem so that this module will also be suitable for ASIC. + logic [width_p-1:0] pipe_reg [pipeline_output_p-1:0]; + + integer j; + always @(posedge clk_i) begin + for (j = pipeline_output_p - 1; j > 0; j = j - 1) begin + if (output_ready_i || ((~valid_pipe_reg_i) >> j)) begin + // output ready or bubble in pipeline; transfer down pipeline + pipe_reg[j] <= pipe_reg[j-1]; + end + end + end + + logic [width_p-1:0] mem [els_p-1:0]; + always @(posedge clk_i) begin + if(w_v_i) + mem[w_addr_i] <= w_data_i; + end + + always @(posedge clk_i) begin + if(r_v_i) + pipe_reg[0] <= mem[r_addr_i]; + end + + assign r_data_o = pipe_reg[pipeline_output_p-1]; + +end else begin + // Equivalent code when pipeline_output_p == 1 but suitable for ASIC + wire unused = | {output_ready_i, valid_pipe_reg_i}; + + logic [`BSG_SAFE_MINUS(width_p, 1):0] r_data_lo; + // latch last read + logic read_en_r; + bsg_dff #( + .width_p(1) + ) read_en_dff ( + .clk_i(clk_i) + ,.data_i(r_v_i) + ,.data_o(read_en_r) + ); + + bsg_dff_en_bypass #( + .width_p(width_p) + ) dff_bypass ( + .clk_i(clk_i) + ,.en_i(read_en_r) + ,.data_i(r_data_lo) + ,.data_o(r_data_o) + ); + + (* KEEP_HIERARCHY = "TRUE" *) + bsg_mem_1r1w_sync #( + .width_p(width_p) + ,.els_p(els_p) + ) mem ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.w_v_i(w_v_i) + ,.w_addr_i(w_addr_i) + ,.w_data_i(w_data_i) + + ,.r_v_i(r_v_i) + ,.r_addr_i(r_addr_i) + ,.r_data_o(r_data_lo) + ); +end + +endmodule + +`BSG_ABSTRACT_MODULE(axis_fifo_mem) diff --git a/ethernet_controller/v/zedboard/bsg_launch_sync_sync.v b/ethernet_controller/v/zedboard/bsg_launch_sync_sync.v new file mode 100644 index 0000000..f8364ff --- /dev/null +++ b/ethernet_controller/v/zedboard/bsg_launch_sync_sync.v @@ -0,0 +1,356 @@ +// MBT 7/24/2014 +// +// This is a launch/synchronization complex. +// The launch flop prevents combinational glitching. +// The two sync flops reduce probability of metastability. +// See MBT's note on async design and CDC. +// +// The three flops should be don't touched in synopsys +// and abutted in physical design to reduce chances of metastability. +// +// Use of reset is optional; it can be used to hold a known value during reset +// if for instance, the value is coming off chip. +// + +// the code is structured this way because synopsys's +// support for hierarchical placement groups appears +// not to work for parameterized modules. +// so we must have a non-parameterized module +// in order to abut the three registers, which +// have two different clocks. +// + +// ASYNC RESET: iclk cannot toggle at deassertion of reset + +`include "bsg_defines.v" + +`ifndef rp_group + `define rp_group(x) + `define rp_place(x) + `define rp_endgroup(x) + `define rp_fill(x) + `define rp_array_dir(up) +`endif + +`define bsg_launch_sync_sync_unit(EDGE,bits) \ + \ +module bsg_launch_sync_sync_``EDGE``_``bits``_unit \ + (input iclk_i \ + , input iclk_reset_i \ + , input oclk_i \ + , input [bits-1:0] iclk_data_i \ + , output [bits-1:0] iclk_data_o \ + , output [bits-1:0] oclk_data_o \ + ); \ + \ + `rp_group (blss_bank) \ + `rp_place (hier blss_launch_1 0 0) \ + `rp_place (hier blss_1 1 0) \ + `rp_place (hier blss_2 2 0) \ + `rp_endgroup (blss_bank) \ + (* ASYNC_REG = "TRUE" *) \ + logic [bits-1:0] bsg_SYNC_LNCH_r; \ + assign iclk_data_o = bsg_SYNC_LNCH_r; \ + \ + always_ff @(EDGE iclk_i) \ + begin \ + `rp_group(blss_launch_1) \ + `rp_fill(0 0 UX) \ + `rp_array_dir(up) \ + `rp_endgroup(blss_launch_1) \ + \ + if (iclk_reset_i) \ + bsg_SYNC_LNCH_r <= {bits{1'b0}}; \ + else \ + bsg_SYNC_LNCH_r <= iclk_data_i; \ + end \ + (* ASYNC_REG = "TRUE" *) \ + logic [bits-1:0] bsg_SYNC_1_r; \ + (* ASYNC_REG = "TRUE" *) \ + logic [bits-1:0] bsg_SYNC_2_r; \ + \ + assign oclk_data_o = bsg_SYNC_2_r; \ + \ + always_ff @(posedge oclk_i) \ + begin \ + `rp_group(blss_1) \ + `rp_fill(0 0 UX) \ + `rp_array_dir(up) \ + `rp_endgroup(blss_1) \ + bsg_SYNC_1_r <= bsg_SYNC_LNCH_r; \ + end \ + \ + always_ff @(posedge oclk_i) \ + begin \ + `rp_group(blss_2) \ + `rp_fill(0 0 UX) \ + `rp_array_dir(up) \ + `rp_endgroup(blss_2) \ + bsg_SYNC_2_r <= bsg_SYNC_1_r; \ + end \ +endmodule + +`define bsg_launch_sync_sync_async_reset_unit(EDGE,bits) \ + \ +module bsg_launch_sync_sync_async_reset_``EDGE``_``bits``_unit \ + (input iclk_i \ + , input iclk_reset_i \ + , input oclk_i \ + , input [bits-1:0] iclk_data_i \ + , output [bits-1:0] iclk_data_o \ + , output [bits-1:0] oclk_data_o \ + ); \ + \ + `rp_group (blss_bank) \ + `rp_place (hier blss_launch_1 0 0) \ + `rp_place (hier blss_1 1 0) \ + `rp_place (hier blss_2 2 0) \ + `rp_endgroup (blss_bank) \ + \ + (* ASYNC_REG = "TRUE" *) \ + logic [bits-1:0] bsg_SYNC_LNCH_r; \ + assign iclk_data_o = bsg_SYNC_LNCH_r; \ + \ + always_ff @(EDGE iclk_i or posedge iclk_reset_i) \ + begin \ + `rp_group(blss_launch_1) \ + `rp_fill(0 0 UX) \ + `rp_array_dir(up) \ + `rp_endgroup(blss_launch_1) \ + \ + if (iclk_reset_i) \ + bsg_SYNC_LNCH_r <= {bits{1'b0}}; \ + else \ + bsg_SYNC_LNCH_r <= iclk_data_i; \ + end \ + \ + (* ASYNC_REG = "TRUE" *) \ + logic [bits-1:0] bsg_SYNC_1_r; \ + (* ASYNC_REG = "TRUE" *) \ + logic [bits-1:0] bsg_SYNC_2_r; \ + \ + assign oclk_data_o = bsg_SYNC_2_r; \ + \ + always_ff @(posedge oclk_i or posedge iclk_reset_i) \ + begin \ + `rp_group(blss_1) \ + `rp_fill(0 0 UX) \ + `rp_array_dir(up) \ + `rp_endgroup(blss_1) \ + if (iclk_reset_i) \ + bsg_SYNC_1_r <= {bits{1'b0}}; \ + else \ + bsg_SYNC_1_r <= bsg_SYNC_LNCH_r; \ + end \ + \ + always_ff @(posedge oclk_i or posedge iclk_reset_i) \ + begin \ + `rp_group(blss_2) \ + `rp_fill(0 0 UX) \ + `rp_array_dir(up) \ + `rp_endgroup(blss_2) \ + if (iclk_reset_i) \ + bsg_SYNC_2_r <= {bits{1'b0}}; \ + else \ + bsg_SYNC_2_r <= bsg_SYNC_1_r; \ + end \ +endmodule + +// bsg_launch_sync_sync_posedge_1_unit +`bsg_launch_sync_sync_unit(posedge,1) +`bsg_launch_sync_sync_unit(posedge,2) +`bsg_launch_sync_sync_unit(posedge,3) +`bsg_launch_sync_sync_unit(posedge,4) +`bsg_launch_sync_sync_unit(posedge,5) +`bsg_launch_sync_sync_unit(posedge,6) +`bsg_launch_sync_sync_unit(posedge,7) +`bsg_launch_sync_sync_unit(posedge,8) + +// bsg_launch_sync_sync_negedge_1_unit +`bsg_launch_sync_sync_unit(negedge,1) +`bsg_launch_sync_sync_unit(negedge,2) +`bsg_launch_sync_sync_unit(negedge,3) +`bsg_launch_sync_sync_unit(negedge,4) +`bsg_launch_sync_sync_unit(negedge,5) +`bsg_launch_sync_sync_unit(negedge,6) +`bsg_launch_sync_sync_unit(negedge,7) +`bsg_launch_sync_sync_unit(negedge,8) + +// bsg_launch_sync_sync_async_reset_posedge_1_unit +`bsg_launch_sync_sync_async_reset_unit(posedge,1) +`bsg_launch_sync_sync_async_reset_unit(posedge,2) +`bsg_launch_sync_sync_async_reset_unit(posedge,3) +`bsg_launch_sync_sync_async_reset_unit(posedge,4) +`bsg_launch_sync_sync_async_reset_unit(posedge,5) +`bsg_launch_sync_sync_async_reset_unit(posedge,6) +`bsg_launch_sync_sync_async_reset_unit(posedge,7) +`bsg_launch_sync_sync_async_reset_unit(posedge,8) + +// bsg_launch_sync_sync_async_reset_negedge_1_unit +`bsg_launch_sync_sync_async_reset_unit(negedge,1) +`bsg_launch_sync_sync_async_reset_unit(negedge,2) +`bsg_launch_sync_sync_async_reset_unit(negedge,3) +`bsg_launch_sync_sync_async_reset_unit(negedge,4) +`bsg_launch_sync_sync_async_reset_unit(negedge,5) +`bsg_launch_sync_sync_async_reset_unit(negedge,6) +`bsg_launch_sync_sync_async_reset_unit(negedge,7) +`bsg_launch_sync_sync_async_reset_unit(negedge,8) + +// warning: if you make this != 8, you need +// to modify other parts of this code + +`define blss_max_block 8 + +// handle trailer bits +`define blss_if_clause(EDGE,num) if ((width_p % `blss_max_block) == num) begin: z \ + bsg_launch_sync_sync_``EDGE``_``num``_unit blss \ + (.iclk_i \ + ,.iclk_reset_i \ + ,.oclk_i \ + ,.iclk_data_i(iclk_data_i[width_p-1-:num]) \ + ,.iclk_data_o(iclk_data_o[width_p-1-:num]) \ + ,.oclk_data_o(oclk_data_o[width_p-1-:num]) \ + ); end + +`define blssar_if_clause(EDGE,num) if ((width_p % `blss_max_block) == num) begin: z \ + bsg_launch_sync_sync_async_reset_``EDGE``_``num``_unit blss \ + (.iclk_i \ + ,.iclk_reset_i \ + ,.oclk_i \ + ,.iclk_data_i(iclk_data_i[width_p-1-:num]) \ + ,.iclk_data_o(iclk_data_o[width_p-1-:num]) \ + ,.oclk_data_o(oclk_data_o[width_p-1-:num]) \ + ); end +(* KEEP_HIERARCHY = "TRUE" *) +module bsg_launch_sync_sync #(parameter `BSG_INV_PARAM(width_p) + , parameter use_negedge_for_launch_p = 0 + , parameter use_async_reset_p = 0) + (input iclk_i + , input iclk_reset_i + , input oclk_i + , input [width_p-1:0] iclk_data_i + , output [width_p-1:0] iclk_data_o // after launch flop + , output [width_p-1:0] oclk_data_o // after sync flops + ); + +// synopsys translate_off + +/* initial + begin + $display("%m: instantiating blss of size %d",width_p); + end + */ +`ifndef VERILATOR + // The comparison to z makes verilator think that iclk_reset_i is a + // tri-state top-level (unsupported in Verilator v4.036) + initial assert (iclk_reset_i !== 'z) + else + begin + $error("%m iclk_reset should be connected"); + $finish(); + end +`endif +// synopsys translate_on + + genvar i; + + if (use_async_reset_p == 0) begin: sync + + if (use_negedge_for_launch_p) + begin: n + for (i = 0; i < (width_p/`blss_max_block); i = i + 1) + begin : maxb + bsg_launch_sync_sync_negedge_8_unit blss + (.iclk_i + ,.iclk_reset_i + ,.oclk_i + ,.iclk_data_i(iclk_data_i[i*`blss_max_block+:`blss_max_block]) + ,.iclk_data_o(iclk_data_o[i*`blss_max_block+:`blss_max_block]) + ,.oclk_data_o(oclk_data_o[i*`blss_max_block+:`blss_max_block]) + ); + end + + `blss_if_clause(negedge,1) else + `blss_if_clause(negedge,2) else + `blss_if_clause(negedge,3) else + `blss_if_clause(negedge,4) else + `blss_if_clause(negedge,5) else + `blss_if_clause(negedge,6) else + `blss_if_clause(negedge,7) + end + else + begin: p + for (i = 0; i < (width_p/`blss_max_block); i = i + 1) + begin : maxb + bsg_launch_sync_sync_posedge_8_unit blss + (.iclk_i + ,.iclk_reset_i + ,.oclk_i + ,.iclk_data_i(iclk_data_i[i*`blss_max_block+:`blss_max_block]) + ,.iclk_data_o(iclk_data_o[i*`blss_max_block+:`blss_max_block]) + ,.oclk_data_o(oclk_data_o[i*`blss_max_block+:`blss_max_block]) + ); + end + + `blss_if_clause(posedge,1) else + `blss_if_clause(posedge,2) else + `blss_if_clause(posedge,3) else + `blss_if_clause(posedge,4) else + `blss_if_clause(posedge,5) else + `blss_if_clause(posedge,6) else + `blss_if_clause(posedge,7) + end + + end + else begin: async + + if (use_negedge_for_launch_p) + begin: n + for (i = 0; i < (width_p/`blss_max_block); i = i + 1) + begin : maxb + bsg_launch_sync_sync_async_reset_negedge_8_unit blss + (.iclk_i + ,.iclk_reset_i + ,.oclk_i + ,.iclk_data_i(iclk_data_i[i*`blss_max_block+:`blss_max_block]) + ,.iclk_data_o(iclk_data_o[i*`blss_max_block+:`blss_max_block]) + ,.oclk_data_o(oclk_data_o[i*`blss_max_block+:`blss_max_block]) + ); + end + + `blssar_if_clause(negedge,1) else + `blssar_if_clause(negedge,2) else + `blssar_if_clause(negedge,3) else + `blssar_if_clause(negedge,4) else + `blssar_if_clause(negedge,5) else + `blssar_if_clause(negedge,6) else + `blssar_if_clause(negedge,7) + end + else + begin: p + for (i = 0; i < (width_p/`blss_max_block); i = i + 1) + begin : maxb + bsg_launch_sync_sync_async_reset_posedge_8_unit blss + (.iclk_i + ,.iclk_reset_i + ,.oclk_i + ,.iclk_data_i(iclk_data_i[i*`blss_max_block+:`blss_max_block]) + ,.iclk_data_o(iclk_data_o[i*`blss_max_block+:`blss_max_block]) + ,.oclk_data_o(oclk_data_o[i*`blss_max_block+:`blss_max_block]) + ); + end + + `blssar_if_clause(posedge,1) else + `blssar_if_clause(posedge,2) else + `blssar_if_clause(posedge,3) else + `blssar_if_clause(posedge,4) else + `blssar_if_clause(posedge,5) else + `blssar_if_clause(posedge,6) else + `blssar_if_clause(posedge,7) + end + + end + +endmodule + +`BSG_ABSTRACT_MODULE(bsg_launch_sync_sync) diff --git a/ethernet_controller/v/zedboard/bsg_mem_1r1w_sync_synth.v b/ethernet_controller/v/zedboard/bsg_mem_1r1w_sync_synth.v new file mode 100644 index 0000000..35cb028 --- /dev/null +++ b/ethernet_controller/v/zedboard/bsg_mem_1r1w_sync_synth.v @@ -0,0 +1,126 @@ +// MBT 7/7/2016 +// +// 1 read-port, 1 write-port ram +// +// reads are synchronous +// +// although we could merge this with normal bsg_mem_1r1w +// and select with a parameter, we do not do this because +// it's typically a very big change to the instantiating code +// to move to/from sync/async, and we want to reflect this. +// +// NOTE: Users of BaseJump STL should not instantiate this module directly +// they should use bsg_mem_1r1w_sync. + +`include "bsg_defines.v" + +module bsg_mem_1r1w_sync_synth #(parameter `BSG_INV_PARAM(width_p) + , parameter `BSG_INV_PARAM(els_p) + , parameter read_write_same_addr_p=0 + , parameter addr_width_lp=`BSG_SAFE_CLOG2(els_p) + , parameter latch_last_read_p=0 + , parameter verbose_p=1 + ) + (input clk_i + , input reset_i + + , input w_v_i + , input [addr_width_lp-1:0] w_addr_i + , input [`BSG_SAFE_MINUS(width_p, 1):0] w_data_i + + // currently unused + , input r_v_i + , input [addr_width_lp-1:0] r_addr_i + + , output logic [`BSG_SAFE_MINUS(width_p, 1):0] r_data_o + ); + + wire unused = reset_i; + + if (width_p == 0) + begin: z + wire unused0 = &{clk_i, w_v_i, w_addr_i, r_v_i, r_addr_i}; + assign r_data_o = '0; + end + else + begin: nz + + // TODO: Try ramstyle = "no_rw_check" + (* ram_style = "block" *) + logic [width_p-1:0] mem [els_p-1:0]; + logic read_en; + logic [width_p-1:0] data_out; + + // this treats the ram as an array of registers for which the + // read addr is latched on the clock, the write + // is done on the clock edge, and actually multiplexing + // of the registers for reading is done after the clock edge. + + // logically, this means that reads happen in time after + // the writes, and "simultaneous" reads and writes to the + // register file are allowed -- IF read_write_same_addr is set. + + // note that this behavior is generally incompatible with + // hardened 1r1w rams, so it's better not to take advantage + // of it if not necessary + + // we explicitly 'X out the read address if valid is not set + // to avoid accidental use of data when the valid signal was not + // asserted. without this, the output of the register file would + // "auto-update" based on new writes to the ram, a spooky behavior + // that would never correspond to that of a hardened ram. + + logic [addr_width_lp-1:0] r_addr_r; + + assign read_en = r_v_i; + assign data_out = mem[r_addr_r]; + + always_ff @(posedge clk_i) + if (r_v_i) + r_addr_r <= r_addr_i; + else + r_addr_r <= 'X; + + if (latch_last_read_p) + begin: llr + logic read_en_r; + + bsg_dff #( + .width_p(1) + ) read_en_dff ( + .clk_i(clk_i) + ,.data_i(read_en) + ,.data_o(read_en_r) + ); + + bsg_dff_en_bypass #( + .width_p(width_p) + ) dff_bypass ( + .clk_i(clk_i) + ,.en_i(read_en_r) + ,.data_i(data_out) + ,.data_o(r_data_o) + ); + end + else + begin: no_llr + assign r_data_o = data_out; + end + + always_ff @(posedge clk_i) + if (w_v_i) + mem[w_addr_i] <= w_data_i; + + end + + // synopsys translate_off + initial + begin + if (verbose_p) + $display("## %L: instantiating width_p=%d, els_p=%d (%m)",width_p,els_p); + end + // synopsys translate_on + +endmodule + +`BSG_ABSTRACT_MODULE(bsg_mem_1r1w_sync_synth) diff --git a/ethernet_controller/v/zedboard/ethernet_controller_wrapper.sv b/ethernet_controller/v/zedboard/ethernet_controller_wrapper.sv new file mode 100644 index 0000000..8a34da2 --- /dev/null +++ b/ethernet_controller/v/zedboard/ethernet_controller_wrapper.sv @@ -0,0 +1,98 @@ + +`include "bsg_defines.v" + + +module ethernet_controller_wrapper # +( + parameter `BSG_INV_PARAM(data_width_p) + , parameter `BSG_INV_PARAM(simulation_p) + , localparam addr_width_lp = 14 + , localparam size_width_lp = `BSG_WIDTH(`BSG_SAFE_CLOG2(data_width_p/8)) +) +( + input logic clk_i + , input logic reset_i + , input logic clk250_i + , input logic clk250_reset_i + + , input logic tx_clk_gen_reset_i + + , output logic tx_clk_o + , input logic tx_reset_i + + , output logic rx_clk_o + , input logic rx_reset_i + + // zynq-7000 specific: 200 MHZ for IDELAY tap value + , input logic iodelay_clk_i + , input logic iodelay_reset_i + + , input logic [addr_width_lp-1:0] addr_i + , input logic write_en_i + , input logic read_en_i + , input logic [data_width_p/8-1:0] write_mask_i + , input logic [data_width_p-1:0] write_data_i + , output logic [data_width_p-1:0] read_data_o // sync read + + , output logic rx_interrupt_pending_o + , output logic tx_interrupt_pending_o + + , input logic rgmii_rx_clk_i + , input logic [3:0] rgmii_rxd_i + , input logic rgmii_rx_ctl_i + , output logic rgmii_tx_clk_o + , output logic [3:0] rgmii_txd_o + , output logic rgmii_tx_ctl_o +); + + logic reset_r_lo; + logic [3:0] rgmii_rxd_delayed_lo; + logic rgmii_rx_ctl_delayed_lo; + + iodelay_control #( + .simulation_p(simulation_p)) + iodelay_control ( + .clk_i(clk_i) + ,.reset_i(reset_i) + ,.iodelay_clk_i(iodelay_clk_i) + ,.iodelay_reset_i(iodelay_reset_i) + ,.rgmii_rxd_i(rgmii_rxd_i) + ,.rgmii_rx_ctl_i(rgmii_rx_ctl_i) + ,.rgmii_rxd_delayed_o(rgmii_rxd_delayed_lo) + ,.rgmii_rx_ctl_delayed_o(rgmii_rx_ctl_delayed_lo) + ); + + ethernet_controller #( + .data_width_p(data_width_p)) + eth_ctr ( + .clk_i + ,.reset_i + ,.clk250_i + ,.clk250_reset_i + ,.tx_clk_gen_reset_i + ,.tx_clk_o + ,.tx_reset_i + ,.rx_clk_o + ,.rx_reset_i + + ,.addr_i + ,.write_en_i + ,.read_en_i + ,.write_mask_i + ,.write_data_i + ,.read_data_o // sync read + + ,.rx_interrupt_pending_o + ,.tx_interrupt_pending_o + + ,.rgmii_rx_clk_i + ,.rgmii_rxd_i(rgmii_rxd_delayed_lo) + ,.rgmii_rx_ctl_i(rgmii_rx_ctl_delayed_lo) + ,.rgmii_tx_clk_o + ,.rgmii_txd_o + ,.rgmii_tx_ctl_o + ); + +endmodule + +`BSG_ABSTRACT_MODULE(ethernet_controller_wrapper) diff --git a/ethernet_controller/v/zedboard/iodelay_control.sv b/ethernet_controller/v/zedboard/iodelay_control.sv new file mode 100644 index 0000000..77f5368 --- /dev/null +++ b/ethernet_controller/v/zedboard/iodelay_control.sv @@ -0,0 +1,70 @@ + +`include "bsg_defines.v" + +module iodelay_control #( + parameter `BSG_INV_PARAM(simulation_p) +) +( + input logic clk_i + , input logic reset_i + , input logic iodelay_clk_i + , input logic iodelay_reset_i + , input logic [3:0] rgmii_rxd_i + , input logic rgmii_rx_ctl_i + , output logic [3:0] rgmii_rxd_delayed_o + , output logic rgmii_rx_ctl_delayed_o +); + +logic iodelay_ref_clk_lo; + +if(simulation_p == 1) begin: sim + assign rgmii_rxd_delayed_o = rgmii_rxd_i; + assign rgmii_rx_ctl_delayed_o = rgmii_rx_ctl_i; +end else begin: nosim + + + BUFG iodelay_clk_bufg( + .I(iodelay_clk_i) + ,.O(iodelay_clk_lo) + ); + + IDELAYCTRL idelayctrl_inst ( + .RDY(/* UNUSED */) + ,.REFCLK(iodelay_clk_lo) + ,.RST(iodelay_reset_i) // active-high reset; should be held high for more than 60ns + ); + + wire [4:0] input_d = {rgmii_rxd_i, rgmii_rx_ctl_i}; + logic [4:0] delayed_d; + + genvar n; + for ( n = 0; n < 5; n = n + 1) begin: idelaye2 + // We need IDELAYE2 to meet the timing + // requirement for RX side of the RGMII signals + + IDELAYE2 #( + .DELAY_SRC("IDATAIN") + ,.IDELAY_TYPE("FIXED") + ,.IDELAY_VALUE(0) + ,.REFCLK_FREQUENCY(200.0) + ,.SIGNAL_PATTERN("DATA") + ) idelaye2_inst ( + .CNTVALUEOUT() // UNUSED + ,.DATAOUT(delayed_d[n]) + ,.C(1'b0) + ,.CE(1'b0) + ,.CINVCTRL(1'b0) + ,.CNTVALUEIN('0) + ,.DATAIN() // UNUSED + ,.IDATAIN(input_d[n]) + ,.INC(1'b0) + ,.LD(1'b0) + ,.LDPIPEEN(1'b0) + ,.REGRST(1'b0) + ); + end + assign {rgmii_rxd_delayed_o, rgmii_rx_ctl_delayed_o} = delayed_d; +end +endmodule + +`BSG_ABSTRACT_MODULE(iodelay_control) diff --git a/ethernet_controller/v/zedboard/ssio_ddr_in.v b/ethernet_controller/v/zedboard/ssio_ddr_in.v new file mode 100644 index 0000000..f2f5d69 --- /dev/null +++ b/ethernet_controller/v/zedboard/ssio_ddr_in.v @@ -0,0 +1,74 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Source synchronous DDR input for Zedboard + */ +module ssio_ddr_in # +( + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q1, + output wire [WIDTH-1:0] output_q2 +); + +wire clk_int; + +// pass through RX clock to logic +BUFR #( + .BUFR_DIVIDE("BYPASS") +) +clk_bufr ( + .I(input_clk), + .O(clk_int), + .CE(1'b1), + .CLR(1'b0) +); +genvar n; +generate +for (n = 0; n < WIDTH; n = n + 1) begin : iddr + bsg_link_iddr_phy #(.width_p(1)) + iddr_inst ( + .clk_i(clk_int) + ,.data_i(input_d[n]) + ,.data_r_o({output_q2[n], output_q1[n]}) + ); +end +endgenerate + +assign output_clk = clk_int; + +endmodule diff --git a/rv_plic/LICENSE b/rv_plic/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/rv_plic/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/rv_plic/Makefile b/rv_plic/Makefile new file mode 100644 index 0000000..9f8b162 --- /dev/null +++ b/rv_plic/Makefile @@ -0,0 +1,43 @@ +.PHONY: all clean + +OPENTITAN_DIR ?= ./import/opentitan + +all: + $(OPENTITAN_DIR)/util/ipgen.py generate -C $(OPENTITAN_DIR)/hw/ip_templates/rv_plic \ + -c rv_plic.ipconfig.hjson -o rv_plic/; mkdir -p v/ ;\ + mv rv_plic/rtl/* v/; rm -rf rv_plic/ + cp $(OPENTITAN_DIR)/hw/top_earlgrey/rtl/top_pkg.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_alert_pkg.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_secded_pkg.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_subreg_pkg.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_mubi_pkg.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_subreg.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_subreg_ext.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_alert_sender.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_secded_inv_64_57_dec.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_secded_inv_64_57_enc.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_sec_anchor_buf.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_sec_anchor_flop.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_diff_decode.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_secded_inv_39_32_enc.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_secded_inv_39_32_dec.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/tlul/rtl/tlul_pkg.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/tlul/rtl/tlul_cmd_intg_gen.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/tlul/rtl/tlul_cmd_intg_chk.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/tlul/rtl/tlul_rsp_intg_chk.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/tlul/rtl/tlul_rsp_intg_gen.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/tlul/rtl/tlul_adapter_reg.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/tlul/rtl/tlul_err.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/tlul/rtl/tlul_data_integ_enc.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/tlul/rtl/tlul_data_integ_dec.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/tlul/rtl/tlul_adapter_host.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_assert.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_assert_dummy_macros.svh v/ + cp $(OPENTITAN_DIR)/hw/ip/prim/rtl/prim_subreg_arb.sv v/ + cp $(OPENTITAN_DIR)/hw/ip/prim_generic/rtl/prim_generic_buf.sv v/prim_buf.sv + cp $(OPENTITAN_DIR)/hw/ip/prim_generic/rtl/prim_generic_flop_2sync.sv v/prim_flop_2sync.sv + cp $(OPENTITAN_DIR)/hw/ip/prim_generic/rtl/prim_generic_flop.sv v/prim_flop.sv + git apply patch.txt + +clean: + rm -rf v/ diff --git a/rv_plic/README b/rv_plic/README new file mode 100644 index 0000000..8d819ba --- /dev/null +++ b/rv_plic/README @@ -0,0 +1,9 @@ +This folder contains RISC-V PLIC from OpenTitan. Run 'make' to +generate all source files. They will all be placed under rtl/. + +PLIC configurations can be found in 'rv_plic.ipconfig.hjson'. + +What does the patch.txt do: +1. Add workaround to avoid bugs in some older versions of Vivado. +2. Remove unpacked arrays. +3. Some minor changes to make the code work. diff --git a/rv_plic/import/opentitan b/rv_plic/import/opentitan new file mode 160000 index 0000000..1e47333 --- /dev/null +++ b/rv_plic/import/opentitan @@ -0,0 +1 @@ +Subproject commit 1e47333bb2ff22b05be24cb8068cc9e4de764f9a diff --git a/rv_plic/patch.txt b/rv_plic/patch.txt new file mode 100644 index 0000000..a993004 --- /dev/null +++ b/rv_plic/patch.txt @@ -0,0 +1,152 @@ +diff --git a/rtl/prim_buf.sv b/rtl/prim_buf.sv +index 1b2fbff..6c6d442 100644 +--- a/rtl/prim_buf.sv ++++ b/rtl/prim_buf.sv +@@ -4,7 +4,7 @@ + + `include "prim_assert.sv" + +-module prim_generic_buf #( ++module prim_buf #( + parameter int Width = 1 + ) ( + input [Width-1:0] in_i, +diff --git a/rtl/prim_flop.sv b/rtl/prim_flop.sv +index 67b6e89..cf2b60a 100644 +--- a/rtl/prim_flop.sv ++++ b/rtl/prim_flop.sv +@@ -4,7 +4,7 @@ + + `include "prim_assert.sv" + +-module prim_generic_flop #( ++module prim_flop #( + parameter int Width = 1, + parameter logic [Width-1:0] ResetValue = 0 + ) ( +diff --git a/rtl/prim_flop_2sync.sv b/rtl/prim_flop_2sync.sv +index 45f802b..3e6e60b 100644 +--- a/rtl/prim_flop_2sync.sv ++++ b/rtl/prim_flop_2sync.sv +@@ -6,7 +6,7 @@ + // This may need to be moved to prim_generic if libraries have a specific cell + // for synchronization + +-module prim_generic_flop_2sync #( ++module prim_flop_2sync #( + parameter int Width = 16, + parameter logic [Width-1:0] ResetValue = '0 + ) ( +diff --git a/rtl/rv_plic.sv b/rtl/rv_plic.sv +index 87c8c7f..c33d866 100644 +--- a/rtl/rv_plic.sv ++++ b/rtl/rv_plic.sv +@@ -14,6 +14,8 @@ + // Verilog parameter + // MAX_PRIO: Maximum value of interrupt priority + ++`include "prim_assert.sv" ++ + module rv_plic import rv_plic_reg_pkg::*; #( + parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + // OpenTitan IP standardizes on level triggered interrupts, +@@ -41,7 +43,7 @@ module rv_plic import rv_plic_reg_pkg::*; #( + + // Interrupt notification to targets + output [NumTarget-1:0] irq_o, +- output [SRCW-1:0] irq_id_o [NumTarget], ++ output [NumTarget-1:0][SRCW-1:0] irq_id_o, + + output logic [NumTarget-1:0] msip_o + ); +@@ -54,21 +56,21 @@ module rv_plic import rv_plic_reg_pkg::*; #( + + logic [NumSrc-1:0] ip; + +- logic [NumSrc-1:0] ie [NumTarget]; ++ logic [NumTarget-1:0][NumSrc-1:0] ie; + + logic [NumTarget-1:0] claim_re; // Target read indicator +- logic [SRCW-1:0] claim_id [NumTarget]; ++ logic [NumTarget-1:0][SRCW-1:0] claim_id; + logic [NumSrc-1:0] claim; // Converted from claim_re/claim_id + + logic [NumTarget-1:0] complete_we; // Target write indicator +- logic [SRCW-1:0] complete_id [NumTarget]; ++ logic [NumTarget-1:0][SRCW-1:0] complete_id; + logic [NumSrc-1:0] complete; // Converted from complete_re/complete_id + +- logic [SRCW-1:0] cc_id [NumTarget]; // Write ID ++ logic [NumTarget-1:0][SRCW-1:0] cc_id; // Write ID + +- logic [PRIOW-1:0] prio [NumSrc]; ++ logic [NumSrc-1:0][PRIOW-1:0] prio; + +- logic [PRIOW-1:0] threshold [NumTarget]; ++ logic [NumTarget-1:0][PRIOW-1:0] threshold; + + // Glue logic between rv_plic_reg_top and others + assign cc_id = irq_id_o; +diff --git a/rtl/rv_plic_target.sv b/rtl/rv_plic_target.sv +index ded960e..d5cf121 100644 +--- a/rtl/rv_plic_target.sv ++++ b/rtl/rv_plic_target.sv +@@ -28,7 +28,7 @@ module rv_plic_target #( + input [N_SOURCE-1:0] ip_i, + input [N_SOURCE-1:0] ie_i, + +- input [PrioWidth-1:0] prio_i [N_SOURCE], ++ input [N_SOURCE-1:0][PrioWidth-1:0] prio_i, + input [PrioWidth-1:0] threshold_i, + + output logic irq_o, +diff --git a/rtl/prim_subreg_arb.sv b/rtl/prim_subreg_arb.sv +index 8003336..128d6e3 100644 +--- a/rtl/prim_subreg_arb.sv ++++ b/rtl/prim_subreg_arb.sv +@@ -27,7 +27,7 @@ module prim_subreg_arb + output logic [DW-1:0] wr_data + ); + +- if (SwAccess inside {SwAccessRW, SwAccessWO}) begin : gen_w ++ if (SwAccess == SwAccessRW || SwAccess == SwAccessWO) begin : gen_w + assign wr_en = we | de; + assign wr_data = (we == 1'b1) ? wd : d; // SW higher priority + // Unused q - Prevent lint errors. +diff --git a/rtl/prim_assert.sv b/rtl/prim_assert.sv +index da016e491..b2ba6aeaf 100644 +--- a/rtl/prim_assert.sv ++++ b/rtl/prim_assert.sv +@@ -79,7 +79,8 @@ + `include "prim_assert_yosys_macros.svh" + `define INC_ASSERT + `else +- `include "prim_assert_standard_macros.svh" ++// `include "prim_assert_standard_macros.svh" ++ `include "prim_assert_dummy_macros.svh" + `define INC_ASSERT + `endif + +diff --git a/rtl/rv_plic_target.sv b/rtl/rv_plic_target.sv +index 7d5e41a..e035b18 100644 +--- a/rtl/rv_plic_target.sv ++++ b/rtl/rv_plic_target.sv +@@ -87,9 +87,15 @@ module rv_plic_target #( + // forwarding muxes + // Note: these ternaries have triggered a synthesis bug in Vivado versions older + // than 2020.2. If the problem resurfaces again, have a look at issue #1408. +- assign is_tree[Pa] = (sel) ? is_tree[C1] : is_tree[C0]; +- assign id_tree[Pa] = (sel) ? id_tree[C1] : id_tree[C0]; +- assign max_tree[Pa] = (sel) ? max_tree[C1] : max_tree[C0]; ++// assign is_tree[Pa] = (sel) ? is_tree[C1] : is_tree[C0]; ++// assign id_tree[Pa] = (sel) ? id_tree[C1] : id_tree[C0]; ++// assign max_tree[Pa] = (sel) ? max_tree[C1] : max_tree[C0]; ++ assign is_tree[Pa] = (sel & is_tree[C1]) | ++ ((~sel) & is_tree[C0]); ++ assign id_tree[Pa] = ({SrcWidth{sel}} & id_tree[C1]) | ++ ({SrcWidth{~sel}} & id_tree[C0]); ++ assign max_tree[Pa] = ({PrioWidth{sel}} & max_tree[C1]) | ++ ({PrioWidth{~sel}} & max_tree[C0]); + end + end : gen_level + end : gen_tree diff --git a/rv_plic/rv_plic.ipconfig.hjson b/rv_plic/rv_plic.ipconfig.hjson new file mode 100644 index 0000000..4503645 --- /dev/null +++ b/rv_plic/rv_plic.ipconfig.hjson @@ -0,0 +1,12 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + instance_name: top_earlgrey_rv_plic + param_values: + { + src: 2 + target: 1 + prio: 1 + } +}