FPGA設計千兆乙太網MAC(3)——數據緩存及位寬轉換模塊設計與驗證

来源:https://www.cnblogs.com/moluoqishi/archive/2018/10/07/9751652.html
-Advertisement-
Play Games

本文設計思想採用明德揚至簡設計法。上一篇博文中定製了自定義MAC IP的結構,在用戶側需要位寬轉換及數據緩存。本文以TX方向為例,設計並驗證發送緩存模塊。這裡定義該模塊可緩存4個最大長度數據包,用戶根據需求改動即可。 該模塊核心是利用非同步FIFO進行跨時鐘域處理,位寬轉換由VerilogHDL實現。 ...


  本文設計思想採用明德揚至簡設計法。上一篇博文中定製了自定義MAC IP的結構,在用戶側需要位寬轉換及數據緩存。本文以TX方向為例,設計並驗證發送緩存模塊。這裡定義該模塊可緩存4個最大長度數據包,用戶根據需求改動即可。

  該模塊核心是利用非同步FIFO進行跨時鐘域處理,位寬轉換由VerilogHDL實現。需要註意的是用戶數據包位寬32bit,因此包尾可能有無效位元組,而轉換為8bit位寬數據幀後是要丟棄無效位元組的。內部邏輯非常簡單,直接上代碼:

  1 `timescale 1ns / 1ps
  2 
  3 // Description: MAC IP TX方向用戶數據緩存及位寬轉換模塊
  4 // 整體功能:將TX方向用戶32bit位寬的數據包轉換成8bit位寬數據包
  5 //用戶側時鐘100MHZ,MAC側125MHZ
  6 //緩存深度:保證能緩存4個最長數據包,TX方向用戶數據包包括
  7 //目的MAC地址  源MAC地址 類型/長度 數據 最長1514byte
  8 
  9 
 10 module tx_buffer#(parameter DATA_W = 32)//位寬不能改動
 11 (
 12     
 13     //全局信號
 14     input                         rst_n,//保證拉低三個時鐘周期,否則FIF可能不會正確複位
 15 
 16     //用戶側信號
 17     input                         user_clk,
 18     input         [DATA_W-1:0]     din,
 19     input                         din_vld,
 20     input                         din_sop,
 21     input                         din_eop,
 22     input         [2-1:0]         din_mod,
 23     output                         rdy,
 24 
 25     //MAC側信號
 26     input                         eth_tx_clk,
 27     output reg     [8-1:0]         dout,
 28     output reg                     dout_sop,
 29     output reg                     dout_eop,
 30     output reg                     dout_vld
 31     );
 32 
 33 
 34     reg wr_en = 0;
 35     reg [DATA_W+4-1:0] fifo_din = 0;
 36     reg [ (2-1):0]  rd_cnt = 0     ;
 37     wire        add_rd_cnt ;
 38     wire        end_rd_cnt ;
 39     wire rd_en;
 40     wire [DATA_W+4-1:0] fifo_dout;
 41     wire rst;
 42     reg [ (2-1):0]  rst_cnt =0    ;
 43     wire        add_rst_cnt ;
 44     wire        end_rst_cnt ;
 45     reg rst_flag = 0;
 46     wire [11 : 0] wr_data_count;
 47     wire empty;
 48     wire full;
 49 
 50 /****************************************寫側*************************************************/
 51 always  @(posedge user_clk or negedge rst_n)begin
 52     if(rst_n==1'b0)begin
 53         wr_en <= 0;
 54     end
 55     else if(rdy)
 56         wr_en <= din_vld;
 57 end
 58 
 59 always  @(posedge user_clk or negedge rst_n)begin
 60     if(rst_n==1'b0)begin
 61         fifo_din <= 0; 
 62     end
 63     else begin//[35] din_sop    [34] din_eop    [33:32] din_mod    [31:0] din
 64         fifo_din <= {din_sop,din_eop,din_mod,din};
 65     end
 66 end
 67 
 68 assign rdy = wr_data_count <= 1516 && !rst && !rst_flag && !full;
 69 
 70 /****************************************讀側*************************************************/
 71 
 72 always @(posedge eth_tx_clk or negedge rst_n) begin 
 73     if (rst_n==0) begin
 74         rd_cnt <= 0; 
 75     end
 76     else if(add_rd_cnt) begin
 77         if(end_rd_cnt)
 78             rd_cnt <= 0; 
 79         else
 80             rd_cnt <= rd_cnt+1 ;
 81    end
 82 end
 83 assign add_rd_cnt = (!empty);
 84 assign end_rd_cnt = add_rd_cnt  && rd_cnt == (4)-1 ;
 85 
 86 assign rd_en = end_rd_cnt;
 87 
 88 always  @(posedge eth_tx_clk or negedge rst_n)begin
 89     if(rst_n==1'b0)begin
 90         dout <= 0;
 91     end
 92     else if(add_rd_cnt)begin
 93         dout <= fifo_dout[DATA_W-1-rd_cnt*8 -:8];
 94     end
 95 end
 96 
 97 always  @(posedge eth_tx_clk or negedge rst_n)begin
 98     if(rst_n==1'b0)begin
 99         dout_vld <= 0;
100     end
101     else if(add_rd_cnt && ((rd_cnt <= 3 - fifo_dout[33:32] && fifo_dout[34]) || !fifo_dout[34]))begin
102         dout_vld <= 1;
103     end
104     else
105         dout_vld <= 0;
106 end
107 
108 always  @(posedge eth_tx_clk or negedge rst_n)begin
109     if(rst_n==1'b0)begin
110         dout_sop <= 0;
111     end
112     else if(add_rd_cnt && rd_cnt == 0 && fifo_dout[35])begin
113         dout_sop <= 1;
114     end
115     else
116         dout_sop <= 0 ;
117 end
118 
119 always  @(posedge eth_tx_clk or negedge rst_n)begin
120     if(rst_n==1'b0)begin
121         dout_eop <= 0;
122     end
123     else if(add_rd_cnt && rd_cnt == 3 - fifo_dout[33:32] && fifo_dout[34])begin
124         dout_eop <= 1;
125     end
126     else
127         dout_eop <= 0;
128 end
129 
130 
131 /******************************FIFO複位邏輯****************************************/
132 assign rst = !rst_n || rst_flag;
133 
134 always  @(posedge user_clk or negedge rst_n)begin 
135     if(!rst_n)begin
136         rst_flag <= 1;
137     end
138     else if(end_rst_cnt)
139         rst_flag <= 0;
140 end
141 
142 always @(posedge user_clk or negedge rst_n) begin 
143     if (rst_n==0) begin
144         rst_cnt <= 0; 
145     end
146     else if(add_rst_cnt) begin
147         if(end_rst_cnt)
148             rst_cnt <= 0; 
149         else
150             rst_cnt <= rst_cnt+1 ;
151    end
152 end
153 assign add_rst_cnt = (rst_flag);
154 assign end_rst_cnt = add_rst_cnt  && rst_cnt == (3)-1 ;
155 
156 
157 
158     //FIFO位寬32bit 一幀數據最長1514byte,即379個16bit數據
159     //FIFO深度:379*4 = 1516  需要2048
160     //非同步FIFO例化
161     fifo_generator_0 fifo (
162   .rst(rst),        // input wire rst
163   .wr_clk(user_clk),  // input wire wr_clk   100MHZ
164   .rd_clk(eth_tx_clk),  // input wire rd_clk  125MHZ
165   .din(fifo_din),        // input wire [33 : 0] din
166   .wr_en(wr_en),    // input wire wr_en
167   .rd_en(rd_en),    // input wire rd_en
168   .dout(fifo_dout),      // output wire [33 : 0] dout
169   .full(full),      // output wire full
170   .empty(empty),    // output wire empty
171   .wr_data_count(wr_data_count)  // output wire [11 : 0] wr_data_count
172 );
173 
174 endmodule
tx_buffer

  接下來是驗證部分,也就是本文的重點。以下的testbench包含了最基本的測試思想:發送測試激勵給UUT,將UUT輸出與黃金參考值進行比較,通過記分牌輸出比較結果。

  1 `timescale 1ns / 1ps
  2 
  3 module tx_buffer_tb( );
  4 
  5 parameter USER_CLK_CYC = 10,
  6           ETH_CLK_CYC = 8,
  7           RST_TIM = 3;
  8           
  9 parameter SIM_TIM = 10_000;
 10 
 11 reg user_clk;
 12 reg rst_n;
 13 reg [32-1:0] din;
 14 reg din_vld,din_sop,din_eop;
 15 reg [2-1:0] din_mod;
 16 wire rdy;
 17 reg eth_tx_clk;
 18 wire [8-1:0] dout;
 19 wire dout_sop,dout_eop,dout_vld;
 20 reg [8-1:0] dout_buf [0:1024-1];
 21 reg [16-1:0] len [0:100-1];
 22 reg [2-1:0] mod [0:100-1];
 23 reg err_flag = 0;
 24 
 25 tx_buffer#(.DATA_W(32))//位寬不能改動
 26 dut
 27 (
 28     
 29     //全局信號
 30    .rst_n      (rst_n) ,//保證拉低三個時鐘周期,否則FIF可能不會正確複位
 31    .user_clk   (user_clk) ,
 32    .din        (din) ,
 33    .din_vld    (din_vld) ,
 34    .din_sop    (din_sop) ,
 35    .din_eop    (din_eop) ,
 36    .din_mod    (din_mod) ,
 37    .rdy        (rdy) ,
 38    .eth_tx_clk (eth_tx_clk) ,
 39    .dout       (dout) ,
 40    .dout_sop   (dout_sop) ,
 41    .dout_eop   (dout_eop) ,
 42    .dout_vld   (dout_vld) 
 43     );
 44     
 45 /***********************************時鐘******************************************/
 46     initial begin
 47         user_clk = 1;
 48         forever #(USER_CLK_CYC/2) user_clk = ~user_clk;
 49     end
 50 
 51     initial begin
 52         eth_tx_clk = 1;
 53         forever #(ETH_CLK_CYC/2) eth_tx_clk = ~eth_tx_clk;
 54     end
 55 /***********************************複位邏輯******************************************/
 56     initial begin
 57         rst_n = 1;
 58         #1;
 59         rst_n = 0;
 60         #(RST_TIM*USER_CLK_CYC);
 61         rst_n = 1;
 62     end
 63     
 64 /***********************************輸入激勵******************************************/
 65 integer gen_time = 0;
 66     initial begin
 67         #1;
 68         packet_initial;
 69         #(RST_TIM*USER_CLK_CYC);
 70         packet_gen(20,2);
 71         #(USER_CLK_CYC*10);
 72         packet_gen(30,1);
 73     end
 74     
 75 /***********************************輸出緩存與檢測******************************************/    
 76 integer j = 0;
 77 integer chk_time = 0;
 78     initial begin
 79         forever begin
 80             @(posedge eth_tx_clk)
 81             if(dout_vld)begin    
 82                 if(dout_sop)begin
 83                     dout_buf[0] = dout;
 84                     j = 1;
 85                 end
 86                 else if(dout_eop)begin
 87                     dout_buf[j] = dout;
 88                     j = j+1;
 89                     packet_check;
 90                 end
 91                 else begin
 92                     dout_buf[j] = dout;
 93                     j = j+1;
 94                 end
 95             end
 96         end
 97     end
 98     
 99 /***********************************score board******************************************/
100 integer fid;
101     initial begin
102         fid = $fopen("test.txt");
103         $fdisplay(fid,"                 Start testing                      \n");
104         #SIM_TIM;
105         if(err_flag)
106             $fdisplay(fid,"Check is failed\n");
107         else
108             $fdisplay(fid,"Check is successful\n");
109         $fdisplay(fid,"                 Testing is finished                \n");
110         $fclose(fid);
111         $stop;
112     end
113 
114 /***********************************子任務******************************************/    
115 //包生成子任務
116     task packet_gen;
117         input [16-1:0] length;
118         input [2-1:0] invalid_byte;
119         integer i;
120         begin
121             len[gen_time] = length;
122             mod[gen_time] = invalid_byte;
123             
124             for(i = 1;i<=length;i=i+1)begin
125                 if(rdy == 1)begin
126                     din_vld = 1;
127                     if(i==1)
128                         din_sop = 1;
129                     else if(i == length)begin
130                         din_eop = 1;
131                         din_mod = invalid_byte;
132                     end
133                     else begin
134                         din_sop = 0;
135                         din_eop = 0;
136                         din_mod = 0;
137                     end
138                     din = i ;
139                 end
140                 
141                 else begin
142                     din_sop = din_sop;
143                     din_eop = din_eop;
144                     din_vld = 0;
145                     din_mod = din_mod;
146                     din = din;
147                     i = i - 1;
148                 end
149                 
150                 #(USER_CLK_CYC*1);
151             end
152             packet_initial;
153             gen_time = gen_time + 1;
154         end
155     endtask
156     
157     task packet_initial;
158         begin
159             din_sop = 0;
160             din_eop = 0;
161             din_vld = 0;
162             din = 0;
163             din_mod = 0;
164         end
165     endtask
166 
167 //包檢測子任務
168     task packet_check;
169         integer k;
170         integer num,packet_len;
171         begin
172             num = 1;
173             $fdisplay(fid,"%dth:Packet checking...\n",chk_time);
174             packet_len = 4*len[chk_time]-mod[chk_time];
175             if(j != packet_len)begin
176                 $fdisplay(fid,"Length of the packet is wrong.\n");
177                 err_flag = 1;
178                 disable packet_check;
179             end
180             
181             for(k=0;k<packet_len;k=k+1)begin
182                 if(k%4 == 3)begin
183                     if(dout_buf[k] != num)begin 
184                         $fdisplay(fid,"Data of the packet is wrong!\n");
185                         err_flag = 1;
186                     end
187                     num = num+1;
188                 end    
189                 else if(dout_buf[k] != 0)begin
190                     $fdisplay(fid,"Data of the packet is wrong,it should be zero!\n");
191                     err_flag = 1;
192                 end
193             end
194             chk_time = chk_time + 1;
195         end
196     endtask
197     
198 endmodule
tx_buffer_tb

  可見主要是task編寫及文件讀寫操作幫了大忙,如果都用眼睛看波形來驗證設計正確性,真的是要搞到眼瞎。為保證測試完備性,測試包生成task可通過輸入介面產生不同長度和無效位元組數的遞增數據包。testbench中每檢測到輸出包尾指示信號eop即調用packet_check task對數值進行檢測。本文的testbench結構較具通用性,可以用來驗證任意對數據包進行處理的邏輯單元。

  之前Modelsim獨立模擬帶有IP核的Vivado工程時經常報錯,只好使用Vivado自帶的模擬工具。一直很頭痛這個問題,這次終於有了進展!首先按照常規流程使用Vivado調用Modelsim進行行為模擬,啟動後會在工程目錄下產生些有用的文件,幫助我們脫離Vivado進行獨立模擬。

  在新建Modelsim工程時,在紅框內選擇Vivado工程中<project>.sim -> sim_1 -> behav下的modelsim.ini文件。之後添加文件包括:待測試設計文件、testbench以及IP核可綜合文件。第三個文件在<project>.srcs -> sources_1 -> ip -> <ip_name> -> synth下。

  現在可以順利啟動模擬了。我們來看下模擬結果:

  文件中信息列印情況:

  從波形和列印信息的結果來看,基本可以證明數據緩存及位寬轉換模塊邏輯功能無誤。為充分驗證要進一步給出覆蓋率較高的測試數據集,後期通過編寫do文件批量模擬實現。在FPGA或IC設計中,驗證占據大半開發周期,可見VerilogHDL的非綜合子集也是至關重要的,今後會多總結高效的驗證方法!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 單例模式 單例模式(Singleton Pattern)是一種常用的軟體設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。 比如,某個伺服器程式的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置 ...
  • 實驗1 用a指令寫入代碼 用t指令單步執行 實驗2 用a指令寫入並修改cs值 用t運行並指定運行17(11H)次,(其實16次(10H)就夠了) 實驗3 d指令查看 得是92年1月1日生產的(很明顯是假的) 嘗試修改 發現並沒有什麼卵用,查了一下,因為這裡是ROM 實驗4 先照例嘗試 發現會在右上角 ...
  • 通過圖書管理系統學習:一對一,一對多,多對多的操作 文件上傳 ...
  • C++基礎部分: 1.數組和指針的區別 (1)數組本身體現出來的就是一個 指針常量的 “特性”,即不能對數組的首地址進行修改,記憶體上的地址就已經是確定了的。而指針本身是一個變數,他指向了一個地址,這個是可以變化的,也就說他可以重新賦值指向新的地址; (2)當調用sizeof函數時,對於數組,得到的是 ...
  • 基本線程管理 join detach join:主線程等待被join線程結束後,主線程才結束。 detach:主線程不等待被detach線程。 問題1:子線程什麼時點開始執行? std::thread t(fun);執行後,就開始執行了。 問題2:在哪裡調用join或者detach 1,使用deta ...
  • 本文回顧了上一篇文章中的HelloWorld程式,並通過類定義、註釋和main方法三個方面對這個程式進行了詳細的剖析。相信讀完這篇文章以後,讀者將會對Java程式的結構有一個整體的認識。 ...
  • final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {} ]public Object invoke(Object proxy, Method method, Object[] arg... ...
  • 1、獲取時間戳 (1)、os.time() --當前時間戳 (2)、 os.time({day=5, month=9, year=2018, hour=12, min=59, sec=59}) --指定時間的時間戳 2、獲取指定格式時間 (1)、時間格式 yyyyMMddHHmmss:os.date ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...