在使用xilinx官方例程《XAPP585》實現CameraLink接口發送或者接收數據時,有個程序還是值得學習的,下面把這段程序截出來:
genvari; genvarj; generate for(i=0;i<=?(N-1)?;?i?=?i+1) begin?:?loop0 serdes_7_to_1_diff_sdr?#( ???????.D???(D), ???????.DATA_FORMAT??(DATA_FORMAT)) dataout?( ?.dataout_p????(dataout_p[D*(i+1)-1:D*i]), ?.dataout_n????(dataout_n[D*(i+1)-1:D*i]), ?.clkout_p????(clkout_p[i]), ?.clkout_n????(clkout_n[i]), ?.txclk??????(txclk), ?.pixel_clk??????(pixel_clk), ?.reset?????(reset), ?.clk_pattern????(clk_pattern), ?.datain????(datain[(D*(i+1)*7)-1:D*i*7]));?? end endgenerate?
主要是generate的用法,整個文件的功能是實現可選多通道數據發送,我們知道Cameralink中對于多通道傳輸時有一部分功能代碼時相同的,只不過需要多通道復用,我們知道generate有一個功能就是重復操作多個模塊的實例引用,當然就適合本例程。
下面我們先講一講generate的用法再結合代碼簡單講解一下,對于generate其實很好理解,只不過寫出來比較難。
generate用法
關鍵字generate和endgenerate(和begin / end類似)作為使用語法的起點,有三種衍生結構,分別為:
generate - for 語句結構
generate - if 語句結構
generate - case 語句結構
使用generate的情況主要如下:
使用 for 循環對模塊進行多次相似實例化
使用參數更改模塊的結構或設計
使用帶有斷言語句進行功能和形式驗證
在這里我們思考一下,generate是在運行中構造重復模塊嗎??
答案是否定的,generate語句不是運行時構造。如果你想一想,這個generate結構實際上是在創建一個重復電路,我們不能即時添加或刪除硬件電路,所以generate在綜合過程中其實是重復構造相似電路,而不是在運行時構造。
下面先按照generate結構分別舉例,然后舉例幾個常用案例。
generate - for語句結構
在使用generate - for語句之前,我們需要先聲明一個變量genvar,用于for循環語句進行判斷。
下面舉兩個不同應用的例子:
上面兩個模塊功能一樣,第一個是對always 塊進行了循環;第二個則是對實例化時的模塊進行了循環。xorLoop 是 generate 語句模塊名,目的是通過它對循環語句進行層次化引用,所以在上面栗子中的 xorLoop 模塊相對層次名為 xorLoop[0].u_xor(后面會舉例說明)
這里在對比兩個常見的例子:
上面的例子功能也一樣,一個使用generate...for語句一個使用for語句,關于這兩者區別我會在文章最后總結里說明,大家可以自己先思考。
generate - if語句結構
generate -if 語句結構比較寬松,即不需要對不需要對generate語句進行命名(generate...for主要是對循環語句進行層次化引用) ,也不需要變量genvar。由于 generate - if 語句結構是通過判斷語句執行代碼塊,這就決定了每次最多執行一個代碼塊,這種情況下,可以對各個代碼塊使用相同命名是合法的,且有助于保持對代碼的層次化引用。
需要注意的一點是,在 generate 塊中的判斷條件必須是常量!
generate - case
generate - case 語句和 generate - if 語句核心思想都是進行條件判斷,用法基本一致。
和 generate - if 語句一樣,case 判斷條件必須是常量。
下面按照應用場景舉例:
循環生成構造
循環生成構造提供了一種簡單而簡潔的方法來創建模塊項的多個實例,例如模塊實例、分配語句、斷言、接口實例等。你可以把它想象成一臺“克隆機”。
本質上,它是一種特殊類型的for循環,其循環索引變量為 datatype genvar。這是一個有趣的事實- genvar它是一個整數數據類型,僅在綜合時存在并在運行時消失。
我們看到的《XAPP585》的例程就是這種運行結構,下面再舉例看下該語句的特點:
/**Example1*/ /** *16inputmux * *ExampleofhowtouseLoopGenerateConstruct */ modulemux_16( inputlogic[0:15][127:0]mux_in, inputlogic[3:0]select, outputlogic[127:0]mux_out ); logic[0:15][127:0]temp; //Thefor-loopcreates16assignstatements genvari; generate for(i=0;i16;?i++)?begin ????????????assign?temp[i]?=?(select?==?i)???mux_in[i]?:?0; ????????end ????endgenerate ????assign?mux_out?=?temp[0]?|?temp[1]?|?temp[2]?|?temp[3]?| ?????????????????????temp[4]?|?temp[5]?|?temp[6]?|?temp[7]?| ?????????????????????temp[8]?|?temp[9]?|?temp[10]?|?temp[11]?| ?????????????????????temp[12]?|?temp[13]?|?temp[14]?|?temp[15]; endmodule:?mux_16
仿真文件如下:
`timescale1ns/1ps /** *Testbenchtoexercisethemux_16module. *Hereweinstantiatethemux4times.Eachinstanceis *fedadifferentinputwithdifferentinput`select`and *theoutputisobserved. */ moduletb_mux_16; logicclk; logic[0:15][127:0]test_in[4]; logic[3:0]test_select[4]; logic[127:0]test_out[4]; inti,j,k; initialbegin clk=0; forever#1nsclk=~clk; end initialbegin //Setinputs for(i=0;i4;?i++)?begin ????????for?(j=0;?j?16;?j++)?begin ????????????test_in[i][j]?=?127'habcd_0000?+?(i?<8)?+?j; ????????end ????????test_select[i]?=?i; ????end ????#2ns; ????//?Print?outputs ????for(k=0;?k?4;?k++)?begin ????????$display("test_out[%0d]?=?0x%x",?k,?test_out[k]); ????end ????#2ns; ????//?Change?input?select? ????for?(i=0;?i?4;?i++)?begin ????????test_select[i]?=?10?+?i; ????end ????#2ns; ????//?Print?outputs?again ????for(k=0;?k?4;?k++)?begin ????????$display("test_out[%0d]?=?0x%x",?k,?test_out[k]); ????end ????#10ns; ????$finish; end genvar?m; generate ????for?(m=0;?m?4;?m++)?begin:?MUX ???????mux_16?imux_16?( ???????????.mux_in(test_in[m]), ???????????.select(test_select[m]), ???????????.mux_out(test_out[m]) ???????);? ????end endgenerate endmodule:?tb_mux_16
我們還可以嵌套generate...for 循環。只需確保genvars將外部循環和內部循環分開使用,并在嵌套的 for 循環中引用這些變量時要小心,這是一個經常犯錯誤的地方。
條件生成構造
條件生成構造允許根據在模塊實例化期間傳遞的參數值更改設計結構。這在為設計創建參數化通用 RTL 模塊時非常有用。
一個簡單的例子:
/**Example2.1*/ /** *Asimplegenerateexample.ThisparamerterOPERATION_TYPE, *passedwhenthismoduleisinstantiated,isusedtoselect *theoperationbetweeninputs`a`and`b`. */ moduleconditional_generate #(parameterOPERATION_TYPE=0) ( inputlogic[31:0]a, inputlogic[31:0]b, outputlogic[63:0]z ); //Thegenerate-endgeneratekeywordsareoptional. //Itistheactofdoingaconditionaloperation //onaparameterthatmakesthisagenerateblock. generate if(OPERATION_TYPE==0)begin assignz=a+b; end elseif(OPERATION_TYPE==1)begin assignz=a-b; end elseif(OPERATION_TYPE==2)begin assignz=(a<1)?+?b;?//?2a+b ????????end ????????else?begin ????????????assign?z?=?b?-?a; ????????end ????endgenerate endmodule:?conditional_generate
另一個例子 - 我們需要創建一個通用 CRC 生成器的任務。團隊中的其他設計人員應該能夠在 3 個多項式中選擇 1 個進行 CRC 計算。
這是一種方法 - 提供一個名為 CRC_SEL 的參數,該參數在此模塊實例化時使用,此CRC_SEL參數用來選擇在模塊中生成哪個 CRC 函數。通過使用generate而不是簡單的多路復用器,可以節省一堆門電路和觸發器,因為不需要的 CRC 函數不會被實例化。
完整代碼如下:
/** *CRCgeneratormodule.Selectthedesiredpolynomial *usingtheCRC_SELparameter. * *Defaultpolynomial:x^16+x^15+x^2+1 *CRC_SEL=0:x^16+x^1+1 *CRC_SEL=1:x^16+x^12+x^5+1 * *USAGE: *+Strobe`start`whendrivingthefirstvalidbyte *+Strobe`done`oneclkafterdrivingthelastvalidbyte *+ThefinalCRCisavailable1clkafterthelastvalidbyte *isdriven.Thisisthesamecycleyou'lldrive`done`. * */ modulecrc_gen #(parameterCRC_SEL=0) ( inputlogicclk, inputlogicrst, inputlogicstart, inputlogicdone, inputlogic[7:0]data_in, inputlogic[15:0]crc_in, outputlogic[15:0]crc_out ); logic[7:0]data_in_d; logic[15:0]crc_in_d; assigncrc_in_d=(start|done)?16'd0:crc_in; assigndata_in_d=(done)?8'd0:data_in; always_ff@(posedgeclk)begin if(rst)begin crc_out<=?'d0; ????????end ????????else?begin ????????????//?Generate?blocks?are?always?assigned?a?name.?If ????????????//?you?don't?name?the?generate?block,?it?will?be ????????????//?given?a?default?auto?generated?name. ????????????// ????????????//?To?invoke?a?function?within?a?generate?block, ????????????//?hierarchically?call?it? ????????????//?. crc_out<=?crc_poly.nextCRC16_D8(data_in_d,?crc_in_d); ????????end ????end ????//?Once?again?the?generate-endgenerate?keywords?are?optional ????//?It?is?the?act?of?using?a?parameter,?CRC_SEL,?in?the?case ????//?statement?that?makes?it?a?generate?block ????// ????//?Also?notice?how?all?the?generate?blocks?are?given?the?same ????//?name?`crc_poly`?and?all?the?function?names?are?the?same ????//?`nextCRC16_D8`.?This?is?correct?because?only?one?of?the ????//?function?declarations?is?compiled?in?during?elaboration ????//?phase. ????generate ????case?(CRC_SEL) ????????0:? ????????begin:?crc_poly ????????????//?polynomial:?x^16?+?x^1?+?1 ????????????//?data?width:?8 ????????????//?convention:?the?first?serial?bit?is?D[7] ????????????function?automatic?[15:0]?nextCRC16_D8; ???????????? ????????????????input?[7:0]?Data; ????????????????input?[15:0]?crc; ????????????????reg?[7:0]?d; ????????????????reg?[15:0]?c; ????????????????reg?[15:0]?newcrc; ????????????????d?=?Data; ????????????????c?=?crc; ???????????????? ????????????????newcrc[0]?=?d[0]?^?c[8]; ????????????????newcrc[1]?=?d[1]?^?d[0]?^?c[8]?^?c[9]; ????????????????newcrc[2]?=?d[2]?^?d[1]?^?c[9]?^?c[10]; ????????????????newcrc[3]?=?d[3]?^?d[2]?^?c[10]?^?c[11]; ????????????????newcrc[4]?=?d[4]?^?d[3]?^?c[11]?^?c[12]; ????????????????newcrc[5]?=?d[5]?^?d[4]?^?c[12]?^?c[13]; ????????????????newcrc[6]?=?d[6]?^?d[5]?^?c[13]?^?c[14]; ????????????????newcrc[7]?=?d[7]?^?d[6]?^?c[14]?^?c[15]; ????????????????newcrc[8]?=?d[7]?^?c[0]?^?c[15]; ????????????????newcrc[9]?=?c[1]; ????????????????newcrc[10]?=?c[2]; ????????????????newcrc[11]?=?c[3]; ????????????????newcrc[12]?=?c[4]; ????????????????newcrc[13]?=?c[5]; ????????????????newcrc[14]?=?c[6]; ????????????????newcrc[15]?=?c[7]; ????????????????nextCRC16_D8?=?newcrc; ????????????endfunction ????????end ????????1: ????????begin:?crc_poly ????????????//?polynomial:?x^16?+?x^12?+?x^5?+?1 ????????????//?data?width:?8 ????????????//?convention:?the?first?serial?bit?is?D[7] ????????????function?automatic?[15:0]?nextCRC16_D8; ???????????? ????????????????input?[7:0]?Data; ????????????????input?[15:0]?crc; ????????????????reg?[7:0]?d; ????????????????reg?[15:0]?c; ????????????????reg?[15:0]?newcrc; ????????????????d?=?Data; ????????????????c?=?crc; ???????????????? ????????????????newcrc[0]?=?d[4]?^?d[0]?^?c[8]?^?c[12]; ????????????????newcrc[1]?=?d[5]?^?d[1]?^?c[9]?^?c[13]; ????????????????newcrc[2]?=?d[6]?^?d[2]?^?c[10]?^?c[14]; ????????????????newcrc[3]?=?d[7]?^?d[3]?^?c[11]?^?c[15]; ????????????????newcrc[4]?=?d[4]?^?c[12]; ????????????????newcrc[5]?=?d[5]?^?d[4]?^?d[0]?^?c[8]?^?c[12]?^?c[13]; ????????????????newcrc[6]?=?d[6]?^?d[5]?^?d[1]?^?c[9]?^?c[13]?^?c[14]; ????????????????newcrc[7]?=?d[7]?^?d[6]?^?d[2]?^?c[10]?^?c[14]?^?c[15]; ????????????????newcrc[8]?=?d[7]?^?d[3]?^?c[0]?^?c[11]?^?c[15]; ????????????????newcrc[9]?=?d[4]?^?c[1]?^?c[12]; ????????????????newcrc[10]?=?d[5]?^?c[2]?^?c[13]; ????????????????newcrc[11]?=?d[6]?^?c[3]?^?c[14]; ????????????????newcrc[12]?=?d[7]?^?d[4]?^?d[0]?^?c[4]?^?c[8]?^?c[12]?^?c[15]; ????????????????newcrc[13]?=?d[5]?^?d[1]?^?c[5]?^?c[9]?^?c[13]; ????????????????newcrc[14]?=?d[6]?^?d[2]?^?c[6]?^?c[10]?^?c[14]; ????????????????newcrc[15]?=?d[7]?^?d[3]?^?c[7]?^?c[11]?^?c[15]; ????????????????nextCRC16_D8?=?newcrc; ????????????endfunction ????????end ????????default:? ????????????begin:?crc_poly ????????????//?polynomial:?x^16?+?x^15?+?x^2?+?1 ????????????//?data?width:?8 ????????????//?convention:?the?first?serial?bit?is?D[7] ????????????function?automatic?[15:0]?nextCRC16_D8; ???????????? ????????????????input?[7:0]?Data; ????????????????input?[15:0]?crc; ????????????????reg?[7:0]?d; ????????????????reg?[15:0]?c; ????????????????reg?[15:0]?newcrc; ????????????????d?=?Data; ????????????????c?=?crc; ???????????????? ????????????????newcrc[0]?=?d[7]?^?d[6]?^?d[5]?^?d[4]?^?d[3]?^?d[2]?^?d[1]?^?d[0]?^?c[8]?^?c[9]?^?c[10]?^?c[11]?^?c[12]?^?c[13]?^?c[14]?^?c[15]; ????????????????newcrc[1]?=?d[7]?^?d[6]?^?d[5]?^?d[4]?^?d[3]?^?d[2]?^?d[1]?^?c[9]?^?c[10]?^?c[11]?^?c[12]?^?c[13]?^?c[14]?^?c[15]; ????????????????newcrc[2]?=?d[1]?^?d[0]?^?c[8]?^?c[9]; ????????????????newcrc[3]?=?d[2]?^?d[1]?^?c[9]?^?c[10]; ????????????????newcrc[4]?=?d[3]?^?d[2]?^?c[10]?^?c[11]; ????????????????newcrc[5]?=?d[4]?^?d[3]?^?c[11]?^?c[12]; ????????????????newcrc[6]?=?d[5]?^?d[4]?^?c[12]?^?c[13]; ????????????????newcrc[7]?=?d[6]?^?d[5]?^?c[13]?^?c[14]; ????????????????newcrc[8]?=?d[7]?^?d[6]?^?c[0]?^?c[14]?^?c[15]; ????????????????newcrc[9]?=?d[7]?^?c[1]?^?c[15]; ????????????????newcrc[10]?=?c[2]; ????????????????newcrc[11]?=?c[3]; ????????????????newcrc[12]?=?c[4]; ????????????????newcrc[13]?=?c[5]; ????????????????newcrc[14]?=?c[6]; ????????????????newcrc[15]?=?d[7]?^?d[6]?^?d[5]?^?d[4]?^?d[3]?^?d[2]?^?d[1]?^?d[0]?^?c[7]?^?c[8]?^?c[9]?^?c[10]?^?c[11]?^?c[12]?^?c[13]?^?c[14]?^?c[15]; ????????????????nextCRC16_D8?=?newcrc; ????????????endfunction ????????end ????endcase ????endgenerate endmodule:?crc_gen
下面是仿真文件及結果:
`timescale1ns/1ps /** *Testbenchtoexercisethemux_16module. *Hereweinstantiatethemux4times.Eachinstanceis *fedadifferentinputwithdifferentinput`select`and *theoutputisobserved. */ moduletb_mux_16; logicclk; logic[0:15][127:0]test_in[4]; logic[3:0]test_select[4]; logic[127:0]test_out[4]; inti,j,k; initialbegin clk=0; forever#1nsclk=~clk; end initialbegin //Setinputs for(i=0;i4;?i++)?begin ????????for?(j=0;?j?16;?j++)?begin ????????????test_in[i][j]?=?127'habcd_0000?+?(i?<8)?+?j; ????????end ????????test_select[i]?=?i; ????end ????#2ns; ????//?Print?outputs ????for(k=0;?k?4;?k++)?begin ????????$display("test_out[%0d]?=?0x%x",?k,?test_out[k]); ????end ????#2ns; ????//?Change?input?select? ????for?(i=0;?i?4;?i++)?begin ????????test_select[i]?=?10?+?i; ????end ????#2ns; ????//?Print?outputs?again ????for(k=0;?k?4;?k++)?begin ????????$display("test_out[%0d]?=?0x%x",?k,?test_out[k]); ????end ????#10ns; ????$finish; end genvar?m; generate ????for?(m=0;?m?4;?m++)?begin:?MUX ???????mux_16?imux_16?( ???????????.mux_in(test_in[m]), ???????????.select(test_select[m]), ???????????.mux_out(test_out[m]) ???????);? ????end endgenerate endmodule:?tb_mux_16 Footer ?2022?GitHub,?Inc. Footer?navigation Terms Privacy Security Status Docs Cont
斷言和形式驗證
generate - case 語句結構在編寫斷言時也非常有用,這反過來有助于形式驗證。
如果對形式驗證有任何經驗,那么就會知道形式工具在嘗試證明屬性時很快就會遇到計算界限。因此,重要的是要保持屬性簡短而簡單。
例如,如果有一個具有 8 個 REQquest 輸入和 8 個 ACK 輸出的仲裁器塊,那么與其編寫單個斷言來覆蓋所有 8 個 REQ/ACK 對,不如將其分解為具有 1 個 REQ/ACK 的 8 個單獨的斷言對。
/**Example3.1*/ genvark; generate for(k=0;k8;?k++)?begin ????????req_a:?assert?property?(req[k]?|=>ack[k]); end endgenerate
分層訪問生成塊
絆倒人們的一件事是如何訪問位于生成塊內的模塊項。
生成塊有一個名字。如果不為其命名,編譯器將自動分配一個通用名稱,例如genblk01,genblk02通常必須轉儲 wave 并查看Visualizer工具以查看分配了哪些名稱。
要訪問生成塊中的模塊項,必須分層訪問它
這是來自 SystemVerilog LRM 1800-2012 的一個很好的示例(示例 4 第 27.5 節)。查看如何訪問塊中定義的任務和模塊實例。
分層實例名稱為:
memory.word16[3].p,memory.word16[2].p, memory.word16[1].p,memory.word16[0].p,
/**Example4*/ moduledimm(addr,ba,rasx,casx,csx,wex,cke,clk,dqm,data,dev_id); parameter[31:0]MEM_WIDTH=16,MEM_SIZE=8; ... genvari; case({MEM_SIZE,MEM_WIDTH}) {32'd8,32'd16}://8Megx16bitswide begin:memory for(i=0;i<4;?i=i+1)?begin:word16 ????????????????sms_08b216t0?p(.clk(clk),?.csb(csx),?.cke(cke),.ba(ba), ????????????????????.addr(addr),?.rasb(rasx),?.casb(casx), ????????????????????.web(wex),?.udqm(dqm[2*i+1]),?.ldqm(dqm[2*i]), ????????????????????.dqi(data[15+16*i:16*i]),?.dev_id(dev_id)); ????????????????//?The?hierarchical?instance?names?are: ????????????????//?memory.word16[3].p,?memory.word16[2].p, ????????????????//?memory.word16[1].p,?memory.word16[0].p, ????????????????//?and?the?task?memory.read_mem ????????????end ????????????task?read_mem; ????????????????input?[31:0]?address; ????????????????output?[63:0]?data; ????????????????begin?//?call?read_mem?in?sms?module ????????????????????word[3].p.read_mem(address,?data[63:48]); ????????????????????word[2].p.read_mem(address,?data[47:32]); ????????????????????word[1].p.read_mem(address,?data[31:16]); ????????????????????word[0].p.read_mem(address,?data[15:?0]); ????????????????end ????????????endtask? ????????end ????... ????endcase endmodule
總結
這篇文章是在閱讀《XAPP585》代碼時候看著generate語法極其方便,所以引出了該篇文章,下面說下generate...for和for的區別:
首先第二個代碼時錯誤的!
只有當 for 循環在 generate 中時,才能將 always 放在 for 循環中!
generate for 循環和常規 for 循環之間的主要區別在于 generate for 循環是為每次迭代生成一個實例。這意味著在示例中將有 3 個 always 塊(與常規循環情況下的 1 個塊相反)。
一個更好的例子是:
moduleA(); .. endmodule; moduleB(); parameterNUM_OF_A_MODULES=2;//shouldbeoverridenfromhigherhierarchy genvari; for(i=0i
在此示例中,常規 for 無法完成創建 NUM_OF_A_MODULES 實例的工作。
審核編輯:彭靜
-
接口
+關注
關注
33文章
8579瀏覽量
151029 -
數據
+關注
關注
8文章
7006瀏覽量
88944 -
代碼
+關注
關注
30文章
4780瀏覽量
68529
原文標題:【Verilog我思我用】-generate
文章出處:【微信號:Open_FPGA,微信公眾號:OpenFPGA】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論