19. 以太网数据回环实验

您所在的位置:网站首页 udp数据包的格式怎么写 19. 以太网数据回环实验

19. 以太网数据回环实验

2024-05-08 23:26| 来源: 网络整理| 查看: 265

19.5.1. 整体说明¶

实验目标已经明确,硬件资源也已经介绍完毕,我们开始程序设计部分的讲解。我们先来对实验工程进行一个整体说明,让读者了解整个实验工程的框架结构。

为了便于读者理解,实验工程的整体框架我们分两部分来说明,第一部分为以太网数据收发器(基于MII接口);第二部分在第一部分的基础上添加MII与RMII转换模块,实现基于RMII接口的以太网数据收发器。之所有这样讲解,一是为了方便读者理解,二是为了读者能够将MII和RMII的相关知识同时掌握。

我们先来第一部分以太网数据收发器的介绍,模块框图,具体见图 69‑14;子功能模块简介,具体见表格 69‑1。

图 69‑14 基于MII接口的UDP顶层模块框图

表格 69‑1 子功能模块功能描述

模块名称

功能描述

ip_receive

以太网数据接收模块

ip_send

以太网数据发送模块

crc_32_d4

CRC校验模块

eth_udp_mii

顶层模块

由上述图表可知,实验工程的第一部分包含4个子功能模块:以太网数据接收模块(ip_receive)、以太网数据发送模块(ip_send)、CRC校验模块(crc_32_d4)和UDP顶层模块(eth_udp_mii)。

以太网数据接收模块(ip_receive),模块功能是接收并处理PC机传入板卡的以太网数据信息;以太网数据发送模块(ip_send),模块功能是发送以太网数据到PC机;CRC校验模块(crc_32_d4),模块功能是对回传至PC机的以太网数据做CRC冗余校验;UDP顶层模块(eth_udp_mii) 为第一部分的顶层模块,内部例化3个子功能模块,连接各自对应信号。

第一部分介绍完毕,开始第二部分基于RMII接口的以太网数据收发器部分的简要说明。模块框图,具体见图 69‑15;子功能模块简介,具体见表格 69‑2。

图 69‑15 实验工程整体框图

表格 69‑2 子功能模块功能描述

模块名称

功能描述

eth_udp_mii

基于MII接口的UDP顶层模块

fifo_2048x32

数据缓存FIFO(IP核)

rmii_to_mii

RMII接口转MII接口模块

mii_to_rmii

MII接口转RMII接口模块

ethernet_udp_rmii

基于RMII接口的UDP顶层模块

由上述图表可知,实验工程的第二部分包含4个子功能模块:UDP顶层模块(eth_udp_mii)、数据缓存FIFO(IP核)、RMII接口转MII接口模块(rmii_to_mii) 、MII接口转RMII接口模块(mii_to_rmii)和顶层模块(ethernet_udp_rmii)。

UDP顶层模块(eth_udp)是第一部分的基于MII接口的以太网数据收发控制器,实现MII接口的以太网数据收发操作,在第二部分作为子功能模块;数据缓存FIFO(IP核),此部分是调用IP核生成的FIFO,缓存以太网有效数据;RMII接口转MII接口模块(rmii_to_mii) 、MII接口转RMII接口模块(mii_to_rmii)实现MII接口与RMII接口的转换;顶层模块(ethernet_udp_rmii)为第二部分的顶层模块,也是整个实验工程的顶层模块,内部例化4个子功能模块,连接各自对应信号。

这一部分我们对实验工程整体做了简要介绍,下面我们将会对实验工程涉及的子功能模块的设计与实现做详细说明。

19.5.1.1. 以太网数据接收模块¶

模块框图

以太网数据接收模块ip_receive,是第一部分以太网数据收发器的子功能模块之一,作用是接收PC端传入板卡的以太网数据信息,检验目的MAC地址、目的IP地址是否与板卡一致,无check_sum和CRC冗余校验,提取有效数据信息,完成4位数据到8位再到32位的转换。模块框图,具体见图 69‑16;输入输出端口简介,具体见表格 69‑3。

图 69‑16 以太网数据接收模块框图

表格 69‑3 输入输出端口简介

信号

位宽

类型

功能描述

sys_clk

1bit

input

模块工作时钟

sys_rst_n

1bit

input

复位信号,低有效

eth_rxdv

1bit

input

以太网数据有效信号

eth_rx_data

4bit

input

输入以太网数据

rec_data_en

1bit

output

数据接收使能信号

rec_data

32bit

output

输出有效以太网数据

rec_end

1bit

output

单包数据接收完成标志信号

rec_data_num

16bit

output

接收有效数据字节数

由上述图表可知,本模块输入输出信号共8路,输入输出各4路。

输入信号中,时钟信号sys_clk,由外部PHY芯片传入(eth_rx_clk),频率25MHz,作为模块工作时钟;复位信号sys_rst_n,低电平有效,由板卡复位按键传入;以太网数据有效信号eth_rxdv,由外部PHY芯片传入,高电平有效; 输入以太网数据eth_rx_data,由外部PHY芯片传入,位宽为4bit,只有当以太网数据有效信号eth_rxdv为有效的高电平时,传入的以太网数据有效。

输出信号中,输出有效以太网数据rec_data,位宽32bit,通过提取并拼接输入的以太网有效数据得到;与其成对出现的是数据接收使能信号rec_data_en,两信号分别作为写数据和写使能,将以太网有效数据暂存到工程顶层的FIFO中;单包数据接收完成标志信号rec_end,单包数据接收完成后保持一个 时钟周期高电平;接收有效数据字节数rec_data_num,表示以太网有效数据的字节个数。

波形图绘制

在模块框图小节,我们已经对以太网数据接收模块的模块功能和输入输出端口做了详细介绍。接下来我们通过波形图的绘制,为读者说明各信号的设计与实现方法,以及模块的功能实现。以太网数据接收模块整体波形图,具体见图 69‑17、图 69‑18、图 69‑19、图 69‑20。

图 69‑17 以太网数据接收模块整体波形图(一)

图 69‑18 以太网数据接收模块整体波形图(二)

图 69‑19 以太网数据接收模块整体波形图(三)

图 69‑20 以太网数据接收模块整体波形图(四)

模块整体波形图已经列出来了,波形图中的各信号波形是如何设计与实现的呢,下面我们会对各波形的设计与实现做详细说明。

第一部分:输入信号

在前文中我们提到,PHY芯片MAC层之间的连接涉及到一个接口协议,常用的接口协议有MII、RMII、SMII、GMII、RGMII。

对于MII、RMII这两种接口协议我们也做了介绍,我们实验中使用的是MII接口协议,MII支持10Mbps和100Mbps的操作,数据位宽为4位,在100Mbps传输速率下,时钟频率为25MHz。

在MII接口协议中,每个时钟周期只传输4位数据, 数据传输时先发送字节的低4位, 再发送字节的高4位,数据和使能信号在时钟的下降沿变化,时钟上升沿可以采到数据稳定状态。MII接口数据接收、发送时序图,具体见图 69‑21、图 69‑22。

图 69‑21 MII接口数据接收时序图

图 69‑22 MII接口数据发送时序图

根据MII接口数据接收、发送时序图,绘制输入信号波形,输入各信号波形如下。

图 69‑23 输入各信号波形图(一)

图 69‑24 输入各信号波形图(二)

第二部分:数据拼接相关信号波形的设计与实现

在MII接口协议中,每个时钟周期只传输4位数据,且字节低4位在前,字节高4位在后,这种传输方式不利于数据的读取与处理,我们需要对传入的数据进行处理,处理为便于读取与检测的单字节数据。

那么如何实现这一转化呢,我们可以使用数据拼接的方式。

首先要对输入的数据使能信号eth_rxdv和数据信号eth_rx_data进行打拍处理,使两信号延后各自源信号一个时钟周期,同步时钟使用系统时钟sys_clk,数据变化沿使用时钟的下降沿,这样能够保证打拍处理是一个完整的时钟周期,以及后续数据的正确拼接。

信号eth_rxdv_reg、eth_rx_data_reg分别由数据使能信号eth_rxdv、数据信号eth_rx_data产生,延后一个时钟周期,相关信号波形如下。

图 69‑25 数据拼接相关信号波形图(一)

图 69‑26 数据拼接相关信号波形图(二)

输入数据在每个完整的时钟周期内出入有效数据4bit,只有两个完整的时钟周期才能传入完整的单字节数据,虽然数据和使能信号完成打拍,但还不能开始进行数据拼接,因为约束条件不足。所以我们需要一个使能信号作为约束条件,每两个时钟周期完成一次数据拼接。

声明数据拼接使能信号data_sw_en,初值为0,当数据使能信号eth_rxdv_reg有效时,每个时钟周期的下降沿对自身取反,这样就会产生一个类似于对系统时钟二分频的使能信号。

在系统时钟的上升沿、数据拼接使能信号为低电平时,进行输入数据的拼接,拼接后数据data = {eth_rx_data, eth_rx_data_reg}。此时时钟信号上升沿采集到数据稳定状态,保证了拼接数据的正确性。

将数据拼接使能信号延后一个时钟周期产生数据使能信号data_en,产生这一信号的作用是作为拼接数据的使能信号,方便拼接数据的读取。

上述各信号信号波形图如下。

图 69‑27 数据拼接相关信号波形图(三)

图 69‑28 数据拼接相关信号波形图(四)

第三部分:状态机及相关信号波形的设计与实现

在第二部分我们已经完成了单字节数据的拼接,拼接后的数据是一个完整的以太网数据包,数据包包括:前导码+帧起始界定符、以太网帧头、IP首部、UDP首部、UDP数据(有效数据)、CRC校验字节。

在以太网数据接收模块中,我们要对输入的目的MAC地址、目的IP地址进行检验,提取有效数据,不进行check_sum和CRC冗余校验。

那么如何进行地址校验与数据提取呢,我们可以使用状态机来实现。

根据以太网数据包中数据类型的不同,我们在模块内定义7个状态:初始状态(IDLE)、数据包头接收状态(PACKET_HEAD)、以太网帧头接收状态(ETH_HEAD)、IP首部接收状态(IP_HEAD)、UDP首部接收状态(UDP_HEAD)、有效数据接收状态(REC_DATA)、结束状态(REC_E ND)。

结合模块定义和模块实现功能,绘制状态机状态转移图。状态状态机状态转移图,具体见图 69‑29。

图 69‑29 状态机状态转移图

由状态机状态转移图可知,我们需要声明两使能信号sw_en、err_en作为约束条件实现状态机的状态跳转。那么这两使能信号是如和设计与产生的呢,下面我们进行详细讲解。

在前文我们也提到了,拼接后的数据包以字节为单位,包含前导码+帧起始界定符、以太网帧头、IP首部、UDP首部、UDP数据(有效数据)、CRC校验字节着几种不同的数据类型,本模块的目的就是核对目的MAC地址、目的IP地址,以及提取有效数据。

为了实现不同类型数据的提取,我们使用状态机,不同状态对应不同不同数据类型,状态机的跳转就对应着不同不同数据间的分界点,那么如何区别不同类型数据呢,我么可以使用字节宽度。以太网数据包中的各类型数据的字节宽度是固定的或包含在数据中的,我们可以利用字节宽度信息作为状态机跳转的约束条件。但有一点还要注意,我 们可以利用字节宽度对不同类型数据进行数据采集,但数据的正确性是未知的,我们还需要结合包内数据对数据正确性进行检测。

系统上电后,模块内状态机一直处于初始状态(IDLE),在系统时钟上升沿,当检测到数据使能信号data_en为高电平、拼接数据为8’h55时,使能信号 sw_en拉高一个时钟周期,状态机跳转到数据包头接收状态(PACKET_HEAD);

在数据包头接收状态内,我们要完成前导码+帧起始界定符(7个8’h55 + 1个8’hd5)的接收与检验。声明字节计数器cnt_byte,计数器初值为0,状态机处于数据包头接收状态时,数据使能信号data_en拉高一次,计数器自加1。当输入拼接数据data为连续的6个8’h55,加上之前的1个8’h5 5,共7个8’h55,前导码通过检验;当计数器cnt_byte计数值为6,拼接数据data为8’hd5,帧起始界定符通过检验,计数器归零,使能信号 sw_en拉高一个时钟周期,状态机跳转到以太网帧头接收状态(ETH_HEAD)。若其中任何一个字节与规定的前导码+帧起始界定符(7个8’h55 + 1个8’hd5)不同,错误使能信号err_en拉高一个时钟周期,计数器归0,状态机跳回初始状态;

状态机跳转到以太网帧头接收状态(ETH_HEAD),在此状态内我们要完成目的MAC地址的检验,此状态内会接收14字节数据,包括目的MAC地址(6字节)、源MAC地址(6字节)、长度/类型字节(2字节)。

字节计数器cnt_byte会在本状态完成0-13,共14个字节的计数,0-5字节就是我们要校验的目的MAC地址。

声明目的MAC地址寄存器des_mac,位宽为6个字节,当字节计数器在0-5计数范围时,对输入拼接数据data进行寄存。声明标志信号mac_flag,当寄存器des_mac寄存的MAC地址与板卡MAC地址相同时,mac_flag信号保持高电平,否者为低电平。

字节计数器计数到13时,表示本状态14个字节接收完毕,此时若标志信号mac_flag信号为高电平,表示目的MAC地址正确,使能信号sw_en拉高一个时钟周期,状态机跳转到IP首部接收状态(IP_HEAD);标志信号mac_flag为低电平,目的MAC地址错误,错误使能信号err_en拉高一个时钟周期 ,状态机跳回到初始状态(IDLE)。

上述三状态各信号波形如下:

图 69‑30 状态机相关信号波形图(一)

目的MAC地址校验通过后,状态机跳转到IP首部接收状态,在此状态我们需要接收并校验目的IP地址。

IP首部所包含的字节数是不固定的,但在IP首部第一字节的低4位数据会告诉我们IP首部所包含的字节数,要注意的是,它的单位是4字节。声明寄存器ip_len寄存IP首部地址字节数,当数据使能信号data_en为高电平、字节计数器计数值为0时,将拼接数据data低4位数据左移2位赋值给ip_len。

IP首部的目的IP地址在首部的第17至20字节,声明目的IP地址寄存器des_ip,当字节计数器16-19计数范围时,对输入拼接数据data进行寄存;声明标志信号ip_flag,当寄存器des_ip寄存的目的IP地址与板卡IP地址相同时,ip_flag信号保持高电平,否者为低电平。

字节计数器计数到(ip_len - 1)时,表示本状态ip_len个字节接收完毕,此时若标志信号ip_flag信号为高电平,表示目的IP地址正确,使能信号sw_en拉高一个时钟周期,状态机跳转到UDP首部接收状态(UDP_HEAD);标志信号ip_flag为低电平,目的IP地址错误,错误使能信号er r_en拉高一个时钟周期,状态机跳回到初始状态(IDLE)。

IP首部接收状态(IP_HEAD)下,各信号波形如下:

图 69‑31 状态机相关信号波形图(二)

目的IP地址校验通过后,状态机跳转到UDP首部接收状态,UDP首部包含8字节数据,分别是源端口、目的端口、数据长度(UDP首部 + 数据长度)、checksum校验字节。在此状态我们不进行端口和checksum的字节校验,只提取数据长度信息,为后面数据接收做准备。

UDP首部的8字节数据中,第5、6字节包含数据数据长度信息,它的单位是字节。声明寄存器udp_len寄存udp首部数据长度信息,长度为2字节,当数据使能信号data_en为高电平、字节计数器cnt_byte计数值为4、5时,将拼接数据data分别赋值给寄存器udp_len的高字节、低字节,对数据长度 信息进行寄存。

完成UDP首部的8字节接收后,即字节计数器cnt_byte计数值为7,数据使能信号data_en为高电平时,使能信号sw_en拉高一个时钟周期,状态机跳转到有效数据接收状态(REC_DATA)。

在有效数据接收状态(REC_DATA),我们需要接收传入的有效数据,那么有效数据的字节长度怎么确定呢。

UDP首部接收状态中,我们声明了寄存器udp_len,它所寄存的数据字节长度信息是UDP首部和有效数据的总字节长度;有效数据的字节长度是要在此基础上减去UDP首部的字节长度。

所以,我们声明寄存器data_len,寄存有效数据字节长度信息,寄存值为udp_len寄存的数据字节长度信息减去UDP首部的字节长度。

同时,声明一个字节计数器cnt_data对传入的有效字节数据进行计数,计数器初值为0,当数据使能信号data_en为高电平时,计数器自加1,当计数到最大值(data_len - 1)时,有效数据接收完毕,计数器归0,使能信号sw_en拉高一个时钟周期,状态机跳转到结束状态(REC_END)。

在结束状态中,模块会接收4字节的CRC校验位,但本模块没有进行CRC冗余校验。当数据使能信号eth_rxdv_reg为无效低电平时,以太网数据接收完毕,状态机跳回到初始状态,等待下一个以太网数据包的传入。各信号波形如下。

图 69‑32 状态机相关信号波形图(三)

图 69‑33 状态机相关信号波形图(四)

图 69‑34 状态机相关信号波形图(五)

第四部分:输出信号

在数据包中完成有效数据提取后,我们需要将提取的有效数据进行输出,但是以太网数据包一般以32bit为单位,为了保持格式一致,我们要把8位数据转成32位数据。

为了实现位宽的转换,声明数据字节计数器cnt_rec_data,对有效字节数据进行计数,初值为0,计数范围0-3;声明输出数据rec_data,位宽为32位,4字节,计数器cnt_rec_data一个循环计数完成一次数据拼接;当计数器计数到最大值3,输出拼接的32位数据rec_data,与输出数据同 步输出的还有输出数据使能信号rec_data_en;数据输出完成后,声明输出信号rec_data_num记录输出数据字节数。上述各信号波形如下图所示。

图 69‑35 输出各信号波形图(一)

图 69‑36 输出各信号波形图(二)

整合各部分信号波形,就能得到模块整体波形图,设计思路仅供参考,读者可按照自己理解进行波形图的设计。

代码编写

以上述绘制波形图为参照,编写模块参考代码。模块参考代码具体见代码清单 69‑1。

代码清单 69‑1 以太网数据接收模块参考代码(ip_receive.v)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335module ip_receive #( parameter BOARD_MAC = 48'hFF_FF_FF_FF_FF_FF , //板卡MAC地址 parameter BOARD_IP = 32'hFF_FF_FF_FF //板卡IP地址 ) ( input wire sys_clk , //时钟信号 input wire sys_rst_n , //复位信号,低电平有效 input wire eth_rxdv , //数据有效信号 input wire [3:0] eth_rx_data , //输入数据 output reg rec_data_en , //数据接收使能信号 output reg [31:0] rec_data , //接收数据 output reg rec_end , //数据包接收完成信号 output reg [15:0] rec_data_num //接收数据字节数 ); //// //\* Parameter and Internal Signal \// //// //parameter define localparam IDLE = 7'b000_0001, //初始状态 PACKET_HEAD = 7'b000_0010, //接收数据包头 ETH_HEAD = 7'b000_0100, //接收以太网首部 IP_HEAD = 7'b000_1000, //接收IP首部 UDP_HEAD = 7'b001_0000, //接收UDP首部 REC_DATA = 7'b010_0000, //接收数据 REC_END = 7'b100_0000; //单包数据传输结束 //wire define wire [15:0] data_len ; //有效数据字节长度 wire ip_flag ; //IP地址正确标志 wire mac_flag ; //MAC地址正确标志 //reg define reg eth_rxdv_reg ; //数据有效信号打拍 reg [3:0] eth_rx_data_reg ; //输入数据打拍 reg data_sw_en ; //数据拼接使能信号 reg data_en ; //拼接后的数据使能信号 reg [7:0] data ; //拼接后的数据 reg [6:0] state ; //状态机状态变量 reg sw_en ; //状态跳转标志信号 reg err_en ; //数据读取错误信号 reg [4:0] cnt_byte ; //字节计数器 reg [47:0] des_mac ; //目的MAC地址,本模块中表示开发板MAC地址 reg [31:0] des_ip ; //目的IP地址,本模块中表示开发板IP地址 reg [5:0] ip_len ; //IP首部字节长度 reg [15:0] udp_len ; //UDP部分字节长度 reg [15:0] cnt_data ; //接收数据字节计数器 reg [1:0] cnt_rec_data ; //数据计数器,单位4字节 //// //\* Main Code \// //// //eth_rxdv_reg:数据有效信号打拍 always@(negedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) eth_rxdv_reg = 16'd18) ? data_len : 16'd18; //state:状态机状态变量 always @(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) state = (send_data_len - 16'd1))) sw_en


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3