大型Verilog代碼快速連線
我們在編寫一些比較復雜的Verilog代碼時,通常需要進行大量的手動連線工作,這種工作十分容易出錯,并且在代碼模塊的嵌套層級較多時,更改里層的一個代碼,可能就需要更改其外部一系列模塊的端口信息等,因此,使用emacs工具能快速實現大批量的、復雜的Verilog模塊之間的連線操作。
emacs安裝
在Ubuntu系統下,直接使用apt安裝即可:
sudoaptinstallemacs
大小大概有170MB左右,安裝完成后不需要進行其他設置操作。
示例工程下載
為了方便演示emacs的Verilog自動連線的強大功能,這里我提供了一個國內的Git倉庫,方便直觀的進行理解和使用,首先下載該倉庫:
gitclonehttps://gitee.com/xlgforever/emacs_verilog.git
該倉庫的README.md文件提供了一個簡單的操作介紹。下面將詳細演示和解釋emacs的連線功能。
該倉庫下主要存在以下文件:
(base)xlg@xlg16p:~/wrk/emacs_verilog$tree-L2 . ├──Makefile ├──README.md ├──src │├──model1.v │├──top_ori2.v │├──top_ori3.v │├──top_ori.v │└──top.v └──src2 └──model2.v 2directories,8files
?model1.v和model2.v是需要在頂層模塊top.v中實例化的兩個模塊,這兩個模塊位于不同的文件夾中,以模擬多處Verilog源碼文件的分布;此外model2還具有parameter,這兩個模塊的內容如下(因為僅用來示范連線功能,因此功能為空):
modulemodel1( inputclk, inputrst, input[7:0]in_data1, outputreg[7:0]out_data1, outputregto_model2 ); endmodule//model1
modulemodel2#( parameterLEN=8 )( inputclk, inputrst, input[LEN-1:0]in_data2, outputreg[LEN-1:0]out_data2, inputfrom_model1 ); endmodule//model2
?而我們的需求是,在頂層模塊top.v中實例化這兩個模塊
情況一:基礎用法
直接通過代碼講解,top_ori.v的代碼如下:
moduletop( /*autoarg*/ ); parameterLEN=8; /*AUTOINPUT*/ /*AUTOOUTPUT*/ /*AUTOWIRE*/ /*model1AUTO_TEMPLATE( .to_model2(from_model1_to_model2[]), ); */ model1u_model1(/*autoinst*/ ); /*model2AUTO_TEMPLATE( .from_model1(from_model1_to_model2[]), ); */ model2#(/*autoinstparam*/)u_model2(/*autoinst*/ ); endmodule //LocalVariables: //verilog-auto-inst-param-value:t //verilog-library-directories:(".""../src2/") //End:
其中:
?/*autoarg*/用于自動將子模塊之間未使用的信號作為父模塊的端口信號,例如如果子模塊有個信號,名為unuse_signal1,并且該信號沒有連接到top模塊的其他子模塊,那么unuse_signal1就會出現在該指令的下方;
?/*AUTOINPUT*/和/*AUTOOUTPUT*/配合/*autoarg*/,自動為端口信號指定輸入輸出方向;
?/*AUTOWIRE*/用于自動為子模塊之間的互聯信號生成wire定義,并且只有子模塊端口名稱不相同的情況下才會生成;(后續會進行解釋)
?AUTO_TEMPLATE,為該子模塊的自動連線指定模板,如果不指定,則會自動生成與子模塊端口名相同的信號以在頂層模塊中使用;
?上述代碼中,model1子模塊指定to_model2所連接到信號名稱為from_model1_to_model2,model2同理;
?此外,由于from_model1_to_model2這個信號與其兩端所連接的端口名不一致,因此在/*AUTOWIRE*/下會自動生成該信號的wire定義;
?[]方括號的意思是,如果該信號的位寬不固定為1,則會自動在wire定義和端口引出處添加位寬;
?/*autoinst*/,如果只是指定了模板,而不使用該指令,也不會自動實例化該子模塊;使用該命令來自動按照所設置的TEMPLATE生成端口連接;
?/*autoinstparam*/,自動將子模塊內部定義為parameter的變量添加在此處,**注意如果不想讓該命令添加的參數,應該設置為localparam**;
?endmodule后面的四行代碼用于指定所需要實例化的子模塊應該在那些目錄中查找其對應的源代碼文件;
?parameter LEN = 8;等和參數相關的代碼最好放在靠前的位置,不然等下自動生成的、使用了該參數的代碼將會報錯,因為該參數需要事先聲明;
在倉庫根目錄執行make emacs -s,上述top.v模塊將自動實例化為以下代碼:
moduletop( /*autoarg*/ //Outputs out_data2,out_data1, //Inputs rst,in_data2,in_data1,clk ); parameterLEN=8; /*AUTOINPUT*/ //Beginningofautomaticinputs(fromunusedautoinstinputs) inputclk;//Tou_model1ofmodel1.v,... input[7:0]in_data1;//Tou_model1ofmodel1.v input[LEN-1:0]in_data2;//Tou_model2ofmodel2.v inputrst;//Tou_model1ofmodel1.v,... //Endofautomatics /*AUTOOUTPUT*/ //Beginningofautomaticoutputs(fromunusedautoinstoutputs) output[7:0]out_data1;//Fromu_model1ofmodel1.v output[LEN-1:0]out_data2;//Fromu_model2ofmodel2.v //Endofautomatics /*AUTOWIRE*/ //Beginningofautomaticwires(forundeclaredinstantiated-moduleoutputs) wirefrom_model1_to_model2;//Fromu_model1ofmodel1.v //Endofautomatics /*model1AUTO_TEMPLATE( .to_model2(from_model1_to_model2[]), ); */ model1u_model1(/*autoinst*/ //Outputs .out_data1(out_data1[7:0]), .to_model2(from_model1_to_model2),//Templated //Inputs .clk(clk), .rst(rst), .in_data1(in_data1[7:0])); /*model2AUTO_TEMPLATE( .from_model1(from_model1_to_model2[]), ); */ model2#(/*autoinstparam*/ //Parameters .LEN(LEN))u_model2(/*autoinst*/ //Outputs .out_data2(out_data2[LEN-1:0]), //Inputs .clk(clk), .rst(rst), .in_data2(in_data2[LEN-1:0]), .from_model1(from_model1_to_model2));//Templated endmodule //LocalVariables: //verilog-auto-inst-param-value:t //verilog-library-directories:(".""../src2/") //End:
可以發現:
?/*autoarg*/將兩個子模塊之間沒有互聯的信號自動添加到了頂層端口的信號;
?/*AUTOINPUT*/和/*AUTOOUTPUT*/配合/*autoarg*/,進一步為頂層端口的信號指明了輸入輸出方向,并且包含了信號的寬度信息;
?/*AUTOWIRE*/僅自動生成了一個信號from_model1_to_model2,因為該信號既不與model1的to_model2端口名相同,也不與model2的from_model1端口名相同;
情況二:匹配模板匹配
打開top_ori2.v文件,其與top_ori.v代碼的區別主要如下:
/*model1AUTO_TEMPLATE( .to_model2(from_model1_to_model2), .out_(.*)1(in_12), .in_(.*)1(out_12), ); */
?.out_(.*)1 (in_12),的意思是,將model1的以out_開頭、1結尾的端口,連接到以in_開頭、2結尾的信號;其效果就是,將model1的out_data1端口,連接到in_data2信號,而in_data2正好是model2的同名端口,且model2的TEMPLATE中沒有指定in_data2的信號連接,即默認連接到與端口同名的信號;
?(.*)這部分代碼表示的是匹配任意長度的任意字符串;
?1表示是,與(.*)成功匹配的字符串;
?在該例子中,(.*)與data成功匹配,所以1代表的就是data;
?.in_(.*)1 (out_12),同理;
在倉庫根目錄執行make emacs2 -s,上述TEMPLATE生成的實例化代碼如下:
model1u_model1(/*autoinst*/ //Outputs .out_data1(in_data2[7:0]),//Templated .to_model2(from_model1_to_model2),//Templated //Inputs .clk(clk), .rst(rst), .in_data1(out_data2[7:0]));//Templated
可以發現,model1的以out或者in開頭信號軍按照我們的規則進行了信號連接。
這種一般用于什么場景呢,想象一下有兩個AXI接口的模塊需要連接信號,Master的信號名可能是M00_AXI_xxxx,而Slave的信號名可能是S00_AXI_xxxx,那么我們在連接信號時,只需要在Master的TEMPLATE中使用.M00_AXI_(.*) (S00_AXI_1),一行命令,就能讓emacs自動連接這兩個模塊。
情況三:使用TEMPLATE實例化多次不同信號連接的子模塊
通常,我們可能需要將一個模塊實例化多次,每個實例的端口所連接到信號會有所區別,這樣,我們就需要在TEMPLATE中使用正則表達式。
modulemodel1( inputclk, inputrst, input[7:0]in_data1, outputreg[7:0]out_data1, outputregto_model2 ); endmodule//model1
通過在TEMPLATE中使用正則表達式,我們可以實現將每個實例的實例名的一部分作為改實例的端口所連接的信號名的一部分,以區分不同實例的信號;
以top_ori3.v為例,我們將model1的TEMPLATE改為:
/*model1AUTO_TEMPLATE"_(.*)"( .to_model2(from_model1_to_model2), .out_(.*)1(in_1_@), .in_(.*)1(@_out_1), ); */ model1u_model1_inst1(/*autoinst*/ ); model1u_model1_inst2(/*autoinst*/ );
此外,上述代碼我們實例化了model1兩次,每次使用了不同的實例名:
?"_(.*)"含義是,匹配實例名中第一個_字符后的后續所有字符;
?對于實例名u_model1_inst1來說,匹配的就是model1_inst1;
?.out_(.*)1 (in_1_@),中@的含義是,使用匹配到的實例名的部分替換@,組成最終的信號名;
執行make emacs2 -s,最終生成的實例的代碼如下:
.in_data1(model1_inst2_out_data));//Templated
model1u_model1_inst1(/*autoinst*/ //Outputs .out_data1(in_data_model1_inst1),//Templated .to_model2(from_model1_to_model2),//Templated //Inputs .clk(clk), .rst(rst), .in_data1(model1_inst1_out_data));//Templated model1u_model1_inst2(/*autoinst*/ //Outputs .out_data1(in_data_model1_inst2),//Templated .to_model2(from_model1_to_model2),//Templated //Inputs .clk(clk), .rst(rst),
我們這里舉例的正則匹配,使用的都是(.*)中的.*規則,即萬能匹配,可以匹配任意多的任意字符,通過修改該正則匹配的規則,可以實現自己想要的其他效果;
大型Verilog代碼快速連線(2)
上面我們熟悉了三種情況下的emacs的連線用法,接下來進行一些進階連線操作的舉例。
正則表達式"([0-9]+)",即匹配至少一個數字,由于該匹配模式很常見,在使用時,可以直接使用@符號來表示。
即
?.pci_req([0-9]+)_1 (pci_req_jtag_[1])
等效于:
?.pci_req@_1 (pci_req_jtag_[1])。
使用AUTO_TEMPLATE匹配多個端口名/將輸入端口賦值為相應寬度的0
在top_ori4.v中,我們分別設置model1和model2的TEMPLATE,并執行make emacs4 -s后,在top.v中得到如下所示結果:
/*model1AUTO_TEMPLATE( .to_model2(from_model1_to_model2), .out_(.*)@(in_1_2), .in_(.*)@(out_1_2), ); */ //執行 make emacs4 -s 之后得到: model1u_model1_inst1(/*autoinst*/ //Outputs .out_data1(in_data_1),//Templated .to_model2(from_model1_to_model2),//Templated //Inputs .clk(clk), .rst(rst), .in_data1(out_data_1));//Templated /*model2AUTO_TEMPLATE( .from_model1(from_model1_to_model2), .in_(.*)({@"vl-width"{1'b0}}), ); */ model2#(/*autoinstparam*/ //Parameters .LEN(LEN))u_model2(/*autoinst*/ //Outputs .out_data2(out_data2[LEN-1:0]), //Inputs .clk(clk), .rst(rst), .in_data2({LEN{1'b0}}),//Templated .from_model1(from_model1_to_model2));//Templated
對于model1:
?(.*)匹配的是model1的端口名中的data,而@等同于([0-9]+),所以,匹配的是1;
?所以根據該TEMPLATE,out_data1連接到了in_data_1,in_data1連接到了out_data_1;
對于model2:
?"vl-width"表示是該端口的位寬;@“vl-width”則表示獲得該端口的位寬;
?所以根據該TEMPLATE,model2的in_data2端口被連接到了長度為LEN的0上;
除了vl-width,emacs的Verilog模式還有以下的內置變量:
?vl-name:端口的名字部分,例如端口如果是port1[7:0],則vl-name代表的是port1;
?vl-bits:端口的寬度部分,例如端口如果是port1[7:0],則vl-bits代表的是[7:0];
?vl-dir:端口的方向,input/output/inout;
?vl-cell-type:Verilog模塊的名稱;
?vl-cell-name:Verilog模塊的實例的名稱;
這些內置變量的使用方法感興趣的可以自己研究一下。
如何信號名全小寫/如何將實例名包含到信號名中
執行make emacs5 -s,打開top.v文件,可以看到模板和生成的實例如下:
/*model1AUTO_TEMPLATE( .to_model2(from_model1_to_model2), .(.*)(@"(downcasevl-name)"[]), ); */ model1u_model1_inst1(/*autoinst*/ //Outputs .out_data1(out_data1[7:0]),//Templated .to_model2(from_model1_to_model2),//Templated //Inputs .clk(clk),//Templated .rst(rst),//Templated .in_data1(in_data1[7:0]));//Templated /*model2AUTO_TEMPLATE( .from_model1(from_model1_to_model2), .in_(.*)(@"vl-cell-name"_i), .out_(.*)(@"vl-cell-name"_o), ); */ model2#(/*autoinstparam*/ //Parameters .LEN(LEN))u_model2(/*autoinst*/ //Outputs .out_data2(u_model2_o),//Templated //Inputs .clk(clk), .rst(rst), .in_data2(u_model2_i),//Templated .from_model1(from_model1_to_model2));//Templated
對于model1:
?.(.*) (@"(downcase vl-name)"[]),中,首先通過萬能匹配(.*),匹配了全部的端口,然后使用vl-name內置變量將端口名全小寫;
?由于本例中沒有含有大寫的端口名,所以看不出效果;
對于model2:
?.in_(.*) (@"vl-cell-name"_i),中,首先是針對所有以in_開頭的端口名,都連接到"實例名" + "_1"的信號上(僅舉例示范);
?.out_(.*) (@"vl-cell-name"_o),同理;
其他
除了某些內置變量,還支持普通的算術運算。例如有一個信號in,我們想要將該信號燈每8bit連接到同一個模塊的不同實例的a端口上,則可以如下:
/*InstModuleAUTOTEMPLATE( .a(@in[@"(+(*8@)7)":@"(*8@)"]), );*/ InstModuleu_a0(/*AUTOINST*/ .a(in[7:0));//Templated InstModuleu_a1(/*AUTOINST*/ .a(in[15:8));//Templated
從上述代碼可以發現,運算符 操作數1 操作數2配合@可以實現很強大的功能:
?* 8 @,運算符為*,第一個操作數為8,第二個操作數為@,而@匹配的是實例名u_a0中的0,所以對u_a0來說,@為0,對u_a1來說,@為1;
?+ (* 8 @) 7,運算符為+,將(* 8 @)的結果作為第一個操作數,第二個操作數為7;
?@"(+ (* 8 @) 7)":將+號產生的結果,通過""號轉換為字符;
詳細資料見:https://www.veripool.org/verilog-mode/help
emacs在小型模塊仿真中的使用技巧
相信很多人都有這種經歷——想對某一個模塊進行一些簡單的仿真測試,但是這個模塊端口很多,寫一個tb文件的話工作量很大,這時候就可以使用emacs來快速生成仿真環境。
我們的需求如下:
?為待測試的模塊的所有INPUT生成reg類型的變量;
?為待測試的模塊的所有OUTPUT生成wire類型的變量;
DUT
我們依然使用model1作為DUT,其端口如下:
modulemodel1( inputclk, inputrst, input[7:0]in_data1, outputreg[7:0]out_data1, outputregto_model2 ); endmodule//model1
新建一個tb.v文件,代碼如下:
moduletb(); /*AUTOREGINPUT*/ /*AUTOWIRE*/ /*model1AUTO_TEMPLATE( ); */ model1u_model1(/*autoinst*/ ); endmodule//tb //LocalVariables: //verilog-auto-inst-param-value:t //verilog-library-directories:(".""../src2/") //End:
?/*AUTOREGINPUT*/為模塊的所有INPUT生成reg類型的變量;
?/*AUTOWIRE*/,同上一節,為模塊的所有OUTPUT生成wire類型的變量;
執行:
emacs--batch./src/tb.v-fverilog-auto-fsave-buffer
生成的tb.v文件如下:
moduletb(); /*AUTOREGINPUT*/ //Beginningofautomaticreginputs(forundeclaredinstantiated-moduleinputs) regclk;//Tou_model1ofmodel1.v reg[7:0]in_data1;//Tou_model1ofmodel1.v regrst;//Tou_model1ofmodel1.v //Endofautomatics /*AUTOWIRE*/ //Beginningofautomaticwires(forundeclaredinstantiated-moduleoutputs) wire[7:0]out_data1;//Fromu_model1ofmodel1.v wireto_model2;//Fromu_model1ofmodel1.v //Endofautomatics /*model1AUTO_TEMPLATE( ); */ model1u_model1(/*autoinst*/ //Outputs .out_data1(out_data1[7:0]), .to_model2(to_model2), //Inputs .clk(clk), .rst(rst), .in_data1(in_data1[7:0])); endmodule//tb //LocalVariables: //verilog-auto-inst-param-value:t //verilog-library-directories:(".""../src2/") //End:
可以看到,emac自動為我們生成了待測試模塊的輸入端口的reg變量和輸出端口的wire變量,然后我們只需要操作這些變量即可,無需手動生成這些信號。
審核編輯:劉清
-
Verilog
+關注
關注
28文章
1343瀏覽量
109980 -
DUT
+關注
關注
0文章
189瀏覽量
12337 -
Ubuntu系統
+關注
關注
0文章
85瀏覽量
3909 -
EMAC
+關注
關注
0文章
4瀏覽量
3324
原文標題:干貨!還不知道怎么用emac 實現Verilog自動連線?
文章出處:【微信號:處芯積律,微信公眾號:處芯積律】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論