1、位寬太小
在FPGA設計中,我們經常需要用寄存器來寄存某些“數量類”的變量,比如FIFO的深度啦、或者計數器的最大值啦;又或者輸入輸出信號也需要將位寬用parameter參數化以便更好的調用等。
舉個簡單的小例子:系統頻率100M(周期10ns),假設需要要求設計一個計時器計時100ns,那么需要計數次數為:100ns/10ns - 1 = 9,9這個數需要用多大位寬的寄存器表示呢?很簡單,以2為底取對數就行,答案是最少4位寬。為了方便地復用這個模塊,我們把計時時間參數化并放到模塊外,如下:
module counter #(
parameter TIME = 'd10 //計時時間,單位10ns
)(
input clk_100M ,
input rst
);
reg [3:0] cnt; //計數器
//計時器
always@(posedge clk_100M)begin
if(rst)
cnt else if(cnt == TIME - 1)
cnt else
cnt end
endmodule
假設我們下次設計需要一個計時器的話,直接調用上面的counter模塊并把TIME這個參數改成自己需要的參數就可以,這樣做理論上是可以的,只是會有一個致命的隱患。不妨再假設:我現在調用了counter模塊,并將TIME設置為20,以實現計時200ns的功能。當TIME = 20這個參數傳遞到被例化模塊后,可以發現由于cnt寄存器的位寬僅為4位,其能表示的最大值為4'b1111(即十進制下的數字15),每次其到達15后就溢出為0重新開始了,也就是說這個200ns的計時器實際上根本就計數不到200ns。
這個隱患發生的原因就是在設計寄存器cnt時的位寬只有4位,無法滿足“大量時間的計時任務”。
2、自己寫一個Function
現在來想一下如何解決上述的位寬不匹配的問題。將寄存器的位寬設計為一個較大的數值(如固定為32bit)不失為一個不錯的方法,但是如果將這條規則適用到每一個寄存器,則勢必造成大量的資源浪費(你資源多你隨便玩)。而且該方法指標不治本,我們需要做的是,這個寄存器應該有多大就設計多大的位寬(有多大的腳就穿多大的鞋,鞋子太大一定能穿,但你腳不一定舒服)。
前面說過寄存器的位寬的計算方法:以2為底取對數。所以我們只需要設計一個Function(可綜合),來實現此項功能即可。剛好在Xilinx的許多源碼都出現了這個簡單的Function,我們直接拿過來用就是的:
// function 實現
function integer clogb2 (input integer bit_depth);
begin
for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
bit_depth = bit_depth >> 1;
end
endfunction
// 使用案例
localparam integer C_TRANSACTIONS_NUM = clogb2(C_M_AXI_BURST_LEN-1);
reg [C_TRANSACTIONS_NUM : 0] write_index;
reg [C_TRANSACTIONS_NUM : 0] read_index;
上面的代碼就是定義了一個求位寬的function,用其求得某類寄存器的位寬,然后再對寄存器賦值時就直接使用求得的位寬來賦值,這樣復用起來就比較方便了。
我們將這個代碼放到上面的計數器模塊中后,不管需要計數多大時間,都能計算出相匹配的寄存器位寬了。
3、無法在輸入輸出端口使用
自己寫Function實現對2取對數的功能也有一定的局限性:無法對輸入輸出端口信號使用該Function。Function是定義在模塊內部,所以若輸入輸出端口也需要根據輸入的parameter參數來以2為底取對數的話此種方法就無能為力了。比如:設計一個同步FIFO,輸出信號fifo_cnt(計數器)是對寫入FIFO的數據進行計數的寄存器,其最大值即為FIFO的深度DATA_DEPTH ,所以fifo_cnt的位寬就需要在定義模塊輸入輸出端口時確定,顯然這無法使用自己構造的 cblogb2 Function。那該當如何?
//計數器法實現同步FIFO
module sync_fifo_cnt
#(
parameter DATA_WIDTH = 'd8 , //FIFO位寬
parameter DATA_DEPTH = 'd16 //FIFO深度
)
(
input clk , //系統時鐘
input rst_n , //低電平有效的復位信號
input [DATA_WIDTH-1:0] data_in , //寫入的數據
input rd_en , //讀使能信號,高電平有效
input wr_en , //寫使能信號,高電平有效
output reg [DATA_WIDTH-1:0] data_out, //輸出的數據
output empty , //空標志,高電平表示當前FIFO已被寫滿
output full , //滿標志,高電平表示當前FIFO已被讀空
output reg [$clog2(DATA_DEPTH) : 0] fifo_cnt //$clog2是以2為底取對數
);
//省略部分代碼
endmodule
4、$clog2系統函數
其實辦法也有,在上面的代碼中也展示出來了,就是使用 $clog2 這個Verilog的系統函數。$clog2是Verilog--2005標準新增的一個系統函數,功能就是對輸入整數實現以2為底取對數,其結果向上取整(如5.5取6)。有一點需要說明的是,目前Vivado2017以上的版本都是支持這個系統函數的(Quartus II不清楚 )。但是百度搜索這條結果的時候有兩條結論是錯誤的:
1、Vivado不支持$clog2系統函數
2、$clog2系統函數在Vivado實現的是以e為底取對數,而不是2
接下來寫個簡單的模塊驗證下Vivado對$clog2系統函數的支持如何
`timescale 1ns / 1ps
module clog2_test#(
parameter integer num = 325
)
(
input clk,
input rst,
output reg [$clog2(num) - 1:0] result
);
always @(posedge clk)begin
if(rst)
result else
result end
endmodule
可以看到最后編譯出的結果是9位的,也就說明Vivado是支持這個系統函數的(版本:2019.2)。
其他變量的位寬設計同理。
審核編輯:湯梓紅
-
寄存器
+關注
關注
31文章
5355瀏覽量
120517 -
計數器
+關注
關注
32文章
2256瀏覽量
94641 -
參數
+關注
關注
11文章
1838瀏覽量
32261
發布評論請先 登錄
相關推薦
評論