先来看看TOP层接口代码。
module udp_top(
input clk , rst ,
//ip address and mac address
input [31:0] cfg_my_ip,
input [47:0] cfg_my_mac,
// udp output port
input [31:0] s_udp_dst_ip,
input [15:0] s_udp_src_port,
input [15:0] s_udp_dst_port ,
output s_udp_tx_busy ,
input [15:0]s_udp_tx_len,
input [7:0]s_udp_tx_dat,
input [15:0]s_udp_ip_id,
input s_udp_tx_start,
// udp input port
output [15:0] m_udp_len ,
output [7:0] m_udp_dout ,
output m_udp_valid,m_udp_sof,m_udp_eof,m_udp_chksum_ok,
output [31:0] m_udp_src_ip,m_udp_dst_ip,
output [15:0] m_udp_src_port,m_udp_dst_port,
//phy interface
output [7:0] phy_tx_dout ,
output phy_tx_err,phy_tx_en,
input [7:0] phy_rx_din,
input phy_rx_dv
);
我们先简单白话一下各个接口的定义,之后再实际抓一下波形实例解析一下。
input clk , rst ,
时钟CLK因为目前只支持千兆网络模式,暂时要求必须是125M,但是可以与PHY芯片不同源。
复位RST脚同步高电平复位。
//phy interface
output [7:0] phy_tx_dout ,
output phy_tx_err,phy_tx_en,
input [7:0] phy_rx_din,
input phy_rx_dv
这里是接PHY芯片的信号,但是并非直接接PHY芯片的引脚,要用双时钟FIFO同步到本模块的125M的CLK时钟域。
-----
// udp send port
input [31:0] s_udp_dst_ip,
input [15:0] s_udp_src_port,
input [15:0] s_udp_dst_port ,
output s_udp_tx_busy ,
input [15:0]s_udp_tx_len,
input [7:0]s_udp_tx_dat,
input [15:0]s_udp_ip_id,
input s_udp_tx_start,
UDP发送接口,其中dst_ip,dst_port是接收主机的 ip地址和udp端口,src_port是发送UDP协议的本地端口(如果有数据返回,在接收方也会被recvfrom函数识别,并作为参数参与sendto的发送,使用时候注意具体规划)。
s_udp_tx_busy:模块正在忙,上一个UDP没有发送完毕,后续的不要发送。
s_udp_tx_len:要发送的数据包长度,虽然是16位,但是不要超过1470长度,(可以低至1,但是MAC等会自动填充到以太网帧得到64字节,多余的填充也会被接收端忽略,用户可以忽略填充)。
s_udp_tx_dat要发送的字节数据流。
s_udp_ip_id 对应IP header头部的ID字段。
s_udp_tx_start 拉高表示此时开始传输,同时s_udp_tx_dat输出第一个字节。
通过分析接口我们看到发送udp包的流程就两个步骤:等待和填充:检测busy信号等待不忙,之后拉起start一个周期的同时填充第一个自己,以后的连续填充数据,直到长度达到udp_tx_len。
上图中没有抓ip和port的信息,都是在tx_start=1时被采集。tx_start的发送必须是在检测busy信号电平为0之后才实施的。
----
另外我们看到发送5个字节用掉100个周期。这端时间里面发生了一次原始内容拷贝,是拷贝到udp缓存,这样我们可以计算出来除去这两次拷贝之外用的周期开销:
100-5=95周期,也就说我们要发送一个字节数为n的,发送的包间隔时间为n+95周期。这90个周期其实包含了UDP头,IP头,MAC头生成,以太包头发送,IP转MAC地址,以及各个环节的状态机转换等所有的开销,以及为了确保UDP的RAM同步,在上述包头都发送完毕后还需要读出UDP的8字节的头部后,busy信号才被释放。
我们分析如果用1400长度的包传递UDP的销量应该是 1400/1400+95 = 93.6% 在125M时钟下面能达到的传输速率是93.6%*125MB/S=111.7M字节每秒。当然这是理想化的。我们不妨实际测测,硬件现成的:
这样的话跟我们的之前估计的加95周期开销是吻合的。在VERILOG端口处我们的数据字节速率应该是 111.7M字节每秒.图中1498-1400多余98个周期跟咱们之前95周期比其实多了一个外部发送控制状态机的轮转多了三两个周期。
按照实际运算1400的实测是 125*(1400/1498) = 116.82243M/秒
在这种情况下我们我们打开任务管理器看网络性能。
这里看到的实际接收的位速率是962M,换算成字节是 962/8=120.25M字节。既然上图给出的是位速率,应该包含MAC,IP,UDP头部开销甚至(应该不会包含ETHER 7+1+4字节的开销)。这些头部的长度是UDP 8 ,IP 20, MAC 12 正好40字节。因此FPGA发送1400字节的包,PC就收到1440个字节。
120.25*(1400/1440) = 116.909722M/秒 这对比我们在FPGA抓包计算的速率(116.8224M字节每秒)相差几乎无几。这个可能还有PC端我们没有计算的一丢丢开销,就基本能说的过去了。
上面的理论推算和实际抓包都是在ARP CACHE里面已经有了IP和MAC条目后的结果。如果要ARP表格里面没有保存此IP对应的MAC地址,需要发送ARP请求并解析ARP回复,使用的周期数更长,根据网络情况这个数值不同,直接连电脑的网卡应该带70us秒以上。
5字节和1400字节的不同还牵扯一个MAC的填充为最小64字节,还好这个填充是取完UDP包头之后,而取完包头后,实际BUSY信号就释放了,认为上层用户接口就可以继续写这个RAM了,反正读的端口在从地址8开始连续读,上层用户这时候从地址8开始连续写也不会有影响。
-----------------------------------------------------------------------
// udp recv port
output [15:0] m_udp_len ,
output [7:0] m_udp_dout ,
output m_udp_valid,m_udp_sof,m_udp_eof,m_udp_chksum_ok,
output [31:0] m_udp_src_ip,m_udp_dst_ip,
output [15:0] m_udp_src_port,m_udp_dst_port,
这接收udp数据包的接口,只要发到本IP的所有的UDP包都会在这里呈现(当然实际使用用户可以在此基础之上继续写下对端口的选择)。
m_udp_src_port,m_udp_dst_port, m_udp_src_ip,m_udp_dst_ip:收发双发的IP地址和端口,这里src是发给我们数据包的对方主机,我们才是dst。这里预留了dst_ip字段其实就是为了以后扩展支持udp广播。
m_udp_len:接收到的UDP数据包长度
m_udp_dout :接收到的UDP数据流输出
m_udp_valid:接收到非0长度数据包
m_udp_sof:对应字节流的第一个字节
m_udp_eof:对应字节流的最后一个字节
m_udp_chksum_ok: UDP校验是否OK (不需要连接,暂不支持)
{{aAxvOXMOIvVUoXMxvoxiowMwWV8xxWTxoxOIOVIUUOvwVOUiIoUvvTMMVMwovWHWX8vOUOUMvTOWxITVvoOVxWVHIVIHWwiUOvHVixIHHMWHiwvomHMoiHIoVU8VvmvIWXTvvOvv8xvMovOWiimvvmMw8UmooOTvvwUIoTwvmWUoiTw8VmvoHWwMIUWOixiowiUoiXwiwwMMIiIXHwUmOWUVmXXwV8iHWOTUiwTU8xwOoV8HVmTWZz}}
-------------
未完,需要完善实例截图。