https://www.veripool.org/wiki/verilog-mode/Verilog-mode_veritedium
Verilog-Mode: Reducing the Veri-Tedium
Wilson Snyder
wsnyder AT wsnyder.org
SNUG San Jose 2001
摘要
Verilog语言有几个设计缺陷,迫使用户输入和维护冗余信息,例如参数列表、灵敏度列表和跨模块连接语句。支持这些信息会导致潜在的错误,缺乏可维护性,以及整体的代码膨胀。
作者扩展了Emacs verillog - mode包,以提供AUTOfeatures。这些特性在Verilogcode中查找/AUTO/注释,并将其展开为适当的文本。AUTO特性的实现即使对非用户也不会造成痛苦;磁盘上的代码始终是有效的Verilog代码,与所有现有工具兼容。虽然可以极大地加快Emacs用户的编码速度,但不需要在以后编辑代码时使用Emacs。
Verilog-Mode允许用户输入:
module tedium (/*AUTOARG*/);
input in1,in2,in3,in4;
input out1,out2,out3,out4;
/*AUTOREG*/
/*AUTOWIRE*/
always @ (/*AUTOSENSE*/) begin
// complex logic here
end
sub sub1 (/*AUTOINST*/);
sub sub2 (/*AUTOINST*/);
endmodule
然后用两个按键将其扩展成一个完整的模块。
摘要
1 Introduction序言
1.1 Verilog乏味吗?
1.2 为什么要减少单调乏味?
1.3 设计目标
1.4 解决方案:使用注释
1.4.1 如何实现这一点?
2 日常使用
2.1 批处理调用
2.2 扩展Autos
2.3 删除Autos
2.4 敏感列表
2.4.1 恒定信号
2.5 参数列表
2.6 实例化
2.6.1 直连的例外情况
2.6.2 模板
2.6.3 分离模板
2.6.4 正则表达式模板
2.7 互连线
2.8 寄存器输出
2.9 Shell模块
2.10 状态机
3 一般提示
3.1 找到模块
3.2 定义
3.3 包含文件
4 获取Verilog-Mode
Verilog有大量冗余,在大多数模块中没有任何有用的用途。让我们来看一下小的Verilog模块,考虑模块中哪些信息是冗余的。
module tedium (i1, i2, o1, o2);
input i1,i2;
output o1,o2;
reg o1;
wire o2;
wire inter1;
always @ (i1 or i2 or inter1)
o1 = i1 | i2 | inter1;
sub1 sub1 (.i1 (i1),
.i2 (i2),
.o2 (o2),
.inter1 (inter1));
sub2 sub2 (.i1 (i1),
.inter1 (inter1));
endmodule
这里用斜体字标出了多余的部分。为什么这个信息是多余的?
参数列表是输入/输出语句的重复。对已经声明为输出的信号,需要使用reg语句。对互连的子模块信号,需要声明wire。对于明显的组合逻辑,需要敏感列表。最后,基于子模块实例化的名称是子模块输入/输出语句的重复。(假定wire名称与它所连接的端口名称匹配。)。Verilog需要的额外负担之多令人惊讶。(感谢上帝,我们没有用VHDL编码,否则会更糟!)
我们为什么要关心我们是否需要做一点额外的输入呢?
首先,也是最明显的一点是,减少代码行数只会让代码更容易理解。去掉无意义的信息会更容易理解。接下来,如果在编写或维护额外的代码时犯了错误,我们可以通过编译器或lint程序进行更多的旋转。这需要时间。
如果我们多次实例化相同的模块,我们将面临剪切和粘贴错误的巨大风险。 也很难理解总线位如何分布在多个实例中。(位0总是来自实例化0吗?)
更糟糕的是,在大的模块中,敏感列表是显而易见的难以写正确。如果从灵敏度列表中忽略了一个信号,并且没有运行lint工具,那么调试可能需要几个小时。
最后,很难通过层次结构更改、添加或删除信号。如果信号向上两层,向下两层,则大约有16行代码需要编辑。
我们怎样才能减少这种乏味呢?
我们可以简化语言本身。这当然是最简洁的;Verilog 2000有许多特性可以减少乏味,包括总是@(*)来消除灵敏度列表。然而,它面临的障碍是需要一个全新的工具套件;除非您使用的所有工具都支持它,否则它实际上是无用的。
我们可以写一个预处理器。这看起来没问题,但是需要在所有Verilog程序之前运行预处理器,更重要的是,每个代码用户都需要有这个工具。正因为如此,如果您向他人提供知识产权,预处理器可能不是一个选择。
我们真正希望的是合法的Verilog进入工具,合法的Verilog出来,这样磁盘上的文件就永远是合法的。只有在编辑代码时,代码才可能不是有效的Verilog代码。因此,不使用我们的工具的人总是有合法和完整的源代码。
我们如何让有效的Verilog代码进出,但仍然保存所有的输入?我们将使用注释来指示注释后面的文本应该自动计算。
以一个简单的Verilog程序为例:
always @(/*AS*/) begin
if (sel) z = a;
else z = b;
end
我们将/AS/作为一个程序可以查找的注释。当它找到它时,程序将扫描注释后的块,并计算灵敏度列表。
运行程序,我们会得到:
always @(/*AS*/a or b or sel) begin
if (sel) z = a;
else z = b;
end
添加灵敏度列表的地方。然后用户可以编辑这段代码来进行RTL更改:
always @(/*AS*/a or b or sel) begin
if (sel) z = a;
else z = s2?c:d;
end
然后重新运行程序,得到正确的灵敏度列表:
always @(/*AS*/a or c or d
or s2 or sel) begin
if (sel) z = a;
else z = s2?c:d;
end
为了可读性,我们还将有一种方法来返回所有计算信息,使代码再次易于阅读:
always @(/*AS*/) begin
if (sel) z = a;
else z = s2?c:d;
end
扩展注释(从这里开始我们将称之为AUTOs)最好在编辑器中完成。这让用户可以随意扩展和删除它们,并获得关于AUTOs是否正在执行所需操作的即时反馈。最重要的是,当代码被保存时,它可以在输出时进行扩展,实现我们的目标,即磁盘上的代码总是有效的。
在编辑时,自动缩进和突出显示注释等也很好。因此,我们需要一个标准的语言模式作为程序的基础。
作者并没有从零开始,而是从主要的Verilog语言模式开始,即Michael [email protected]的Verilog- mode。在此基础上增加了autos的解析和扩展。
现在我将讨论AUTOs的每个特性以及如何使用它们。
在本节中,我将介绍几个键序列,因为键更容易在文档中表示,而且对高级用户来说更快。但是,喜欢使用鼠标的用户可以在菜单栏的Verilog选项下执行此命令。同样的,所有的命令和文档都可以在菜单下找到。
对于那些使用vi或使用脚本生成代码的人来说,能够从shell扩展自动操作是很有用的。使用shell命令执行批处理调用:
emacs --batch filename.v -f verilog-auto -f save-buffer
这将以批处理模式(没有打开窗口)调用Emacs,读取文件,展开自动操作,然后最后保存文件。
如果你有一个大的站点初始化很慢的文件,你可以通过直接加载显式verillog-mode(更快):
emacs --batch --no-site-file -l verilog-mode.el filename.v -f verilog-auto -f save-buffer
当您编写perl脚本或生成Verilog代码时,您会发现这非常有价值。只需让您的脚本打印AUTO注释,让Emacs担心敏感性列表等。它将使您的脚本简单得多。
只有三个关键序列实现所有重要的功能。前两个用于扩展AUTOs,最后一个用于删除它们。
一旦您第一次添加了AUTO注释,并且/或希望更新它们,键C-c C-a或M-x verilog-auto将扩展自动操作。
在尝试了几次之后,你可能会开始扩展自动文件,然后保存文件,然后运行你的lint程序。由于这个序列是如此常见,C-c C-s或M-x verilog-auto-save-compile将完成所有这三个任务:扩展自动化、保存缓冲区和运行编译器(应该将其设置为检测工具)。这很容易记住,因为它类似于标准的保存缓冲键,C-x C-s。
如果您希望查看文件的小版本,或者计划进行大量编辑,并希望删除额外的包袱,那么可以使用C-c C-d或M-x verilog-delete-auto删除所有的自动工具。上面讨论的C-c C-a或M-x verilog-auto将得到扩展的结果。
AUTO的第一个注释解决了Verilog中最大的乏味之处:敏感列表。verillog-mode将/AUTOSENSE/或简称/AS/替换为注释后块的敏感性列表。例如:
always @ (/*AUTOSENSE*/) begin
outin = ina | inb;
out = outin;
end
输入C-c C-a将使这个成为:
always @ (/*AUTOSENSE*/ina or inb) begin
outin = ina | inb;
out = outin;
end
verillog-mode理解在相同的always块(该块的输出)中生成的信号不会被放入灵敏度列表中,因此out和inout不会出现在列表中。
Verilog语言也不理解敏感列表中的内存(多维数组),所以AUTOSENSE会排除它们,并添加一个/内存或/注释,提醒你内存更改不会激活该块。
在本例中,我们在always后面使用了开始/结束对。这确保verilog-mode能够识别块结束的位置,并解决verilog-mode早期版本的一个bug。(最新版本还没有被骗过,但为了安全起见,最好使用begin/end或case/endcase对,以防遇到旧版本的verillog-mode)。
当你第一次在block中使用常量时,AUTOSENSE可能不会做你期望的事情:
always @ (/*AUTOSENSE*/ina or inb or `constant) begin
out = ina | inb | `constant;
end
AUTOSENSE不能总是确定constant的定义值是数字常量还是信号。有许多针对这个问题的修复:
在模块的任何地方都可以使用AUTO_CONSTANT声明(圆括号是必需的):
/* AUTO_CONSTANT (`this_is_really_constant_dont_autosense_it) */
使用在模块中声明的参数,而不是define,这将自动被理解为常量。(它的附加优势是作用域是本地的,而不是污染全局名称空间)
使用verilog-read-defines或verilog-read-includes如下面实例化部分所述。
设定verilog-auto-sense-defines-constant,这将把所有define标记为常量。这最好在文件的底部完成:
// Local Variables:
// verilog-auto-sense-define-constant:t
// End:
在改变任何这样的局部变量块之后,你需要重新访问(type C-x C-v RET)文件;Emacs只在读入文件时解析它。
参数列表使用/AUTOARG/创建。它解析input/output/inout语句并生成参数列表。例如:
module ex_arg (/*AUTOARG*/);
input i;
output o;
endmodule
输入C-c C-a将会变成:
module ex_arg (/*AUTOARG*/
// Outputs
o,
// Inputs
i);
input i;
output o;
endmodule
不支持连接和输出部分总线,也不支持’ifdefs等。你可以通过在(和/AUTOARG/之间放置端口来解决这个问题。Verilog-Mode将假定这些是预先声明的,不会被AUTOARG重新声明。如果可以的话,避免这样做,因为如果这个模块在/AUTOINST/中使用,它只会导致更多的ifdef。
module ex_arg (
`ifdef need_x_input
x,
`endif
/*AUTOARG*/
// Outputs
o,
// Inputs
i);
`ifdef need_x_input
input x; // This is an optional input, if `need_x_signal is defined
`endif
...
因为参数的生成是防止误操作的,你将可能从不需要编辑或再次考虑模块参数列表。AUTOARG也是一个在现有模块上进行尝试的优秀工具;它的功能非常明显,只需要编译就可以确保它的测试与原始版本相同。
实例化是Verilog中最令人沮丧的部分,因为每个信号必须列出三次;一次作为子模块中的输入/输出语句,一次作为实例化的引脚名,一次作为实例化的连接线名。
AUTOINST理解如何查找子模块,并将为您创建基于名称的实例化。
假设你有一个子模块,存储在fanout.v文件中:
module fanout (o,i)
input i;
output [31:0] o;
wire [31:0] o = {32{i}};
endmodule
现在你想在上层模块中使用该子模块:
module ex_inst (o,i)
input i;
output [31:0] o;
fanout fanout (/*AUTOINST*/);
endmodule
输入C-c C-a将成上层模块变成:
module ex_inst (o,i)
output o;
input i;
fanout fanout (/*AUTOINST*/
// Outputs
.o (o[31:0]),
// Inputs
.i (i));
endmodule
输入和输出列表来自读取fanout.v的输入和输出语句。
AUTOINST假定一个一对一的端口名称到信号名称映射。这是最常见的情况。
除非您要多次实例化一个模块,或者该模块是通用部分(如加法器),否则不要在整个层次结构中更改信号名称。它只会导致代码不可维护。通常,我将父节点或子节点重命名以保持一致。要做到这一点,请尝试我的vrename来自http://veripool.com。Vrename可以在30秒内修复所有不匹配的名称,并为您提供许多下游利益匹配。
当您需要违背这个建议时,最简单的方法是直接在/AUTOINST/之前指定端口。在/AUTOINST/之前定义的任何端口都不包含在自动列表中。你也应该在端口之前放一个//Input或//Output注释,以便AUTOWIRE,下面讨论,将知道信号的方向。
fanout fanout (
// Inputs
.i (my_i_dont_mess_with_it),
/*AUTOINST*/
// Outputs
.o (o[31:0]));
如果同一个模块被实例化多次,您可以使用/AUTO_TEMPLATE/只指定一次例外。用AUTO_TEMPLATE创建注释模板作为实例名:
/* psm_mas AUTO_TEMPLATE (
.PTL_MAPVALIDX (PTL_MAPVALID[@]),
.PTL_BUS (PTL_BUSNEW[]),
); */
模板中的模块名必须与实例化中的模块名相同。只需要列出对于每个实例化必须不同的信号。
每行有一个端口,以逗号或)结束是很重要的;就像AUTOINST生成一样。
实际上,生成AUTO_TEMPLATE最简单的方法是创建一个AUTOINST,展开AUTOINST,然后去掉插入到AUTOINST的verillog-mode行,并将它们粘贴到模板中。
模板位于实例化之上。展开实例化时,verillog-mode只搜索最近的模板。因此,你可以为同一个子模块拥有多个模板,只需在实例化的模板和实例化本身之间交替使用。
上面的psm_mas模板将转换为:
psm_mas ms2 (/*AUTOINST*/);
//输入C-c C-a将变成:
psm_mas ms2 (/*AUTOINST*/
// Outputs
.INSTDATAOUT (INSTDATAOUT),
.PTL_MAPVALIDX (PTL_MAPVALID[2]), // Templated
.PTL_BUS (PTL_BUSNEW[3:0]), // Templated
....
@字符是一种非常有用的魔法。@将被实例化名称的第一个数字所取代。注意@字符被替换为“ms2”中的2。另外,模板中没有列出的端口假定为直接连接。
AUTOs将模板中的替换为子模块所需的bit范围,如果是单bit端口,则不替换。注意PTL_BUSNEW[]如何变成上面的PTL_BUSNEW[3:0]。通常使用[]比将bits放到模板中更好,因为如果子模块在其输入或输出语句中更改了bit范围,它会自动将更改传播给父模块。(这就是AUTOs的全部意义,隐藏信息和自动化更改的影响。)
在AUTO_TEMPLATE中指定一个简单的wire名并不总是足够的,尤其是在跨多个实例化分散bit时。因此,verilog-mode允许你编写一个程序来计算连接到给定端口的wire名称:
/* psm_mas AUTO_TEMPLATE (
.PTL_MAPVALIDP1X (PTL_MAPVALID[@"(% (+ 1 @) 4)"]));
*/
submod i0 (/*AUTOINST*/);
submod i1 (/*AUTOINST*/);
submod i2 (/*AUTOINST*/);
submod i3 (/*AUTOINST*/);
当遇到语法@“(…)”,引号中的表达式将被作为Emacs Lisp表达式计算,@被实例化数字替换。Lisp编程相当简单,只要把操作符放在前面(前缀表示法)。表达式内部的任何双引号都需要用前导反斜杠"括起来。
上面的MAPVALIDP1X示例将[@+1 modulo 4]放入括号中。这样实例化i0得到PTL_MAPVALID[1],i1得到PTL_MAPVALID[2],i2得到PTL_MAPVALID[3],i3得到PTL_MAPVALID[0]。(编码器选择端口名称MAPVALIDP1X表示MAPVALID加上1个实例化数。)这种类型的东西是非常有用的桶移位之类的。
普通的lisp变量可以在AUTO_TEMPLATE表达式中使用。另外,还定义了一些特殊的变量:
vl-name是输入/输出端口的名称部分(`PTL_MAPVALIDP1X’)。vl-bits是输入/输出端口(‘[2:0]’)的总线位部分。
此外,下面描述的verilog-read-define将为模块中的每个定义创建avh-{definename}变量。更好的是,表单中的任何注释:
/AUTO_LISP(setq foo 1)/将计算缓冲区开始和AUTOINST点之间圆括号内的任何Lisp表达式。这允许在每次实例化之间更改变量,并且可以制作非常复杂的模板,包括基于字符串的替换。
有时在AUTO_TEMPLATES中固定的端口名是不够的,用一个模板行匹配许多端口会很有用。最常见的情况是位处理或更改大量引脚的命名约定,例如在芯片的顶部。
表单的AUTO_TEMPLATE项:
.pci_req\([0-9]+\)_l (pci_req_jtag_[\1]),
应用Emacs样式的正则表达式搜索。这个示例匹配任何以“pci_req”开头,后跟数字,以“l”结尾的端口。该端口连接到pci_req_jtag[]线,总线下标来自第一个集合中匹配的内容。因此,pci_req2_l连接到pci_req_jtag_[2]。
由于将[0−9]+[0−9]+匹配到端口名是如此的常见和难看,@做同样的事情。(注意,这与实例化数不同,这是@在模板右侧所做的。)因此,下面的模板等价于上面的模板:
.pci_req@_l (pci_req_jtag_[\1]),
下面是另一个删除_l的例子,可能是因为命名约定只指定_表示活动的low。注意使用[]来自动确定总线下标:
.\(.*\)_l (\1_[]),
这是一个非常有用的最终模板:
/* submod AUTO_TEMPLATE (
.\(.*[^0-9]\)@ (\1[\2]),
);*/
上面说的是,取字母DIGITS格式的一个端口,并将其转换为字母[DIGITS],方便地对信号进行矢量化。需要[^0-9],因为否则.*可能匹配末尾的数字。
当模块之间连接时,子模块的输出需要wire语句。/AUTOWIRE/将为所有子模块的输出声明wire。这对于在两个子模块之间,并且在顶层模块本身中不使用的信号特别好。
AUTOWIRE假设要么使用/AUTOINST/,要么手动添加// Output注释到实例化。例如:
module top (o,i)
output o;
input i;
/*AUTOWIRE*/
inst inst (/*AUTOINST*/);
other other (/*AUTOINST*/);
endmodule
输入C-c C-a将会变成:
module ex_wire (o,i)
output o;
input i;
/*AUTOWIRE*/
// Beginning of automatic wires
wire [31:0] ins2oth; // From inst of inst.v
wire [31:0] oth2ins; // From other of other.v
// End of automatics
inst inst (/*AUTOINST*/
// Outputs
.ins2oth (ins2oth[31:0]),
.o (o),
// Inputs
.oth2ins (oth2ins[31:0]),
.i (i));
other other (/*AUTOINST*/
// Outputs
.oth2ins (oth2ins[31:0]),
// Inputs
.ins2oth (ins2oth[31:0]),
.i (i));
endmodule
声明Cross和other2in是因为它们是子模块的输出。它还添加了一个很好的注释,使它很容易理解电线来自哪里。即使总线位来自多个子模块,或者连接是手动声明的,AUTOWIRE也会做正确的事情。
如果模块输出来自寄存器,信号需要同时声明为寄存器和输出。AUTOREG会读取输出声明,并为你做寄存器声明。正如预期的那样,如果输出来自子模块或wire,则不会添加寄存器语句。例如:
module ex_reg (o,i)
output o;
input i;
/*AUTOREG*/
always o = i;
endmodule
输入C-c C-a将变成:
module ex_reg (o,i)
output o;
input i;
/*AUTOREG*/
// Beginning of automatic regs
reg o;
// End of automatics
always o = i;
endmodule
其中o需要同时是输出和寄存器。
通常,一个模块需要与另一个模块相同的端口列表。这需要在原始模块周围生成一个shell,或者创建一个空模块。
/AUTOINOUTMODULE(module)/复制指定模块的input/output/inout语句,并插入到当前模块中。当前模块中已经定义的任何I/O都不会被重新定义。
例如:
module ex_shell (/*AUTOARG*/)
/*AUTOINOUTMODULE("ex_main")*/
endmodule
module ex_main (i,o,io)
input i;
output o;
inout io;
endmodule
输入C-c C-a将变成:
module ex_shell (/*AUTOARG*/i,o,io)
/*AUTOINOUTMODULE("ex_main")*/
// Beginning of automatic in/out/inouts
input i;
output o;
inout io;
// End of automatics
endmodule
当你在波形中调试状态机时,有一个你能看到的带有状态名称的信号是非常好的。同样地,如果使用$display语句,您希望有一个带有状态名字的字符串。/AUTOASCIIENUM(in,out,prefix)/接受一组参数,并生成一个块来解码状态向量。例如:
//== State enumeration
parameter [2:0] // synopsys enum state_info
SM_IDLE = 3'b000,
SM_SEND = 3'b001,
SM_WAIT1 = 3'b010;
//== State variables
reg [2:0] /* synopsys enum state_info */
state_r; /* synopsys state_vector state_r */
reg [2:0] /* synopsys enum state_info */
state_e1;
//== ASCII state decoding
/*AUTOASCIIENUM("state_r", "_stateascii_r", "sm_")*/
输入C-c C-a将变成:
... same front matter ...
//== ASCII state decoding
/*AUTOASCIIENUM("state_r", "_stateascii_r", "sm_")*/
// Beginning of automatic ASCII enum decoding
reg [39:0] _stateascii_r; // Decode of state_r
always @(state_r) begin
casex ({state_r}) // synopsys full_case parallel_case
SM_IDLE: _stateascii_r = "idle ";
SM_SEND: _stateascii_r = "send ";
SM_WAIT1: _stateascii_r = "wait1";
default: _stateascii_r = "%Erro";
endcase
end
// End of automatics
以下是它的使用方法:
本节介绍一些使用AUTOs的一般技巧。
如果您已经通读了实例化部分,您可能想知道verillog-mode如何知道在哪里找到给定的模块声明。
verillog-mode首先在当前文件中查找,以防您在那里定义了多个模块。然后它在verilog-library-extensions中查找带有每个扩展名的模块名,通常是一个’.v’。最后,它在每个定义了在verilog-library-directories的目录中搜索。
因此,如果您有一个需要在子目录中查找子模块的顶级模块,您需要告诉verillog-mode在子目录中查找。最好的方法是在每个需要库变量的Verilog文件的末尾定义它们:
// Local Variables:
// verilog-library-directories:("." "subdir" "subdir2")
// verilog-library-files:("/some/path/technology.v" "/some/path/tech2.v")
// verilog-library-extensions:(".v" ".h")
// End:
Emacs自动解析本节并设置适当的变量。因为Emacs不会注意到对它们的编辑,直到文件被重新读取,记住如果改变了本地变量,重新执行C-x C-v RET!
这三个变量的作用如下:
verilog-library-directories
变量verilog-library-directories包含了要在其中查找模块的目录列表。至少有当前目录(默认)是个好主意。
verilog-library-extensions
变量verilog-library-extensions包含一个文件扩展名列表,试图将其附加到模块名以生成文件名。通常只是“.v”。
verilog-library-files
变量verilog-library-files包含一个文件列表,这些文件将完整地搜索模块。这通常是到技术文件的完整路径,其中定义了许多标准单元。
在使用AUTOINST或AUTOSENSE时,verilog-mode有时需要知道当前文件中定义了哪些定义。这允许verillog-mode正确地解析模块,并知道哪些定义代表常量。
如果宏在同一个文件中前面定义过,你想要它们的值,你可以自动读取它们:
// Local Variables:
// eval:(verilog-read-defines)
// eval:(verilog-read-defines "group_standard_includes.v")
// End:
第一次eval将读取当前文件中所有的(define)定义。第二次eval将读取特定文件中的定义,例如如果有一个标准的但在模块本身中没有特定的(
include)include文件。
定义必须是简单的文本替换,一个在一行中,从行首开始。define周围的任何ifdef或多行注释都会被忽略。
由于速度的原因,verilog-mode不会自动读取include文件。这意味着在include文件中定义的常量不会被视为AUTOSENSE的常量。这可以通过告诉Emacs在读取文件时读取include来修复:
// Local Variables:
// eval:(verilog-read-includes)
// End:
这将解析刚刚读入的文件,并读取它在该文件中找到的任何(`include)。记住在改变局部变量后的重新输入C-x C-v RET !
养成在每个需要它的.v文件中包含所有需要它的文件的习惯是很好的,而不是等待编译时。这将有助于verilog-mode、Verilint和可读性。为了防止在多个模块一起编译时反复定义相同的变量,对每个include文件的全部内容进行测试:
// include if not already included
`ifdef _FOO_V `else
`define _FOO_V
... contents of file
`endif // _FOO_V
这是C头文件的标准程序,出于同样的原因,应该成为Verilog中的标准实践。
探索verillog-mode的最好方法是获得它,并尝试一些简单的特性,如AUTOARG和AUTOSENSE。你会发现越来越多的autos潜入你的代码中,你很快就会加入成千上万的工程师行列,没有它们就无法工作。
verilog-mode是VCS自带的,但是这个版本已经相当老了,除非您最近下载了最新版本(撰写本文时是3.50版本),否则您最好在开始之前下载最新版本。要下载,请点击作者网站的链接:http://veripool.com。
请向verillog-mode GNATS服务器[email protected]报告bug。
如需直接联系作者,请发送电子邮件。
emacs verilog-mode对IC顶层集成的帮助
原创 2017年07月09日 09:55:17
标签:
emacs /
verilog
背景介绍
AUTOINST和AUTOWIRE的应用
背景介绍
emacs默认自带verilog-mode插件,不仅仅支持语法高亮、代码段自动补全等功能,核心应用还有/AUTOXXX/。
IC顶层集成,最常见的工作就是实例化和端口线网的连接。可以利用/AUTOINST/和/AUTOWIRE/节省很多工作量,减少出错可能性。
AUTOINST和AUTOWIRE的应用
下面是示例。用到的技巧包括:
下面有两段代码,第一段代码,按键ctrl+c ctrl+a之后,会自动生成第二段代码。
==================================
module noc(
output z1,
output z2,
output [31:0] z3,
input a1,
input a2,
input [31:0] a3
);
endmodule
module noc1(
output z1,
output z2,
output [31:0] z3,
output a1,
output a2,
output [31:0] a3
);
endmodule
module a;
/*AUTOWIRE*/
noc u_noc(/*AUTOINST*/);
endmodule
module a;
/*AUTOWIRE*/
noc1 u_noc1(/*AUTOINST*/);
endmodule
module a;
/*AUTOWIRE*/
/* noc AUTO_TEMPLATE(
.z1 (output1),
.z2 (output2),
.z3 (output3),
.a3 (input3),
);
*/
noc u_noc(/*AUTOINST*/);
endmodule
===========================================================
module noc(
output z1,
output z2,
output [31:0] z3,
input a1,
input a2,
input [31:0] a3
);
endmodule
module noc1(
output z1,
output z2,
output [31:0] z3,
output a1,
output a2,
output [31:0] a3
);
endmodule
module a;
/*AUTOWIRE*/
// Beginning of automatic wires (for undeclared instantiated-module outputs)
wire output1; // From u_noc of noc.v
wire output2; // From u_noc of noc.v
wire output3; // From u_noc of noc.v
// End of automatics
noc u_noc(/*AUTOINST*/
// Outputs
.z1 (output1), // Templated
.z2 (output2), // Templated
.z3 (output3), // Templated
// Inputs
.a1 (a1),
.a2 (a2),
.a3 (input3)); // Templated
endmodule
module a;
/*AUTOWIRE*/
// Beginning of automatic wires (for undeclared instantiated-module outputs)
wire a1; // From u_noc1 of noc1.v
wire a2; // From u_noc1 of noc1.v
wire [31:0] a3; // From u_noc1 of noc1.v
wire z1; // From u_noc1 of noc1.v
wire z2; // From u_noc1 of noc1.v
wire [31:0] z3; // From u_noc1 of noc1.v
// End of automatics
noc1 u_noc1(/*AUTOINST*/
// Outputs
.z1 (z1),
.z2 (z2),
.z3 (z3[31:0]),
.a1 (a1),
.a2 (a2),
.a3 (a3[31:0]));
endmodule
module a;
/*AUTOWIRE*/
// Beginning of automatic wires (for undeclared instantiated-module outputs)
wire output1; // From u_noc of noc.v
wire output2; // From u_noc of noc.v
wire output3; // From u_noc of noc.v
// End of automatics
/* noc AUTO_TEMPLATE(
.z1 (output1),
.z2 (output2),
.z3 (output3),
.a3 (input3),
);
*/
noc u_noc(/*AUTOINST*/
// Outputs
.z1 (output1), // Templated
.z2 (output2), // Templated
.z3 (output3), // Templated
// Inputs
.a1 (a1),
.a2 (a2),
.a3 (input3)); // Templated
endmodule
————————————————
版权声明:本文为CSDN博主「fpxBGDBN」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/liufengl138/article/details/78932954