1)实验平台:正点原子开拓者
FPGA 开发板
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html
第四十三章 以太网通信实验
以太网(Ethernet)是当今现有局域网采用的最通用的通信协议标准,该标准定义了在局域
网中采用的电缆类型和信号处理方法。以太网凭借其成本低、通信速率高、抗干扰性强等优点
被广泛应用在网络远程监控、交换机、工业自动化等对通信速率要求较高的场合。本章我们将
使用FPGA开发板上的以太网接口完成上位机与FPGA通信的功能。
本章包括以下几个部分:
43.1 以太网简介
43.2 实验任务
43.3 硬件设计
43.4 程序设计
43.5 下载验证
以太网简介
以太网是一种产生较早,使用相当广泛的局域网。其最初是由Xerox(施乐)公司创建并
由Xerox、Intel和DEC公司联合开发的基带局域网规范,后来被电气与
电子工程师协会(IEEE)
所采纳作为802.3的标准。
以太网的分类有标准以太网(10Mbit/s),快速以太网(100Mbit/s)和千兆以太网
(1000Mbit/s)。随着以太网技术的飞速发展,市场上也出现了万兆以太网(10Gbit/s),它
扩展了IEEE802.3协议和MAC规范,使其技术支持10Gbit/s的传输速率。然而在实际应用中,标
准以太网和快速以太网已经能够满足我们的日常需求,对通信速率要求较高的场合,才会用到
千兆以太网。
以太网通信离不开连接端口的支持,网络数据连接的端口就是以太网接口。以太网接口类
型有RJ45接口,RJ11接口(电话线接口),SC光纤接口等。其中RJ45接口是我们现在最常见的
网络设备接口(如:电脑网口),我们开发板使用的就是这种接口。
RJ45接口俗称“水晶头”,专业术语为RJ45连接器,由插头(接头、水晶头)和插座(母
座)组成,属于双绞线以太网接口类型。RJ45插头只能沿固定方向插入,设有一个塑料弹片与
RJ45插槽卡住以防止脱落。
RJ45接口样式如图 43.1.1所示:
图 43.1.1 RJ45插头(左)、插座(右)
RJ45接口定义以及各引脚功能说明如图 43.1.2所示,在以太网中只使用了1、2、3、6这
四根线,其中1、2这组负责传输数据(TX+、TX-),而3、6这组负责接收数据(RX+、RX-),
另外四根线是备用的。
图 43.1.2 RJ45插座接口定义
以太网是目前应用最广泛的局域网通讯方式,同时也是一种协议。以太网协议定义了一系
列软件和硬件标准,从而将不同的计算机设备连接在一起。我们知道串口通信单次只传输一个
字节,而以太网通信是以数据包的形式传输,其单包数据量达到几十,甚至成百上千个字节。
下图为以太网通过UDP(User Datagram Protocol,用户数据报协议)传输单包数据的格式,
从图中可以看出,以太网的数据包就是对各层协议的逐层封装来实现数据的传输。这里只是让
大家了解下以太网数据包的格式,后面会逐个展开来讲。
图 43.1.3 以太网包数据格式
以太网MAC帧格式
以太网技术的正式标准是IEEE 802.3,它规定了以太网传输数据的帧结构,我们可以把以
太网MAC层理解成高速公路,我们必须遵循它的规则才能在上面通行,以太网MAC层帧格式如图
1.4所示。
图 43.1.4 以太网帧格式
以太网传输数据时按照上面的顺序从头到尾依次被发送和接收,我们下面进一步解释各个
区域。
前导码(Preamble):为了实现底层数据的正确阐述,物理层使用7个字节同步码(0和1
交替(55-55-55-55-55-55-55))实现数据的同步。
帧起始界定符(SFD,Start Frame Delimiter):使用1个字节的SFD(固定值为0xd5)来
表示一帧的开始,即后面紧跟着传输的就是以太网的帧头。
目的MAC地址:即接收端物理MAC地址,占用6个字节。MAC地址从应用上可分为单播地址、
组播地址和广播地址。单播地址:第一个字节的最低位为0,比如00-00-00-11-11-11,一般用
于标志唯一的设备;组播地址:第一个字节的最低位为1,比如01-00-00-11-11-11,一般用于
标志同属一组的多个设备;广播地址:所有48bit全为1,即FF-FF-FF-FF-FF-FF,它用于标志
同一网段中的所有设备。
源MAC地址:即发送端物理MAC地址,占用6个字节。
长度/类型:上图中的长度/类型具有两个意义,当这两个字节的值小于1536(十六进制为
0x0600)时,代表该以太网中数据段的长度;如果这两个字节的值大于1536,则表示该以太网
中的数据属于哪个上层协议,例如0x0800代表IP协议(网际协议)、0x0806代表ARP协议(地
址解析协议)等。
数据:以太网中的数据段长度最小46个字节,最大1500个字节。最大值1500称为以太网的
最大传输单元(MTU,Maximum Transmission Unit),之所以限制最大传输单元是因为在多个
计算机的数据帧排队等待传输时,如果某个数据帧太大的话,那么其它数据帧等待的时间就会
加长,导致体验变差,这就像一个十字路口的红绿灯,你可以让绿灯持续亮一小时,但是等红
灯的人一定不愿意的。另外还要考虑网络I/O控制器缓存区资源以及网络最大的承载能力等因
素,因此最大传输单元是由各种综合因素决定的。为了避免增加额外的配置,通常以太网的有
效数据字段小于1500个字节。
帧检验序列(FCS,Frame Check Sequence):为了确保数据的正确传输,在数据的尾部
加入了4个字节的循环冗余校验码(CRC校验)来检测数据是否传输错误。CRC数据校验从以太
网帧头开始即不包含前导码和帧起始界定符。通用的CRC标准有CRC-8、CRC-16、CRC-32、CRC
CCIT,其中在网络通信系统中应用最广泛的是CRC-32标准。
在这里还有一个要注意的地方就是以太网相邻两帧之间的时间间隔,即帧间隙(IFG,
Interpacket Gap)。帧间隙的时间就是网络设备和组件在接收一帧之后,需要短暂的时间来
恢复并为接收下一帧做准备的时间,IFG的最小值是96 bit
time,即在媒介中发送96位原始数
据所需要的时间,在不同媒介中IFG的最小值是不一样的。不管10M/100M/1000M的以太网,两
帧之间最少要有96bit time,IFG的最少间隔时间计算方法如下:
10Mbit/s最小时间为:96*100ns = 9600ns;
100Mbit/s最小时间为:96*10ns = 960ns;
1000Mbit/s最小时间为:96*1ns = 96ns。
接下来我们介绍IP协议以及它和以太网MAC层的关系。在介绍IP协议之前,我们先了解下
TCP(传输控制协议)/IP(网际协议)协议簇。TCP/IP是网络使用中最基本的通信协议,虽然
从名字看上去TCP/IP包括两个协议,TCP和IP,但TCP/IP实际上是一组协议,它包括上百个各
种功能的协议,如:TCP、IP、UDP、ICMP(网际控制报文协议)等。而TCP协议和IP协议是保
证数据完整传输的两个重要的协议,因此TCP/IP协议用来表示Internet协议簇。
TCP/IP协议不仅可以运行在以太网上,也可以运行在FDDI(光纤分布式数据接口)和WLAN
(无线局域网)上。反过来,以太网的高层协议不仅可以是TCP/IP协议,也可以是IPX协议(互
联网分组交换协议)等,只不过以太网+TCP/IP成为IT行业中应用最普遍的技术。下面我们来
熟悉下IP协议。
IP协议
IP协议是TCP/IP协议簇中的核心协议,也是TCP/IP协议的载体,IP协议规定了数据传输时
的基本单元和格式。从前面介绍的图 43.1.3中可以看出,IP协议位于以太网MAC帧格式的数据
段,IP协议内容由IP首部和数据字段组成。所有的TCP、UDP及ICMP数据都以IP数据报格式传输,
IP数据包格式如图 43.1.5所示。
图 43.1.5 IP数据报格式
前20个字节和紧跟其后的可选字段是IP数据报的首部,前20个字节是固定的,后面可选字
段是可有可无的,首部的每一行以32位(4个字节)为单位。
版本:4位IP版本号(Version),这个值设置为二进制的0100时表示IPv4,设置为0110时
表示IPv6,目前使用比较多的IP协议版本号是4。
首部长度:4位首部长度(IHL,Internet Header Length),表示IP首部一共有多少个32
位(4个字节)。在没有可选字段时,IP首部长度为20个字节,因此首部长度的值为5。
服务类型:8位服务类型(TOS,Type of service),该字段被划分成两个子字段:3位优
先级字段(现在已经基本忽略掉了)和4位TOS字段,最后一位固定为0。服务类型为0时表示一
般服务。
总长度:16位IP数据报总长度(Total Length),包括IP首部和IP数据部分,以字节为单
位。我们利用IP首部长度和IP数据报总长度,就可以知道IP数据报中数据内容的起始位置和长
度。由于该字段长16bit,所以IP数据报最长可达65535字节。尽管理论上可以传输长达65535
字节的IP数据报,但实际上还要考虑网络的最大承载能力等因素。
标识字段:16位标识(Identification)字段,用来标识主机发送的每一份数据报。通常
每发送一份报文它的值就会加1。
标志字段:3位标志(Flags)字段,第1位为保留位;第2位表示禁止分片(1表示不分片
0:允许分片);第3位标识更多分片(除了数据报的最后一个分片外,其它分片都为1)。
片偏移:13位片偏移(Fragment Offset),在接收方进行数据报重组时用来标识分片的
顺序。
生存时间:8位生存时间字段,TTL(Time To Live)域防止丢失的数据包在无休止的传播,
一般被设置为64或者128。
协议:8位协议(Protocol)类型,表示此数据报所携带上层数据使用的协议类型,ICMP
为1,TCP为6,UDP为17。
首部校验和:16位首部校验和(Header Checksum),该字段只校验数据报的首部,不包
含数据部分;校验IP数据报头部是否被破坏、篡改和丢失等。
源IP地址:32位源IP地址(Source Address),即发送端的IP地址,如192.168.1.123。
目的IP地址:32位目的IP地址(Destination Address),即接收端的IP地址,如
192.168.1.102。
可选字段:是数据报中的一个可变长度的可选信息,选项字段以32bit为界,不足时插入
值为0的填充字节,保证IP首部始终是32bit的整数倍。
以上内容是对IP首部格式的详细阐述,还需要补充的内容是IP首部校验和的计算方法,其
计算步骤如下:
1、将16位检验和字段置为0,然后将IP首部按照16位分成多个单元;
2、对各个单元采用反码加法运算(即高位溢出位会加到低位,通常的补码运算是直接丢
掉溢出的高位);
3、此时仍然可能出现进位的情况,将得到的和再次分成高16位和低16位进行累加;
4、最后将得到的和的反码填入校验和字段。
例如,我们使用IP协议发送一个IP数据报总长度为50个字节(有效数据为30个字节)的数
据包,发送端IP地址为192.168.1.123,接收端IP地址为192.168.102,则IP首部数据如下:
图 43.1.6 IP首部数据
按照上述提到的 IP 首部校验和的方法计算 IP 首部校验和,即:
1、 0x4500 + 0x0032 + 0x0000 + 0x4000 + 0x4011 + 0x0000(计算时强制置0) + 0xc0a8 + 0x017b
+ 0xc0a8 + 0x0166 = 0x24974
2、 0x0002 + 0x4974 = 0x4976
3、 0x0000 + 0x4976 = 0x4976(此种情况并未出现进位)
4、 check_sum = ~0x4976(按位取反) = 0xb689
到此为止IP协议内容已经介绍完了,我们从前面介绍的图 43.1.3可以知道,UDP的首部和
数据位于IP协议的数据段。既然已经有IP协议了,为什么还需要UDP协议呢?为什么我们选择
的是UDP还不是传输更可靠的TCP呢?带着这些疑问我们继续往下看。
UDP协议
首先回答为什么还需要UDP协议?事实上数据是可以直接封装在IP协议里而不使用TCP、
UDP或者其它上层协议的。然而在网络传输中同一IP服务器需要提供各种不同的服务,各种不
同的服务类型是使用端口号来区分的,例如用于浏览网页服务的80端口,用于FTP(文件传输
协议)服务的21端口等。TCP和UDP都使用两个字节的端口号,理论上可以表示的范围为0~65535,
足够满足各种不同的服务类型。
然后是为什么不选择传输更可靠的TCP协议,而是UDP协议呢?TCP协议与UDP协议作为传输
层最常用的两种传输协议,这两种协议都是使用IP作为网络层协议进行传输。下面是TCP协议
与UDP协议的区别:
1、TCP协议面向连接,是流传输协议,通过连接发送数据,而UDP协议传输不需要连接,是数据报协议;
2.TCP为可靠传输协议,而UDP为不可靠传输协议。即TCP协议可以保证数据的完整和有序,
而UDP不能保证;
3.UDP由于不需要连接,故传输速度比TCP快,且占用资源比TCP少;
4.应用场合:TCP协议常用在对数据文件完整性较高的一些场景中,如文件传输等。UDP常
用于对通讯速度有较高要求或者传输数据较少时,比如对速度要求较高的视频直播和传输数据
较少的QQ等。
首先可以肯定的告诉大家,使用FPGA实现TCP协议是完全没有问题的,但是,FPGA发展到
现在,却鲜有成功商用的RTL级的TCP协议设计,大部分以太网传输都是基于比较简单的UDP协
议。TCP协议设计之初是根据软件灵活性设计的,如果使用硬件逻辑实现,工程量会十分巨大,
而且功能和性能无法得到保证,因此,TCP协议设计并不适合使用硬件逻辑实现。UDP协议是一
种不可靠传输,发送方只负责数据发送出去,而不管接收方是否正确的接收。在很多场合,是
可以接受这种潜在的不可靠性的,例如视频实时传输显示等。
UDP数据格式如图 43.1.7所示:
图 43.1.7 UDP数据格式
UDP首部共8个字节,同IP首部一样,也是一行以32位(4个字节)为单位。
源端口号:16位发送端端口号,用于区分不同服务的端口,端口号的范围从0到65535。
目的端口号:16位接收端端口号。
UDP长度:16位UDP长度,包含UDP首部长度+数据长度,单位是字节(byte)。
UDP校验和:16位UDP校验和。UDP计算校验和的方法和计算IP数据报首部校验和的方法相
似,但不同的是IP数据报的校验和只检验IP数据报的首部,而UDP校验和包含三个部分:UDP伪
首部,UDP首部和UDP的数据部分。伪首部的数据是从IP数据报头和UDP数据报头获取的,包括源IP地址,目的IP地址,协议类型和UDP长度,其目的是让UDP两次检查数据是否已经正确到达
目的地,只是单纯为了做校验用的。在大多数使用场景中接收端并不检测UDP校验和,因此这
里不做过多介绍。
以太网的帧格式、IP数据报协议以及UDP协议到这里已经全部介绍完了,关于用户数据、
UDP、IP、MAC四个报文的关系如下图所示:
图 43.1.8 以太网包数据格式
用户数据打包在UDP协议中,UDP协议又是基于IP协议之上的,IP协议又是走MAC层发送的,
即从包含关系来说:MAC帧中的数据段为IP数据报,IP报文中的数据段为UDP报文,UDP报文中
的数据段为用户希望传输的数据内容。现在再回过头看图 43.1.8的内容是不是很好理解了呢?
前面介绍的内容全部都是和协议相关的,以太网通信如果只有协议,没有硬件芯片的支持
是不行的,这个硬件芯片就是以太网PHY芯片。PHY是物理接口收发器,它实现物理层IEEE 802.3
标准定义的以太网PHY,包括MII(媒体独立接口)/GMII(千兆媒体独立接口)等。
以太网PHY芯片
PHY在发送数据的时候,接收MAC发过来的数据(对PHY来说,没有帧的概念,都是数据而
不管什么地址,数据还是CRC),把并行数据转化为串行流数据,按照物理层的编码规则把数
据编码转换为模拟信号发送出去,收数据时的流程反之。PHY还提供了和对端设备连接的重要
功能并通过LED灯显示出自己目前的连接状态和工作状态。当我们给网卡接入网线的时候,PHY
芯片不断发出脉冲信号来检测对端是否有设备,它们通过标准的“语言”交流,互相协商并确
定连接速度、双工模式、是否采用流控等。通常情况下,协商的结果是两个设备中能同时支持
的最大速度和最好的双工模式。这个技术被称为Auto Negotiation,即自动协商。以太网MAC
和PHY之间有一个接口,常用的接口有MII、RMII、GMII、RGMII等。
MII(Medium Independent Interface):MII支持10Mbps和100Mbps的操作,数据位宽为4位,在100Mbps传输速率下,时钟频率为25Mhz。
RMII(Reduced MII):RMII是MII的简化版,数据位宽为2位,在100Mbps传输速率下,时
钟频率为50Mhz。
GMII(Gigabit MII):GMII接口向下兼容MII接口,支持10Mbps、100Mbps和1000Mbps的
操作,数据位宽为8位,在1000Mbps传输速率下,时钟频率为125Mhz。
RGMII(Reduced GMII):RGMII是GMII的简化版,数据位宽为4位,在1000Mbps传输速率
下,时钟频率为125Mhz,但是在时钟的上下沿同时采样数据。
在百兆以太网中,常用的接口为MII接口,下图是MAC侧与PHY侧接口的连接。
图 43.1.9 MAC侧与PHY侧接口连接
ETH_RXC:接收数据参考时钟,100Mbps速率下,时钟频率为25MHz;10Mbps速率下,时钟
频率为2.5MHz,RX_CLK由PHY侧提供。
ETH_RXDV:接收数据有效信号,只有在ETH_RXDV为高电平时接收到的数据才有效。
ETH_RXD:四位并行的接收数据线。
ETH_TXC:发送参考时钟,100Mbps速率下,时钟频率为25MHz;10Mbps速率下,时钟频率
为2.5MHz,TX_CLK由PHY侧提供。
ETH_TXEN:发送数据有效信号,只有在ETH_TXEN为高电平时发送的数据才有效。
ETH_RESET:芯片复位信号,低电平有效
ETH_MDC:数据管理时钟(Management Data Clock),该引脚对ETH_MDIO信号提供了一
个同步的时钟。
ETH_MDIO:数据输入/输出管理(Management Data Input/Output),该引脚提供了一
个双向信号用于传递管理信息。
MII发送和接收时序图如下图所示,数据传输时先发送字节的低4位,再发送字节的高4位。
图 43.1.11 MII接收时序图
实验任务
本节实验任务是上位机通过网口调试助手发送数据给FPGA,FPGA通过以太网接口接收数据
并将接收到的数据发送给上位机,完成以太网数据的环回。
硬件设计
我们的开拓者FPGA开发板上有一个RJ45以太网接口,用于连接网线,其原理图如图 43.3.1
所示:
图 43.3.1 RJ45接口原理图
以太网的数据传输离不开以太网PHY(物理层)芯片的支持,物理层定义了数据发送与接
收所需要的电信号、线路状态、时钟基准、数据编码和
电路等,并向数据链路层设备提供标准
接口。我们的开拓者FPGA开发板上使用的PHY芯片为Realtek公司的RTL8201CP,其原理图如图
43.3.2所示:
图 43.3.2 以太网接口原理图
Realtek RTL8201CP是一个快速以太网物理层收发器,它为MAC层提供了可选择的MII(媒体独立接口)或SNI(串行网络接口)接口,实现了全部的10/100M以太网物理层功能。SNI接口仅
支持10Mbps的通信速率,而MII最大支持100Mbps的通信速率,所以本次收发实验采用的是MII
接口。RTL8201CP芯片的参数可以通过MDC/MDIO接口来配置,因为其默认的参数就可以实现MII
接口的自适应10M/100M收发数据,因此可不必对芯片做配置。
本实验中,以太网管脚分配如下表所示,从管脚分配图中可知,表中没有FPGA输入的系统
时钟信号(板载的50Mhz),从上图中的原理图可知,第46、47脚已经接了一个25Mhz的晶振,
ETH_RXC(接收时钟)和ETH_TXC(发送时钟)都是由以太网PHY侧提供的,我们程序设计直接
使用这两个时钟就可以了。
表 43.3.1 以太网通信实验管脚分配
程序设计
通过前面介绍的以太网相关协议和MII接口可知,我们只需要把数据封装成以太网包的格
式通过MII接口传输数据即可。根据实验任务,以太网环回实验应该有一个以太网接收模块和以太网发送模块,因为发送模块需要CRC校验,因此还需要一个以太网发送CRC校验模块;为了
在其它工程中比较方便的调用以太网的程序以提高项目的开发效率,我们把上面三个模块封装
成一个UDP模块。以太网单次会接收到大量数据,因此还需要一个FIFO模块用来缓存数据,尽
管ETH_RXC和ETH_TXC时钟频率相同,但是相位的偏差是不确定的,所以ETH_RXC和ETH_TXC为异
步时钟,因此我们需要使用异步fifo来缓存数据。对于异步时钟域下需要传递的数据量较多的
情况,一般使用异步fifo来同步数据;而对于数据量较少或者数据长时间才会改变一次的情况,
一般使用脉冲信号同步处理的方法来采集数据;以太网发送模块和接收模块会有除有效数据外
的其它少量数据的交互,因此我们还需要一个脉冲信号同步处理模块。由此画出系统总体框架
如图 43.4.1所示。以太网收到的数据缓存到FIFO中,单包数据接收完成后发送FIFO中的数据,
实现以太网通信的环回实验。
以太网通信实验的系统框图如下图所示:
图 43.4.1 以太网通信系统框图
顶层模块的原理图如下图所示:
图 43.4.2 顶层模块原理图
FPGA顶层模块(eth_pc_loop)例化了以下三个模块:UDP模块(udp)、脉冲信号同步处
理模块(pulse_sync_pro)和FIFO缓存数据模块(async_fifo_2048x32b)。
顶层模块(eth_pc_loop):顶层模块完成了对其它三个模块的例化,将UDP的收发数据用
户接口连接到FIFO的读写端口,UDP两个时钟域需要交互数据的接口连接到了脉冲同步处理模
块,从而实现了数据的接收、缓存、发送以及数据的脉冲同步处理。
UDP模块(udp):UDP模块是本实验以太网传输数据的核心代码,其输入输出端口被封装
成用户方便调用的接口,其它工程如果用到了UDP通信,可直接例化此模块。
脉冲信号同步处理模块(pulse_sync_pro):脉冲信号同步处理模块负责将一个时钟域下
的脉冲信号同步到另一个时钟域下的脉冲信号。
FIFO缓存数据模块(async_fifo_2048x32b):缓存数据模块是由Quartus软件自带的FIFO
软核生成的,缓存的大小为2048个32bit,为了能够满足单包数据量较大的情况(尽管通常情
况下,以太网帧有效数据不超过1500个字节),所以FIFO的深度最好设置的大一点,这里把深
度设置为2048,宽度为32位。
其中UDP模块(udp)例化了以太网接收模块(ip_receive)、以太网发送模块(ip_send)
和发送CRC校验模块(crc32_d4),UDP模块(udp)端口及信号连接如图 43.4.3所示:
图 43.4.3 udp模块原理图
由上图可知,以太网发送模块(ip_send)和以太网接收模块(ip_receive)在UDP模块内
数据端口没有信号连接,数据的交互放在了eth_pc_loop模块里面。主要是以太网发送模块和
CRC32校验模块(crc32_d4)数据信号的连接,发送模块端口的eth_tx_data(MII输出数据)
连接到CRC32的输入数据端口,CRC32校验模块将校验结果连接到发送模块。
以太网接收模块(ip_receive):以太网的接收模块较为简单,因为我们不需要对数据做
IP首部校验也不需要做CRC循环冗余校验,只需要判断目的MAC地址与开发板MAC地址、目的IP
地址与开发板IP地址是否一致即可。接收模块的解析顺序是:前导码+帧起始界定符→以太网
帧头→IP首部→UDP首部→UDP数据(有效数据)→接收结束。MII接口数据为4位数据,先把4
位数据转成8位数据会方便解析数据,IP数据报一般以32bit为单位,为了和IP数据报格式保持
一致,所以要把8位数据转成32位数据,因此接收模块实际上是完成了4位数据转32位数据的功
能。
以太网发送模块(ip_send):以太网发送模块和接收模块比较类似,但是多了IP首部校
验和和CRC循环冗余校验的计算。CRC的校验并不是在发送模块完成,而是在CRC校验模块
(crc32_d4)里完成的。发送模块的发送顺序是前导码+帧起始界定符→以太网帧头→IP首部
→UDP首部→UDP数据(有效数据)→CRC校验。输入的有效数据为32位数据,MII接口为4位数
据接口,因此发送模块实际上完成的是32位数据转4位数据的功能。
CRC校验模块(crc32_d4):CRC校验模块是对以太网发送模块的数据(不包括前导码和帧起始界定符)做校验,把校验结果值拼在以太网帧格式的FCS字段,如果CRC校验值计算错误或
者没有的话,那么电脑网卡会直接丢弃该帧导致收不到数据(有些网卡是可以设置不做校验的)。
CRC32校验在FPGA实现的原理是LFSR(Linear Feedback Shift Register,线性反馈移位寄存
器),其思想是各个寄存器储存着上一次CRC32运算的结果,寄存器的输出即为CRC32的值。
以上是对各个模块的划分、信号的连接以及模块的设计思路,下面是具体的源代码。
顶层模块的代码如下:
1
module eth_pc_loop
(
2
input sys_rst_n
, //系统复位信号,低电平有效
3 //以太网接口
4
input eth_rx_clk
, //MII接收数据时钟
5
input eth_rxdv
, //MII输入数据有效信号
6
input eth_rx_data
, //MII输入数据
7
input [3
:0
] eth_tx_clk
, //MII发送数据时钟
8
output eth_tx_en
, //MII输出数据有效信号
9
output [3
:0
] eth_tx_data
, //MII输出数据
10
output eth_rst_n //以太网芯片复位信号,低电平有效
11
);
12
13 //parameter define
14 //开发板MAC地址 00-11-22-33-44-55
15
parameter BOARD_MAC
= 48'h00_11_22_33_44_55
;
16 //开发板IP地址 192.168.1.123
17
parameter BOARD_IP
= {8'd192
,8'd168
,8'd1
,8'd123
};
18 //目的MAC地址 ff_ff_ff_ff_ff_ff
19
parameter DES_MAC
= 48'hff_ff_ff_ff_ff_ff
;
20 //目的IP地址 192.168.1.102
21
parameter DES_IP
= {8'd192
,8'd168
,8'd1
,8'd102
};
22
23 //wire define
24
wire rec_pkt_done
; //以太网单包数据接收完成信号
25
wire rec_en
; //以太网接收的数据使能信号
26
wire [31
:0
] rec_data
; //以太网接收的数据
27
wire [15
:0
] rec_byte_num
; //以太网接收的有效字节数 单位:byte
28
wire tx_done
; //以太网发送完成信号
29
wire tx_req
; //读数据请求信号
30
31
wire tx_start_en
; //以太网开始发送信号
32
wire [31
:0
] tx_data
; //以太网待发送数据
33
34 //*****************************************************
35 //** main code
36 //*****************************************************
37
38 //UDP模块
39 udp //参数例化
40
#(
41
.BOARD_MAC
(BOARD_MAC
),
42
.BOARD_IP
(BOARD_IP
),
43
.DES_MAC
(DES_MAC
),
44
.DES_IP
(DES_IP
)
45
)
46 u_udp
(
47
.eth_rx_clk
(eth_rx_clk
),
48
.rst_n
(sys_rst_n
),
49
.eth_rxdv
(eth_rxdv
),
50
.eth_rx_data
(eth_rx_data
),
51
.eth_tx_clk
(eth_tx_clk
),
52
.tx_start_en
(tx_start_en
),
53
.tx_data
(tx_data
),
54
.tx_byte_num
(rec_byte_num
),
55
.tx_done
(tx_done
),
56
.tx_req
(tx_req
),
57
.rec_pkt_done
(rec_pkt_done
),
58
.rec_en
(rec_en
),
59
.rec_data
(rec_data
),
60
.rec_byte_num
(rec_byte_num
),
61
.eth_tx_en
(eth_tx_en
),
62
.eth_tx_data
(eth_tx_data
),
63
.eth_rst_n
(eth_rst_n
)
64
);
65
66 //脉冲信号同步处理模块
67 pulse_sync_pro u_pulse_sync_pro
(
68
.clk_a
(eth_rx_clk
),
69
.rst_n
(sys_rst_n
),
70
.pulse_a
(rec_pkt_done
),
71
.clk_b
(eth_tx_clk
),
72
.pulse_b
(tx_start_en
)
73
);
74
75 //fifo模块,用于缓存单包数据
76 async_fifo_2048x32b u_fifo_2048x32b
(
77
.aclr
(~sys_rst_n
),
78
.data
(rec_data
), //fifo写数据
79
.rdclk
(eth_tx_clk
),
80
.rdreq
(tx_req
), //fifo读使能
81
.wrclk
(eth_rx_clk
),
82
.wrreq
(rec_en
), //fifo写使能
83
.q
(tx_data
), //fifo读数据
84
.rdempty
(),
85
.wrfull
()
86
);
87
88
endmodule
在代码的第58至第59行中,UDP模块输出的rec_en(数据有效信号)和rec_data(有效数
据)连接到FIFO缓存数据模块的写入端口,从而将以太网接收到的数据缓存至FIFO。在代码的
第56行和第53行中,UDP模块输出的tx_req(读数据请求信号)和输入的tx_data(发送的数据)
连接到FIFO缓存模块的读出端口,从而将FIFO中的数据发送出去。
以太网通信的环回功能是在单包数据接收完成之后才开始发送数据的,所以可以将以太网
接收模块输出的接收完成信号(rec_pkt_done)作为以太网发送模块的开始发送信号(tx_start_en)。我们知道,eth_rx_clk和eth_tx_clk为两个异步时钟,如果接收完成信号
直接作为以太网发送模块的开始发送信号的话,会出现亚稳态,即信号有可能没有被正确采集
到。所以我们添加一个脉冲信号同步处理模块,将接收完成信号连接至脉冲信号同步处理模块
的pulse_a(输入脉冲同步信号),脉冲信号同步处理模块输出的pulse_b(输出脉冲同步信号)
连接至开始发送信号(如代码的第52行、第57行、第70行和第72行所示),有关脉冲信号同步
处理模块是如何完成两个时钟域下的数据同步的,我们在后面会有详细的介绍。
在代码的第60行rec_byte_num(以太网接收的有效字节数)直接连接到了第54行
tx_byte_num(以太网发送的有效字节数),开发板将接收到的有效字节数直接返回给PC,因
此收发数据的有效字节数是一致的。需要注意的是rec_byte_num和tx_byte_num分别是
eth_rx_clk和eth_tx_clk两个异步时钟下的数据,处理这种异步时钟下的数据要格外小心,必
须在数据保持稳定之后采集,否则很容易出现亚稳态,即数据采集错误的情况。以太网发送模
块是在rec_pkt_done信号的上升沿锁存发送字节个数的,接收字节个数在此时一段时间内是保
持不变的,不会出现数据采集错误的情况,因此rec_byte_num和tx_byte_num这两个信号可以
直接连在一起。
需要注意的是,顶层模块中第15至第21行定义了四个参数:开发板MAC地址BOARD_MAC,开
发板IP地址BOARD_IP,目的MAC地址DES_MAC(和PC做环回实验,这里指PC MAC地址),目的IP
地址DES_IP(PC IP地址)。开发板的MAC地址和IP地址是我们随意定义的,只要不和目的MAC
地址和目的IP地址一样就可以,否则会产生地址冲突。目的MAC地址这里写的是公共MAC地址
(48'hff_ff_ff_ff_ff_ff),也可以修改成电脑网口的MAC地址,DES_IP是对应电脑以太网的
IP地址,这里定义的四个参数是向下传递的,需要修改MAC地址或者IP地址时直接在这里修改
即可,而不用在udp模块里面修改。
脉冲信号同步处理模块的代码如下所示:
1
module pulse_sync_pro
(
2
input clk_a
, //输入时钟A
3
input rst_n
, //复位信号
4
input pulse_a
, //输入脉冲A
5
input clk_b
, //输入时钟B
6
output pulse_b //输出脉冲B
7
);
8
9 //reg define
10
reg pulse_inv
; //脉冲信号转换成电平信号
11
reg pulse_inv_d0
; //时钟B下打拍
12
reg pulse_inv_d1
;
13
reg pulse_inv_d2
;
14
15 //*****************************************************
16 //** main code
17 //*****************************************************
18
19
assign pulse_b
= pulse_inv_d1
^ pulse_inv_d2
;
20
21 //输入脉冲转成电平信号,确保时钟B可以采到
22
always @(posedge clk_a
or negedge rst_n
) begin
23
if(rst_n
==1'b0
)
24 pulse_inv
<= 1'b0
;
25
else if(pulse_a
)
26 pulse_inv
<= ~pulse_inv
;
27
end
28
29 //A时钟下电平信号转成时钟B下的脉冲信号
30
always @(posedge clk_b
or negedge rst_n
) begin
31
if(rst_n
==1'b0
) begin
32 pulse_inv_d0
<= 1'b0
;
33 pulse_inv_d1
<= 1'b0
;
34 pulse_inv_d2
<= 1'b0
;
35
end
36
else begin
37 pulse_inv_d0
<= pulse_inv
;
38 pulse_inv_d1
<= pulse_inv_d0
;
39 pulse_inv_d2
<= pulse_inv_d1
;
40
end
41
end
42
43
endmodule
代码中的pulse_a信号是clk_a时钟域下的脉冲信号(之所以称为脉冲信号,是因为pulse_a
高电平持续一个时钟周期),pulse_b信号是clk_b时钟域下的脉冲信号,脉冲信号同步处理模
块完成一个时钟域下的脉冲信号同步到另一时钟域下的脉冲信号。在代码的第22行开始的
always块中,将输入的脉冲信号(pulse_a)转换成电平信号(pulse_inv),转换后的电平信
号在clk_b时钟域下打拍,打拍后的信号进行异或处理后,输出一个clk_b时钟域下的脉冲信号。
图 43.4.4为脉冲同步过程中SignalTap抓取的波形图,pulse_a信号拉高后,pulse_inv信
号取反由低电平变成高电平。clk_b时钟对取反后的信号进行打拍,打拍后的信号进行异或运
算得到持续一个时钟周期的pulse_b信号。
图 43.4.4 脉冲同步SignalTap波形图
UDP模块的代码如下所示:
1
module udp
2
#(
3 //开发板MAC地址 00-11-22-33-44-55
4
parameter BOARD_MAC
= 48'h00_11_22_33_44_55
,
5 //开发板IP地址 192.168.1.123
6
parameter BOARD_IP
= {8'd192
,8'd168
,8'd1
,8'd123
},
7 //目的MAC地址 ff_ff_ff_ff_ff_ff
8
parameter DES_MAC
= 48'hff_ff_ff_ff_ff_ff
,
9 //目的IP地址 192.168.1.102
10
parameter DES_IP
= {8'd192
,8'd168
,8'd1
,8'd102
}
11
)
12
(
13
input eth_rx_clk
, //MII接收数据时钟
14
input rst_n
, //复位信号,低电平有效
15
input eth_rxdv
, //MII输入数据有效信号
16
input [3
:0
] eth_rx_data
, //MII输入数据
17
input eth_tx_clk
, //MII发送数据时钟
18
input tx_start_en
, //以太网开始发送信号
19
input [31
:0
] tx_data
, //以太网待发送数据
20
input [15
:0
] tx_byte_num
, //以太网发送的有效字节数 单位:byte
21
output tx_done
, //以太网发送完成信号
22
output tx_req
, //读数据请求信号
23
output rec_pkt_done
, //以太网单包数据接收完成信号
24
output rec_en
, //以太网接收的数据使能信号
25
output [31
:0
] rec_data
, //以太网接收的数据
26
output [15
:0
] rec_byte_num
, //以太网接收的有效字节数 单位:byte
27
output eth_tx_en
, //MII输出数据有效信号
28
output [3
:0
] eth_tx_data
, //MII输出数据
29
output eth_rst_n //以太网芯片复位信号,低电平有效
30
);
31
32 //wire define
33
wire crc_en
; //CRC开始校验使能
34
wire crc_clr
; //CRC数据复位信号
35
wire [3
:0
] crc_d4
; //输入待校验4位数据
36
37
wire [31
:0
] crc_data
; //CRC校验数据
38
wire [31
:0
] crc_next
; //CRC下次校验完成数据
39
40 //*****************************************************
41 //** main code
42 //*****************************************************
43
44
assign crc_d4
= eth_tx_data
;
45
assign eth_rst_n
= 1'b1
; //复位信号一直拉高
46
47 //以太网接收模块
48 ip_receive
49
#(
50
.BOARD_MAC
(BOARD_MAC
), //参数例化
51
.BOARD_IP
(BOARD_IP
)
52
)
53 u_ip_receive
(
54
.clk
(eth_rx_clk
),
55
.rst_n
(rst_n
),
56
.eth_rxdv
(eth_rxdv
),
57
.eth_rx_data
(eth_rx_data
),
58
.rec_pkt_done
(rec_pkt_done
),
59
.rec_en
(rec_en
),
60
.rec_data
(rec_data
),
61
.rec_byte_num
(rec_byte_num
)
62
);
63
64 //以太网发送模块
65 ip_send
66
#(
67
.BOARD_MAC
(BOARD_MAC
), //参数例化
68
.BOARD_IP
(BOARD_IP
),
69
.DES_MAC
(DES_MAC
),
70
.DES_IP
(DES_IP
)
71
)
72 u_ip_send
(
73
.clk
(eth_tx_clk
),
74
.rst_n
(rst_n
),
75
.tx_start_en
(tx_start_en
),
76
.tx_data
(tx_data
),
77
.tx_byte_num
(tx_byte_num
),
78
.crc_data
(crc_data
),
79
.crc_next
(crc_next
[31
:28
]),
80
.tx_done
(tx_done
),
81
.tx_req
(tx_req
),
82
.eth_tx_en
(eth_tx_en
),
83
.eth_tx_data
(eth_tx_data
),
84
.crc_en
(crc_en
),
85
.crc_clr
(crc_clr
)
86
);
87
88 //以太网发送CRC校验模块
89 crc32_d4 u_crc32_d4
(
90
.clk
(eth_tx_clk
),
91
.rst_n
(rst_n
),
92
.data
(crc_d4
),
93
.crc_en
(crc_en
),
94
.crc_clr
(crc_clr
),
95
.crc_data
(crc_data
),
96
.crc_next
(crc_next
)
97
);
98
99
endmodule