[X]关闭

[米联客-安路飞龙DR1-FPSOC] UDP通信篇连载-08 仿真验证

文档创建者:FPGA课程
浏览次数:272
最后更新:2024-08-10
文档课程分类-安路-DR1
安路-DR1: FPSOC-DR1-FPGA部分 » 2_FPGA实验篇(仅旗舰) » 4-FPGA UDP通信
软件版本: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. .b_r_udp_valid      (b_r_udp_valid),
  17. .b_r_udp_data       (b_r_udp_data),
  18. .b_r_udp_data_len   (b_r_udp_data_len),
  19. .b_r_udp_src_port   (b_r_udp_src_port),

  20. .O_a_ip_rx_error    (O_a_ip_rx_error),
  21. .O_a_mac_rx_error   (O_a_mac_rx_error),
  22. .O_b_ip_rx_error    (O_b_ip_rx_error),
  23. .O_b_mac_rx_error   (O_b_mac_rx_error)
  24. );
  25. initial begin
  26.     I_clk = 0;
  27.     I_reset = 1;
  28.     #2000;
  29.     I_reset = 0;
  30. end
  31. always #4 I_clk <= ~I_clk;     
  32. 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. module test_udp_loop(
  3. input  wire        I_reset,
  4. input  wire        I_clk,

  5. output wire        b_r_udp_valid,
  6. output wire [7 :0] b_r_udp_data,
  7. output wire [15:0] b_r_udp_data_len,
  8. output wire [15:0] b_r_udp_src_port,

  9. output wire        O_a_ip_rx_error,
  10. output wire        O_a_mac_rx_error,
  11. output wire        O_b_ip_rx_error,
  12. output wire        O_b_mac_rx_error
  13. );


  14. wire        a_w_udp_rdy;
  15. reg         a_w_udp_req;
  16. reg         a_w_udp_valid;
  17. wire [7:0]  a_w_udp_data;
  18. reg  [15:0] a_w_udp_len;
  19. reg         a_w_udp_data_read;

  20. wire        b_w_udp_rdy;
  21. reg         b_w_udp_req;
  22. reg         b_w_udp_valid;
  23. wire [7:0]  b_w_udp_data;
  24. reg  [15:0] b_w_udp_len;
  25. reg         b_w_udp_data_read;

  26. wire        a_r_udp_valid;
  27. wire [7 :0] a_r_udp_data;
  28. wire [15:0] a_r_udp_data_len;
  29. wire [15:0] a_r_udp_src_port;

  30. reg  [9 :0] test_data;
  31. reg  [1 :0] STATE;

  32. parameter  WAIT_UDP_READY = 0;
  33. parameter  WAIT_UDP_ACK = 1;
  34. parameter  SEND_DATA = 2;

  35. always@(posedge I_clk or posedge I_reset)begin
  36.    if(I_reset) begin
  37.         a_w_udp_req         <= 1'b0;
  38.         a_w_udp_valid       <= 1'b0;
  39.         a_w_udp_len         <= 16'd0;
  40.         test_data           <= 10'd0;
  41.         STATE               <= WAIT_UDP_READY;
  42.         
  43.     end
  44.     else begin  
  45.         case(STATE)
  46.             WAIT_UDP_READY:begin
  47.                 if(~a_w_udp_rdy) begin
  48.                     a_w_udp_req             <= 1'b1;
  49.                     STATE                   <= WAIT_UDP_ACK;
  50.                 end
  51.                 else begin
  52.                     a_w_udp_req             <= 1'b0;
  53.                     STATE                   <= WAIT_UDP_READY;
  54.                 end
  55.             end
  56.             WAIT_UDP_ACK:begin
  57.                 if(a_w_udp_rdy) begin
  58.                     a_w_udp_len             <= 16'd768;
  59.                     a_w_udp_valid           <= 1'b1;
  60.                     a_w_udp_req             <= 1'b0;               
  61.                     STATE                   <= SEND_DATA;
  62.                 end
  63.                 else
  64.                     STATE <= WAIT_UDP_ACK;
  65.             end
  66.             SEND_DATA:begin
  67.                 if(test_data == 10'd767) begin
  68.                     a_w_udp_valid           <= 1'b0;
  69.                     a_w_udp_len             <= 16'd0;
  70.                     test_data               <= 0;
  71.                     STATE                   <= WAIT_UDP_READY;
  72.                 end
  73.                 else begin
  74.                     a_w_udp_valid           <= 1'b1;
  75.                     test_data               <= test_data + 1'b1;
  76.                     STATE                   <= SEND_DATA;
  77.                 end
  78.             end
  79.         endcase
  80.     end
  81. end

  82. //以下实现A发送,B接收的仿真测试
  83. wire [7:0]  a_gmii_tdata;
  84. wire        a_gmii_tvalid;
  85. wire [7:0]  b_gmii_tdata;
  86. wire        b_gmii_tvalid;

  87. assign  a_w_udp_data = test_data[7:0];

  88. udp_stack #
  89. (
  90. .CRC_GEN_EN             (1'b1),
  91. .INTER_FRAME_GAP        (4'd12)
  92. )
  93. A_udp_stack
  94. (
  95. .I_uclk                 (I_clk),
  96. .I_reset                (I_reset),

  97. .I_mac_local_addr       (48'h0123456789a2),//本地MAC地址   
  98. .I_udp_local_port       (16'd6002       ), //本地端口号
  99. .I_ip_local_addr        (32'hc0a88902   ), //本地ip地址

  100. .I_udp_dest_port        (16'd6001       ), //目的端口
  101. .I_ip_dest_addr         (32'hc0a88901   ), //目的IP地址

  102. .O_W_udp_busy           (a_w_udp_rdy),
  103. .I_W_udp_req            (a_w_udp_req),
  104. .I_W_udp_valid          (a_w_udp_valid),
  105. .I_W_udp_data           (a_w_udp_data),
  106. .I_W_udp_len            (a_w_udp_len),

  107. .O_R_udp_valid          (),
  108. .O_R_udp_data           (),
  109. .O_R_udp_len            (),
  110. .O_R_udp_src_port       (),

  111. .I_gmii_rclk            (I_clk),
  112. .I_gmii_rvalid          (b_gmii_tvalid),
  113. .I_gmii_rdata           (b_gmii_tdata),

  114. .I_gmii_tclk            (I_clk),
  115. .O_gmii_tvalid          (a_gmii_tvalid),
  116. .O_gmii_tdata           (a_gmii_tdata),
  117. .O_ip_rerror                (O_a_ip_rx_error),
  118. .O_mac_rerror           (O_a_mac_rx_error)
  119. );

  120. udp_stack #
  121. (
  122. .CRC_GEN_EN             (1'b1),
  123. .INTER_FRAME_GAP        (4'd12)
  124. )
  125. B_udp_stack
  126. (
  127. .I_uclk                 (I_clk),
  128. .I_reset                (I_reset),

  129. .I_mac_local_addr       (48'h0123456789a1),//本地MAC地址   
  130. .I_udp_local_port       (16'd6001       ), //本地端口号
  131. .I_ip_local_addr        (32'hc0a88901   ), //本地ip地址

  132. .I_udp_dest_port        (16'd6002   ), //目的端口
  133. .I_ip_dest_addr         (32'hc0a88902   ), //目的IP地址

  134. .O_W_udp_busy           (),
  135. .I_W_udp_req            (0),
  136. .I_W_udp_valid          (0),
  137. .I_W_udp_data           (0),
  138. .I_W_udp_len            (0),

  139. .O_R_udp_valid          (b_r_udp_valid),
  140. .O_R_udp_data           (b_r_udp_data),
  141. .O_R_udp_len            (b_r_udp_data_len),
  142. .O_R_udp_src_port       (b_r_udp_src_port),

  143. .I_gmii_rclk            (I_clk),
  144. .I_gmii_rvalid          (a_gmii_tvalid),
  145. .I_gmii_rdata           (a_gmii_tdata),

  146. .I_gmii_tclk            (I_clk),
  147. .O_gmii_tvalid          (b_gmii_tvalid),
  148. .O_gmii_tdata           (b_gmii_tdata),
  149. .O_ip_rerror                (O_b_ip_rx_error),
  150. .O_mac_rerror           (O_b_mac_rx_error)
  151. );

  152. endmodule
复制代码
4.1.2 ARP请求仿真结果
主机A将数据打包发送给主机B时,由于主机A的cache中查询不到主机B的MAC地址,主机A会先发送一个ARP请求包给主机B。
013fbafa50b548659f957a736d137b6e.jpg
当数据帧发送至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广播仿真波形图。
4452115c114e41a99b634f0ad63ffc86.jpg
最后信号经过MAC层组帧后通过GMII接口发送至主机B。
4.1.3 ARP应答仿真结果
主机B接收到ARP请求包后,会将ARP包中的MAC地址解析,并将自己的MAC地址通过ARP应答包发送给主机A。主机A收到ARP应答包后,将主机B的MAC地址存入cache中。下图为解包后发送至arp_rx模块的数据。
de393489984542afa9390225a9811c59.jpg
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请求包数据。
0ad75dd141724074ba9675a705e89b21.jpg
主机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包的仿真波形图
79aeada386ed4fd3920bcdda97611677.jpg
下图为IP层组IP包的仿真波形图
1d463feef55d47e0b4d111b33d772d4a.jpg
由于数据都写入了data_fifo,当数据有效数据全部写入完成后,才会将数据读出,所以图中组的MAC包是上一个数据包正在发送的数据包IP头部的标识为16’h0000,而从上层传入的IP数据包包头标识为16’h0001。
下图为MAC层组MAC包的仿真波形图
b825366548884b50b785a95b9a802fc8.jpg
4.1.5 UDP接收仿真结果
主机B的GMII接口接收到主机A发送的数据包,会将数据先存入FIFO做跨时钟域,当数据全部接收完成后,才会将数据读出,图中正在接收的数据包IP头部标识为16’h0003,发送至上层协议的数据包标识为16’h0002,即发送至IP层的数据包为上一个帧。下图为过滤MAC头部的仿真波形图
6b12b62c975a4f9d925b0ed852cb4d76.jpg
通过计数器去掉IP头部和UDP头部,最终得到有效数据,和发送的数据一致,如图所示。
39f62e4eff454e7f9e74e0581a458d20.jpg
bdecbe355bf842a5a4c473b3af7f77ba.jpg
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.                     8   :begin  pause_data  <=  8'h01;end
  34.                     9   :begin  pause_data  <=  8'h80;end
  35.                     10  :begin  pause_data  <=  8'hc2;end
  36.                     11  :begin  pause_data  <=  8'h00;end
  37.                     12  :begin  pause_data  <=  8'h00;end
  38.                     13  :begin  pause_data  <=  8'h01;end//pause广播地址
  39.                     14  :begin  pause_data  <=  8'h01;end
  40.                     15  :begin  pause_data  <=  8'h23;end
  41.                     16  :begin  pause_data  <=  8'h45;end
  42.                     17  :begin  pause_data  <=  8'h67;end
  43.                     18  :begin  pause_data  <=  8'h89;end
  44.                     19  :begin  pause_data  <=  8'ha1;end

  45.                     20  :begin  pause_data  <=  8'h88;end
  46.                     21  :begin  pause_data  <=  8'h08;end

  47.                     22  :begin  pause_data  <=  8'h00;end
  48.                     23  :begin  pause_data  <=  8'h01;end

  49.                     24  :begin  pause_data  <=  8'h00;end
  50.                     25  :begin  pause_data  <=  8'h7f;end

  51.                     26  :begin  pause_data  <=  8'hff;end
  52.                     27  :begin  pause_data  <=  8'hff;end
  53.                     36  :begin  pause_data  <=  8'h15;end
  54.                     37  :begin  pause_data  <=  8'hbf;end
  55.                     38  :begin  pause_data  <=  8'h4b;end
  56.                     39  :begin  pause_data  <=  8'h6c;end
  57.                     40  :begin  
  58.                         pause_data  <=  8'h00;
  59.                         pause_vld   <=  1'b0;
  60.                         STATE2  <=  WAIT_PAUSE_RDY;
  61.                     end
  62.                     default:pause_data  <=  8'h00;
  63.                 endcase
  64.             end
  65.             default: begin
  66.                 pause_data      <=      8'd0;
  67.                 pause_vld       <=      1'b0;
  68.                 cnt1            <=      'd0;
  69.                 data_cnt        <=      'd0;
  70.                 STATE2  <=  WAIT_PAUSE_RDY;
  71.             end
  72.         endcase
  73.     end
  74. end
复制代码
4.2.2 PAUSE流控仿真结果
主机A接收到PAUSE帧后,会将PAUSE帧里的信息送入mac_tx_frame_ctrl模块中进行解析,得到暂停时间,并发送给至mac_tx模块,如图所示。 2bf7092a56454959b72706892aa3da3c.jpg
当mac_tx模块中的读状态机进入帧间隔状态时,通过计数器将pause_flag拉高一定的时间。检测到下一帧发送方的MAC地址与PAUSE帧发送方的MAC地址相同,均为48’h0123456789a1,进入暂停状态。
仿真波形图如图,由下图可知,pause_flag拉高的时间为65024ns,即为8128个时钟周期。
938a548d94544677bd8dedb5a58328b5.jpg
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模块。
e23dffec2e804b31abff714a763e0a3f.jpg
a829caecd8734af3a305153e4f1fe0f9.jpg
请求信号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请求包额外数据保持一致。
5b9dd46e721849d2b9a598160be6c550.jpg

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则