加入星計劃,您可以享受以下權益:

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

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

2016/08/16
50
  • 1評論
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

?

老衲第一次學習 Verilog 語言,基本就到前面幾講的程度,頂多加上了解`define 宏定義。于是對于能設計 IP 核的人,那是佩服的五體投地,如黃河泛濫一發(fā)不可收拾。直到 Verilog 2001 出了參數(shù)(parameter)和生成塊(generate)功能,做 IP 核就成了人人可以掌握的技能了。對頭,下面老僧就和施主們講這些內容。


同樣的代碼 / 模塊要簡化不難,這就是所謂模塊化的作用。但是類似的模塊 ---- 例如同樣是計數(shù)器,只是內部 D 觸發(fā)器位寬的不同 ---- 要聚類就需要一些技巧了,因為電路是不支持結構靈活變化的。為了能夠合并類似模塊,Verilog 發(fā)展出了宏和參數(shù)這兩個玩意。


最后呢,在 Verilog 2001 里面,設計了塊生成的功能。這個功能使得用戶可以更加簡單的根據(jù)外部的輸入,靈活的產生最佳的電路結構。


1. 參數(shù)定義,結構變化
參數(shù)的定義方法和傳遞方式見表 1 所示。定義有兩種方法,傳遞也有兩種方法,在加上參數(shù)列表的兩種表示,又是一個“一題多解”。那種方式好?沒標準答案,按照施主喜歡的方式來即可。但是一個項目里面,最好選擇一種組合模式,方便其他人閱讀。


表 1 參數(shù)的定于與傳遞


“parameter_list”為參數(shù)列表,參數(shù)之間用逗號“,”隔開;其中,“parameter_name”是用戶定義的參數(shù)名稱,建議采用容易閱讀的命名方式;“parameter_initial_value”為該參數(shù)對應的初始值,在本模塊的實例未被定義參數(shù)的時候使用。


“parameter_list_seperated”為獨立于 module 定義之外的參數(shù)列表,需要在模塊行為描述之前書寫。它由若干個關鍵字“parameter”開頭的參數(shù)定義構成;如果一個“parameter”帶多個參數(shù)定義,則這些參數(shù)需要被逗號“,”隔離;每個“parameter”開頭的定義,末尾用分號“;”表示結束。


“parameter_assignment_list”是參數(shù)賦值列表,由關鍵字“defparam”開頭,后面是各個參數(shù)的賦值。對于每個參數(shù),參數(shù)名稱需要用“module_instance_index”指定是哪一個例化模塊對應的參數(shù);模塊索引“module_instance_index”的格式是按照最高模塊例化名稱到最底層例化的順序排列,模塊例化名稱之間用點“.”隔離;“parameter_value”是對應參數(shù)的值;參數(shù)賦值之間用逗號“,”隔離,末尾需要有分號“;”。


“parameter_assignment_list_sequence”是按照定義順序的參數(shù)傳遞列表,其中各個對應參數(shù)值“parameter_value”的順序必須和定義時候的順序一致。


“parameter_assignment_list_named”是按照指定名稱的參數(shù)傳遞列表,其中各個對應參數(shù)值“parameter_value”有前面帶點“.”的參數(shù)名稱“parameter_name”指定。所以在參數(shù)的排列順序上可以任意。


當在實例化模塊的同時傳遞參數(shù)的時候,請注意模塊名稱之后是參數(shù)傳遞列表,然后才是模塊的實例名等,這個順序不能搞錯。參數(shù)傳遞列表被#(…)標記出來。


參數(shù)的值可以說明位寬,但是一般而言,參數(shù)的值是一個常數(shù)不需要定義位寬的。
代碼中引用參數(shù)的時候,直接使用其名稱,無需象宏定義那樣用“`”開頭。


例 2 給出了一個位寬和最大值參數(shù)化的計數(shù)器的例子,請參考。例子中,這個計數(shù)器完成到達最大值就清零的不停的計數(shù)功能。頂層模塊調用了兩個參數(shù)不同的計數(shù)器。

?


【例 2】位寬和最大值參數(shù)化的計數(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ù)值,這點要強調一下,變結構的電路是不可能被綜合的。這個常數(shù)值可以是已知的常數(shù),也可以通過常數(shù)之間的計算得到。


參數(shù)是給綜合軟件用的,所以在實現(xiàn)的電路里面不可能明顯的看到參數(shù)的值。


除了參數(shù),Verilog 2001 里面還定義了一個很多工程師都不知道干嘛用的本地參數(shù)“l(fā)ocalparam”。本地參數(shù)的定義方法和參數(shù)一樣,作用域也是相同的。這兩類參數(shù)的區(qū)別在于,本地參數(shù)不同通過參數(shù)傳遞方式進行修改。

?


2. 生成有塊,更加靈活
一般而言,生成塊要和參數(shù)功能合作,完成動態(tài)產生電路的作用。當然這個功能也可以不動態(tài)產生電路,但是這樣相當于用寶劍來做木匠活,不僅大材小用還不方便使用。


生成塊的關鍵詞是“generate”,英文產生的意思。一個生成塊被
generate
operations
endgenerate
這樣的框架包裹,其中“operations”是快生成的功能部分,用來描述實際有用的邏輯。生成塊功能分為:條件、case 和循環(huán)三個類型,待貧僧一一道來。


條件嘛,莫過于就是“if…else if…”的樣子了,看到這里的觀眾們應該可以耳熟能詳了。但是注意,生成塊的條件里面不是什么都可以裝的,可以用于生成塊的條件功能的內容僅限于:模塊(例化)、UDP、Verilog 門原語、連續(xù)賦值,initial 塊和 always 塊等。這個要注意,否則報錯是不可避免的。


生成塊的語法結構,想必大伙兒也猜得出:
generate
if (condition)
??? operation_1
else
??? operation_2
endgenerate


其中,“condition”是邏輯表達式,是判決條件。當判決條件為真的時候,進行“operation_1”的操作;否則,進行“operation_2”的操作。


就和 if 與 case 的關系一般,生成塊里面既然有 if 也少不得 case 來搭配。生成塊的 case 的語法結構是:
generate
case (constant_express)
value_1: operation_1
value_2: operation_2
……
value_n: operation_n
default: operation_default
endgenerate

如何使用,不必啰嗦。生成塊的 case 里面可以包含的內容和生成塊 if 的一樣的。強調一下,僅僅包括:模塊(例化)、UDP、Verilog 門原語、連續(xù)賦值,initial 塊和 always 塊。


如何選擇 if 和 case 的應用場景也是類似的情況,老衲也不多嘴。


看到上面的內容,施主們一定會產生輕敵的思想:生成塊莫過如此,easy!兵法有云:“驕兵必敗”,這個想法要不得。下面給大伙說說循環(huán)類型的生成塊,這個很容易引起歧義,老衲需要細細講解。


欲說循環(huán)生成,先要介紹生成索引變量“genvar”,其語法結構是:
genvar genvar_name_1, genvar_name_2, ……, genvar_name_n;
其中,“genvar_name”是不同循環(huán)索引變量的名稱,要求符合 Verilog 對于變量名名的要求。這個變量是和循環(huán)生成共生的,看起來像是循環(huán)里面的循環(huán)變量。實際中,它比循環(huán)變量的應用范圍專業(yè),只用于循環(huán)生成的電路模塊的“索引”。具體啥叫“索引”和“索引”啥,先買個關子,后面再說。


循環(huán)式生成塊的語法結構是
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 塊這幾個里面的一個或者幾個。“genvar”就是前面說到的生成索引變量。最大的不同是操作一定要有“begin……end”括在內部,而且“begin”之后要有“:? instant_name”這個結構。冒號表示風格,不多說;“instant_name”就是所謂的生成索引的名字。換句話說,“instant_name”表示 for 內部實現(xiàn)的模塊、變量的名稱,以防止混淆。


這里說了這么許多,很多施主肯定已經迷糊了,下來給個例子十分必要。

?


看一個簡單的例子,用循環(huán)生成做一個位寬參數(shù)化的加法鏈,如例 2 所示。由于最低比特是一個半加器,這個在代理里面特別處理了。特別說明一下,for 循環(huán)里面實現(xiàn)了若干個全加器,這些全加器的被命名為:full_adder[0].F,full_adder[1].F……這就是所謂的生成索引。


【例 2】位寬參數(shù)化的加法鏈(半加器外置)

代碼

電路代碼

綜合軟件內的代碼

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


施主們就看到了生成塊是可以嵌套的,這個一般的代碼類似。還是一句順口溜:嵌套用得好,寫核難不倒。意思是說:生成塊的嵌套用好了,自己寫 IP 核就是探囊取物一般簡單。這方面的資料相對較少,所以老僧就不厭其煩再給幾個例子。


還是例 3 場景,實現(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 語句里面對應的 if、case 和 for 很容易混淆,在最后貧僧在表 3 里面幫大家做了一個總結,請參考。


表 3 生成塊和 Verilog 語句的區(qū)別

?

生成塊:if 與 case

Verilog 語句的 if 與 case

功能

綜合軟件根據(jù)條件,判斷選擇的電路器件

直接產生電路

電路映射

不產生電路,隱式體現(xiàn)

產生電路,一般為選擇器

內部可包含

模塊(例化)、UDP、Verilog 門原語、連續(xù)賦值,initial 塊和 always 塊

Verilog 語句

條件表達式

參數(shù)化的常數(shù)表達式

常數(shù)表達式(不一定參數(shù)化)或者變量

可綜合性要求

內部為模塊(例化)、連續(xù)賦值和 / 或 always 塊,并且內部語句可綜合

內部語句可綜合

?

生成塊:for

Verilog 語句的 for

功能

綜合軟件循環(huán)次數(shù),實現(xiàn)電路

綜合軟件循環(huán)次數(shù),實現(xiàn)電路

電路映射

不產生電路,隱式體現(xiàn)

不產生電路,隱式體現(xiàn)

內部可包含

變量聲明、模塊(例化)、UDP、Verilog 門原語、連續(xù)賦值,initial 塊和 always 塊

其他可綜合語句(不能實現(xiàn)模塊例化等)

信號、模塊數(shù)量

可參數(shù)化生成

規(guī)模固定[1]

循環(huán)變量類型

生成索引變量

一般變量

格式

必須由“begin …… end”括住,而且 begin 后面需要“:? instant_name”結構

如果是單獨語句語法上可以沒有“begin …… end”括住[2]

可綜合性要求

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


[1]Verilog 語句 for 可以使得部分信號、模塊的實例在實際中不被使用,但是理論上這些信號、模塊依然被語言約束存在于電路中。當然好的綜合軟件會優(yōu)化掉這些無用的信號、模塊,但是這不是語法要求的。

[2]工程代碼里面不建議這樣的寫法。


由此可見,雖然生成塊和 Verilog 語句里面 if、case 和 for 的樣子完全一樣,但是應用場合卻是大相徑庭的。施主們一定要注意區(qū)別對待,用錯了地方可是要鬧笑話的。
這正是:



禹王量水號神針,大圣降妖棒一根。如今列位有福分,語言里面塊生成。
模塊選擇參數(shù)能,數(shù)目大小循環(huán)認。靈活運用仙界登,不怕小核把亂生。

與非網原創(chuàng)內容,謝絕轉載!

系列匯總:

之一:溫故而知新:從電路里來,到 Verilog 里去!

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

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

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

之五:Verilog 不難學,聊聊時序邏輯那些事兒

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

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

相關推薦

電子產業(yè)圖譜

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