?
老衲第一次學(xué)習(xí) Verilog 語言,基本就到前面幾講的程度,頂多加上了解`define 宏定義。于是對(duì)于能設(shè)計(jì) IP 核的人,那是佩服的五體投地,如黃河泛濫一發(fā)不可收拾。直到 Verilog 2001 出了參數(shù)(parameter)和生成塊(generate)功能,做 IP 核就成了人人可以掌握的技能了。對(duì)頭,下面老僧就和施主們講這些內(nèi)容。
同樣的代碼 / 模塊要簡(jiǎn)化不難,這就是所謂模塊化的作用。但是類似的模塊 ---- 例如同樣是計(jì)數(shù)器,只是內(nèi)部 D 觸發(fā)器位寬的不同 ---- 要聚類就需要一些技巧了,因?yàn)?a class="article-link" target="_blank" href="/tag/%E7%94%B5%E8%B7%AF/">電路是不支持結(jié)構(gòu)靈活變化的。為了能夠合并類似模塊,Verilog 發(fā)展出了宏和參數(shù)這兩個(gè)玩意。
最后呢,在 Verilog 2001 里面,設(shè)計(jì)了塊生成的功能。這個(gè)功能使得用戶可以更加簡(jiǎn)單的根據(jù)外部的輸入,靈活的產(chǎn)生最佳的電路結(jié)構(gòu)。
1. 參數(shù)定義,結(jié)構(gòu)變化
參數(shù)的定義方法和傳遞方式見表 1 所示。定義有兩種方法,傳遞也有兩種方法,在加上參數(shù)列表的兩種表示,又是一個(gè)“一題多解”。那種方式好?沒標(biāo)準(zhǔn)答案,按照施主喜歡的方式來即可。但是一個(gè)項(xiàng)目里面,最好選擇一種組合模式,方便其他人閱讀。
表 1 參數(shù)的定于與傳遞
“parameter_list”為參數(shù)列表,參數(shù)之間用逗號(hào)“,”隔開;其中,“parameter_name”是用戶定義的參數(shù)名稱,建議采用容易閱讀的命名方式;“parameter_initial_value”為該參數(shù)對(duì)應(yīng)的初始值,在本模塊的實(shí)例未被定義參數(shù)的時(shí)候使用。
“parameter_list_seperated”為獨(dú)立于 module 定義之外的參數(shù)列表,需要在模塊行為描述之前書寫。它由若干個(gè)關(guān)鍵字“parameter”開頭的參數(shù)定義構(gòu)成;如果一個(gè)“parameter”帶多個(gè)參數(shù)定義,則這些參數(shù)需要被逗號(hào)“,”隔離;每個(gè)“parameter”開頭的定義,末尾用分號(hào)“;”表示結(jié)束。
“parameter_assignment_list”是參數(shù)賦值列表,由關(guān)鍵字“defparam”開頭,后面是各個(gè)參數(shù)的賦值。對(duì)于每個(gè)參數(shù),參數(shù)名稱需要用“module_instance_index”指定是哪一個(gè)例化模塊對(duì)應(yīng)的參數(shù);模塊索引“module_instance_index”的格式是按照最高模塊例化名稱到最底層例化的順序排列,模塊例化名稱之間用點(diǎn)“.”隔離;“parameter_value”是對(duì)應(yīng)參數(shù)的值;參數(shù)賦值之間用逗號(hào)“,”隔離,末尾需要有分號(hào)“;”。
“parameter_assignment_list_sequence”是按照定義順序的參數(shù)傳遞列表,其中各個(gè)對(duì)應(yīng)參數(shù)值“parameter_value”的順序必須和定義時(shí)候的順序一致。
“parameter_assignment_list_named”是按照指定名稱的參數(shù)傳遞列表,其中各個(gè)對(duì)應(yīng)參數(shù)值“parameter_value”有前面帶點(diǎn)“.”的參數(shù)名稱“parameter_name”指定。所以在參數(shù)的排列順序上可以任意。
當(dāng)在實(shí)例化模塊的同時(shí)傳遞參數(shù)的時(shí)候,請(qǐng)注意模塊名稱之后是參數(shù)傳遞列表,然后才是模塊的實(shí)例名等,這個(gè)順序不能搞錯(cuò)。參數(shù)傳遞列表被#(…)標(biāo)記出來。
參數(shù)的值可以說明位寬,但是一般而言,參數(shù)的值是一個(gè)常數(shù)不需要定義位寬的。
代碼中引用參數(shù)的時(shí)候,直接使用其名稱,無需象宏定義那樣用“`”開頭。
例 2 給出了一個(gè)位寬和最大值參數(shù)化的計(jì)數(shù)器的例子,請(qǐng)參考。例子中,這個(gè)計(jì)數(shù)器完成到達(dá)最大值就清零的不停的計(jì)數(shù)功能。頂層模塊調(diào)用了兩個(gè)參數(shù)不同的計(jì)數(shù)器。
?
【例 2】位寬和最大值參數(shù)化的計(jì)數(shù)器
`define WIDTH_1 8
`define WIDTH_2 4
`define WIDTH_3 3
//Bit width for different sub modules
`define MAX_1 200
`define MAX_2 13
`define MAX_3 5
//Bit width for different sub modules
module top_counter_parameter
? (
??? input clk, RST,
??? output[`WIDTH_1 - 1:0] counter1,
??? output[`WIDTH_2 - 1:0] counter2,
??? output[`WIDTH_3 - 1:0] counter3??????
? );
//Load other module(s)
counter_parameter C1(.clk(clk), .RST(RST), .counter(counter1));
counter_parameter C2(.clk(clk), .RST(RST), .counter(counter2));
??? defparam? C2.WIDTH = `WIDTH_2, C2.MAX_VALUE = `MAX_2;
counter_parameter #(.WIDTH(`WIDTH_3), .MAX_VALUE(`MAX_3))
????????????????? C3(.clk(clk), .RST(RST), .counter(counter3));
//Definition for Variables in the module
//Logic
endmodule
……
module counter_parameter
#(parameter WIDTH = 8,
//Bit width for output
MAX_VALUE = 200)
//Maximun value for counter)
? (
??? input clk, RST,
??? output reg[WIDTH - 1:0] counter
? );
?
//Load other module(s)
//Definition for Variables in the module
//Logic
always @(posedge clk or negedge RST)
begin
??? if (!RST)
??? begin
??????? counter <= 1'h0;
??? end
??? else if (counter < MAX_VALUE)
??? begin
?????? counter <= counter + 1'h1;????
??? end
??? else
??? begin
?????? counter <= 1'h0;????
??? end
end
endmodule
參數(shù)型常數(shù)常用于定義延遲時(shí)間和變量寬度,在模塊和實(shí)例引用時(shí),可通過參數(shù)傳遞改變?cè)诒灰媚K或?qū)嵗幸讯x的參數(shù)。參數(shù)在被綜合的時(shí)候必須是常數(shù)值,這點(diǎn)要強(qiáng)調(diào)一下,變結(jié)構(gòu)的電路是不可能被綜合的。這個(gè)常數(shù)值可以是已知的常數(shù),也可以通過常數(shù)之間的計(jì)算得到。
參數(shù)是給綜合軟件用的,所以在實(shí)現(xiàn)的電路里面不可能明顯的看到參數(shù)的值。
除了參數(shù),Verilog 2001 里面還定義了一個(gè)很多工程師都不知道干嘛用的本地參數(shù)“l(fā)ocalparam”。本地參數(shù)的定義方法和參數(shù)一樣,作用域也是相同的。這兩類參數(shù)的區(qū)別在于,本地參數(shù)不同通過參數(shù)傳遞方式進(jìn)行修改。
?
2. 生成有塊,更加靈活
一般而言,生成塊要和參數(shù)功能合作,完成動(dòng)態(tài)產(chǎn)生電路的作用。當(dāng)然這個(gè)功能也可以不動(dòng)態(tài)產(chǎn)生電路,但是這樣相當(dāng)于用寶劍來做木匠活,不僅大材小用還不方便使用。
生成塊的關(guān)鍵詞是“generate”,英文產(chǎn)生的意思。一個(gè)生成塊被
generate
operations
endgenerate
這樣的框架包裹,其中“operations”是快生成的功能部分,用來描述實(shí)際有用的邏輯。生成塊功能分為:條件、case 和循環(huán)三個(gè)類型,待貧僧一一道來。
條件嘛,莫過于就是“if…else if…”的樣子了,看到這里的觀眾們應(yīng)該可以耳熟能詳了。但是注意,生成塊的條件里面不是什么都可以裝的,可以用于生成塊的條件功能的內(nèi)容僅限于:模塊(例化)、UDP、Verilog 門原語、連續(xù)賦值,initial 塊和 always 塊等。這個(gè)要注意,否則報(bào)錯(cuò)是不可避免的。
生成塊的語法結(jié)構(gòu),想必大伙兒也猜得出:
generate
if (condition)
??? operation_1
else
??? operation_2
endgenerate
其中,“condition”是邏輯表達(dá)式,是判決條件。當(dāng)判決條件為真的時(shí)候,進(jìn)行“operation_1”的操作;否則,進(jìn)行“operation_2”的操作。
就和 if 與 case 的關(guān)系一般,生成塊里面既然有 if 也少不得 case 來搭配。生成塊的 case 的語法結(jié)構(gòu)是:
generate
case (constant_express)
value_1: operation_1
value_2: operation_2
……
value_n: operation_n
default: operation_default
endgenerate
如何使用,不必啰嗦。生成塊的 case 里面可以包含的內(nèi)容和生成塊 if 的一樣的。強(qiáng)調(diào)一下,僅僅包括:模塊(例化)、UDP、Verilog 門原語、連續(xù)賦值,initial 塊和 always 塊。
如何選擇 if 和 case 的應(yīng)用場(chǎng)景也是類似的情況,老衲也不多嘴。
看到上面的內(nèi)容,施主們一定會(huì)產(chǎn)生輕敵的思想:生成塊莫過如此,easy!兵法有云:“驕兵必?cái) ?,這個(gè)想法要不得。下面給大伙說說循環(huán)類型的生成塊,這個(gè)很容易引起歧義,老衲需要細(xì)細(xì)講解。
欲說循環(huán)生成,先要介紹生成索引變量“genvar”,其語法結(jié)構(gòu)是:
genvar genvar_name_1, genvar_name_2, ……, genvar_name_n;
其中,“genvar_name”是不同循環(huán)索引變量的名稱,要求符合 Verilog 對(duì)于變量名名的要求。這個(gè)變量是和循環(huán)生成共生的,看起來像是循環(huán)里面的循環(huán)變量。實(shí)際中,它比循環(huán)變量的應(yīng)用范圍專業(yè),只用于循環(huán)生成的電路模塊的“索引”。具體啥叫“索引”和“索引”啥,先買個(gè)關(guān)子,后面再說。
循環(huán)式生成塊的語法結(jié)構(gòu)是
generate
genvar genvar;
??? for (genvar = start_value; end_condition; circle_express)
??? begin:? instant_name
??????? operations
??? end
endgenerate
其中, “start_vlue”、“end_condition”、“circle_expree”是和循環(huán)語句 for 是一樣一樣的含義?!皁perations”是每次循環(huán)的操作,這只能是變量聲明、模塊(例化)、UDP、Verilog 門原語、連續(xù)賦值,initial 塊和 always 塊這幾個(gè)里面的一個(gè)或者幾個(gè)。“genvar”就是前面說到的生成索引變量。最大的不同是操作一定要有“begin……end”括在內(nèi)部,而且“begin”之后要有“:? instant_name”這個(gè)結(jié)構(gòu)。冒號(hào)表示風(fēng)格,不多說;“instant_name”就是所謂的生成索引的名字。換句話說,“instant_name”表示 for 內(nèi)部實(shí)現(xiàn)的模塊、變量的名稱,以防止混淆。
這里說了這么許多,很多施主肯定已經(jīng)迷糊了,下來給個(gè)例子十分必要。
?
看一個(gè)簡(jiǎn)單的例子,用循環(huán)生成做一個(gè)位寬參數(shù)化的加法鏈,如例 2 所示。由于最低比特是一個(gè)半加器,這個(gè)在代理里面特別處理了。特別說明一下,for 循環(huán)里面實(shí)現(xiàn)了若干個(gè)全加器,這些全加器的被命名為:full_adder[0].F,full_adder[1].F……這就是所謂的生成索引。
【例 2】位寬參數(shù)化的加法鏈(半加器外置)
代碼 |
電路代碼 |
綜合軟件內(nèi)的代碼 |
module? adder_line_generate #(parameter WIDTH = 8) ?( ??? input[WIDTH - 1 :0] a0, a1, ??? output[WIDTH :0] sum ? ); //Definition for Variables in the module wire[WIDTH - 1:0]? c; //Carried bits in the line //Load other module(s) half_adder HALF_ADDER(.a0(a0[0]), .a1(a1[0]), ????????? .s(sum[0]),.c1(c[0])); //First bit: half adder ????????? generate //Other bits: full_adder genvar loop; begin ??? for (loop = 1; loop < WIDTH; loop = loop + 1) ??? begin: FULL_ADDER ??????? full_adder F(.a0(a0[loop]), .a1(a1[loop]), .c0(c[loop - 1]), ???????????????? .s(sum[loop]), .c1(c[loop]) ); ? ??end end endgenerate //Logic assign sum[WIDTH] = c[WIDTH-1]; //Carried bit for the result endmodule |
module? adder_line_generate #(parameter WIDTH = 8) ?( ??? input[WIDTH - 1 :0] a0, a1, ??? output[WIDTH :0] sum ? ); //Definition for Variables in the module wire[WIDTH - 1:0]? c; //Carried bits in the line //Load other module(s) half_adder HALF_ADDER(.a0(a0[0]), .a1(a1[0]), ????????? .s(sum[0]),.c1(c[0])); //First bit: half adder ?????????
//Other bits: full_adder
??? begin: FULL_ADDER ??????? full_adder F(.a0(a0[loop]), .a1(a1[loop]), .c0(c[loop - 1]), ???????????????? .s(sum[loop]), .c1(c[loop]) );
//Logic assign sum[WIDTH] = c[WIDTH-1]; //Carried bit for the result endmodule |
//Definition for Variables in the module
//Carried bits in the line //Load other module(s)
//First bit: half adder ????????? generate //Other bits: full_adder genvar loop; begin ??? for (loop = 1; loop < WIDTH; loop = loop + 1) ??? begin: FULL_ADDER
??? end end endgenerate //Logic
//Carried bit for the result
|
施主們就看到了生成塊是可以嵌套的,這個(gè)一般的代碼類似。還是一句順口溜:嵌套用得好,寫核難不倒。意思是說:生成塊的嵌套用好了,自己寫 IP 核就是探囊取物一般簡(jiǎn)單。這方面的資料相對(duì)較少,所以老僧就不厭其煩再給幾個(gè)例子。
還是例 3 場(chǎng)景,實(shí)現(xiàn)位寬參數(shù)化的加法鏈。
【例 3】位寬參數(shù)化的加法鏈(全加器 / 半加器合并)
module? adder_line_generate #(parameter WIDTH = 8)
(
??? input[WIDTH - 1 :0] a0, a1,
??? output[WIDTH :0] sum
? );
//Definition for Variables in the module
?????????
generate
//Other bits: full adder
genvar loop;
begin
??? for (loop = 0; loop < WIDTH; loop = loop + 1)
??? begin: ADDER
?????? wire c;
?????? //Carried bit in the loop named ADDER[loop].c
?????? if (loop == 0)
?????? begin
?????????? half_adder h(.a0(a0[loop]), .a1(a1[loop]),
???????????????????????????? .s(sum[loop]), .c1(c) );
?????? end
?????? else
?????? begin
?????????? if (loop == WIDTH - 1)
?????????? begin
??????????????????????????? full_adder F(.a0(a0[loop]), .a1(a1[loop]), .c0(ADDER[loop-1].c),
???????????????????????????????? .s(sum[loop]), .c1(sum[WIDTH]) );
??????????? end
??????????? else
??????????? begin
??????????????? full_adder F(.a0(a0[loop]), .a1(a1[loop]), .c0(ADDER[loop-1].c),
???????????????????? .s(sum[loop]), .c1(c) );????????????
??????????? end
??????? end
??? end
end
endgenerate
//Logic
endmodule
生成塊和 Verilog 語句里面對(duì)應(yīng)的 if、case 和 for 很容易混淆,在最后貧僧在表 3 里面幫大家做了一個(gè)總結(jié),請(qǐng)參考。
表 3 生成塊和 Verilog 語句的區(qū)別
? |
生成塊:if 與 case |
Verilog 語句的 if 與 case |
功能 |
綜合軟件根據(jù)條件,判斷選擇的電路器件 |
直接產(chǎn)生電路 |
電路映射 |
不產(chǎn)生電路,隱式體現(xiàn) |
產(chǎn)生電路,一般為選擇器 |
內(nèi)部可包含 |
模塊(例化)、UDP、Verilog 門原語、連續(xù)賦值,initial 塊和 always 塊 |
Verilog 語句 |
條件表達(dá)式 |
參數(shù)化的常數(shù)表達(dá)式 |
常數(shù)表達(dá)式(不一定參數(shù)化)或者變量 |
可綜合性要求 |
內(nèi)部為模塊(例化)、連續(xù)賦值和 / 或 always 塊,并且內(nèi)部語句可綜合 |
內(nèi)部語句可綜合 |
? |
生成塊:for |
Verilog 語句的 for |
功能 |
綜合軟件循環(huán)次數(shù),實(shí)現(xiàn)電路 |
綜合軟件循環(huán)次數(shù),實(shí)現(xiàn)電路 |
電路映射 |
不產(chǎn)生電路,隱式體現(xiàn) |
不產(chǎn)生電路,隱式體現(xiàn) |
內(nèi)部可包含 |
變量聲明、模塊(例化)、UDP、Verilog 門原語、連續(xù)賦值,initial 塊和 always 塊 |
其他可綜合語句(不能實(shí)現(xiàn)模塊例化等) |
信號(hào)、模塊數(shù)量 |
可參數(shù)化生成 |
規(guī)模固定[1] |
循環(huán)變量類型 |
生成索引變量 |
一般變量 |
格式 |
必須由“begin …… end”括住,而且 begin 后面需要“:? instant_name”結(jié)構(gòu) |
如果是單獨(dú)語句語法上可以沒有“begin …… end”括住[2] |
可綜合性要求 |
|
|
[1]Verilog 語句 for 可以使得部分信號(hào)、模塊的實(shí)例在實(shí)際中不被使用,但是理論上這些信號(hào)、模塊依然被語言約束存在于電路中。當(dāng)然好的綜合軟件會(huì)優(yōu)化掉這些無用的信號(hào)、模塊,但是這不是語法要求的。
[2]工程代碼里面不建議這樣的寫法。
由此可見,雖然生成塊和 Verilog 語句里面 if、case 和 for 的樣子完全一樣,但是應(yīng)用場(chǎng)合卻是大相徑庭的。施主們一定要注意區(qū)別對(duì)待,用錯(cuò)了地方可是要鬧笑話的。
這正是:
“
禹王量水號(hào)神針,大圣降妖棒一根。如今列位有福分,語言里面塊生成。
模塊選擇參數(shù)能,數(shù)目大小循環(huán)認(rèn)。靈活運(yùn)用仙界登,不怕小核把亂生。
”
與非網(wǎng)原創(chuàng)內(nèi)容,謝絕轉(zhuǎn)載!
系列匯總:
之二:Verilog 編程無法一蹴而就,語言層次講究“名正則言順”
之三:數(shù)字邏輯不容小窺,電路門一統(tǒng)江湖
之五:Verilog 不難學(xué),聊聊時(shí)序邏輯那些事兒