【Verilog我思我用】-generate
【Verilog我思我用】-generate
在使用xilinx官方例程《XAPP585》實(shí)現(xiàn)CameraLink接口發(fā)送或者接收數(shù)據(jù)時(shí),有個(gè)程序還是值得學(xué)習(xí)的,下面把這段程序截出來:
genvar?i?; genvar?j?;generate for?(i?=?0?;?i?<=?(N-1)?;?i?=?i+1) begin?:?loop0serdes_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的用法,整個(gè)文件的功能是實(shí)現(xiàn)可選多通道數(shù)據(jù)發(fā)送,我們知道Cameralink中對于多通道傳輸時(shí)有一部分功能代碼時(shí)相同的,只不過需要多通道復(fù)用,我們知道generate有一個(gè)功能就是重復(fù)操作多個(gè)模塊的實(shí)例引用,當(dāng)然就適合本例程。
下面我們先講一講generate的用法再結(jié)合代碼簡單講解一下,對于generate其實(shí)很好理解,只不過寫出來比較難。
generate用法
關(guān)鍵字generate和endgenerate(和begin / end類似)作為使用語法的起點(diǎn),有三種衍生結(jié)構(gòu),分別為:
generate - for 語句結(jié)構(gòu)
generate - if 語句結(jié)構(gòu)
generate - case 語句結(jié)構(gòu)
使用generate的情況主要如下:
使用 for 循環(huán)對模塊進(jìn)行多次相似實(shí)例化
使用參數(shù)更改模塊的結(jié)構(gòu)或設(shè)計(jì)
使用帶有斷言語句進(jìn)行功能和形式驗(yàn)證
在這里我們思考一下,generate是在運(yùn)行中構(gòu)造重復(fù)模塊嗎??
答案是否定的,generate語句不是運(yùn)行時(shí)構(gòu)造。如果你想一想,這個(gè)generate結(jié)構(gòu)實(shí)際上是在創(chuàng)建一個(gè)重復(fù)電路,我們不能即時(shí)添加或刪除硬件電路,所以generate在綜合過程中其實(shí)是重復(fù)構(gòu)造相似電路,而不是在運(yùn)行時(shí)構(gòu)造。
下面先按照generate結(jié)構(gòu)分別舉例,然后舉例幾個(gè)常用案例。
generate - for語句結(jié)構(gòu)
在使用generate - for語句之前,我們需要先聲明一個(gè)變量genvar,用于for循環(huán)語句進(jìn)行判斷。
下面舉兩個(gè)不同應(yīng)用的例子:
上面兩個(gè)模塊功能一樣,第一個(gè)是對always 塊進(jìn)行了循環(huán);第二個(gè)則是對實(shí)例化時(shí)的模塊進(jìn)行了循環(huán)。xorLoop 是 generate 語句模塊名,目的是通過它對循環(huán)語句進(jìn)行層次化引用,所以在上面栗子中的 xorLoop 模塊相對層次名為 xorLoop[0].u_xor(后面會(huì)舉例說明)
這里在對比兩個(gè)常見的例子:
上面的例子功能也一樣,一個(gè)使用generate...for語句一個(gè)使用for語句,關(guān)于這兩者區(qū)別我會(huì)在文章最后總結(jié)里說明,大家可以自己先思考。
generate - if語句結(jié)構(gòu)
generate -if 語句結(jié)構(gòu)比較寬松,即不需要對不需要對generate語句進(jìn)行命名(generate...for主要是對循環(huán)語句進(jìn)行層次化引用) ,也不需要變量genvar。由于 generate - if 語句結(jié)構(gòu)是通過判斷語句執(zhí)行代碼塊,這就決定了每次最多執(zhí)行一個(gè)代碼塊,這種情況下,可以對各個(gè)代碼塊使用相同命名是合法的,且有助于保持對代碼的層次化引用。
需要注意的一點(diǎn)是,在 generate 塊中的判斷條件必須是常量!
generate - case
generate - case 語句和 generate - if 語句核心思想都是進(jìn)行條件判斷,用法基本一致。
和 generate - if 語句一樣,case 判斷條件必須是常量。
下面按照應(yīng)用場景舉例:
循環(huán)生成構(gòu)造
循環(huán)生成構(gòu)造提供了一種簡單而簡潔的方法來創(chuàng)建模塊項(xiàng)的多個(gè)實(shí)例,例如模塊實(shí)例、分配語句、斷言、接口實(shí)例等。你可以把它想象成一臺(tái)“克隆機(jī)”。
本質(zhì)上,它是一種特殊類型的for循環(huán),其循環(huán)索引變量為 datatype genvar。這是一個(gè)有趣的事實(shí)- genvar它是一個(gè)整數(shù)數(shù)據(jù)類型,僅在綜合時(shí)存在并在運(yùn)行時(shí)消失。
我們看到的《XAPP585》的例程就是這種運(yùn)行結(jié)構(gòu),下面再舉例看下該語句的特點(diǎn):
/**?Example?1?*/ /***?16?input?mux**?Example?of?how?to?use?Loop?Generate?Construct*/ module?mux_16(input??logic?[0:15]?[127:0]?mux_in,input??logic?[3:0]?select,output?logic?[127:0]?mux_out );logic?[0:15]?[127:0]?temp;//?The?for-loop?creates?16?assign?statementsgenvar?i;generatefor?(i=0;?i?<?16;?i++)?beginassign?temp[i]?=?(select?==?i)???mux_in[i]?:?0;endendgenerateassign?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仿真文件如下:
`timescale?1ns/1ps /***?Testbench?to?exercise?the?mux_16?module.*?Here?we?instantiate?the?mux?4?times.?Each?instance?is*?fed?a?different?input?with?different?input?`select`?and*?the?output?is?observed.*/ module?tb_mux_16;logic???????????????clk; logic?[0:15][127:0]?test_in[4]; logic?[3:0]?????????test_select[4]; logic?[127:0]???????test_out[4];int?i,?j,?k;initial?beginclk?=?0;forever?#1ns?clk?=?~clk; endinitial?begin//?Set?inputsfor?(i=0;?i?<?4;?i++)?beginfor?(j=0;?j?<?16;?j++)?begintest_in[i][j]?=?127'habcd_0000?+?(i?<<?8)?+?j;endtest_select[i]?=?i;end#2ns;//?Print?outputsfor(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++)?begintest_select[i]?=?10?+?i;end#2ns;//?Print?outputs?againfor(k=0;?k?<?4;?k++)?begin$display("test_out[%0d]?=?0x%x",?k,?test_out[k]);end#10ns;$finish; endgenvar?m; generatefor?(m=0;?m?<?4;?m++)?begin:?MUXmux_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 循環(huán)。只需確保genvars將外部循環(huán)和內(nèi)部循環(huán)分開使用,并在嵌套的 for 循環(huán)中引用這些變量時(shí)要小心,這是一個(gè)經(jīng)常犯錯(cuò)誤的地方。
條件生成構(gòu)造
條件生成構(gòu)造允許根據(jù)在模塊實(shí)例化期間傳遞的參數(shù)值更改設(shè)計(jì)結(jié)構(gòu)。這在為設(shè)計(jì)創(chuàng)建參數(shù)化通用 RTL 模塊時(shí)非常有用。
一個(gè)簡單的例子:
/**?Example?2.1?*/ /***?A?simple?generate?example.?This?paramerter?OPERATION_TYPE,*?passed?when?this?module?is?instantiated,?is?used?to?select*?the?operation?between?inputs?`a`?and?`b`.*/ module?conditional_generate#(parameter?OPERATION_TYPE?=?0)(input??logic?[31:0]?a,input??logic?[31:0]?b,output?logic?[63:0]?z);//?The?generate-endgenerate?keywords?are?optional.//?It?is?the?act?of?doing?a?conditional?operation//?on?a?parameter?that?makes?this?a?generate?block.generateif?(OPERATION_TYPE?==?0)?beginassign?z?=?a?+?b;endelse?if?(OPERATION_TYPE?==?1)?beginassign?z?=?a?-?b;endelse?if?(OPERATION_TYPE?==?2)?beginassign?z?=?(a?<<?1)?+?b;?//?2a+bendelse?beginassign?z?=?b?-?a;endendgenerate endmodule:?conditional_generate另一個(gè)例子 - 我們需要?jiǎng)?chuàng)建一個(gè)通用 CRC 生成器的任務(wù)。團(tuán)隊(duì)中的其他設(shè)計(jì)人員應(yīng)該能夠在 3 個(gè)多項(xiàng)式中選擇 1 個(gè)進(jìn)行 CRC 計(jì)算。
這是一種方法 - 提供一個(gè)名為 CRC_SEL 的參數(shù),該參數(shù)在此模塊實(shí)例化時(shí)使用,此CRC_SEL參數(shù)用來選擇在模塊中生成哪個(gè) CRC 函數(shù)。通過使用generate而不是簡單的多路復(fù)用器,可以節(jié)省一堆門電路和觸發(fā)器,因?yàn)椴恍枰?CRC 函數(shù)不會(huì)被實(shí)例化。
完整代碼如下:
/***?CRC?generator?module.?Select?the?desired?polynomial*?using?the?CRC_SEL?parameter.*?*?Default?polynomial?:?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`?when?driving?the?first?valid?byte*?+?Strobe?`done`?one?clk?after?driving?the?last?valid?byte*?+?The?final?CRC?is?available?1?clk?after?the?last?valid?byte*???is?driven.?This?is?the?same?cycle?you'll?drive?`done`.**/ module?crc_gen#(parameter?CRC_SEL?=?0)(input??logic?clk,input??logic?rst,input??logic?start,input??logic?done,input??logic?[7:0]?data_in,input??logic?[15:0]?crc_in,output?logic?[15:0]?crc_out);logic?[7:0]??data_in_d;logic?[15:0]?crc_in_d;assign?crc_in_d?=?(start?|?done)???16'd0?:?crc_in;assign?data_in_d?=?(done)???8'd0?:?data_in;always_ff?@(posedge?clk)?beginif?(rst)?begincrc_out?<=?'d0;endelse?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?//?<generate_blk_name>.<function_name>crc_out?<=?crc_poly.nextCRC16_D8(data_in_d,?crc_in_d);endend//?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.generatecase?(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;endfunctionend1: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;endfunctionenddefault:?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;endfunctionendendcaseendgenerateendmodule:?crc_gen下面是仿真文件及結(jié)果:
`timescale?1ns/1ps /***?Testbench?to?exercise?the?mux_16?module.*?Here?we?instantiate?the?mux?4?times.?Each?instance?is*?fed?a?different?input?with?different?input?`select`?and*?the?output?is?observed.*/ module?tb_mux_16;logic???????????????clk; logic?[0:15][127:0]?test_in[4]; logic?[3:0]?????????test_select[4]; logic?[127:0]???????test_out[4];int?i,?j,?k;initial?beginclk?=?0;forever?#1ns?clk?=?~clk; endinitial?begin//?Set?inputsfor?(i=0;?i?<?4;?i++)?beginfor?(j=0;?j?<?16;?j++)?begintest_in[i][j]?=?127'habcd_0000?+?(i?<<?8)?+?j;endtest_select[i]?=?i;end#2ns;//?Print?outputsfor(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++)?begintest_select[i]?=?10?+?i;end#2ns;//?Print?outputs?againfor(k=0;?k?<?4;?k++)?begin$display("test_out[%0d]?=?0x%x",?k,?test_out[k]);end#10ns;$finish; endgenvar?m; generatefor?(m=0;?m?<?4;?m++)?begin:?MUXmux_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斷言和形式驗(yàn)證
generate - case 語句結(jié)構(gòu)在編寫斷言時(shí)也非常有用,這反過來有助于形式驗(yàn)證。
如果對形式驗(yàn)證有任何經(jīng)驗(yàn),那么就會(huì)知道形式工具在嘗試證明屬性時(shí)很快就會(huì)遇到計(jì)算界限。因此,重要的是要保持屬性簡短而簡單。
例如,如果有一個(gè)具有 8 個(gè) REQquest 輸入和 8 個(gè) ACK 輸出的仲裁器塊,那么與其編寫單個(gè)斷言來覆蓋所有 8 個(gè) REQ/ACK 對,不如將其分解為具有 1 個(gè) REQ/ACK 的 8 個(gè)單獨(dú)的斷言對。
/**?Example?3.1?*/ genvar?k; generatefor?(k=0;?k?<?8;?k++)?beginreq_a:?assert?property?(req[k]?|=>?ack[k]);end endgenerate分層訪問生成塊
絆倒人們的一件事是如何訪問位于生成塊內(nèi)的模塊項(xiàng)。
生成塊有一個(gè)名字。如果不為其命名,編譯器將自動(dòng)分配一個(gè)通用名稱,例如genblk01,genblk02通常必須轉(zhuǎn)儲(chǔ) wave 并查看Visualizer工具以查看分配了哪些名稱。
要訪問生成塊中的模塊項(xiàng),必須分層訪問它<generate_blk_name>.<module_item_name>。
這是來自 SystemVerilog LRM 1800-2012 的一個(gè)很好的示例(示例 4 第 27.5 節(jié))。查看如何訪問塊中定義的任務(wù)和模塊實(shí)例。
分層實(shí)例名稱為:
memory.word16[3].p,?memory.word16[2].p, memory.word16[1].p,?memory.word16[0].p, /**?Example?4?*/ module?dimm(addr,?ba,?rasx,?casx,?csx,?wex,?cke,?clk,?dqm,?data,?dev_id);parameter?[31:0]?MEM_WIDTH?=?16,?MEM_SIZE?=?8;...?genvar?i;case?({MEM_SIZE,?MEM_WIDTH}){32'd8,?32'd16}:?//?8Meg?x?16?bits?widebegin:?memoryfor?(i=0;?i<4;?i=i+1)?begin:word16sms_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_memendtask?read_mem;input?[31:0]?address;output?[63:0]?data;begin?//?call?read_mem?in?sms?moduleword[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]);endendtask?end...endcase endmodule總結(jié)
這篇文章是在閱讀《XAPP585》代碼時(shí)候看著generate語法極其方便,所以引出了該篇文章,下面說下generate...for和for的區(qū)別:
首先第二個(gè)代碼時(shí)錯(cuò)誤的!
只有當(dāng) for 循環(huán)在 generate 中時(shí),才能將 always 放在 for 循環(huán)中!
generate for 循環(huán)和常規(guī) for 循環(huán)之間的主要區(qū)別在于 generate for 循環(huán)是為每次迭代生成一個(gè)實(shí)例。這意味著在示例中將有 3 個(gè) always 塊(與常規(guī)循環(huán)情況下的 1 個(gè)塊相反)。
一個(gè)更好的例子是:
module?A(); .. endmodule;module?B(); parameter?NUM_OF_A_MODULES?=?2;?//?should?be?overriden?from?higher?hierarchy genvar?i; for?(i=0?i<NUM_OF_A_MODULES;?i=i+1)?{A?A_inst(); } endmodule;在此示例中,常規(guī) for 無法完成創(chuàng)建 NUM_OF_A_MODULES 實(shí)例的工作。
參考
https://blog.csdn.net/weixin_42150654/article/details/123132249
夏宇聞. Verilog數(shù)字系統(tǒng)設(shè)計(jì)教程(第三版)[M]. 北京: 北京航空航天大學(xué)出版社, 2013: 68.
數(shù)字IC小站, Verilog中g(shù)enerate的使用[EB/OL], https://zhuanlan.zhihu.com/p/107047600, 2020-02-15.
Formal Verification - Erik Seligman, et al.
題目練習(xí)
HDLbits上有兩道使用Generate的題目,如下:
https://hdlbits.01xz.net/wiki/Adder100i
https://hdlbits.01xz.net/wiki/Bcdadd100
無需注冊,可以練習(xí)Generate的語法使用,同時(shí)還有仿真。后續(xù)我會(huì)在《HDLBits: 在線學(xué)習(xí) SystemVerilog》系列解析這兩個(gè)題目。?
總結(jié)
以上是生活随笔為你收集整理的【Verilog我思我用】-generate的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【AndroidUI设计】Button按
- 下一篇: 四国军棋之偷鸡