[米联客-安路飞龙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,等待主机AO_W_udp_busy信号为低时,用户端将I_W_udp_req信号拉高,状态机进入WAIT_UDP_ACK状态。待主机AO_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时,由于主机Acache中查询不到主机BMAC地址,主机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_reqip_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应答包后,将主机BMAC地址存入cache中。下图为解包后发送至arp_rx模块的数据。

MAC层解析数据的类型为ARP包,将该包数据通过ip_arp_rx模块发送给arp_rx模块,解析出IP地址、MAC地址,并将该包识别为ARP请求包。arp_rx发送arp_req_valid,信号给arp_tx模块,请求发送ARP应答包,同时将ARP请求包中的主机AMAC地址写入cache中。arp_tx模块接收到应答请求并寄存,向ip_arp_tx模块发送请求,ip_arp_tx模块将busy信号拉高以表示握手成功。busy信号为高后,arp_tx模块组ARP应答包,将本地MAC地址等信息发送至下层协议。

ARP层发出的应答数据包经MAC层组帧,通过GMII接口发送至主机A。下图为主机A GMII接口接收到的ARP请求包数据。

主机AMAC层识别数据为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接收仿真结果

主机BGMII接口接收到主机A发送的数据包,会将数据先存入FIFO做跨时钟域,当数据全部接收完成后,才会将数据读出,图中正在接收的数据包IP头部标识为16'h0003,发送至上层协议的数据包标识为16'h0002,即发送至IP层的数据包为上一个帧。下图为过滤MAC头部的仿真波形图

通过计数器去掉IP头部和UDP头部,最终得到有效数据,和发送的数据一致,如图所示。

4.2 PAUSE流控仿真

发送数据的内容和速率与上一节的仿真中一致,每隔一定的时间,在用户端组一个PAUSE帧发送至主机A,暂停时间设置为16'h007F,即主机A实际暂停时间为8128个时钟周期(7Fh<< 6 = 8128d))。

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'h01ICMP头部的type字段为8'h08code字段为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'h00code字段为8'h00,表示回显应答,其有效数据和接收到的ping请求包额外数据保持一致。

posted @ 2024-08-10 16:45  米联客(milianke)  阅读(49)  评论(0编辑  收藏  举报