该存储库在 Bluespec SystemVerilog(BSV) 中实现了一系列与以太网相关的组件,以便在 FPGA 上进行高性能数据包处理。具体来说,该存储库提供了用于生成和解析 UDP/IP/以太网数据包的模块。还提供具有存储地址信息的非阻塞缓存的 ARP 处理单元,以自动处理 MAC 地址解析。除了构建标准的UDP/IP/以太网堆栈外,blue-ethernet还增加了对RoCE(RDMA over Converged Ethernet)的支持:1)在UDP/IP数据包处理中集成ICRC(Invariant Cyclic Redundancy)的生成和验证; 2)提供PFC(Priority Flow Control)处理模块,实现无损网络传输。最后还提供了接口转换模块,供数据包生成器和解析器与 Xilinx 100G 以太网子系统 (CMAC) 交互。
该仓库的一些关键目录如下所示:
├── lib # external libraries/repos
│ ├── blue-crc # high-performance CRC hardware implementation
│ └── blue-wrapper # BSV wrappers for generating ready-to-use Verilog interface
├── scripts # scripts used to build project
├── src # design source files
│ └── includes # files containing some commonly-used BSV types and modules
├── syn # scripts for vivado synthesis and implementation
└── test # source files for verification
├── bluesim # testbenches based on bluesim
├── cocotb # python testbenches based on cocotb
└── vivado # co-simulation with cmac using vivado
以下是一些关键源文件的列表:
./src
├── ArpCache.bsv # Cache implementation storing MAC addresses got from ARP
├── ArpProcessor.bsv # processing unit handling ARP requests and responses
├── includes
│ ├── CompletionBuf.bsv
│ ├── ContentAddressMem.bsv
│ ├── EthernetTypes.bsv # numeric and struct types about protocol definition
│ ├── PortConversion.bsv # interface conversion modules used to generate ready-to-use Verilog
│ ├── Ports.bsv # numeric and struct types about in/output ports of modules
│ ├── RFile.bsv
│ ├── StreamHandler.bsv # modules implemented for manipulating data stream
│ └── EthUtils.bsv # utility functions and modules
├── MacLayer.bsv # generator and parser for Ethernet packet
├── PfcUdpIpArpEthRxTx.bsv # generator and parser for UDP/IP/Ethernet packet with PFC
├── PriorityFlowControl.bsv # modules handling PFC
├── UdpIpArpEthRxTx.bsv # generator and parser for UDP/IP/Ethernet packet
├── UdpIpEthRx.bsv # parser for UDP/IP/Ethernet packet
├── UdpIpEthTx.bsv # generator for UDP/IP/Ethernet packet
├── UdpIpLayer.bsv # parser and generator for UDP/IP packet
├── UdpIpLayerForRdma.bsv # parser and generator for UDP/IP packet with support for RoCE
└── XilinxCmacRxTxWrapper.bsv # bridge modules between parser/generator and Xilinx CMAC
本节详细描述了蓝色以太网中实现的一些重要组件,包括它们的功能、接口和硬件架构。
以太网相关的硬件组件所做的基本上是一系列流操作。数据包生成器负责将标头流插入到有效负载流的头部以生成完整的数据包流。相反,解析器所做的就是从数据包流中提取头部流和有效负载流。至于为数据包添加校验和,数据包流被传递到 CRC 计算器,然后输出的 CRC 值被附加到数据包流的尾部。
我们这里提到的流对应的硬件实体实际上是由有效-就绪控制信号对守护的一组数据信号。有效信号表明源组件想要传输数据。而ready表示sink已经准备好接收来自source的数据。仅当 valid 和 ready 都为高电平时,源和接收器之间的传输才会成功。如果要传输的数据大小大于一次传输的大小,则需要将数据分片并在一系列传输中传输。
流处理中最棘手、最容易出错的部分是如何处理不同流的有效就绪控制信号。在BSV中,控制信号的操作是由编译器实现的,在语法层面上是不可见的,这有助于设计者专注于流处理的逻辑。
用于在不同组件之间传输数据包流的数据信号被封装在DataStream结构体中,其中包括256位数据信号、32位字节使能信号、两个布尔信号表示本次传输是数据包流的最后一个还是第一个。
typedef 256 DATA_BUS_WIDTH ;
typedef TDiv # ( DATA_BUS_WIDTH , 8 ) DATA_BUS_BYTE_WIDTH ;
typedef Bit # ( DATA_BUS_WIDTH ) Data ;
typedef Bit # ( DATA_BUS_BYTE_WIDTH ) ByteEn ;
typedef struct {
Data data ;
ByteEn byteEn ;
Bool isFirst ;
Bool isLast ;
} DataStream deriving ( Bits , Bounded , Eq , FShow ) ;
module mkAppendDataStreamHead # (
IsSwapEndian swapDataStream ,
IsSwapEndian swapAppendData ,
FifoOut # ( DataStream ) dataStreamIn ,
FifoOut # ( dType ) appendDataIn
)( FifoOut # ( DataStream )) ;
module mkAppendDataStreamTail # (
IsSwapEndian swapDataStream ,
IsSwapEndian swapAppendData ,
FifoOut # ( DataStream ) dataStreamIn ,
FifoOut # ( dType ) appendDataIn ,
FifoOut # ( Bit # ( streamLenWidth )) streamLengthIn
)( FifoOut # ( DataStream )) ;
interface ExtractDataStream # ( type dType ) ;
interface FifoOut # ( dType ) extractDataOut ;
interface FifoOut # ( DataStream ) dataStreamOut ;
endinterface
module mkExtractDataStreamHead # (
FifoOut # ( DataStream ) dataStreamIn
)( ExtractDataStream # ( dType )) ;
UdpIpLayer包中的模块用于生成和解析 UDP/IP 数据包。
数据包生成器接收包含 UDP/IP 标头信息和有效负载流的UdpIpMetaData ,并输出完整的 UDP/IP 数据包流。数据包解析器以相反的方式工作,从 UDP/IP 数据包流中提取UdpIpMetaData和有效负载流。
typedef struct {
UdpLength dataLen ; # The Length of payload data
IpAddr ipAddr ; # Desitnation IP address
IpDscp ipDscp ; # DSCP field used for PFC
IpEcn ipEcn ; # ECN field
UdpPort dstPort ; # Destination port number
UdpPort srcPort ; # Source port number
} UdpIpMetaData ;
封装在UdpIpMetaData结构中的信号不涵盖 UDP/IP 标头中定义的所有字段。标头的某些字段对于特定网络设备是固定的,这些字段封装在UdpConfig结构体中,需要在发送或接收任何数据包之前进行配置。还有一些其他字段是恒定的并且硬编码在硬件组件中。
typedef struct {
EthMacAddr macAddr ; # Source MAC address
IpAddr ipAddr ; # Source IP address
IpNetMask netMask ; # IP netmask
IpGateWay gateWay ; # IP gateway
} UdpConfig ;
module mkUdpIpStream # (
UdpConfig udpConfig ,
FifoOut # ( DataStream ) dataStreamIn ,
FifoOut # ( UdpIpMetaData ) udpIpMetaDataIn ,
function UdpIpHeader genHeader ( UdpIpMetaData meta , UdpConfig udpConfig , IpID ipId )
)( FifoOut # ( DataStream )) ;
interface UdpIpMetaDataAndDataStream ;
interface FifoOut # ( UdpIpMetaData ) udpIpMetaDataOut ;
interface FifoOut # ( DataStream ) dataStreamOut ;
endinterface
module mkUdpIpMetaDataAndDataStream # (
UdpConfig udpConfig ,
FifoOut # ( DataStream ) udpIpStreamIn ,
function UdpIpMetaData extractMetaData ( UdpIpHeader hdr )
)( UdpIpMetaDataAndDataStream ) ;
UdpIpLayerForRdma包中的模块基于UdpIpLayer实现,支持 RoCE(RDMA over Converged Ethernet)。为支持 RoCE 而添加的附加功能是生成和验证 RoCE 数据包所需的 ICRC(不变 CRC)。 RoCE数据包的格式定义如下:
MacLayer包中的模块用于生成和解析以太网数据包。生成器将以太网报头插入到UDP/IP数据包流的头部以生成以太网数据包流。解析器从以太网数据包流中提取以太网报头和UDP/IP数据包流。
用于生成以太网数据包的标头信息在MacMetaData结构中定义。
typedef struct {
EthMacAddr macAddr ; # Destination MAC address
EthType ethType ; # Type of Ethernet frame
} MacMetaData deriving ( Bits , Eq , FShow ) ;
需要注意的是, MacLayer处理的以太网数据包仅涵盖下图中红色矩形框内的字段。其他字段由 Xilinx CMAC IP 处理。
module mkMacStream # (
FifoOut # ( DataStream ) udpIpStreamIn ,
FifoOut # ( MacMetaData ) macMetaDataIn ,
UdpConfig udpConfig
)( FifoOut # ( DataStream )) ;
interface MacMetaDataAndUdpIpStream ;
interface FifoOut # ( MacMetaData ) macMetaDataOut ;
interface FifoOut # ( DataStream ) udpIpStreamOut ;
endinterface
module mkMacMetaDataAndUdpIpStream # (
FifoOut # ( DataStream ) macStreamIn ,
UdpConfig udpConfig
)( MacMetaDataAndUdpIpStream ) ;
地址解析协议 (ARP) 用于发现与给定 IP 地址关联的 MAC 地址。在blue-ethernet中,实现了ARP处理模块mkArpProcessor ,该模块集成了ARP数据包生成器、解析器和存储MAC地址的mkArpCache模块。
对于ARP处理中使用的缓存,32位IP地址对应缓存地址,48位MAC地址对应缓存数据。 ARP 缓存的默认内存阵列排列如下所示,为 4 路组相联结构,每路包含 64 行,每行包括 1 位有效、26 位标记和 48 位数据。此默认阵列配置的总大小约为 1.2KB。支持通过设置行数和路数来改变内存阵列的大小。基于该内存阵列,缓存被设计为非阻塞的,支持未完成的请求(飞行中的多个请求)并使用伪LRU算法进行缓存行替换。
mkArpCache模块的接口定义和简化结构图如下所示。 ArpCache有两个子接口: cacheServer处理与 MAC 地址解析服务的组件的交互; arpClient处理与mkArpProcessor的交互以发起 ARP 请求并从 ARP 响应中获取 MAC 地址。 mkArpCache模块的基本工作流程如下:
当cache收到读请求时,它首先搜索内存阵列以获取与给定IP地址对应的所有标签和数据。然后它检查标签以查看我们需要的数据是否存储在缓存中。如果缓存命中,则将获取的数据发送到hitBuf 。或者将IP地址发送到arpReqBuf发起ARP请求。当ARP响应返回时,其携带的数据和地址信息都被写入cacheWrBuf和missHitBuf ,以更新内存阵列并返回缓存读响应。
interface ArpCache ;
interface Server # ( CacheAddr , CacheData ) cacheServer ;
interface Client # ( CacheAddr , ArpResp ) arpClient ;
endinterface
缓存实现中最困难的部分是支持一个突出的特性,即支持飞行中的多个读请求。未完成的问题是每个动态 ARP 请求的响应时间不同,这意味着迟到的请求可能会先收到响应。因此需要重排序机制来保证缓存未命中时请求地址和响应数据的对应关系。为了实现有序响应,完成缓冲区respCBuf和内容可寻址存储器missReqTab集成在数据流中。完成缓冲区的工作方式类似于 FIFO,并额外支持预留功能。在实际入队操作之前,我们可以首先在完成缓冲区中保留一个订单。无论入队操作的实际顺序如何,出队操作都遵循保留的顺序。对于每个读取请求,一旦收到,就会在respCBuf中反转出队顺序。而由于ARP请求中不能携带订单信息,所以实现了missReqTab来存储订单信息。
该模块既可以充当 ARP 客户端,也可以充当服务器。作为服务器,如果目标IP的MAC地址未知,处理器需要生成ARP请求,然后等待目标设备的ARP响应。 ARP处理器作为客户端,接收其他设备的ARP请求,并返回携带自己MAC地址的ARP响应。
interface ArpProcessor ;
interface FifoOut # ( DataStream ) arpStreamOut ;
interface FifoOut # ( MacMetaData ) macMetaDataOut ;
interface Put # ( UdpConfig ) udpConfig ;
endinterface
module mkArpProcessor # (
FifoOut # ( DataStream ) arpStreamIn ,
FifoOut # ( UdpIpMetaData ) udpIpMetaDataIn
)( ArpProcessor ) ;
UdpIpEthRx包中的模块用于接收和解析 UDP/IP/以太网数据包。
interface UdpIpEthRx ;
interface Put # ( UdpConfig ) udpConfig ;
interface Put # ( AxiStream512 ) axiStreamIn ;
interface FifoOut # ( MacMetaData ) macMetaDataOut ;
interface FifoOut # ( UdpIpMetaData ) udpIpMetaDataOut ;
interface FifoOut # ( DataStream ) dataStreamOut ;
endinterface
module mkGenericUdpIpEthRx # ( Bool isSupportRdma )( UdpIpEthRx )
UdpIpEthTx包中的模块用于生成和传输 UDP/IP/以太网数据包。
interface UdpIpEthTx ;
interface Put # ( UdpConfig ) udpConfig ;
interface Put # ( UdpIpMetaData ) udpIpMetaDataIn ;
interface Put # ( MacMetaData ) macMetaDataIn ;
interface Put # ( DataStream ) dataStreamIn ;
interface AxiStream512FifoOut axiStreamOut ;
endinterface
module mkGenericUdpIpEthTx # ( Bool isSupportRdma )( UdpIpEthTx ) ;
UdpIpArpEthRxTx包中提供的模块旨在接收和发送 UDP/IP/以太网数据包,同时处理 ARP 请求和响应。
该模块可分为两条相反的码流路径,包括发送路径和接收路径:
对于传输路径,它接收携带负载流的dataStreamInTx和携带头信息流的udpIpMetaDataIn ,并生成携带UDP/IP/以太网数据包流的axiStreamOutTx 。不需要提供包含以太网头信息的MacMetaData作为mkUdpIpEthTx模块,因为mkArpProcessor负责处理MAC地址解析和生成以太网头信息。
对于接收路径,则以相反的方式工作,从携带UDP/IP/以太网数据包流的axiStreamInRx中提取携带有效负载流的dataStreamOutRx和携带头信息流的udpIpMetaDataOutRx 。
以太网数据包生成器和解析器由UDP/IP数据包和ARP数据包共享,因此在传输和接收路径中需要额外的Mux和Demux来进行流仲裁和分发。模块参数isSupportRdma指定是否支持RoCE数据包处理。如果禁用对 RDMA 的支持,我们只需要分别在发送和接收路径中使用mkUdpIpStream和mkUdpIpMetaAndDataStream 。
interface UdpIpArpEthRxTx ;
interface Put # ( UdpConfig ) udpConfig ;
// Tx
interface Put # ( UdpIpMetaData ) udpIpMetaDataInTx ;
interface Put # ( DataStream ) dataStreamInTx ;
interface AxiStream512FifoOut axiStreamOutTx ;
// Rx
interface Put # ( AxiStream512 ) axiStreamInRx ;
interface FifoOut # ( UdpIpMetaData ) udpIpMetaDataOutRx ;
interface FifoOut # ( DataStream ) dataStreamOutRx ;
endinterface
module mkGenericUdpIpArpEthRxTx # ( Bool isSupportRdma )( UdpIpArpEthRxTx ) ;
该模块使用 blue-wrapper 中提供的模块包装mkGenericUdpIpArpEthRxTx ,以便生成随时可用的 Verilog 接口。
该模块集成了mkGenericUdpIpArpEthRxTx模块和mkXilinxCmacTxWrapper模块。它旨在与 Xilinx CMAC IP 交互,以在物理介质之间传输和接收 UDP/IP/以太网数据包。
PriorityFlowControl包中的模块实现了优先级流量控制机制,保证网络无损传输。
interface PriorityFlowControlTx ;
interface Get # ( UdpIpMetaData ) udpIpMetaDataOut ;
interface Get # ( DataStream ) dataStreamOut ;
endinterface
module mkPriorityFlowControlTx # (
FifoOut # ( FlowControlReqVec ) flowControlReqVecIn ,
Vector # ( VIRTUAL_CHANNEL_NUM , DataStreamFifoOut ) dataStreamInVec ,
Vector # ( VIRTUAL_CHANNEL_NUM , UdpIpMetaDataFifoOut ) udpIpMetaDataInVec
)( PriorityFlowControlTx ) ;
interface PriorityFlowControlRx # (
numeric type bufPacketNum ,
numeric type maxPacketFrameNum ,
numeric type pfcThreshold
) ;
interface FifoOut # ( FlowControlReqVec ) flowControlReqVecOut ;
interface Vector # ( VIRTUAL_CHANNEL_NUM , Get # ( DataStream )) dataStreamOutVec ;
interface Vector # ( VIRTUAL_CHANNEL_NUM , Get # ( UdpIpMetaData )) udpIpMetaDataOutVec ;
endinterface
module mkPriorityFlowControlRx # (
DataStreamFifoOut dataStreamIn ,
UdpIpMetaDataFifoOut udpIpMetaDataIn
)( PriorityFlowControlRx # ( bufPacketNum , maxPacketFrameNum , pfcThreshold )) ;
mkGenericPfcUdpIpArpEthRxTx集成了mkPriorityFlowControlRx/Tx和mkGenericUdpIpArpEthRxTx ,提供生成和解析 UDP/IP/以太网数据包的功能,同时支持优先级流量控制。对于数据包传输,它需要八个通道的有效负载流和UDP/IP头信息,并输出一个UDP/IP/以太网数据包流。对于数据包接收,它接收一个 UDP/IP/以太网数据包流,并将提取的 UDP/IP 标头和有效负载流路由到八个输出通道之一。
mkPfcUdpIpArpEthCmacRxTx集成了mkGenericPfcUdpIpArpEthRxTx模块和mkXilinxCmacTxWrapper模块。它旨在与 Xilinx CMAC IP 交互,以在物理介质之间传输和接收 UDP/IP/以太网数据包。
主模块mkGenericUdpIpArpEthRxTx的综合和实现是基于Xilinx xcvu9p器件使用Vivado进行的。结果表明,该电路可以达到500MHz的工作频率,并提供128Gbps的峰值吞吐量。硬件资源使用情况如下:
CLB Logic
+----------------------------+-------+-------+------------+-----------+-------+
| Site Type | Used | Fixed | Prohibited | Available | Util % |
+----------------------------+-------+-------+------------+-----------+-------+
| CLB LUTs | 63886 | 0 | 0 | 1182240 | 5.40 |
| LUT as Logic | 41242 | 0 | 0 | 1182240 | 3.49 |
| LUT as Memory | 22644 | 0 | 0 | 591840 | 3.83 |
| LUT as Distributed RAM | 22644 | 0 | | | |
| LUT as Shift Register | 0 | 0 | | | |
| CLB Registers | 44099 | 0 | 0 | 2364480 | 1.87 |
| Register as Flip Flop | 44099 | 0 | 0 | 2364480 | 1.87 |
| Register as Latch | 0 | 0 | 0 | 2364480 | 0.00 |
| CARRY8 | 73 | 0 | 0 | 147780 | 0.05 |
| F7 Muxes | 194 | 0 | 0 | 591120 | 0.03 |
| F8 Muxes | 28 | 0 | 0 | 295560 | < 0.01 |
| F9 Muxes | 0 | 0 | 0 | 147780 | 0.00 |
+----------------------------+-------+-------+------------+-----------+-------+
BLOCKRAM
+-------------------+------+-------+------------+-----------+-------+
| Site Type | Used | Fixed | Prohibited | Available | Util % |
+-------------------+------+-------+------------+-----------+-------+
| Block RAM Tile | 4.5 | 0 | 0 | 2160 | 0.21 |
| RAMB36 / FIFO * | 4 | 0 | 0 | 2160 | 0.19 |
| RAMB36E2 only | 4 | | | | |
| RAMB18 | 1 | 0 | 0 | 4320 | 0.02 |
| RAMB18E2 only | 1 | | | | |
| URAM | 0 | 0 | 0 | 960 | 0.00 |
+-------------------+------+-------+------------+-----------+-------+
本节介绍如何开始这个项目。在执行任何其他步骤之前,您首先需要参考脚本 setup.sh 设置开发环境。这是依赖项列表:
设置环境后,将此存储库克隆到特定目录。这里我们将该目录称为 BLUE_ETH:
git clone --recursive https://github.com/wengwz/blue-ethernet.git $( BLUE_ETH )
蓝色以太网提供了三个不同级别的测试平台:
# Specify TARGET to the name of target component
cd $( BLUE_ETH ) /test/bluesim
make TARGET=ArpCache
# Run tests of UdpIpEthRx/Tx
# Enable/Disable support for RDMA by setting SUPPORT_RDAM to True/False
cd $( BLUE_ETH ) /test/cocotb
make cocotb TARGET=UdpIpEthTx SUPPORT_RDMA=TRUE
# Run simulation on virtual network
# Change NET_IFC in run_docker_net_test.sh to the name of your network card
cd $( BLUE_ETH ) /test/cocotb
docker build -f ./build_docker/Dockerfile -t ethernet-test ./build_docker
./run_docker_net_test.sh
# Available TARGET includes UdpIpArpEthCmacRxTx/PfcUdpIpArpEthCmacRxTx
# Enable/Disable support for RDMA by setting SUPPORT_RDAM to True/False
cd $( BLUE_ETH ) /test/vivado
make sim TARGET=UdpIpArpEthCmacRxTx SUPPORT_RDMA=False
用于运行综合和设计实现的脚本在目录 $(BLUE_ETH)/syn 中提供。
# TARGET specifies the top module to be synthsized or implemented
# SUPPORT_RDMA specifies whether modules supports RoCE packet processing
# ONLYSYNTH decides whether or not run implemetation after synthesis
cd $( BLUE_ETH ) /syn
make vivado TARGET=UdpIpArpEthRxTx SUPPORT_RDMA=False ONLYSYNTH=0
# TARGET specifies the name of top module to be generated
# Specify SUPPORT_RDMA if needed
cd $( BLUE_ETH ) /test/cocotb
make verilog TARGET=UdpIpEthTx SUPPORT_RDMA=TRUE
bsc -p +: $( BLUE_ETH ) /src: $( BLUE_ETH ) /src/includes ...
蓝色以太网的实现涉及以下外部库的使用: