[米联客-安路飞龙DR1-FPSOC] UDP通信篇连载-08 仿真验证
软件版本:Anlogic -TD5.9.1-DR1_ES1.1
操作系统:WIN10 64bit
硬件平台:适用安路(Anlogic)FPGA
实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板
板卡获取平台:https://milianke.tmall.com/
登录"米联客"FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
4 仿真验证
仿真代码的顶层如下:
1 `timescale 1ns / 1ps 2 module sim_top; 3 reg I_reset; 4 reg I_clk; 5 wire b_r_udp_valid; 6 wire [7 :0] b_r_udp_data; 7 wire [15:0] b_r_udp_data_len; 8 wire [15:0] b_r_udp_src_port; 9 wire O_a_ip_rx_error; 10 wire O_a_mac_rx_error; 11 wire O_b_ip_rx_error; 12 wire O_b_mac_rx_error; 13 test_udp_loop test_udp_loop_u( 14 .I_reset (I_reset), 15 .I_clk (I_clk), 16 17 .b_r_udp_valid (b_r_udp_valid), 18 .b_r_udp_data (b_r_udp_data), 19 .b_r_udp_data_len (b_r_udp_data_len), 20 .b_r_udp_src_port (b_r_udp_src_port), 21 22 .O_a_ip_rx_error (O_a_ip_rx_error), 23 .O_a_mac_rx_error (O_a_mac_rx_error), 24 .O_b_ip_rx_error (O_b_ip_rx_error), 25 .O_b_mac_rx_error (O_b_mac_rx_error) 26 ); 27 initial begin 28 I_clk = 0; 29 I_reset = 1; 30 #2000; 31 I_reset = 0; 32 end 33 always #4 I_clk <= ~I_clk; 34 endmodule
4.1 数据通信仿真
例化两个UDP协议栈模块,分别为主机A和主机B,主机A将用户端输入的数据打包,通过GMII接口发送给主机B,主机B将数据解包后输出至用户端,观察各层的信号变化。
4.1.1 仿真代码编写
初始状态机为WAIT_UDP_RDY,等待主机A的O_W_udp_busy信号为低时,用户端将I_W_udp_req信号拉高,状态机进入WAIT_UDP_ACK状态。待主机A将O_W_udp_busy拉高时,用户端将I_W_udp_req拉低,状态机跳转至SEND_DATA状态,计数器开始计数,发送的数据为计数器的低8位。当数据都发送完毕时,状态机跳回WAIT_UDP_RDY状态,等待下一次数据的发送。仿真代码如下:
1 `timescale 1ns / 1ps 2 3 module test_udp_loop( 4 input wire I_reset, 5 input wire I_clk, 6 7 output wire b_r_udp_valid, 8 output wire [7 :0] b_r_udp_data, 9 output wire [15:0] b_r_udp_data_len, 10 output wire [15:0] b_r_udp_src_port, 11 12 output wire O_a_ip_rx_error, 13 output wire O_a_mac_rx_error, 14 output wire O_b_ip_rx_error, 15 output wire O_b_mac_rx_error 16 ); 17 18 19 wire a_w_udp_rdy; 20 reg a_w_udp_req; 21 reg a_w_udp_valid; 22 wire [7:0] a_w_udp_data; 23 reg [15:0] a_w_udp_len; 24 reg a_w_udp_data_read; 25 26 wire b_w_udp_rdy; 27 reg b_w_udp_req; 28 reg b_w_udp_valid; 29 wire [7:0] b_w_udp_data; 30 reg [15:0] b_w_udp_len; 31 reg b_w_udp_data_read; 32 33 wire a_r_udp_valid; 34 wire [7 :0] a_r_udp_data; 35 wire [15:0] a_r_udp_data_len; 36 wire [15:0] a_r_udp_src_port; 37 38 reg [9 :0] test_data; 39 reg [1 :0] STATE; 40 41 parameter WAIT_UDP_READY = 0; 42 parameter WAIT_UDP_ACK = 1; 43 parameter SEND_DATA = 2; 44 45 always@(posedge I_clk or posedge I_reset)begin 46 if(I_reset) begin 47 a_w_udp_req <= 1'b0; 48 a_w_udp_valid <= 1'b0; 49 a_w_udp_len <= 16'd0; 50 test_data <= 10'd0; 51 STATE <= WAIT_UDP_READY; 52 53 end 54 else begin 55 case(STATE) 56 WAIT_UDP_READY:begin 57 if(~a_w_udp_rdy) begin 58 a_w_udp_req <= 1'b1; 59 STATE <= WAIT_UDP_ACK; 60 end 61 else begin 62 a_w_udp_req <= 1'b0; 63 STATE <= WAIT_UDP_READY; 64 end 65 end 66 WAIT_UDP_ACK:begin 67 if(a_w_udp_rdy) begin 68 a_w_udp_len <= 16'd768; 69 a_w_udp_valid <= 1'b1; 70 a_w_udp_req <= 1'b0; 71 STATE <= SEND_DATA; 72 end 73 else 74 STATE <= WAIT_UDP_ACK; 75 end 76 SEND_DATA:begin 77 if(test_data == 10'd767) begin 78 a_w_udp_valid <= 1'b0; 79 a_w_udp_len <= 16'd0; 80 test_data <= 0; 81 STATE <= WAIT_UDP_READY; 82 end 83 else begin 84 a_w_udp_valid <= 1'b1; 85 test_data <= test_data + 1'b1; 86 STATE <= SEND_DATA; 87 end 88 end 89 endcase 90 end 91 end 92 93 //以下实现A发送,B接收的仿真测试 94 wire [7:0] a_gmii_tdata; 95 wire a_gmii_tvalid; 96 wire [7:0] b_gmii_tdata; 97 wire b_gmii_tvalid; 98 99 assign a_w_udp_data = test_data[7:0]; 100 101 udp_stack # 102 ( 103 .CRC_GEN_EN (1'b1), 104 .INTER_FRAME_GAP (4'd12) 105 ) 106 A_udp_stack 107 ( 108 .I_uclk (I_clk), 109 .I_reset (I_reset), 110 111 .I_mac_local_addr (48'h0123456789a2),//本地MAC地址 112 .I_udp_local_port (16'd6002 ), //本地端口号 113 .I_ip_local_addr (32'hc0a88902 ), //本地ip地址 114 115 .I_udp_dest_port (16'd6001 ), //目的端口 116 .I_ip_dest_addr (32'hc0a88901 ), //目的IP地址 117 118 .O_W_udp_busy (a_w_udp_rdy), 119 .I_W_udp_req (a_w_udp_req), 120 .I_W_udp_valid (a_w_udp_valid), 121 .I_W_udp_data (a_w_udp_data), 122 .I_W_udp_len (a_w_udp_len), 123 124 .O_R_udp_valid (), 125 .O_R_udp_data (), 126 .O_R_udp_len (), 127 .O_R_udp_src_port (), 128 129 .I_gmii_rclk (I_clk), 130 .I_gmii_rvalid (b_gmii_tvalid), 131 .I_gmii_rdata (b_gmii_tdata), 132 133 .I_gmii_tclk (I_clk), 134 .O_gmii_tvalid (a_gmii_tvalid), 135 .O_gmii_tdata (a_gmii_tdata), 136 .O_ip_rerror (O_a_ip_rx_error), 137 .O_mac_rerror (O_a_mac_rx_error) 138 ); 139 140 udp_stack # 141 ( 142 .CRC_GEN_EN (1'b1), 143 .INTER_FRAME_GAP (4'd12) 144 ) 145 B_udp_stack 146 ( 147 .I_uclk (I_clk), 148 .I_reset (I_reset), 149 150 .I_mac_local_addr (48'h0123456789a1),//本地MAC地址 151 .I_udp_local_port (16'd6001 ), //本地端口号 152 .I_ip_local_addr (32'hc0a88901 ), //本地ip地址 153 154 .I_udp_dest_port (16'd6002 ), //目的端口 155 .I_ip_dest_addr (32'hc0a88902 ), //目的IP地址 156 157 .O_W_udp_busy (), 158 .I_W_udp_req (0), 159 .I_W_udp_valid (0), 160 .I_W_udp_data (0), 161 .I_W_udp_len (0), 162 163 .O_R_udp_valid (b_r_udp_valid), 164 .O_R_udp_data (b_r_udp_data), 165 .O_R_udp_len (b_r_udp_data_len), 166 .O_R_udp_src_port (b_r_udp_src_port), 167 168 .I_gmii_rclk (I_clk), 169 .I_gmii_rvalid (a_gmii_tvalid), 170 .I_gmii_rdata (a_gmii_tdata), 171 172 .I_gmii_tclk (I_clk), 173 .O_gmii_tvalid (b_gmii_tvalid), 174 .O_gmii_tdata (b_gmii_tdata), 175 .O_ip_rerror (O_b_ip_rx_error), 176 .O_mac_rerror (O_b_mac_rx_error) 177 ); 178 179 endmodule
4.1.2 ARP请求仿真结果
主机A将数据打包发送给主机B时,由于主机A的cache中查询不到主机B的MAC地址,主机A会先发送一个ARP请求包给主机B。
当数据帧发送至ip_arp_tx模块时,该模块向ARP层的cache发送一个MAC地址查询请求I_mac_cache_ren和需要查询的IP地址I_mac_cache_rip_addr,查询结束后O_mac_cache_rdone拉高,返回的MAC地址为48'h0,说明未查询到MAC地址,ip_arp_tx模块将I_arp_treq_en拉高,准备发送ARP广播。arp_tx模块接收到I_arp_treq_en高电平时,发送ARP请求O_arp_req,ip_arp_tx模块将I_arp_busy拉高,表示握手成功,arp_tx模块开始组ARP广播包。下图为GMII接口发送ARP广播仿真波形图。
最后信号经过MAC层组帧后通过GMII接口发送至主机B。
4.1.3 ARP应答仿真结果
主机B接收到ARP请求包后,会将ARP包中的MAC地址解析,并将自己的MAC地址通过ARP应答包发送给主机A。主机A收到ARP应答包后,将主机B的MAC地址存入cache中。下图为解包后发送至arp_rx模块的数据。
MAC层解析数据的类型为ARP包,将该包数据通过ip_arp_rx模块发送给arp_rx模块,解析出IP地址、MAC地址,并将该包识别为ARP请求包。arp_rx发送arp_req_valid,信号给arp_tx模块,请求发送ARP应答包,同时将ARP请求包中的主机A的MAC地址写入cache中。arp_tx模块接收到应答请求并寄存,向ip_arp_tx模块发送请求,ip_arp_tx模块将busy信号拉高以表示握手成功。busy信号为高后,arp_tx模块组ARP应答包,将本地MAC地址等信息发送至下层协议。
ARP层发出的应答数据包经MAC层组帧,通过GMII接口发送至主机A。下图为主机A GMII接口接收到的ARP请求包数据。
主机A的MAC层识别数据为ARP数据包类型,发送至arp_rx模块中,arp_rx模块解析对方发送的IP地址和MAC地址,将MAC地址存入cache中。至此,地址解析完成,两机之间可以完成正常数据通信。
4.1.4 UDP发送仿真结果
udp_tx模块与下层模块握手成功后,将数据通过移位寄存器延迟8个周期,组成UDP包,发送至ip_tx模块,组成IP数据包,然后数据经ip_arp_tx模块仲裁,发送至mac_tx模块,组成MAC包后通过GMII接口发送。
下图为UDP层组UDP包的仿真波形图
下图为IP层组IP包的仿真波形图
由于数据都写入了data_fifo,当数据有效数据全部写入完成后,才会将数据读出,所以图中组的MAC包是上一个数据包正在发送的数据包IP头部的标识为16'h0000,而从上层传入的IP数据包包头标识为16'h0001。
下图为MAC层组MAC包的仿真波形图
4.1.5 UDP接收仿真结果
主机B的GMII接口接收到主机A发送的数据包,会将数据先存入FIFO做跨时钟域,当数据全部接收完成后,才会将数据读出,图中正在接收的数据包IP头部标识为16'h0003,发送至上层协议的数据包标识为16'h0002,即发送至IP层的数据包为上一个帧。下图为过滤MAC头部的仿真波形图
通过计数器去掉IP头部和UDP头部,最终得到有效数据,和发送的数据一致,如图所示。
4.2 PAUSE流控仿真
发送数据的内容和速率与上一节的仿真中一致,每隔一定的时间,在用户端组一个PAUSE帧发送至主机A,暂停时间设置为16'h007F,即主机A实际暂停时间为8128个时钟周期(7F(h)<< 6 = 8128(d))。
4.2.1 仿真代码编写
通过状态机和计数器,控制PAUSE帧发送到主机A的时序和间隔,同时不断地向主机A的用户端写数据。仿真代码如下:
1 always@(posedge I_clk or posedge I_reset)begin 2 if(I_reset) begin 3 pause_data <= 8'd0; 4 pause_vld <= 1'b0; 5 cnt1 <= 'd0; 6 data_cnt <= 'd0; 7 STATE2 <= WAIT_PAUSE_RDY; 8 end 9 else begin 10 case(STATE2) 11 WAIT_PAUSE_RDY:begin 12 data_cnt <= 'd0; 13 if(cnt1 == 16'd5000) begin 14 cnt1 <= 'd0; 15 STATE2 <= SEND_PAUSE_DATA; 16 end 17 else begin 18 cnt1 <= cnt1 + 1'b1; 19 STATE2 <= WAIT_PAUSE_RDY; 20 end 21 end 22 SEND_PAUSE_DATA:begin 23 data_cnt <= data_cnt + 1'b1; 24 case(data_cnt) 25 0 :begin pause_data <= 8'h55; pause_vld <= 1'b1;end 26 1 :begin pause_data <= 8'h55;end 27 2 :begin pause_data <= 8'h55;end 28 3 :begin pause_data <= 8'h55;end 29 4 :begin pause_data <= 8'h55;end 30 5 :begin pause_data <= 8'h55;end 31 6 :begin pause_data <= 8'h55;end 32 7 :begin pause_data <= 8'hd5;end 33 34 8 :begin pause_data <= 8'h01;end 35 9 :begin pause_data <= 8'h80;end 36 10 :begin pause_data <= 8'hc2;end 37 11 :begin pause_data <= 8'h00;end 38 12 :begin pause_data <= 8'h00;end 39 13 :begin pause_data <= 8'h01;end//pause广播地址 40 14 :begin pause_data <= 8'h01;end 41 15 :begin pause_data <= 8'h23;end 42 16 :begin pause_data <= 8'h45;end 43 17 :begin pause_data <= 8'h67;end 44 18 :begin pause_data <= 8'h89;end 45 19 :begin pause_data <= 8'ha1;end 46 47 20 :begin pause_data <= 8'h88;end 48 21 :begin pause_data <= 8'h08;end 49 50 22 :begin pause_data <= 8'h00;end 51 23 :begin pause_data <= 8'h01;end 52 53 24 :begin pause_data <= 8'h00;end 54 25 :begin pause_data <= 8'h7f;end 55 56 26 :begin pause_data <= 8'hff;end 57 27 :begin pause_data <= 8'hff;end 58 36 :begin pause_data <= 8'h15;end 59 37 :begin pause_data <= 8'hbf;end 60 38 :begin pause_data <= 8'h4b;end 61 39 :begin pause_data <= 8'h6c;end 62 40 :begin 63 pause_data <= 8'h00; 64 pause_vld <= 1'b0; 65 STATE2 <= WAIT_PAUSE_RDY; 66 end 67 default:pause_data <= 8'h00; 68 endcase 69 end 70 default: begin 71 pause_data <= 8'd0; 72 pause_vld <= 1'b0; 73 cnt1 <= 'd0; 74 data_cnt <= 'd0; 75 STATE2 <= WAIT_PAUSE_RDY; 76 end 77 endcase 78 end 79 end
4.2.2 PAUSE流控仿真结果
主机A接收到PAUSE帧后,会将PAUSE帧里的信息送入mac_tx_frame_ctrl模块中进行解析,得到暂停时间,并发送给至mac_tx模块,如图所示。
当mac_tx模块中的读状态机进入帧间隔状态时,通过计数器将pause_flag拉高一定的时间。检测到下一帧发送方的MAC地址与PAUSE帧发送方的MAC地址相同,均为48'h0123456789a1,进入暂停状态。
仿真波形图如图,由下图可知,pause_flag拉高的时间为65024ns,即为8128个时钟周期。
4.3 ICMP层仿真
向主机A发送一个ping请求包,主机A成功接收到ping请求包后,发送一个ping应答包,其中的额外数据与请求包相同。
4.3.1 仿真代码编写
组一个ping请求包给主机A,其中IP头部的协议类型为8'h01,ICMP头部的type字段为8'h08,code字段为8'h00,表示主动请求。仿真代码如下:
4.3.2 ICMP回显应答仿真结果
如图所示,ip_rx模块接收到ICMP数据报文会将标识符、序列号、校验和等信息拆解出来,并将有效数据存入FIFO。一帧数据接收完成后,将发送ping应答请求信号给ip_tx模块。
请求信号icmp_req_en拉高后,icmp_pkg_tx模块中的icmp_pkg_req信号也拉高,并发送给ip_tx模块,等待ip_tx模块将icmp_pkg_req拉高,表示握手成功,开始发送icmp回显应答数据。ping应答包的type字段为8'h00,code字段为8'h00,表示回显应答,其有效数据和接收到的ping请求包额外数据保持一致。
本文来米联客(milianke),作者:米联客(milianke),转载请注明原文链接:https://www.cnblogs.com/milianke/p/18351351