fpga: x400: Add support for X410 motherboard FPGA
Co-authored-by: Andrew Moch <Andrew.Moch@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com> Co-authored-by: Joerg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Kumaran Subramoniam <kumaran.subramoniam@ni.com> Co-authored-by: Max Köhler <max.koehler@ni.com> Co-authored-by: Michael Auchter <michael.auchter@ni.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Wade Fife <wade.fife@ettus.com> Co-authored-by: Hector Rubio <hrubio@ni.com> Original-commit: 6d3765605262016a80f71e36357f749ea35cbe5a
This commit is contained in:
committed by
Aaron Rossetto
co-authored by
Andrew Moch
Daniel Jepson
Javier Valenzuela
Joerg Hofrichter
Kumaran Subramoniam
Max Köhler
Michael Auchter
Paul Butler
Hector Rubio
parent
bfef20ea45
commit
61782b02d7
@@ -0,0 +1,53 @@
|
||||
#
|
||||
# Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
include $(TOOLS_DIR)/make/viv_ip_builder.mak
|
||||
|
||||
IP_100G_HDL_SRCS = $(addprefix $(IP_DIR)/eth_100g_bd/, \
|
||||
PkgEth100gLbus.sv \
|
||||
eth_100g.sv \
|
||||
eth_100g_axis2lbus.sv \
|
||||
eth_100g_lbus2axis.sv \
|
||||
)
|
||||
|
||||
IP_100G_HDL_SIM_SRCS = $(addprefix $(IP_DIR)/eth_100g_bd/, \
|
||||
model_100gbe.sv \
|
||||
) \
|
||||
$(wildcard $(addprefix $(IP_BUILD_DIR)/eth_100g_bd/eth_100g_bd/, \
|
||||
sim/eth_100g_bd.v\
|
||||
ip/*/ip_0/sim/*.v\
|
||||
ip/*/sim/*.h\
|
||||
ip/*/sim/*.v\
|
||||
ip/eth_100g_bd_cmac_usplus_0_0/cmac_usplus_v2_6_1/*.v\
|
||||
ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0/example_design/*.v\
|
||||
ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0/header_files/*.h\
|
||||
ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0.v\
|
||||
ipshared/*/hdl/*.v\
|
||||
ipshared/*/hdl/*.sv\
|
||||
))
|
||||
|
||||
IP_100G_ORIG_SRCS = $(addprefix $(IP_DIR)/eth_100g_bd/, \
|
||||
eth_100g_bd.tcl \
|
||||
)
|
||||
|
||||
IP_100G_BDTCL_SRCS = $(addprefix $(IP_BUILD_DIR)/eth_100g_bd/, \
|
||||
eth_100g_bd.tcl \
|
||||
)
|
||||
|
||||
IP_100G_BD_SRCS = $(addprefix $(IP_BUILD_DIR)/eth_100g_bd/, \
|
||||
eth_100g_bd/eth_100g_bd.bd \
|
||||
)
|
||||
|
||||
BD_100G_BD_OUTS = $(addprefix $(IP_BUILD_DIR)/eth_100g_bd/, \
|
||||
eth_100g_bd.bd.out \
|
||||
eth_100g_bd/eth_100g_bd_ooc.xdc \
|
||||
eth_100g_bd/synth/eth_100g_bd.v \
|
||||
)
|
||||
|
||||
EMPTY_IP_SRCS =
|
||||
|
||||
$(IP_100G_BD_SRCS) $(BD_100G_BD_OUTS) $(IP_100G_BDTCL_SRCS): $(IP_100G_ORIG_SRCS)
|
||||
$(call BUILD_VIVADO_BDTCL,eth_100g_bd,$(ARCH),$(PART_ID),$(IP_DIR),$(IP_BUILD_DIR),$(EMPTY_IP_SRCS))
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: PkgEth100gLbus
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Package to define an Lbus record
|
||||
//
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Lbus interface
|
||||
//
|
||||
// This is the segmented local bus interface on the Xilinx CMAC IP
|
||||
// see Xilinx CMAC documentation for detail
|
||||
// https://www.xilinx.com/support/documentation/ip_documentation/cmac_usplus/v2_4/pg203-cmac-usplus.pdf
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
package PkgEth100gLbus;
|
||||
|
||||
localparam DATA_WIDTH = 512;
|
||||
localparam NUM_SEG = 4;
|
||||
localparam SEG_DATA_WIDTH = DATA_WIDTH/NUM_SEG;
|
||||
|
||||
typedef struct packed {
|
||||
logic [SEG_DATA_WIDTH-1:0] data;
|
||||
logic [$clog2(SEG_DATA_WIDTH/8)-1:0] mty;
|
||||
logic sop;
|
||||
logic eop;
|
||||
logic err;
|
||||
logic ena;
|
||||
} lbus_t;
|
||||
|
||||
endpackage : PkgEth100gLbus
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,120 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: eth_100g_axi2lbus
|
||||
//
|
||||
// Description:
|
||||
// Translate from AXI4S (Xilinx segmented ifc) to lbus.
|
||||
//
|
||||
// Built using example provided from Xilinx
|
||||
//
|
||||
// Parameters:
|
||||
// - FIFO_DEPTH - FIFO will be 2** deep
|
||||
// - NUM_SEG - Number of lbus segments coming in
|
||||
|
||||
import PkgEth100gLbus::*;
|
||||
|
||||
module eth_100g_axi2lbus #(
|
||||
parameter FIFO_DEPTH = 5,
|
||||
parameter NUM_SEG = 4
|
||||
)
|
||||
(
|
||||
|
||||
// AXIS IF
|
||||
AxiStreamIf.slave axis,
|
||||
|
||||
// Lbus Segments
|
||||
|
||||
input logic lbus_rdy,
|
||||
output lbus_t lbus_out [NUM_SEG-1:0]
|
||||
|
||||
);
|
||||
|
||||
localparam SEG_DATA_WIDTH = DATA_WIDTH/NUM_SEG;
|
||||
localparam SEG_BYTES = SEG_DATA_WIDTH/8;
|
||||
localparam SEG_MTY_WIDTH = $clog2(SEG_BYTES);
|
||||
|
||||
// post rotation lbus signals
|
||||
lbus_t lbus_d [NUM_SEG-1:0];
|
||||
|
||||
// Find last so we can find SOP
|
||||
logic found_last;
|
||||
|
||||
// Propagate ready when asserting , propagate delayed ready while deasserting
|
||||
logic axis_tready_i;
|
||||
assign axis.tready = axis_tready_i | lbus_rdy;
|
||||
|
||||
always @(posedge axis.clk) begin
|
||||
axis_tready_i <= lbus_rdy;
|
||||
end
|
||||
|
||||
//declare a segment width axis bus so I can use it's methods
|
||||
AxiStreamIf #(.DATA_WIDTH(SEG_DATA_WIDTH),.USER_WIDTH($clog2(SEG_BYTES)))
|
||||
seg_axi (axis.clk, axis.rst);
|
||||
assign seg_axi.tlast = 1'b1;
|
||||
|
||||
logic [NUM_SEG:0] valid;
|
||||
assign valid[NUM_SEG] = 1'b0;
|
||||
|
||||
genvar b;
|
||||
genvar s;
|
||||
generate begin : lbus_gen
|
||||
for (s=0; s < NUM_SEG; s=s+1) begin : segment_loop
|
||||
// Reverse data byte ordering on each segment
|
||||
for (b = 0; b < DATA_WIDTH/32; b=b+1) begin : byte_loop
|
||||
assign lbus_d[s].data[b*8 +: 8] = axis.tdata[((s+1)*(DATA_WIDTH/NUM_SEG)-8-(b*8)) +: 8];
|
||||
end : byte_loop
|
||||
// valid if tkeep is set for any bytes in the segment
|
||||
assign valid[s] = (| axis.tkeep[s*SEG_BYTES +: SEG_BYTES]) & axis.tvalid;
|
||||
// enable when valid and transfering
|
||||
assign lbus_d[s].ena = valid[s] & axis.tready;
|
||||
// eop on last valid byte if last is set
|
||||
// we init an extra valid bit to 0 so if all valid bits for all segments are set we trigger an eop on the final segment
|
||||
assign lbus_d[s].eop = (valid[s] ^ valid[s+1]) & axis.tlast;
|
||||
// set error on all segmetns if tuser is set
|
||||
assign lbus_d[s].err = valid[s] & axis.tuser;
|
||||
// translate keep to trailing bytes and invert sign
|
||||
always_comb begin
|
||||
if (lbus_d[s].eop) begin
|
||||
lbus_d[s].mty = SEG_BYTES - seg_axi.keep2trailing(axis.tkeep[s*SEG_BYTES+: SEG_BYTES]);
|
||||
end else begin
|
||||
lbus_d[s].mty = 'b0;
|
||||
end
|
||||
end
|
||||
//SOP can only occur on segment 0, so init all the bits to zero, then assign segment 0
|
||||
if (s==0) begin
|
||||
assign lbus_d[s].sop = found_last & axis.tvalid & axis.tready;
|
||||
end else begin
|
||||
assign lbus_d[s].sop = 1'b0;
|
||||
end
|
||||
// assign the output DFF's
|
||||
always_ff @(posedge axis.clk) begin
|
||||
lbus_out[s].data <= lbus_d[s].data;
|
||||
lbus_out[s].ena <= lbus_d[s].ena;
|
||||
lbus_out[s].sop <= lbus_d[s].sop;
|
||||
lbus_out[s].eop <= lbus_d[s].eop;
|
||||
lbus_out[s].err <= lbus_d[s].err;
|
||||
lbus_out[s].mty <= lbus_d[s].mty;
|
||||
end
|
||||
|
||||
end : segment_loop
|
||||
end : lbus_gen
|
||||
endgenerate
|
||||
|
||||
|
||||
|
||||
// SOP Statemachine
|
||||
always_ff @(posedge axis.clk) begin : sop_sm
|
||||
if(axis.rst) begin
|
||||
found_last <= 1'b1;
|
||||
end else begin
|
||||
if(axis.tvalid & axis.tlast & axis.tready) found_last <= 1'b1;
|
||||
else if(axis.tvalid & axis.tready) found_last <= 1'b0;
|
||||
end
|
||||
end : sop_sm
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
|
||||
################################################################
|
||||
# This is a generated script based on design: eth_100g_bd
|
||||
#
|
||||
# Though there are limitations about the generated script,
|
||||
# the main purpose of this utility is to make learning
|
||||
# IP Integrator Tcl commands easier.
|
||||
################################################################
|
||||
|
||||
namespace eval _tcl {
|
||||
proc get_script_folder {} {
|
||||
set script_path [file normalize [info script]]
|
||||
set script_folder [file dirname $script_path]
|
||||
return $script_folder
|
||||
}
|
||||
}
|
||||
variable script_folder
|
||||
set script_folder [_tcl::get_script_folder]
|
||||
|
||||
################################################################
|
||||
# Check if script is running in correct Vivado version.
|
||||
################################################################
|
||||
set scripts_vivado_version 2019.1
|
||||
set current_vivado_version [version -short]
|
||||
|
||||
if { [string first $scripts_vivado_version $current_vivado_version] == -1 } {
|
||||
puts ""
|
||||
catch {common::send_msg_id "BD_TCL-109" "ERROR" "This script was generated using Vivado <$scripts_vivado_version> and is being run in <$current_vivado_version> of Vivado. Please run the script in Vivado <$scripts_vivado_version> then open the design in Vivado <$current_vivado_version>. Upgrade the design by running \"Tools => Report => Report IP Status...\", then run write_bd_tcl to create an updated script."}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
################################################################
|
||||
# START
|
||||
################################################################
|
||||
|
||||
# To test this script, run the following commands from Vivado Tcl console:
|
||||
# source eth_100g_bd_script.tcl
|
||||
|
||||
# If there is no project opened, this script will create a
|
||||
# project, but make sure you do not have an existing project
|
||||
# <./myproj/project_1.xpr> in the current working folder.
|
||||
|
||||
set list_projs [get_projects -quiet]
|
||||
if { $list_projs eq "" } {
|
||||
create_project project_1 myproj -part xczu28dr-ffvg1517-1-e
|
||||
}
|
||||
|
||||
|
||||
# CHANGE DESIGN NAME HERE
|
||||
variable design_name
|
||||
set design_name eth_100g_bd
|
||||
|
||||
# If you do not already have an existing IP Integrator design open,
|
||||
# you can create a design using the following command:
|
||||
# create_bd_design $design_name
|
||||
|
||||
# Creating design if needed
|
||||
set errMsg ""
|
||||
set nRet 0
|
||||
|
||||
set cur_design [current_bd_design -quiet]
|
||||
set list_cells [get_bd_cells -quiet]
|
||||
|
||||
if { ${design_name} eq "" } {
|
||||
# USE CASES:
|
||||
# 1) Design_name not set
|
||||
|
||||
set errMsg "Please set the variable <design_name> to a non-empty value."
|
||||
set nRet 1
|
||||
|
||||
} elseif { ${cur_design} ne "" && ${list_cells} eq "" } {
|
||||
# USE CASES:
|
||||
# 2): Current design opened AND is empty AND names same.
|
||||
# 3): Current design opened AND is empty AND names diff; design_name NOT in project.
|
||||
# 4): Current design opened AND is empty AND names diff; design_name exists in project.
|
||||
|
||||
if { $cur_design ne $design_name } {
|
||||
common::send_msg_id "BD_TCL-001" "INFO" "Changing value of <design_name> from <$design_name> to <$cur_design> since current design is empty."
|
||||
set design_name [get_property NAME $cur_design]
|
||||
}
|
||||
common::send_msg_id "BD_TCL-002" "INFO" "Constructing design in IPI design <$cur_design>..."
|
||||
|
||||
} elseif { ${cur_design} ne "" && $list_cells ne "" && $cur_design eq $design_name } {
|
||||
# USE CASES:
|
||||
# 5) Current design opened AND has components AND same names.
|
||||
|
||||
set errMsg "Design <$design_name> already exists in your project, please set the variable <design_name> to another value."
|
||||
set nRet 1
|
||||
} elseif { [get_files -quiet ${design_name}.bd] ne "" } {
|
||||
# USE CASES:
|
||||
# 6) Current opened design, has components, but diff names, design_name exists in project.
|
||||
# 7) No opened design, design_name exists in project.
|
||||
|
||||
set errMsg "Design <$design_name> already exists in your project, please set the variable <design_name> to another value."
|
||||
set nRet 2
|
||||
|
||||
} else {
|
||||
# USE CASES:
|
||||
# 8) No opened design, design_name not in project.
|
||||
# 9) Current opened design, has components, but diff names, design_name not in project.
|
||||
|
||||
common::send_msg_id "BD_TCL-003" "INFO" "Currently there is no design <$design_name> in project, so creating one..."
|
||||
|
||||
create_bd_design $design_name
|
||||
|
||||
common::send_msg_id "BD_TCL-004" "INFO" "Making design <$design_name> as current_bd_design."
|
||||
current_bd_design $design_name
|
||||
|
||||
}
|
||||
|
||||
common::send_msg_id "BD_TCL-005" "INFO" "Currently the variable <design_name> is equal to \"$design_name\"."
|
||||
|
||||
if { $nRet != 0 } {
|
||||
catch {common::send_msg_id "BD_TCL-114" "ERROR" $errMsg}
|
||||
return $nRet
|
||||
}
|
||||
|
||||
set bCheckIPsPassed 1
|
||||
##################################################################
|
||||
# CHECK IPs
|
||||
##################################################################
|
||||
set bCheckIPs 1
|
||||
if { $bCheckIPs == 1 } {
|
||||
set list_check_ips "\
|
||||
xilinx.com:ip:cmac_usplus:2.6\
|
||||
xilinx.com:ip:xlconstant:1.1\
|
||||
"
|
||||
|
||||
set list_ips_missing ""
|
||||
common::send_msg_id "BD_TCL-006" "INFO" "Checking if the following IPs exist in the project's IP catalog: $list_check_ips ."
|
||||
|
||||
foreach ip_vlnv $list_check_ips {
|
||||
set ip_obj [get_ipdefs -all $ip_vlnv]
|
||||
if { $ip_obj eq "" } {
|
||||
lappend list_ips_missing $ip_vlnv
|
||||
}
|
||||
}
|
||||
|
||||
if { $list_ips_missing ne "" } {
|
||||
catch {common::send_msg_id "BD_TCL-115" "ERROR" "The following IPs are not found in the IP Catalog:\n $list_ips_missing\n\nResolution: Please add the repository containing the IP(s) to the project." }
|
||||
set bCheckIPsPassed 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if { $bCheckIPsPassed != 1 } {
|
||||
common::send_msg_id "BD_TCL-1003" "WARNING" "Will not continue with creation of design due to the error(s) above."
|
||||
return 3
|
||||
}
|
||||
|
||||
##################################################################
|
||||
# DESIGN PROCs
|
||||
##################################################################
|
||||
|
||||
|
||||
|
||||
# Procedure to create entire design; Provide argument to make
|
||||
# procedure reusable. If parentCell is "", will use root.
|
||||
proc create_root_design { parentCell } {
|
||||
|
||||
variable script_folder
|
||||
variable design_name
|
||||
|
||||
if { $parentCell eq "" } {
|
||||
set parentCell [get_bd_cells /]
|
||||
}
|
||||
|
||||
# Get object for parentCell
|
||||
set parentObj [get_bd_cells $parentCell]
|
||||
if { $parentObj == "" } {
|
||||
catch {common::send_msg_id "BD_TCL-100" "ERROR" "Unable to find parent cell <$parentCell>!"}
|
||||
return
|
||||
}
|
||||
|
||||
# Make sure parentObj is hier blk
|
||||
set parentType [get_property TYPE $parentObj]
|
||||
if { $parentType ne "hier" } {
|
||||
catch {common::send_msg_id "BD_TCL-101" "ERROR" "Parent <$parentObj> has TYPE = <$parentType>. Expected to be <hier>."}
|
||||
return
|
||||
}
|
||||
|
||||
# Save current instance; Restore later
|
||||
set oldCurInst [current_bd_instance .]
|
||||
|
||||
# Set parent object as current
|
||||
current_bd_instance $parentObj
|
||||
|
||||
|
||||
# Create interface ports
|
||||
set core_drp [ create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:drp_rtl:1.0 core_drp ]
|
||||
|
||||
set eth100g_rx [ create_bd_intf_port -mode Master -vlnv xilinx.com:display_cmac_usplus:lbus_ports:2.0 eth100g_rx ]
|
||||
|
||||
set eth100g_tx [ create_bd_intf_port -mode Slave -vlnv xilinx.com:display_cmac_usplus:lbus_ports:2.0 eth100g_tx ]
|
||||
|
||||
set gt_rx [ create_bd_intf_port -mode Slave -vlnv xilinx.com:display_cmac_usplus:gt_ports:2.0 gt_rx ]
|
||||
|
||||
set gt_tx [ create_bd_intf_port -mode Master -vlnv xilinx.com:display_cmac_usplus:gt_ports:2.0 gt_tx ]
|
||||
|
||||
set refclk [ create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:diff_clock_rtl:1.0 refclk ]
|
||||
set_property -dict [ list \
|
||||
CONFIG.FREQ_HZ {156250000} \
|
||||
] $refclk
|
||||
|
||||
set s_axi [ create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:aximm_rtl:1.0 s_axi ]
|
||||
set_property -dict [ list \
|
||||
CONFIG.ADDR_WIDTH {32} \
|
||||
CONFIG.ARUSER_WIDTH {0} \
|
||||
CONFIG.AWUSER_WIDTH {0} \
|
||||
CONFIG.BUSER_WIDTH {0} \
|
||||
CONFIG.DATA_WIDTH {32} \
|
||||
CONFIG.HAS_BRESP {1} \
|
||||
CONFIG.HAS_BURST {0} \
|
||||
CONFIG.HAS_CACHE {0} \
|
||||
CONFIG.HAS_LOCK {0} \
|
||||
CONFIG.HAS_PROT {0} \
|
||||
CONFIG.HAS_QOS {0} \
|
||||
CONFIG.HAS_REGION {0} \
|
||||
CONFIG.HAS_RRESP {1} \
|
||||
CONFIG.HAS_WSTRB {1} \
|
||||
CONFIG.ID_WIDTH {0} \
|
||||
CONFIG.MAX_BURST_LENGTH {1} \
|
||||
CONFIG.NUM_READ_OUTSTANDING {1} \
|
||||
CONFIG.NUM_READ_THREADS {1} \
|
||||
CONFIG.NUM_WRITE_OUTSTANDING {1} \
|
||||
CONFIG.NUM_WRITE_THREADS {1} \
|
||||
CONFIG.PROTOCOL {AXI4LITE} \
|
||||
CONFIG.READ_WRITE_MODE {READ_WRITE} \
|
||||
CONFIG.RUSER_BITS_PER_BYTE {0} \
|
||||
CONFIG.RUSER_WIDTH {0} \
|
||||
CONFIG.SUPPORTS_NARROW_BURST {0} \
|
||||
CONFIG.WUSER_BITS_PER_BYTE {0} \
|
||||
CONFIG.WUSER_WIDTH {0} \
|
||||
] $s_axi
|
||||
|
||||
|
||||
# Create ports
|
||||
set ctl_tx_pause_req [ create_bd_port -dir I -from 8 -to 0 ctl_tx_pause_req ]
|
||||
set ctl_tx_resend_pause [ create_bd_port -dir I ctl_tx_resend_pause ]
|
||||
set drp_clk [ create_bd_port -dir I -type clk drp_clk ]
|
||||
set gt_txusrclk2 [ create_bd_port -dir O -type clk gt_txusrclk2 ]
|
||||
set_property -dict [ list \
|
||||
CONFIG.ASSOCIATED_BUSIF {eth100g_rx:eth100g_tx} \
|
||||
CONFIG.FREQ_HZ {322265625} \
|
||||
] $gt_txusrclk2
|
||||
set init_clk [ create_bd_port -dir I -type clk init_clk ]
|
||||
set pm_tick [ create_bd_port -dir I pm_tick ]
|
||||
set rx_clk [ create_bd_port -dir I -type clk rx_clk ]
|
||||
set_property -dict [ list \
|
||||
CONFIG.FREQ_HZ {322265625} \
|
||||
] $rx_clk
|
||||
set s_axi_aclk [ create_bd_port -dir I -type clk s_axi_aclk ]
|
||||
set s_axi_sreset [ create_bd_port -dir I -type rst s_axi_sreset ]
|
||||
set_property -dict [ list \
|
||||
CONFIG.POLARITY {ACTIVE_HIGH} \
|
||||
] $s_axi_sreset
|
||||
set stat_rx_aligned [ create_bd_port -dir O stat_rx_aligned ]
|
||||
set stat_rx_pause_req [ create_bd_port -dir O -from 8 -to 0 stat_rx_pause_req ]
|
||||
set sys_reset [ create_bd_port -dir I -type rst sys_reset ]
|
||||
set_property -dict [ list \
|
||||
CONFIG.POLARITY {ACTIVE_HIGH} \
|
||||
] $sys_reset
|
||||
set tx_ovfout [ create_bd_port -dir O tx_ovfout ]
|
||||
set tx_unfout [ create_bd_port -dir O tx_unfout ]
|
||||
set usr_rx_reset [ create_bd_port -dir O -type rst usr_rx_reset ]
|
||||
set usr_tx_reset [ create_bd_port -dir O -type rst usr_tx_reset ]
|
||||
|
||||
# Create instance: cmac_usplus_0, and set properties
|
||||
set cmac_usplus_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:cmac_usplus:2.6 cmac_usplus_0 ]
|
||||
set_property -dict [ list \
|
||||
CONFIG.CMAC_CAUI4_MODE {1} \
|
||||
CONFIG.CMAC_CORE_SELECT {CMACE4_X0Y0} \
|
||||
CONFIG.ENABLE_AXI_INTERFACE {1} \
|
||||
CONFIG.GT_DRP_CLK {100} \
|
||||
CONFIG.GT_GROUP_SELECT {X0Y4~X0Y7} \
|
||||
CONFIG.GT_REF_CLK_FREQ {156.25} \
|
||||
CONFIG.INCLUDE_AUTO_NEG_LT_LOGIC {0} \
|
||||
CONFIG.INCLUDE_RS_FEC {1} \
|
||||
CONFIG.INCLUDE_SHARED_LOGIC {2} \
|
||||
CONFIG.INCLUDE_STATISTICS_COUNTERS {1} \
|
||||
CONFIG.LANE10_GT_LOC {NA} \
|
||||
CONFIG.LANE1_GT_LOC {X0Y4} \
|
||||
CONFIG.LANE2_GT_LOC {X0Y5} \
|
||||
CONFIG.LANE3_GT_LOC {X0Y6} \
|
||||
CONFIG.LANE4_GT_LOC {X0Y7} \
|
||||
CONFIG.LANE5_GT_LOC {NA} \
|
||||
CONFIG.LANE6_GT_LOC {NA} \
|
||||
CONFIG.LANE7_GT_LOC {NA} \
|
||||
CONFIG.LANE8_GT_LOC {NA} \
|
||||
CONFIG.LANE9_GT_LOC {NA} \
|
||||
CONFIG.NUM_LANES {4} \
|
||||
CONFIG.RX_CHECK_ACK {0} \
|
||||
CONFIG.RX_EQ_MODE {AUTO} \
|
||||
CONFIG.RX_FLOW_CONTROL {1} \
|
||||
CONFIG.TX_FLOW_CONTROL {1} \
|
||||
CONFIG.USER_INTERFACE {LBUS} \
|
||||
] $cmac_usplus_0
|
||||
|
||||
# Create instance: tie_loopback, and set properties
|
||||
set tie_loopback [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 tie_loopback ]
|
||||
set_property -dict [ list \
|
||||
CONFIG.CONST_VAL {0} \
|
||||
CONFIG.CONST_WIDTH {12} \
|
||||
] $tie_loopback
|
||||
|
||||
# Create instance: tie_zero, and set properties
|
||||
set tie_zero [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 tie_zero ]
|
||||
set_property -dict [ list \
|
||||
CONFIG.CONST_VAL {0} \
|
||||
] $tie_zero
|
||||
|
||||
# Create interface connections
|
||||
connect_bd_intf_net -intf_net RefClk_1 [get_bd_intf_ports refclk] [get_bd_intf_pins cmac_usplus_0/gt_ref_clk]
|
||||
connect_bd_intf_net -intf_net Rx_1 [get_bd_intf_ports gt_rx] [get_bd_intf_pins cmac_usplus_0/gt_rx]
|
||||
connect_bd_intf_net -intf_net cmac_usplus_0_gt_tx [get_bd_intf_ports gt_tx] [get_bd_intf_pins cmac_usplus_0/gt_tx]
|
||||
connect_bd_intf_net -intf_net cmac_usplus_0_lbus_rx [get_bd_intf_ports eth100g_rx] [get_bd_intf_pins cmac_usplus_0/lbus_rx]
|
||||
connect_bd_intf_net -intf_net eth_100g_tx_1 [get_bd_intf_ports eth100g_tx] [get_bd_intf_pins cmac_usplus_0/lbus_tx]
|
||||
connect_bd_intf_net -intf_net sDrp_1 [get_bd_intf_ports core_drp] [get_bd_intf_pins cmac_usplus_0/core_drp]
|
||||
connect_bd_intf_net -intf_net s_axi_1 [get_bd_intf_ports s_axi] [get_bd_intf_pins cmac_usplus_0/s_axi]
|
||||
|
||||
# Create port connections
|
||||
connect_bd_net -net SysClk_1 [get_bd_ports init_clk] [get_bd_pins cmac_usplus_0/init_clk]
|
||||
connect_bd_net -net aResetIn_1 [get_bd_ports sys_reset] [get_bd_pins cmac_usplus_0/sys_reset]
|
||||
connect_bd_net -net cmac_usplus_0_gt_txusrclk2 [get_bd_ports gt_txusrclk2] [get_bd_pins cmac_usplus_0/gt_txusrclk2]
|
||||
connect_bd_net -net cmac_usplus_0_stat_rx_aligned [get_bd_ports stat_rx_aligned] [get_bd_pins cmac_usplus_0/stat_rx_aligned]
|
||||
connect_bd_net -net cmac_usplus_0_stat_rx_pause_req [get_bd_ports stat_rx_pause_req] [get_bd_pins cmac_usplus_0/stat_rx_pause_req]
|
||||
connect_bd_net -net cmac_usplus_0_tx_ovfout [get_bd_ports tx_ovfout] [get_bd_pins cmac_usplus_0/tx_ovfout]
|
||||
connect_bd_net -net cmac_usplus_0_tx_unfout [get_bd_ports tx_unfout] [get_bd_pins cmac_usplus_0/tx_unfout]
|
||||
connect_bd_net -net cmac_usplus_0_usr_rx_reset [get_bd_ports usr_rx_reset] [get_bd_pins cmac_usplus_0/usr_rx_reset]
|
||||
connect_bd_net -net cmac_usplus_0_usr_tx_reset [get_bd_ports usr_tx_reset] [get_bd_pins cmac_usplus_0/usr_tx_reset]
|
||||
connect_bd_net -net ctl_tx_pause_req_1 [get_bd_ports ctl_tx_pause_req] [get_bd_pins cmac_usplus_0/ctl_tx_pause_req]
|
||||
connect_bd_net -net ctl_tx_resend_pause_1 [get_bd_ports ctl_tx_resend_pause] [get_bd_pins cmac_usplus_0/ctl_tx_resend_pause]
|
||||
connect_bd_net -net drp_clk_1 [get_bd_ports drp_clk] [get_bd_pins cmac_usplus_0/drp_clk]
|
||||
connect_bd_net -net pm_tick_1 [get_bd_ports pm_tick] [get_bd_pins cmac_usplus_0/pm_tick]
|
||||
connect_bd_net -net rx_clk_1 [get_bd_ports rx_clk] [get_bd_pins cmac_usplus_0/rx_clk]
|
||||
connect_bd_net -net s_axi_aclk_1 [get_bd_ports s_axi_aclk] [get_bd_pins cmac_usplus_0/s_axi_aclk]
|
||||
connect_bd_net -net s_axi_sreset_1 [get_bd_ports s_axi_sreset] [get_bd_pins cmac_usplus_0/s_axi_sreset]
|
||||
connect_bd_net -net tie_loopback_dout [get_bd_pins cmac_usplus_0/gt_loopback_in] [get_bd_pins tie_loopback/dout]
|
||||
connect_bd_net -net tie_zero_dout [get_bd_pins cmac_usplus_0/gtwiz_reset_rx_datapath] [get_bd_pins cmac_usplus_0/gtwiz_reset_tx_datapath] [get_bd_pins tie_zero/dout]
|
||||
|
||||
# Create address segments
|
||||
create_bd_addr_seg -range 0x00002000 -offset 0x00000000 [get_bd_addr_spaces s_axi] [get_bd_addr_segs cmac_usplus_0/s_axi/Reg] SEG_cmac_usplus_0_Reg
|
||||
|
||||
|
||||
# Restore current instance
|
||||
current_bd_instance $oldCurInst
|
||||
|
||||
validate_bd_design
|
||||
save_bd_design
|
||||
}
|
||||
# End of create_root_design()
|
||||
|
||||
|
||||
##################################################################
|
||||
# MAIN FLOW
|
||||
##################################################################
|
||||
|
||||
create_root_design ""
|
||||
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: eth_100g_lbus2axi
|
||||
//
|
||||
// Description:
|
||||
// Translate from lbus (xilinx segmented ifc) to
|
||||
// AXI4S.
|
||||
//
|
||||
// Built using example provided from Xilinx
|
||||
//
|
||||
// Parameters:
|
||||
// - FIFO_DEPTH - FIFO will be 2** deep
|
||||
// - NUM_SEG - Number of lbus segments coming in
|
||||
//
|
||||
// Notes on timing difficulty
|
||||
// The path back to pop is challenged
|
||||
// -LBUS is popped out of the FIFO (SRL read can be slow)
|
||||
// -LBUS is rotated N to 1 Mux (N= number of segments) For 100g N=4
|
||||
// -Find where EOP is (search for the first 1)
|
||||
// -Unrotate the number of words and use that to calculate pop
|
||||
//
|
||||
// Fifo Output
|
||||
// Data starts from the SRL and is indexed by the read pointer
|
||||
// Data_Valid comes from a comparison on fullness
|
||||
// Invalid control is forced to zero (necessary for algorithm)
|
||||
// It's not necessary to force all the data to zero just the control plane.
|
||||
//
|
||||
// Fifo output data is rotated (4 to 1) mux then reinterpreted as lbus data
|
||||
//
|
||||
// The rotated control signals are analyzed to determine
|
||||
// no_eop, no_sop, some_empty, no_ena
|
||||
//
|
||||
// eop is specifically inspected in a 4in,4out function to find a pseudo
|
||||
// one hot. this is unrotated along with enable, and combined with
|
||||
// datavalid to determine the next pop, which controls incrementing of the
|
||||
// rd_pointer.
|
||||
//
|
||||
|
||||
import PkgEth100gLbus::*;
|
||||
|
||||
module eth_100g_lbus2axi #(
|
||||
parameter FIFO_DEPTH = 5,
|
||||
parameter NUM_SEG = 4
|
||||
)
|
||||
(
|
||||
|
||||
// AXIS IF
|
||||
AxiStreamIf.master axis,
|
||||
|
||||
// Lbus Segments
|
||||
input lbus_t lbus_in [NUM_SEG-1:0]
|
||||
|
||||
);
|
||||
|
||||
localparam SEG_BYTES = SEG_DATA_WIDTH/8;
|
||||
localparam SEG_MTY_WIDTH = $clog2(SEG_BYTES);
|
||||
localparam SEG_SHMEAR_WIDTH = SEG_DATA_WIDTH + SEG_MTY_WIDTH + 4;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////// Data Input to FIFO ///////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
lbus_t lbus_fout_p[NUM_SEG-1:0]; //{ena,err,eop,sop,mty,data}
|
||||
lbus_t lbus_fout[NUM_SEG-1:0]; //{ena,err,eop,sop,mty,data}
|
||||
//FIFO Logic
|
||||
logic push;
|
||||
logic [NUM_SEG-1:0] pop;
|
||||
|
||||
logic [NUM_SEG-1:0] full;
|
||||
logic [NUM_SEG-1:0] empty;
|
||||
|
||||
// always push the fifo on all lanes
|
||||
assign push = lbus_in[0].ena;
|
||||
|
||||
// For each lane of incoming data place it into a separate FIFO
|
||||
generate
|
||||
genvar b1,gseg1;
|
||||
begin : gen_seg_fifo
|
||||
for(gseg1 = 0; gseg1 < NUM_SEG; gseg1=gseg1+1) begin
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// INLINE FIFO
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// simulation error if we push a full fifo
|
||||
always_comb begin
|
||||
if (push) begin
|
||||
assert (!full[gseg1]) else $error("Pushing full fifo!");
|
||||
end
|
||||
end
|
||||
|
||||
// limit fanout to improve timing
|
||||
(* max_fanout = 75 *) logic [4:0] a;
|
||||
|
||||
for (b1=0;b1<SEG_DATA_WIDTH;b1=b1+1) begin : gen_srl_data
|
||||
SRLC32E srl_data(
|
||||
.Q(lbus_fout_p[gseg1].data[b1]), .Q31(),
|
||||
.A(a),
|
||||
.CE(push),.CLK(axis.clk),.D(lbus_in[gseg1].data[b1])
|
||||
);
|
||||
end
|
||||
for (b1=0;b1<SEG_MTY_WIDTH;b1=b1+1) begin : gen_srl_mty
|
||||
SRLC32E srl_mty(
|
||||
.Q(lbus_fout_p[gseg1].mty[b1]), .Q31(),
|
||||
.A(a),
|
||||
.CE(push),.CLK(axis.clk),.D(lbus_in[gseg1].mty[b1])
|
||||
);
|
||||
end
|
||||
SRLC32E srl_err(
|
||||
.Q(lbus_fout_p[gseg1].err), .Q31(),
|
||||
.A(a),
|
||||
.CE(push),.CLK(axis.clk),.D(lbus_in[gseg1].err)
|
||||
);
|
||||
|
||||
// empty on prebuffer and SRL
|
||||
logic my_empty;
|
||||
always @(posedge axis.clk)
|
||||
begin
|
||||
if(axis.rst) begin
|
||||
a <= 0;
|
||||
my_empty <= 1;
|
||||
full[gseg1] <= 0;
|
||||
end else if(pop[gseg1] & ~push) begin
|
||||
full[gseg1] <= 0;
|
||||
if(a==0) begin
|
||||
my_empty <= 1;
|
||||
end else begin
|
||||
a <= a - 1;
|
||||
end
|
||||
end else if(push & ~pop[gseg1]) begin
|
||||
my_empty <= 0;
|
||||
if(~my_empty) begin
|
||||
a <= a + 1;
|
||||
end
|
||||
if(a == 30) begin
|
||||
full[gseg1] <= 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// FIFO for time sensitive control signals. This creates a separate 31 deep fifo from
|
||||
// DFF's on just 3 signals. The data signals continue to use an SRL to save space.
|
||||
// The design bellow is a FIFO followed by a single DFF regsiter that is automatically
|
||||
// prefilled when the FIFO has data.
|
||||
logic [4:0] w_ptr,r_ptr,r_ptr_d,fullness;
|
||||
logic [31:0] ena_mem, sop_mem, eop_mem;
|
||||
|
||||
// Final fifo stage after memory to remove address muxing from timing path
|
||||
// this adds one clock of latency to empty flag as it will take 2 clocks to propagate
|
||||
// into fifo.
|
||||
|
||||
//push critical timing signals to final flop. This adds 1 clock of latency on
|
||||
// the final empty flag, but removes muxing of the memory elements
|
||||
logic push_dff;
|
||||
|
||||
//using r_ptr_d to avoid extra latency in fullness change
|
||||
always_comb begin
|
||||
if (pop[gseg1]) begin
|
||||
r_ptr_d = r_ptr+1;
|
||||
end else begin
|
||||
r_ptr_d = r_ptr;
|
||||
end
|
||||
fullness = w_ptr-r_ptr_d;
|
||||
push_dff = (fullness != 0) & (pop[gseg1] | empty[gseg1]);
|
||||
end
|
||||
|
||||
// speedier fifo implementation on these three control signals
|
||||
// THE goal of this complexity is to have the outputs be a direct FF output
|
||||
// instead of a muxed memory output.
|
||||
always @(posedge axis.clk)
|
||||
begin
|
||||
if(axis.rst) begin
|
||||
ena_mem <= '0;
|
||||
sop_mem <= '0;
|
||||
eop_mem <= '0;
|
||||
lbus_fout_p[gseg1].ena <= 1'b0;
|
||||
lbus_fout_p[gseg1].sop <= 1'b0;
|
||||
lbus_fout_p[gseg1].eop <= 1'b0;
|
||||
w_ptr <= 0;
|
||||
r_ptr <= 0;
|
||||
empty[gseg1] <= 1'b1;
|
||||
end else begin
|
||||
if(push) begin
|
||||
ena_mem[w_ptr] <= lbus_in[gseg1].ena;
|
||||
sop_mem[w_ptr] <= lbus_in[gseg1].sop;
|
||||
eop_mem[w_ptr] <= lbus_in[gseg1].eop;
|
||||
w_ptr <= w_ptr+1;
|
||||
end
|
||||
|
||||
r_ptr <= r_ptr_d;
|
||||
|
||||
if (push_dff) begin
|
||||
empty[gseg1] <= 1'b0;
|
||||
lbus_fout_p[gseg1].ena <= ena_mem[r_ptr_d];
|
||||
lbus_fout_p[gseg1].sop <= sop_mem[r_ptr_d];
|
||||
lbus_fout_p[gseg1].eop <= eop_mem[r_ptr_d];
|
||||
end else if (pop[gseg1]) begin
|
||||
empty[gseg1] <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// clear the enables if this fifo segment is not valid
|
||||
always_comb begin
|
||||
//default assignment
|
||||
lbus_fout[gseg1] = lbus_fout_p[gseg1];
|
||||
if (empty[gseg1]) begin
|
||||
// clear ena,err,eop,sop,mty (But not data - saves fanout!)
|
||||
lbus_fout[gseg1].ena = 0;
|
||||
lbus_fout[gseg1].err = 0;
|
||||
lbus_fout[gseg1].eop = 0;
|
||||
lbus_fout[gseg1].sop = 0;
|
||||
lbus_fout[gseg1].mty = '0;
|
||||
end else begin
|
||||
// clear bits if the segment isn't enabled
|
||||
lbus_fout[gseg1].eop = lbus_fout_p[gseg1].eop && lbus_fout_p[gseg1].ena;
|
||||
lbus_fout[gseg1].sop = lbus_fout_p[gseg1].sop && lbus_fout_p[gseg1].ena;
|
||||
lbus_fout[gseg1].err = lbus_fout_p[gseg1].err && lbus_fout_p[gseg1].ena;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end : gen_seg_fifo
|
||||
endgenerate
|
||||
|
||||
// post rotation lbus signals
|
||||
lbus_t lbus_rot [NUM_SEG-1:0];
|
||||
|
||||
// rotated signals as vectors for decision making
|
||||
logic [NUM_SEG-1:0] ena;
|
||||
logic [NUM_SEG-1:0] sop;
|
||||
logic [NUM_SEG-1:0] eop;
|
||||
logic [NUM_SEG-1:0] rot_ena;
|
||||
logic [NUM_SEG-1:0] rot_sop;
|
||||
logic [NUM_SEG-1:0] rot_eop;
|
||||
logic [NUM_SEG-1:0] rot_empty;
|
||||
|
||||
always_comb begin
|
||||
foreach (rot_ena[s]) begin
|
||||
ena[s] = lbus_fout[s].ena;
|
||||
sop[s] = lbus_fout[s].sop;
|
||||
eop[s] = lbus_fout[s].eop;
|
||||
rot_ena[s] = lbus_rot[s].ena;
|
||||
rot_sop[s] = lbus_rot[s].sop;
|
||||
rot_eop[s] = lbus_rot[s].eop;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
logic [$clog2(NUM_SEG)-1:0] rot;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////// Generate Decision Information ///////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
logic no_sop;
|
||||
logic no_eop;
|
||||
logic no_ena;
|
||||
logic some_empty;
|
||||
logic send_idle;
|
||||
|
||||
always_comb begin
|
||||
no_sop = sop == 0;
|
||||
no_eop = eop == 0;
|
||||
no_ena = ena == 0;
|
||||
// check for an empy byte
|
||||
some_empty = 1'b0;
|
||||
foreach (ena[seg]) begin : segment_loop
|
||||
if (ena[seg] == 0) begin
|
||||
some_empty = 1'b1;
|
||||
end
|
||||
end : segment_loop;
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
if (no_ena) begin
|
||||
send_idle = 1'b0;
|
||||
end else begin
|
||||
// generally either there is an EOP with some empty segments
|
||||
// or all empty segments on an unrotated bus. I'm not sure
|
||||
// what this implies on an rotated bus
|
||||
send_idle = no_eop & some_empty;
|
||||
end
|
||||
end
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////// Calculate Pop ///////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// After rotation figure out how far till eop
|
||||
|
||||
//==========================================================================
|
||||
// one-hot to thermometer code
|
||||
// The goal is to find how far down till we reach the first eop
|
||||
// This represents the bytes we will trasnfer this clock
|
||||
//==========================================================================
|
||||
// Xilinx example
|
||||
// case (in_reqs)
|
||||
// 4'b1000: onehot2thermo = 4'b1111;
|
||||
//
|
||||
// 4'b1100: onehot2thermo = 4'b0111;
|
||||
// 4'b0100: onehot2thermo = 4'b0111;
|
||||
//
|
||||
// 4'b1110: onehot2thermo = 4'b0011;
|
||||
// 4'b0110: onehot2thermo = 4'b0011;
|
||||
// 4'b0010: onehot2thermo = 4'b0011;
|
||||
//
|
||||
// 4'b1111: onehot2thermo = 4'b0001;
|
||||
// 4'b0111: onehot2thermo = 4'b0001;
|
||||
// 4'b0011: onehot2thermo = 4'b0001;
|
||||
// 4'b0001: onehot2thermo = 4'b0001;
|
||||
//
|
||||
// default: onehot2thermo = 4'b0000;
|
||||
// endcase
|
||||
logic [NUM_SEG-1:0] rot_xfer_now;
|
||||
logic [NUM_SEG-1:0] mask [NUM_SEG-1:0];
|
||||
logic [NUM_SEG-1:0] m1hot [NUM_SEG-1:0];
|
||||
logic [NUM_SEG-1:0] meop [NUM_SEG-1:0];
|
||||
logic [NUM_SEG-1:0] match;
|
||||
|
||||
always_comb begin
|
||||
rot_xfer_now = '0;
|
||||
foreach (rot_eop[s]) begin
|
||||
// The function
|
||||
// XXX1=>0001
|
||||
// XX10=>0011
|
||||
// X100=>0111
|
||||
// 1000=>1111
|
||||
// MASK
|
||||
// 2**(0+1)-1 = 0001
|
||||
// 2**(1+1)-1 = 0011
|
||||
// 2**(2+1)-1 = 0111
|
||||
// 2**(3+1)-1 = 1111
|
||||
mask[s] = 2**(s+1)-1; // Constant
|
||||
// MASK
|
||||
// 2**0 = 0001
|
||||
// 2**1 = 0010
|
||||
// 2**2 = 0100
|
||||
// 2**3 = 1000
|
||||
m1hot[s] = 2**s; // Constant
|
||||
// Mask valid_eop
|
||||
meop[s] = rot_eop & mask[s];
|
||||
// compare against 1hot
|
||||
match[s] = meop[s] == m1hot[s];
|
||||
if (match[s]) begin
|
||||
rot_xfer_now = mask[s];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// unrotate the values and calculate pop
|
||||
logic [NUM_SEG-1:0] xfer_now;
|
||||
logic [NUM_SEG-1:0] filler_seg;
|
||||
|
||||
always_comb begin
|
||||
if (send_idle)
|
||||
xfer_now = '0;
|
||||
else if (no_eop | no_sop)
|
||||
xfer_now = '1;
|
||||
else
|
||||
// rotate left
|
||||
xfer_now = {rot_xfer_now,rot_xfer_now} >> (NUM_SEG - rot);
|
||||
end
|
||||
|
||||
// Flush out valid segments with no enable
|
||||
assign filler_seg = ~ena & ~empty;
|
||||
|
||||
assign pop = (xfer_now | filler_seg) & ~empty;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////// Calculate Rotate for the next clock ///////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
logic [$clog2(NUM_SEG)-1:0] next_rot;
|
||||
always_comb begin
|
||||
next_rot = 0;
|
||||
foreach (rot_empty[s]) begin
|
||||
if (~rot_empty[s] & lbus_rot[s].sop) begin
|
||||
next_rot = s;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge axis.clk)
|
||||
begin
|
||||
if(axis.rst) begin
|
||||
rot <= '0;
|
||||
//no valid data on any segment
|
||||
end else if( no_ena ) begin
|
||||
rot <= '0;
|
||||
// If EOP, but no SoP
|
||||
end else if( no_sop & ~no_eop & some_empty) begin
|
||||
rot <= '0;
|
||||
// If SOP, accumulate rotation to push to seg 0
|
||||
end else if( ~no_sop ) begin
|
||||
rot <= rot+next_rot;
|
||||
end
|
||||
end
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////// Rotation of segments from fifo output ///////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
generate
|
||||
genvar b2,gseg2;
|
||||
begin : rotate_lbus
|
||||
//perform a bitwise rotation.
|
||||
for(b2 = 0; b2 < SEG_SHMEAR_WIDTH; b2=b2+1) begin
|
||||
logic [NUM_SEG-1:0] slice, slice_rotated;
|
||||
|
||||
//copy a horizontal slice across the segments
|
||||
for(gseg2 = 0; gseg2 < NUM_SEG; gseg2=gseg2+1) begin
|
||||
assign slice[gseg2] = lbus_fout[gseg2][b2];
|
||||
end
|
||||
|
||||
// rotate the slice (should make SEG_SHMEAR_WIDTH copies of NUM_SEG to 1 mux)
|
||||
assign slice_rotated = {slice,slice} >> rot; //rotate_right
|
||||
|
||||
// Copy slice back to the struct
|
||||
for(gseg2 = 0; gseg2 < NUM_SEG; gseg2=gseg2+1) begin
|
||||
assign lbus_rot[gseg2][b2] = slice_rotated[gseg2];
|
||||
end
|
||||
|
||||
end
|
||||
end : rotate_lbus
|
||||
endgenerate
|
||||
|
||||
always_comb begin : rotate_data_valid
|
||||
rot_empty = {empty,empty} >> rot; //rotate_right
|
||||
end : rotate_data_valid
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////// LBUS out DFF /////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// This pipe stage is mainly to allow suming MTY bits and to add space for
|
||||
// Vivado to try to pipeline the output
|
||||
// post rotation lbus signals
|
||||
lbus_t lbus_out [NUM_SEG-1:0];
|
||||
logic [NUM_SEG-1:0] axi_seg_valid;
|
||||
|
||||
always_ff @(posedge axis.clk)
|
||||
begin
|
||||
if (axis.rst) begin
|
||||
foreach (lbus_out[seg]) begin : segment_loop
|
||||
lbus_out[seg] <= '0;
|
||||
end
|
||||
axi_seg_valid <= '0;
|
||||
end else begin
|
||||
lbus_out <= lbus_rot;
|
||||
if (send_idle)
|
||||
axi_seg_valid <= '0;
|
||||
else if (no_eop)
|
||||
axi_seg_valid <= '1;
|
||||
else
|
||||
axi_seg_valid <= rot_xfer_now;
|
||||
end
|
||||
end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Generate AXI
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
logic [axis.DATA_WIDTH - 1:0] axis_tdata_w;
|
||||
logic [$clog2(axis.DATA_WIDTH/8) - 1:0] axis_tuser_bytes_w;
|
||||
logic [axis.DATA_WIDTH/8 - 1:0] axis_tkeep_w;
|
||||
logic [NUM_SEG-1:0] axis_tlast_w;
|
||||
logic [NUM_SEG-1:0] axis_tvalid_w;
|
||||
logic [NUM_SEG-1:0] axis_tuser_err_w;
|
||||
|
||||
always_comb begin : axis_translate
|
||||
axis_tuser_bytes_w = 'd0; // init to zero before summing
|
||||
foreach (axis_tvalid_w[seg]) begin : segment_loop
|
||||
axis_tvalid_w[seg] = lbus_out[seg].ena & axi_seg_valid[seg];
|
||||
axis_tlast_w[seg] = lbus_out[seg].eop;
|
||||
axis_tuser_err_w[seg] = lbus_out[seg].err;
|
||||
|
||||
// sum all the segment mty vectors
|
||||
if (lbus_out[seg].ena && axi_seg_valid[seg]) begin
|
||||
axis_tuser_bytes_w += SEG_DATA_WIDTH/8 - lbus_out[seg].mty;
|
||||
end
|
||||
|
||||
// 512 bit word = 64 bytes = 4 X 128 bit(16 byte) segments
|
||||
// assign bytes : LbusOrder
|
||||
// S0 : S0B0..S0B15
|
||||
// S1 : S1B0..S1B15
|
||||
// S2 : S2B0..S2B15
|
||||
// S3 : S3B0..S3B15
|
||||
// AXI (swap Endianess on each segment)
|
||||
// AXI = S3B15..S3B0, S2B15..S2B0, S1B15..S1B0, S0B15..S0B0
|
||||
for(int b = 0; b < SEG_BYTES; b=b+1) begin : tdata_loop
|
||||
// ( 1 * 128 )-8- 0*8) 120+:8 = S0B0
|
||||
// ( 1 * 128 )-8- 1*8) 112+:8 = S0B1
|
||||
// ...
|
||||
// ( 1 * 128 )-8-14*8) 8+:8 = S0B14
|
||||
// ( 1 * 128 )-8-15*8) 0+:8 = S0B15
|
||||
////////////////////////////////////
|
||||
// ( 2 * 128 )-8- 0*8) 248+:8 = S1B0
|
||||
// ( 2 * 128 )-8- 1*8) 240+:8 = S1B1
|
||||
// ...
|
||||
// ( 2 * 128 )-8-14*8) 136+:8 = S1B14
|
||||
// ( 2 * 128 )-8-15*8) 128+:8 = S1B15
|
||||
////////////////////////////////////
|
||||
// ...
|
||||
////////////////////////////////////
|
||||
// ( 4 * 128 )-8- 0*8) 504+:8 = S3B0
|
||||
// ( 4 * 128 )-8- 1*8) 496+:8 = S3B1
|
||||
// ...
|
||||
// ( 4 * 128 )-8-14*8) 136+:8 = S3B14
|
||||
// ( 4 * 128 )-8-15*8) 384+:8 = S3B15
|
||||
axis_tdata_w[((seg+1)*axis.DATA_WIDTH/NUM_SEG-8-b*8) +: 8] = lbus_out[seg].data[b*8 +: 8];
|
||||
end : tdata_loop
|
||||
end : segment_loop
|
||||
end : axis_translate
|
||||
|
||||
// convert bytes to keep
|
||||
always_comb begin
|
||||
axis_tkeep_w = '1;
|
||||
if (axis_tlast_w != 0 && axis_tuser_bytes_w != 0) begin
|
||||
foreach(axis_tkeep_w[b]) begin
|
||||
axis_tkeep_w[b] = axis_tuser_bytes_w > b;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////// AXIS output flop /////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
localparam AXIS_MTY_WIDTH = $clog2(axis.BYTES_PER_WORD);
|
||||
|
||||
always_ff @(posedge axis.clk)
|
||||
begin
|
||||
if (axis.rst) begin
|
||||
axis.tdata <= '0;
|
||||
axis.tvalid <= 1'b0;
|
||||
axis.tlast <= 1'b0;
|
||||
axis.tuser <= '0;
|
||||
axis.tkeep <= '0;
|
||||
end else begin
|
||||
axis.tdata <= axis_tdata_w;
|
||||
axis.tvalid <= |axis_tvalid_w;
|
||||
axis.tlast <= |axis_tlast_w;
|
||||
|
||||
if (axis.TKEEP == 1) begin
|
||||
axis.tkeep <= axis_tkeep_w;
|
||||
end else begin
|
||||
axis.tkeep <= 'X;
|
||||
end
|
||||
|
||||
// trailing bytes in last word
|
||||
axis.tuser[AXIS_MTY_WIDTH-1:0] <= axis_tuser_bytes_w;
|
||||
// MSB is error
|
||||
axis.tuser[AXIS_MTY_WIDTH] <= |axis_tuser_err_w;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,62 @@
|
||||
#
|
||||
# Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
#-------------------------------------------------
|
||||
# Top-of-Makefile
|
||||
#-------------------------------------------------
|
||||
# Define BASE_DIR to point to the "top" dir
|
||||
BASE_DIR = $(abspath ../../../../../top)
|
||||
# Include viv_sim_preamble after defining BASE_DIR
|
||||
include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
|
||||
|
||||
#-------------------------------------------------
|
||||
# Design Specific
|
||||
#-------------------------------------------------
|
||||
# Include makefiles and sources for the DUT and its dependencies
|
||||
include $(BASE_DIR)/../lib/axi4s_sv/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/fifo/Makefile.srcs
|
||||
|
||||
|
||||
# If you generate the Xilinx CORE with an AXI interface you can find Xilinx's LBUS translators here
|
||||
#$(abspath ../../../build-ip/xczu28drffvg1517-1e/eth_100g_bd/eth_100g_bd/ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0/example_design/eth_100g_bd_cmac_usplus_0_0_lbus2axis_segmented_top.v) \
|
||||
#$(abspath ../../../build-ip/xczu28drffvg1517-1e/eth_100g_bd/eth_100g_bd/ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0/example_design/eth_100g_bd_cmac_usplus_0_0_axis2lbus_segmented_top.v) \
|
||||
|
||||
DESIGN_SRCS = $(abspath \
|
||||
$(abspath ../eth_100g_axis2lbus.sv) \
|
||||
$(abspath ../eth_100g_lbus2axis.sv) \
|
||||
$(FIFO_SRCS) \
|
||||
$(AXI4S_SV_SRCS) \
|
||||
)
|
||||
|
||||
#-------------------------------------------------
|
||||
# Testbench Specific
|
||||
#-------------------------------------------------
|
||||
MODELSIM_LIBS += secureip unimacro_ver unisims_ver xilinx_vip xpm fifo_generator_v13_2_4
|
||||
MODELSIM_ARGS += glbl -t 1fs
|
||||
# Define toplevel module
|
||||
TB_TOP_MODULE ?= lbus_all_tb
|
||||
SIM_TOP = $(TB_TOP_MODULE)
|
||||
|
||||
SIM_SRCS = \
|
||||
$(abspath ../PkgEth100gLbus.sv) \
|
||||
$(abspath axi_lbus_tb.sv) \
|
||||
$(abspath lbus_axi_tb.sv) \
|
||||
$(abspath $(TB_TOP_MODULE).sv) \
|
||||
$(VIVADO_PATH)/data/verilog/src/glbl.v \
|
||||
|
||||
# Suppressing the following worthless reminder.
|
||||
#* Warning: M:/usrp4-hw/oss-repo/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes.sv(228): (vlog-2583) [SVCHK] -
|
||||
# Extra checking for conflicts with always_comb and always_latch variables is done at vopt time
|
||||
SVLOG_ARGS = -suppress 2583 -keep_delta
|
||||
VLOG_ARGS = -keep_delta
|
||||
|
||||
#-------------------------------------------------
|
||||
# Bottom-of-Makefile
|
||||
#-------------------------------------------------
|
||||
# Include all simulator specific makefiles here
|
||||
# Each should define a unique target to simulate
|
||||
# e.g. xsim, vsim, etc and a common "clean" target
|
||||
include $(BASE_DIR)/../tools/make/viv_simulator.mak
|
||||
@@ -0,0 +1,285 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axi_lbus_tb
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Testbench for eth_interface
|
||||
//
|
||||
|
||||
module axi_lbus_tb #(
|
||||
parameter TEST_NAME = ""
|
||||
)(
|
||||
/* no IO */
|
||||
);
|
||||
// Include macros and time declarations for use with PkgTestExec
|
||||
`define TEST_EXEC_OBJ test
|
||||
`include "test_exec.svh"
|
||||
import PkgAxiStreamBfm::*;
|
||||
import PkgEthernet::*;
|
||||
import PkgTestExec::*;
|
||||
import PkgEth100gLbus::*;
|
||||
|
||||
localparam DATA_WIDTH = 512;
|
||||
localparam USER_WIDTH = $clog2(DATA_WIDTH/8)+1;
|
||||
localparam NUM_SEG = 4;
|
||||
localparam SEG_DATA_WIDTH = DATA_WIDTH/NUM_SEG;
|
||||
localparam SEG_BYTES = SEG_DATA_WIDTH/8;
|
||||
|
||||
//----------------------------------------------------
|
||||
// clocks
|
||||
//----------------------------------------------------
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
sim_clock_gen #(.PERIOD(5), .AUTOSTART(1))
|
||||
clk_gen (.clk(clk), .rst(rst));
|
||||
|
||||
//----------------------------------------------------
|
||||
//interfaces
|
||||
//----------------------------------------------------
|
||||
AxiStreamIf #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH))
|
||||
axis (clk, rst);
|
||||
|
||||
//----------------------------------------------------
|
||||
// DUT
|
||||
//----------------------------------------------------
|
||||
lbus_t lbus_out [NUM_SEG-1:0];
|
||||
lbus_t lbus_g [NUM_SEG-1:0];
|
||||
logic lbus_rdy = 1;
|
||||
|
||||
eth_100g_axi2lbus #(.FIFO_DEPTH(5),.NUM_SEG(NUM_SEG)) DUT (
|
||||
.axis(axis),
|
||||
.lbus_rdy(lbus_rdy),
|
||||
.lbus_out(lbus_out)
|
||||
);
|
||||
|
||||
//----------------------------------------------------
|
||||
// Xilinx golden model
|
||||
// When Xilinx is generated with an AXI interface xilinx prints
|
||||
// an axi2lbus converter that has trouble meeting timing
|
||||
// this is preserved to allow comparison to that
|
||||
// TODO: remove when timing is passing and refactor is done
|
||||
//----------------------------------------------------
|
||||
/*
|
||||
eth_100g_bd_cmac_usplus_0_0_axis2lbus_segmented_top GOLD (
|
||||
.core_clk(clk),
|
||||
.core_rst(rst),
|
||||
|
||||
// AXIS IF
|
||||
.axis_tvalid(axis.tvalid),
|
||||
.axis_tready(),
|
||||
.axis_tdata(axis.tdata),
|
||||
.axis_tlast(axis.tlast),
|
||||
.axis_tkeep(axis.tkeep),
|
||||
.axis_tuser(1'b0),
|
||||
// LBUS IF
|
||||
.lbus_rdyout(lbus_rdy),
|
||||
.lbus_ovfout(1'b0),
|
||||
.lbus_unfout(1'b0),
|
||||
// Segment 0
|
||||
.lbus_ena0(lbus_g[0].ena),
|
||||
.lbus_data0(lbus_g[0].data),
|
||||
.lbus_sop0(lbus_g[0].sop),
|
||||
.lbus_eop0(lbus_g[0].eop),
|
||||
.lbus_mty0(lbus_g[0].mty),
|
||||
.lbus_err0(lbus_g[0].err),
|
||||
// Segment 1
|
||||
.lbus_ena1(lbus_g[1].ena),
|
||||
.lbus_data1(lbus_g[1].data),
|
||||
.lbus_sop1(lbus_g[1].sop),
|
||||
.lbus_eop1(lbus_g[1].eop),
|
||||
.lbus_mty1(lbus_g[1].mty),
|
||||
.lbus_err1(lbus_g[1].err),
|
||||
// Segment 2
|
||||
.lbus_ena2(lbus_g[2].ena),
|
||||
.lbus_data2(lbus_g[2].data),
|
||||
.lbus_sop2(lbus_g[2].sop),
|
||||
.lbus_eop2(lbus_g[2].eop),
|
||||
.lbus_mty2(lbus_g[2].mty),
|
||||
.lbus_err2(lbus_g[2].err),
|
||||
// Segment 3
|
||||
.lbus_ena3(lbus_g[3].ena),
|
||||
.lbus_data3(lbus_g[3].data),
|
||||
.lbus_sop3(lbus_g[3].sop),
|
||||
.lbus_eop3(lbus_g[3].eop),
|
||||
.lbus_mty3(lbus_g[3].mty),
|
||||
.lbus_err3(lbus_g[3].err)
|
||||
);
|
||||
*/
|
||||
//----------------------------------------------------
|
||||
//BFMS
|
||||
//----------------------------------------------------
|
||||
|
||||
TestExec test = new();
|
||||
AxiStreamBfm #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH)) axi =
|
||||
new(.slave(null),.master(axis));
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Tests
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// use Test timeout to check reset goes away
|
||||
task test_reset();
|
||||
test.start_test({TEST_NAME,"Wait for Reset"}, 10us);
|
||||
wait(!rst);
|
||||
repeat (10) @(posedge clk);
|
||||
test.end_test();
|
||||
endtask : test_reset
|
||||
|
||||
typedef AxiStreamPacket #(DATA_WIDTH,USER_WIDTH) AxisPacket_t;
|
||||
typedef XportStreamPacket #(DATA_WIDTH) XportPacket_t;
|
||||
|
||||
|
||||
task automatic check_lbus(AxisPacket_t packets[$]);
|
||||
|
||||
int idle_insert = 0;
|
||||
int byte_count = 0;
|
||||
int first_clk = 1;
|
||||
|
||||
// loop over packets
|
||||
foreach(packets[i]) begin
|
||||
automatic raw_pkt_t pay;
|
||||
pay = packets[i].dump_bytes;
|
||||
|
||||
first_clk=1;
|
||||
while (pay.size() > 0) begin
|
||||
@(posedge clk);
|
||||
if (lbus_rdy) begin
|
||||
if (lbus_out[0].ena) begin
|
||||
for (int s=0; s < NUM_SEG; s++) begin
|
||||
byte_count = 0;
|
||||
for (int b = SEG_DATA_WIDTH/8-1; b >= 0; b--) begin
|
||||
if (pay.size() > 0) begin
|
||||
assert (lbus_out[s].data[b*8 +: 8] == pay.pop_front()) else $error("Data Mismatch");
|
||||
byte_count++;
|
||||
end
|
||||
end
|
||||
assert (lbus_out[s].ena == (byte_count > 0) ) else $error("Ena Mismatch");
|
||||
if (lbus_out[s].ena) begin
|
||||
if (first_clk && s==0) begin
|
||||
assert (lbus_out[s].sop == 1) else $error("Sop not set");
|
||||
end else begin
|
||||
assert (lbus_out[s].sop == 0) else $error("Sop Set");
|
||||
end
|
||||
assert (lbus_out[s].mty == (SEG_BYTES-byte_count) % SEG_BYTES) else $error("Mty Mismatch");
|
||||
assert (lbus_out[s].eop != (pay.size() > 0) ) else $error("Eop Mismatch");
|
||||
assert (lbus_out[s].err == 0) else $error("Err Mismatch"); // not checking error yet
|
||||
end else begin
|
||||
assert (lbus_out[s].sop == 0) else $error("Sop Set while disabled");
|
||||
assert (lbus_out[s].mty == 0) else $error("Mty set while disabled");
|
||||
assert (lbus_out[s].eop == 0) else $error("Eop set while disabled");
|
||||
assert (lbus_out[s].err == 0) else $error("Err set while disabled");
|
||||
end
|
||||
end // segment loop
|
||||
first_clk=0;
|
||||
end // first segment is enabled
|
||||
|
||||
end else begin //not lbus_rdy
|
||||
for (int s=0; s < NUM_SEG; s++) begin
|
||||
assert (lbus_out[s].ena == 0) else $error("Idle Ena set");
|
||||
assert (lbus_out[s].err == 0) else $error("Idle Err set");
|
||||
assert (lbus_out[s].sop == 0) else $error("Idle Sop set");
|
||||
assert (lbus_out[s].eop == 0) else $error("Idle Eop set");
|
||||
assert (lbus_out[s].mty == 0) else $error("Idle Mty non zero");
|
||||
end
|
||||
end
|
||||
end // while pay.size > 0
|
||||
end // foreach packet
|
||||
|
||||
endtask : check_lbus;
|
||||
|
||||
task automatic test_transfers(int num_samples[$]);
|
||||
automatic AxisPacket_t send[$];
|
||||
automatic AxisPacket_t expected[$];
|
||||
automatic int sample_sum = 0;
|
||||
|
||||
test.start_test({TEST_NAME,"Test Transfer"}, 200us);
|
||||
|
||||
foreach (num_samples[i]) begin
|
||||
automatic raw_pkt_t pay;
|
||||
|
||||
expected[i] = new;
|
||||
send[i] = new;
|
||||
|
||||
get_ramp_raw_pkt(.num_samps(num_samples[i]),.ramp_start((sample_sum)%256),
|
||||
.ramp_inc(1),.pkt(pay),.SWIDTH(8));
|
||||
sample_sum += num_samples[i];
|
||||
send[i].push_bytes(pay);
|
||||
|
||||
// rebuild the expected packet for comparison without the preamble
|
||||
expected[i].push_bytes(pay);
|
||||
end
|
||||
|
||||
fork
|
||||
begin // tx_thread
|
||||
foreach (send[i]) begin
|
||||
axi.put(send[i]);
|
||||
end
|
||||
end
|
||||
begin //rx_thread
|
||||
check_lbus(expected);
|
||||
end
|
||||
join
|
||||
|
||||
test.end_test();
|
||||
endtask : test_transfers
|
||||
|
||||
//----------------------------------------------------
|
||||
// Main test loop
|
||||
//----------------------------------------------------
|
||||
initial begin : tb_main
|
||||
automatic int num_samples[$];
|
||||
automatic int random_value;
|
||||
clk_gen.reset();
|
||||
|
||||
// stalling is not allowed by whoever is reading the MAC
|
||||
// i.e. ready is ignored
|
||||
axi.set_master_stall_prob(0);
|
||||
axi.run();
|
||||
test_reset();
|
||||
|
||||
$display("Fixed Sequences");
|
||||
num_samples = {64,65,66,67,68,69,70,71};
|
||||
test_transfers(num_samples);
|
||||
num_samples = {64,128,256,512,256,64,64,64};
|
||||
test_transfers(num_samples);
|
||||
num_samples = {65,64,64,64,64,64,64,64};
|
||||
test_transfers(num_samples);
|
||||
num_samples = {65,64,80,80,80,80,80,80};
|
||||
test_transfers(num_samples);
|
||||
num_samples = {64,64,96,80,96,80,96,80};
|
||||
test_transfers(num_samples);
|
||||
$display("Tons of 64");
|
||||
num_samples.delete();
|
||||
repeat(1000) begin
|
||||
num_samples.push_back(64);
|
||||
end
|
||||
test_transfers(num_samples);
|
||||
$display("Tons of 65");
|
||||
num_samples.delete();
|
||||
repeat(1000) begin
|
||||
num_samples.push_back(65);
|
||||
end
|
||||
test_transfers(num_samples);
|
||||
$display("Tons of Random");
|
||||
num_samples.delete();
|
||||
repeat(1000) begin
|
||||
random_value = $urandom_range(64,512);
|
||||
num_samples.push_back(random_value);
|
||||
end
|
||||
test_transfers(num_samples);
|
||||
|
||||
|
||||
// End the TB, but don't $finish, since we don't want to kill other
|
||||
// instances of this testbench that may be running.
|
||||
test.end_tb(0);
|
||||
|
||||
// Kill the clocks to end this instance of the testbench
|
||||
clk_gen.kill();
|
||||
end // initial begin
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: lbus_all_tb
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Testbench for LBU<->AXI
|
||||
//
|
||||
|
||||
module lbus_all_tb #(
|
||||
/* no PARAM */
|
||||
)(
|
||||
/* no IO */
|
||||
);
|
||||
|
||||
lbus_axi_tb #(.TEST_NAME("L2A")) L2A ();
|
||||
axi_lbus_tb #(.TEST_NAME("A2L")) A2L ();
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,343 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: lbus_axi_tb
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Testbench for eth_interface
|
||||
//
|
||||
|
||||
module lbus_axi_tb #(
|
||||
parameter TEST_NAME = ""
|
||||
)(
|
||||
/* no IO */
|
||||
);
|
||||
// Include macros and time declarations for use with PkgTestExec
|
||||
`define TEST_EXEC_OBJ test
|
||||
`include "test_exec.svh"
|
||||
import PkgAxiStreamBfm::*;
|
||||
import PkgEthernet::*;
|
||||
import PkgTestExec::*;
|
||||
import PkgEth100gLbus::*;
|
||||
|
||||
localparam DATA_WIDTH = 512;
|
||||
localparam USER_WIDTH = $clog2(DATA_WIDTH/8)+1;
|
||||
localparam NUM_SEG = 4;
|
||||
localparam SEG_DATA_WIDTH = DATA_WIDTH/NUM_SEG;
|
||||
localparam SEG_BYTES = SEG_DATA_WIDTH/8;
|
||||
|
||||
//----------------------------------------------------
|
||||
// clocks
|
||||
//----------------------------------------------------
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
sim_clock_gen #(.PERIOD(5), .AUTOSTART(1))
|
||||
clk_gen (.clk(clk), .rst(rst));
|
||||
|
||||
//----------------------------------------------------
|
||||
//interfaces
|
||||
//----------------------------------------------------
|
||||
AxiStreamIf #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH))
|
||||
axis (clk, rst);
|
||||
|
||||
AxiStreamIf #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH))
|
||||
axig (clk, rst);
|
||||
|
||||
//----------------------------------------------------
|
||||
// DUT
|
||||
//----------------------------------------------------
|
||||
lbus_t lbus_in [NUM_SEG-1:0];
|
||||
|
||||
eth_100g_lbus2axi #(.FIFO_DEPTH(5),.NUM_SEG(NUM_SEG)) DUT (
|
||||
.axis(axis),
|
||||
.lbus_in(lbus_in)
|
||||
);
|
||||
|
||||
//----------------------------------------------------
|
||||
// Xilinx golden model
|
||||
// When Xilinx is generated with an AXI interface xilinx prints
|
||||
// a lbus2axis converter that has trouble meeting timing
|
||||
// this is preserved to allow comparison to that
|
||||
// TODO: remove when timing is passing and refactor is done
|
||||
//----------------------------------------------------
|
||||
/*
|
||||
eth_100g_bd_cmac_usplus_0_0_lbus2axis_segmented_top GOLD (
|
||||
.core_clk(clk),
|
||||
.core_rst(rst),
|
||||
|
||||
// AXIS IF
|
||||
.axis_tvalid(axig.tvalid),
|
||||
.axis_tdata(axig.tdata),
|
||||
.axis_tlast(axig.tlast),
|
||||
.axis_tkeep(axig.tkeep),
|
||||
.axis_tuser(),
|
||||
// Segment 0
|
||||
.lbus_ena0(lbus_in[0].ena),
|
||||
.lbus_data0(lbus_in[0].data),
|
||||
.lbus_sop0(lbus_in[0].sop),
|
||||
.lbus_eop0(lbus_in[0].eop),
|
||||
.lbus_mty0(lbus_in[0].mty),
|
||||
.lbus_err0(lbus_in[0].err),
|
||||
// Segment 1
|
||||
.lbus_ena1(lbus_in[1].ena),
|
||||
.lbus_data1(lbus_in[1].data),
|
||||
.lbus_sop1(lbus_in[1].sop),
|
||||
.lbus_eop1(lbus_in[1].eop),
|
||||
.lbus_mty1(lbus_in[1].mty),
|
||||
.lbus_err1(lbus_in[1].err),
|
||||
// Segment 2
|
||||
.lbus_ena2(lbus_in[2].ena),
|
||||
.lbus_data2(lbus_in[2].data),
|
||||
.lbus_sop2(lbus_in[2].sop),
|
||||
.lbus_eop2(lbus_in[2].eop),
|
||||
.lbus_mty2(lbus_in[2].mty),
|
||||
.lbus_err2(lbus_in[2].err),
|
||||
// Segment 3
|
||||
.lbus_ena3(lbus_in[3].ena),
|
||||
.lbus_data3(lbus_in[3].data),
|
||||
.lbus_sop3(lbus_in[3].sop),
|
||||
.lbus_eop3(lbus_in[3].eop),
|
||||
.lbus_mty3(lbus_in[3].mty),
|
||||
.lbus_err3(lbus_in[3].err)
|
||||
);
|
||||
*/
|
||||
//----------------------------------------------------
|
||||
//BFMS
|
||||
//----------------------------------------------------
|
||||
|
||||
TestExec test = new();
|
||||
AxiStreamBfm #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH)) axi =
|
||||
new(.slave(axis),.master(null));
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Tests
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// use Test timeout to check reset goes away
|
||||
task test_reset();
|
||||
test.start_test({TEST_NAME,"Wait for Reset"}, 10us);
|
||||
wait(!rst);
|
||||
repeat (10) @(posedge clk);
|
||||
test.end_test();
|
||||
endtask : test_reset
|
||||
|
||||
typedef AxiStreamPacket #(DATA_WIDTH,USER_WIDTH) AxisPacket_t;
|
||||
typedef XportStreamPacket #(DATA_WIDTH) XportPacket_t;
|
||||
|
||||
task automatic clear_lbus_in();
|
||||
for (int i=0 ; i < NUM_SEG ; ++i) begin
|
||||
lbus_in[i].data = 'b0;
|
||||
lbus_in[i].mty = 'b0;
|
||||
lbus_in[i].sop = 1'b0;
|
||||
lbus_in[i].eop = 1'b0;
|
||||
lbus_in[i].err = 1'b0;
|
||||
lbus_in[i].ena = 1'b0;
|
||||
end
|
||||
endtask : clear_lbus_in;
|
||||
|
||||
task automatic send_lbus(AxisPacket_t packets[$]);
|
||||
|
||||
int seg = 0;
|
||||
int b = 0;
|
||||
int idle_insert = 0;
|
||||
|
||||
// loop over packets
|
||||
foreach(packets[i]) begin
|
||||
automatic raw_pkt_t pay;
|
||||
pay = packets[i].dump_bytes;
|
||||
// set for the first segment
|
||||
lbus_in[seg].sop = 1;
|
||||
// empty this packets payload
|
||||
while (pay.size() > 0) begin
|
||||
lbus_in[seg].ena = 1;
|
||||
lbus_in[seg].data |= pay.pop_front()<<(SEG_DATA_WIDTH-b*8-8);
|
||||
if (pay.size() == 0) begin
|
||||
lbus_in[seg].eop = 1;
|
||||
lbus_in[seg].mty = SEG_DATA_WIDTH-1-b;
|
||||
end
|
||||
if (seg == NUM_SEG-1 && b == SEG_DATA_WIDTH/8-1) begin
|
||||
@(posedge clk);
|
||||
clear_lbus_in();
|
||||
idle_insert++;
|
||||
// insert period idle period
|
||||
if (idle_insert > 2) begin
|
||||
@(posedge clk);
|
||||
idle_insert =0;
|
||||
end
|
||||
end
|
||||
b = (b+1) % (SEG_DATA_WIDTH/8);
|
||||
if (b==0) begin
|
||||
seg = (seg+1) % NUM_SEG;
|
||||
end
|
||||
end // pay.size > 0
|
||||
if (b != 0) begin
|
||||
seg = (seg+1) % NUM_SEG;
|
||||
if (seg == 0) begin
|
||||
@(posedge clk);
|
||||
clear_lbus_in();
|
||||
idle_insert++;
|
||||
// insert period idle period
|
||||
if (idle_insert > 2) begin
|
||||
@(posedge clk);
|
||||
idle_insert =0;
|
||||
end
|
||||
end
|
||||
b = 0;
|
||||
end
|
||||
|
||||
// 25% of the time we start a new packet
|
||||
if ($urandom_range(99) < 25) begin
|
||||
b = 0;
|
||||
seg = 0;
|
||||
// 25% of the time add an idle cycle
|
||||
if ($urandom_range(99) < 25) begin
|
||||
@(posedge clk);
|
||||
clear_lbus_in();
|
||||
idle_insert++;
|
||||
// insert period idle period
|
||||
if (idle_insert > 2) begin
|
||||
idle_insert =0;
|
||||
@(posedge clk);
|
||||
end
|
||||
@(posedge clk);
|
||||
end else begin
|
||||
@(posedge clk);
|
||||
clear_lbus_in();
|
||||
idle_insert++;
|
||||
// insert period idle period
|
||||
if (idle_insert > 2) begin
|
||||
idle_insert =0;
|
||||
@(posedge clk);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end // foreach packet
|
||||
|
||||
@(posedge clk);
|
||||
clear_lbus_in();
|
||||
|
||||
endtask : send_lbus;
|
||||
|
||||
task automatic compare_packet(AxisPacket_t actual, expected);
|
||||
|
||||
automatic XportPacket_t actual_copy = new();
|
||||
automatic XportPacket_t expected_copy = new();
|
||||
actual_copy.import_axis(actual);
|
||||
expected_copy.import_axis(expected);
|
||||
|
||||
expected_copy.tkeep_to_tuser();
|
||||
actual_copy.clear_unused_bytes();
|
||||
|
||||
if (!expected_copy.equal(actual_copy)) begin
|
||||
$display("Expected");
|
||||
expected_copy.print();
|
||||
$display("Actual");
|
||||
actual_copy.print();
|
||||
if (!expected_copy.equal(actual_copy))
|
||||
$error("ERROR :: packet mismatch");
|
||||
|
||||
end
|
||||
|
||||
endtask : compare_packet;
|
||||
|
||||
task automatic test_transfers(int num_samples[$]);
|
||||
automatic AxisPacket_t send[$];
|
||||
automatic AxisPacket_t expected[$];
|
||||
automatic int sample_sum = 0;
|
||||
|
||||
test.start_test({TEST_NAME,"Test Transfer"}, 200us);
|
||||
|
||||
foreach (num_samples[i]) begin
|
||||
automatic raw_pkt_t pay;
|
||||
|
||||
expected[i] = new;
|
||||
send[i] = new;
|
||||
|
||||
get_ramp_raw_pkt(.num_samps(num_samples[i]),.ramp_start((sample_sum)%256),
|
||||
.ramp_inc(1),.pkt(pay),.SWIDTH(8));
|
||||
sample_sum += num_samples[i];
|
||||
send[i].push_bytes(pay);
|
||||
|
||||
// rebuild the expected packet for comparison without the preamble
|
||||
expected[i].push_bytes(pay);
|
||||
end
|
||||
|
||||
fork
|
||||
begin // tx_thread
|
||||
send_lbus(send);
|
||||
end
|
||||
begin //rx_thread
|
||||
foreach(expected[i]) begin
|
||||
automatic AxisPacket_t actual;
|
||||
axi.get(actual);
|
||||
compare_packet(actual,expected[i]);
|
||||
end
|
||||
end
|
||||
join
|
||||
|
||||
test.end_test();
|
||||
endtask : test_transfers
|
||||
|
||||
//----------------------------------------------------
|
||||
// Main test loop
|
||||
//----------------------------------------------------
|
||||
initial begin : tb_main
|
||||
automatic int num_samples[$];
|
||||
automatic int random_value;
|
||||
clk_gen.reset();
|
||||
|
||||
// stalling is not allowed by whoever is reading the MAC
|
||||
// i.e. ready is ignored
|
||||
axi.slave_tready_init = 1'b1;
|
||||
axi.set_master_stall_prob(0);
|
||||
axi.set_slave_stall_prob(0);
|
||||
axi.run();
|
||||
clear_lbus_in();
|
||||
test_reset();
|
||||
|
||||
$display("Fixed Sequences");
|
||||
num_samples = {64,65,66,67,68,69,70,71};
|
||||
test_transfers(num_samples);
|
||||
num_samples = {64,128,256,512,256,64,64,64};
|
||||
test_transfers(num_samples);
|
||||
num_samples = {65,64,64,64,64,64,64,64};
|
||||
test_transfers(num_samples);
|
||||
num_samples = {65,64,80,80,80,80,80,80};
|
||||
test_transfers(num_samples);
|
||||
num_samples = {64,64,96,80,96,80,96,80};
|
||||
test_transfers(num_samples);
|
||||
$display("Tons of 64");
|
||||
num_samples.delete();
|
||||
repeat(1000) begin
|
||||
num_samples.push_back(64);
|
||||
end
|
||||
test_transfers(num_samples);
|
||||
$display("Tons of 65");
|
||||
num_samples.delete();
|
||||
repeat(1000) begin
|
||||
num_samples.push_back(65);
|
||||
end
|
||||
test_transfers(num_samples);
|
||||
$display("Tons of Random");
|
||||
num_samples.delete();
|
||||
repeat(1000) begin
|
||||
random_value = $urandom_range(64,512);
|
||||
num_samples.push_back(random_value);
|
||||
end
|
||||
test_transfers(num_samples);
|
||||
|
||||
|
||||
// End the TB, but don't $finish, since we don't want to kill other
|
||||
// instances of this testbench that may be running.
|
||||
test.end_tb(0);
|
||||
|
||||
// Kill the clocks to end this instance of the testbench
|
||||
clk_gen.kill();
|
||||
end // initial begin
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,234 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: model_100gbe
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// A wrapper of the 100gbe core to axistream interface. this model can be
|
||||
// used drive packets into the X400 translated to serial Ethernet. This is
|
||||
// generally pretty slower than just driving things in at the output of the
|
||||
// mac.
|
||||
//
|
||||
|
||||
package Pkg100gbMac;
|
||||
import PkgAxiLite::*;
|
||||
import PkgAxiLiteBfm::*;
|
||||
import PkgTestExec::*;
|
||||
|
||||
localparam DATA_WIDTH =32;
|
||||
localparam ADDR_WIDTH =15;
|
||||
|
||||
typedef AxiLiteBfm #(DATA_WIDTH, ADDR_WIDTH) MacAxiLiteBfm_t;
|
||||
|
||||
// defined in https://www.xilinx.com/support/documentation/ip_documentation/cmac_usplus/v2_4/pg203-cmac-usplus.pdf
|
||||
// pg 187
|
||||
localparam CONFIGURATION_TX_REG1 = 32'h000C;
|
||||
localparam ctl_tx_ctl_enable = 0;
|
||||
localparam ctl_tx_ctl_tx_send_lfi = 3;
|
||||
localparam ctl_tx_ctl_tx_send_rfi = 4;
|
||||
localparam ctl_tx_ctl_tx_send_idle = 5;
|
||||
localparam ctl_tx_ctl_test_pattern = 16;
|
||||
|
||||
localparam CONFIGURATION_RX_REG1 = 32'h0014;
|
||||
localparam ctl_rx_ctl_enable = 0;
|
||||
localparam ctl_rx_ctl_rx_force_resync = 7;
|
||||
localparam ctl_rx_ctl_test_pattern = 8;
|
||||
|
||||
localparam RSFEC_CONFIG_INDICATION_CORRECTION = 32'h1000;
|
||||
localparam rs_fec_in_ctl_rx_rsfec_enable_correction = 0;
|
||||
localparam rs_fec_in_ctl_rx_rsfec_enable_indication = 1;
|
||||
localparam rs_fec_in_ctl_rsfec_ieee_error_indication_mode = 2;
|
||||
|
||||
localparam RSFEC_CONFIG_ENABLE = 32'h107C;
|
||||
localparam rs_fec_in_ctl_rx_rsfec_enable = 0;
|
||||
localparam rs_fec_in_ctl_tx_rsfec_enable = 1;
|
||||
|
||||
localparam STAT_RX_STATUS_REG = 32'h0204;
|
||||
localparam stat_rx_status = 0;
|
||||
localparam stat_rx_aligned = 1;
|
||||
localparam stat_rx_misaligned = 2;
|
||||
localparam stat_rx_aligned_err = 3;
|
||||
|
||||
task automatic init_mac (int offset, MacAxiLiteBfm_t axi);
|
||||
automatic logic [31:0] data;
|
||||
automatic resp_t resp;
|
||||
|
||||
// start transmitting alignment pattern
|
||||
data = 0;
|
||||
data[ctl_tx_ctl_enable] = 0;
|
||||
data[ctl_tx_ctl_tx_send_idle] = 0;
|
||||
data[ctl_tx_ctl_tx_send_lfi] = 0;
|
||||
data[ctl_tx_ctl_tx_send_rfi] = 1;
|
||||
data[ctl_tx_ctl_test_pattern] = 0;
|
||||
axi.wr(CONFIGURATION_TX_REG1+offset,data);
|
||||
|
||||
// configure fec
|
||||
data = 0;
|
||||
data[rs_fec_in_ctl_rx_rsfec_enable_correction] = 1;
|
||||
data[rs_fec_in_ctl_rx_rsfec_enable_indication] = 1;
|
||||
data[rs_fec_in_ctl_rsfec_ieee_error_indication_mode] = 1;
|
||||
axi.wr(RSFEC_CONFIG_INDICATION_CORRECTION+offset,data);
|
||||
|
||||
data = 0;
|
||||
data[rs_fec_in_ctl_rx_rsfec_enable] = 1;
|
||||
data[rs_fec_in_ctl_tx_rsfec_enable] = 1;
|
||||
axi.wr(RSFEC_CONFIG_ENABLE+offset,data);
|
||||
|
||||
// turn on RX interface
|
||||
data = 0;
|
||||
data[ctl_rx_ctl_enable] = 1;
|
||||
data[ctl_rx_ctl_rx_force_resync] = 0;
|
||||
data[ctl_rx_ctl_test_pattern] = 0;
|
||||
axi.wr(CONFIGURATION_RX_REG1+offset,data);
|
||||
|
||||
do begin
|
||||
axi.rd_block(STAT_RX_STATUS_REG+offset,data,resp);
|
||||
assert (resp==OKAY);
|
||||
end while (data[stat_rx_aligned] !== 1);
|
||||
|
||||
// stop transmitting alignment pattern
|
||||
// and start transmitting data
|
||||
data = 0;
|
||||
data[ctl_tx_ctl_enable] = 1;
|
||||
data[ctl_tx_ctl_tx_send_idle] = 0;
|
||||
data[ctl_tx_ctl_tx_send_lfi] = 0;
|
||||
data[ctl_tx_ctl_tx_send_rfi] = 0;
|
||||
data[ctl_tx_ctl_test_pattern] = 0;
|
||||
axi.wr(CONFIGURATION_TX_REG1+offset,data);
|
||||
|
||||
endtask : init_mac
|
||||
|
||||
endpackage : Pkg100gbMac
|
||||
|
||||
module model_100gbe (
|
||||
input logic areset,
|
||||
// 156.25 Mhz refclk
|
||||
input logic ref_clk,
|
||||
|
||||
// QSFP high-speed IO
|
||||
output logic [3:0] tx_p,
|
||||
output logic [3:0] tx_n,
|
||||
input logic [3:0] rx_p,
|
||||
input logic [3:0] rx_n,
|
||||
|
||||
// CLK and RESET out
|
||||
output logic mgt_clk,
|
||||
output logic mgt_rst,
|
||||
output logic link_up,
|
||||
|
||||
// Data port
|
||||
AxiStreamIf.slave mgt_tx,
|
||||
AxiStreamIf.master mgt_rx
|
||||
|
||||
);
|
||||
|
||||
// Include macros and time declarations for use with PkgTestExec
|
||||
`define TEST_EXEC_OBJ test
|
||||
`include "test_exec.svh"
|
||||
import PkgAxiLiteBfm::*;
|
||||
import PkgTestExec::*;
|
||||
|
||||
logic refclk_p;
|
||||
logic refclk_n;
|
||||
|
||||
logic clk40,clk40_rst;
|
||||
logic clk100,clk100_rst;
|
||||
logic phy_reset;
|
||||
|
||||
assign refclk_p = ref_clk;
|
||||
assign refclk_n = ~ref_clk;
|
||||
|
||||
//interface
|
||||
AxiLiteIf #(Pkg100gbMac::DATA_WIDTH,Pkg100gbMac::ADDR_WIDTH)
|
||||
mgt_axil (clk40, clk40_rst);
|
||||
//bfm
|
||||
Pkg100gbMac::MacAxiLiteBfm_t axi = new(.master(mgt_axil));
|
||||
TestExec mac_test = new();
|
||||
|
||||
sim_clock_gen #(.PERIOD(25.0), .AUTOSTART(1))
|
||||
clk40_gen (.clk(clk40), .rst(clk40_rst));
|
||||
sim_clock_gen #(.PERIOD(100.0), .AUTOSTART(1))
|
||||
clk100_gen (.clk(clk100), .rst(clk100_rst));
|
||||
|
||||
initial begin : init_model
|
||||
clk40_gen.reset();
|
||||
axi.run();
|
||||
wait(!clk40_rst);
|
||||
repeat (10) @(posedge clk40);
|
||||
|
||||
wait(!phy_reset); // both usr_clk's are ok
|
||||
|
||||
mac_test.start_test("model_100gbe::Wait for MAC link_up", 150us);
|
||||
//Added autoconnect - uncomment to test connecting over AXI
|
||||
//Pkg100gbMac::init_mac(0,axi);
|
||||
mac_test.end_test();
|
||||
end
|
||||
|
||||
|
||||
AxiStreamIf #(.DATA_WIDTH(512),.USER_WIDTH(7),.TKEEP(0))
|
||||
eth100g_rx(mgt_clk,mgt_rst);
|
||||
|
||||
always_comb begin
|
||||
mgt_rx.tdata = eth100g_rx.tdata;
|
||||
mgt_rx.tuser = eth100g_rx.tuser;
|
||||
mgt_rx.tkeep = eth100g_rx.trailing2keep(eth100g_rx.tuser);
|
||||
mgt_rx.tvalid = eth100g_rx.tvalid;
|
||||
mgt_rx.tlast = eth100g_rx.tlast;
|
||||
eth100g_rx.tready = mgt_rx.tready;
|
||||
// The MAC ignores hold off. Data must be consumed every clock it is valid.
|
||||
if (!mgt_rst) begin
|
||||
if (!mgt_rx.tready && mgt_rx.tvalid) begin
|
||||
$error("Model 100Gbe : can't hold off the MAC");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// model does not pause. Users could access this heirarchically to test it
|
||||
logic mgt_pause_req;
|
||||
assign mgt_pause_req = 1'b0;
|
||||
|
||||
// hold off link up untill the stat_auto_config writes are complete
|
||||
logic link_up_model;
|
||||
logic [31:0] mac_status;
|
||||
always_comb begin
|
||||
link_up = link_up_model && mac_status[4];
|
||||
end
|
||||
|
||||
eth_100g #(.PAUSE_QUANTA(10),.PAUSE_REFRESH(100)) eth_100gx (
|
||||
.areset(areset),
|
||||
//-- Free running 100 MHz clock used for InitClk and AxiLite to mac
|
||||
//-- 3.125 - 161.132812 MHz.
|
||||
.clk100(clk100),
|
||||
// MGT Reference Clock 100/125/156.25/161.1328125 MHz
|
||||
.refclk_p(refclk_p),
|
||||
.refclk_n(refclk_n),
|
||||
// MGT TX/RX differential signals
|
||||
.tx_p(tx_p),
|
||||
.tx_n(tx_n),
|
||||
.rx_p(rx_p),
|
||||
.rx_n(rx_n),
|
||||
// 322.26666 Mhz clock generated by 100G Phy from RefClock
|
||||
.mgt_clk(mgt_clk),
|
||||
.mgt_rst(mgt_rst),
|
||||
// pause
|
||||
.mgt_pause_req(mgt_pause_req),
|
||||
//------------------------ AXI Stream TX Interface ------------------------
|
||||
.mgt_tx(mgt_tx),
|
||||
//---------------------- AXI Stream RX Interface ------------------------
|
||||
// There is no RxTReady signal support by the Ethernet100G IP. Received data has to
|
||||
// be read immediately or it is lost.
|
||||
// tUser indicates an error on rcvd packet
|
||||
.mgt_rx(eth100g_rx),
|
||||
.mgt_axil(mgt_axil),
|
||||
// LEDs of QSFP28 port
|
||||
.phy_status(),
|
||||
.mac_ctrl(32'h01000001), // autoconfig / pause mask set to global
|
||||
.mac_status(mac_status),
|
||||
.phy_reset(phy_reset),
|
||||
.link_up(link_up_model)
|
||||
);
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user