軟體版本: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 模擬驗證
模擬程式碼的頂層如下:
`timescale 1ns / 1ps module sim_top; reg I_reset; reg I_clk; wire b_r_udp_valid; wire [7 :0] b_r_udp_data; wire [15:0] b_r_udp_data_len; wire [15:0] b_r_udp_src_port; wire O_a_ip_rx_error; wire O_a_mac_rx_error; wire O_b_ip_rx_error; wire O_b_mac_rx_error; test_udp_loop test_udp_loop_u( .I_reset (I_reset), .I_clk (I_clk),
.b_r_udp_valid (b_r_udp_valid), .b_r_udp_data (b_r_udp_data), .b_r_udp_data_len (b_r_udp_data_len), .b_r_udp_src_port (b_r_udp_src_port),
.O_a_ip_rx_error (O_a_ip_rx_error), .O_a_mac_rx_error (O_a_mac_rx_error), .O_b_ip_rx_error (O_b_ip_rx_error), .O_b_mac_rx_error (O_b_mac_rx_error) ); initial begin I_clk = 0; I_reset = 1; #2000; I_reset = 0; end always #4 I_clk <= ~I_clk; 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狀態,等待下一次資料的傳送。模擬程式碼如下:
`timescale 1ns / 1ps
module test_udp_loop( input wire I_reset, input wire I_clk,
output wire b_r_udp_valid, output wire [7 :0] b_r_udp_data, output wire [15:0] b_r_udp_data_len, output wire [15:0] b_r_udp_src_port,
output wire O_a_ip_rx_error, output wire O_a_mac_rx_error, output wire O_b_ip_rx_error, output wire O_b_mac_rx_error );
wire a_w_udp_rdy; reg a_w_udp_req; reg a_w_udp_valid; wire [7:0] a_w_udp_data; reg [15:0] a_w_udp_len; reg a_w_udp_data_read;
wire b_w_udp_rdy; reg b_w_udp_req; reg b_w_udp_valid; wire [7:0] b_w_udp_data; reg [15:0] b_w_udp_len; reg b_w_udp_data_read;
wire a_r_udp_valid; wire [7 :0] a_r_udp_data; wire [15:0] a_r_udp_data_len; wire [15:0] a_r_udp_src_port;
reg [9 :0] test_data; reg [1 :0] STATE;
parameter WAIT_UDP_READY = 0; parameter WAIT_UDP_ACK = 1; parameter SEND_DATA = 2;
always@(posedge I_clk or posedge I_reset)begin if(I_reset) begin a_w_udp_req <= 1'b0; a_w_udp_valid <= 1'b0; a_w_udp_len <= 16'd0; test_data <= 10'd0; STATE <= WAIT_UDP_READY;
end else begin case(STATE) WAIT_UDP_READY:begin if(~a_w_udp_rdy) begin a_w_udp_req <= 1'b1; STATE <= WAIT_UDP_ACK; end else begin a_w_udp_req <= 1'b0; STATE <= WAIT_UDP_READY; end end WAIT_UDP_ACK:begin if(a_w_udp_rdy) begin a_w_udp_len <= 16'd768; a_w_udp_valid <= 1'b1; a_w_udp_req <= 1'b0; STATE <= SEND_DATA; end else STATE <= WAIT_UDP_ACK; end SEND_DATA:begin if(test_data == 10'd767) begin a_w_udp_valid <= 1'b0; a_w_udp_len <= 16'd0; test_data <= 0; STATE <= WAIT_UDP_READY; end else begin a_w_udp_valid <= 1'b1; test_data <= test_data + 1'b1; STATE <= SEND_DATA; end end endcase end end
//以下實現A傳送,B接收的模擬測試 wire [7:0] a_gmii_tdata; wire a_gmii_tvalid; wire [7:0] b_gmii_tdata; wire b_gmii_tvalid;
assign a_w_udp_data = test_data[7:0];
udp_stack # ( .CRC_GEN_EN (1'b1), .INTER_FRAME_GAP (4'd12) ) A_udp_stack ( .I_uclk (I_clk), .I_reset (I_reset),
.I_mac_local_addr (48'h0123456789a2),//本地MAC地址 .I_udp_local_port (16'd6002 ), //本地埠號 .I_ip_local_addr (32'hc0a88902 ), //本地ip地址
.I_udp_dest_port (16'd6001 ), //目的埠 .I_ip_dest_addr (32'hc0a88901 ), //目的IP地址
.O_W_udp_busy (a_w_udp_rdy), .I_W_udp_req (a_w_udp_req), .I_W_udp_valid (a_w_udp_valid), .I_W_udp_data (a_w_udp_data), .I_W_udp_len (a_w_udp_len),
.O_R_udp_valid (), .O_R_udp_data (), .O_R_udp_len (), .O_R_udp_src_port (),
.I_gmii_rclk (I_clk), .I_gmii_rvalid (b_gmii_tvalid), .I_gmii_rdata (b_gmii_tdata),
.I_gmii_tclk (I_clk), .O_gmii_tvalid (a_gmii_tvalid), .O_gmii_tdata (a_gmii_tdata), .O_ip_rerror (O_a_ip_rx_error), .O_mac_rerror (O_a_mac_rx_error) );
udp_stack # ( .CRC_GEN_EN (1'b1), .INTER_FRAME_GAP (4'd12) ) B_udp_stack ( .I_uclk (I_clk), .I_reset (I_reset),
.I_mac_local_addr (48'h0123456789a1),//本地MAC地址 .I_udp_local_port (16'd6001 ), //本地埠號 .I_ip_local_addr (32'hc0a88901 ), //本地ip地址
.I_udp_dest_port (16'd6002 ), //目的埠 .I_ip_dest_addr (32'hc0a88902 ), //目的IP地址
.O_W_udp_busy (), .I_W_udp_req (0), .I_W_udp_valid (0), .I_W_udp_data (0), .I_W_udp_len (0),
.O_R_udp_valid (b_r_udp_valid), .O_R_udp_data (b_r_udp_data), .O_R_udp_len (b_r_udp_data_len), .O_R_udp_src_port (b_r_udp_src_port),
.I_gmii_rclk (I_clk), .I_gmii_rvalid (a_gmii_tvalid), .I_gmii_rdata (a_gmii_tdata),
.I_gmii_tclk (I_clk), .O_gmii_tvalid (b_gmii_tvalid), .O_gmii_tdata (b_gmii_tdata), .O_ip_rerror (O_b_ip_rx_error), .O_mac_rerror (O_b_mac_rx_error) );
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的使用者端寫資料。模擬程式碼如下:
always@(posedge I_clk or posedge I_reset)begin if(I_reset) begin pause_data <= 8'd0; pause_vld <= 1'b0; cnt1 <= 'd0; data_cnt <= 'd0; STATE2 <= WAIT_PAUSE_RDY; end else begin case(STATE2) WAIT_PAUSE_RDY:begin data_cnt <= 'd0; if(cnt1 == 16'd5000) begin cnt1 <= 'd0; STATE2 <= SEND_PAUSE_DATA; end else begin cnt1 <= cnt1 + 1'b1; STATE2 <= WAIT_PAUSE_RDY; end end SEND_PAUSE_DATA:begin data_cnt <= data_cnt + 1'b1; case(data_cnt) 0 :begin pause_data <= 8'h55; pause_vld <= 1'b1;end 1 :begin pause_data <= 8'h55;end 2 :begin pause_data <= 8'h55;end 3 :begin pause_data <= 8'h55;end 4 :begin pause_data <= 8'h55;end 5 :begin pause_data <= 8'h55;end 6 :begin pause_data <= 8'h55;end 7 :begin pause_data <= 8'hd5;end
8 :begin pause_data <= 8'h01;end 9 :begin pause_data <= 8'h80;end 10 :begin pause_data <= 8'hc2;end 11 :begin pause_data <= 8'h00;end 12 :begin pause_data <= 8'h00;end 13 :begin pause_data <= 8'h01;end//pause廣播地址 14 :begin pause_data <= 8'h01;end 15 :begin pause_data <= 8'h23;end 16 :begin pause_data <= 8'h45;end 17 :begin pause_data <= 8'h67;end 18 :begin pause_data <= 8'h89;end 19 :begin pause_data <= 8'ha1;end
20 :begin pause_data <= 8'h88;end 21 :begin pause_data <= 8'h08;end
22 :begin pause_data <= 8'h00;end 23 :begin pause_data <= 8'h01;end
24 :begin pause_data <= 8'h00;end 25 :begin pause_data <= 8'h7f;end
26 :begin pause_data <= 8'hff;end 27 :begin pause_data <= 8'hff;end 36 :begin pause_data <= 8'h15;end 37 :begin pause_data <= 8'hbf;end 38 :begin pause_data <= 8'h4b;end 39 :begin pause_data <= 8'h6c;end 40 :begin pause_data <= 8'h00; pause_vld <= 1'b0; STATE2 <= WAIT_PAUSE_RDY; end default:pause_data <= 8'h00; endcase end default: begin pause_data <= 8'd0; pause_vld <= 1'b0; cnt1 <= 'd0; data_cnt <= 'd0; STATE2 <= WAIT_PAUSE_RDY; end endcase end 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請求包額外資料保持一致。