加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

從電路到Verilog | IP設(shè)計(jì)可企及,宏和參數(shù)只是為了合并同類模塊

2016/08/16
51
  • 1評(píng)論
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

?

老衲第一次學(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

?????????

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

?????????

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


施主們就看到了生成塊是可以嵌套的,這個(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]

可綜合性要求

  • 內(nèi)部為變量聲明、模塊(例化)、連續(xù)賦值和 / 或 always 塊,并且內(nèi)部語句可綜合
  • 循環(huán)次數(shù)為常數(shù)
  • 內(nèi)部語句可綜合
  • 循環(huán)次數(shù)為常數(shù)


[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 里去!

之二:Verilog 編程無法一蹴而就,語言層次講究“名正則言順”

之三:數(shù)字邏輯不容小窺,電路門一統(tǒng)江湖

之四:Verilog 語言:還真的是人格分裂的語言

之五:Verilog 不難學(xué),聊聊時(shí)序邏輯那些事兒

之六:數(shù)字電路設(shè)計(jì):有理論、有電路、有代碼“三位一體”

之七:熟讀語言要素,不會(huì)編程也懂 verilog

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

本名:吳濤,通信專業(yè)博士,畢業(yè)后十多年從事無線通訊產(chǎn)品的研發(fā)工作。了解W-CDMA、TDS-CDMA和LTE的標(biāo)準(zhǔn)協(xié)議、接收機(jī)算法以及系統(tǒng)架構(gòu)和開發(fā)。從事過關(guān)于W-CDMA的FPGA IP core設(shè)計(jì)工作,也完成過W-CDMA和TDS-CDMA的接收機(jī)理論研究和鏈路仿真工作。綜合上面的工作,最終選擇了無線通訊的系統(tǒng)設(shè)計(jì)和標(biāo)準(zhǔn)設(shè)計(jì)工作。目前擁有100多個(gè)已授權(quán)的發(fā)明專利,是某通訊行業(yè)標(biāo)準(zhǔn)文件的第一作者,亦有專利思想被寫入3GPP協(xié)議。已出版FPGA設(shè)計(jì)專業(yè)著作《IP核芯志-數(shù)字邏輯設(shè)計(jì)思想》和《Verilog傳奇-從電路出發(fā)的HDL代碼設(shè)計(jì)》。