Verilog-Mode入门
Verilog Mode
What
Verilog-mode是Emacs的一种编辑模式,主要面对verilog的开发环境,拥有很方便的自动缩进机制和AUTO机制。AUTO机制是Emacs verilog-mode中一些自动化实现脚本的功能,比如自动填充模块参数列表、自动完成模块例化、自动声明连线等。非常适合顶层模块集成使用,避免Designer的重复劳动和低级错误。
Why
verilog语法中有很多内容是冗余的,模块中必须出现却起不到什么功能作用,列举如下:
- 模块参数列表和模块端口声明input/output;
- reg语句和已经被声明为输出的信号;
- 子模块实例化的连线声明;
- 子模块的实例化语句和子模块的端口声明;
- 组合逻辑always语句的敏感信号列表(不过已经可以使用*来代替了);
可见verilog语法中的冗余信息还是不少的,不过这是语法规则导致的,是语言本身的缺陷,作为使用者只能遵守语法规则。这些冗余信息中比如参数列表和模块例化连线,不仅需要花费时间去编写,而且还特别容易出错,给RTL编写以及后续的修改维护都带来很多问题。那么如果解决这些问题,会带来什么效果呢?个人认为有以下几点:
- 代码整洁,便于阅读;
- 提高编码效率,尤其是顶层实例化;
- 减少拼写错误;
- 便于维护,比如修改、增加和删除端口,无需修改参数列表,比如修改、增加和删除子模块端口,无需修改顶层实例化;
How
Emacs verilog-mode的AUTO机制,就是在代码中相应的位置写一些/AUTO/类似的注释,verilog-mode可以自动替换为所需的内容。Emacs编辑器和verilog-mode的AUTO机制结合,可以很方便的看到AUTO的效果,而且AUTO是以注释形式添加到verilog文件,在语法上本身是合法的,不会影响EDA工具的使用。这个环境也可以在Vim编辑器下使用,举例如下:
自动补全
- 不想写入数据的输入输出信号列表、组合逻辑的敏感信号列表;
/*AUTOARG*/:自动生成模块参数表;
/*AUTOSENSE*/ or /*AS*/:自动生成组合逻辑的敏感信号列表;(在verilog 2000中,已经对语法做出了简化,比如使用@(*)来代替敏感信号列表,但是需要EDA工具的支持。不过现在EDA工具都已经支持verilog 2005了,可以将敏感信号列表直接写为@(*)即可,所以AUTOSENSE功能可以不使用。)
1 | module auto_arg_as(/*AUTOARG*/); |
- 不想写内部的wire、reg数据类型定义;
/*AUTOWIRE*/:自动补全wire的定义,在重复例化模块时使用,会将内部连线的信号自动生成wire定义;(注意只在重复例化的时候使用,设计功能模块时勿用)
/*AUTOREG*/:会将reg类型的output信号补全reg的定义;
自动例化与连线
- 不想例化重复的模块;
如果一个模块被实例化多次,你可以使用verilog的generate语法,也可以使用Emacs verilog-mode的AUTO_TEMPLATE 和 AUTO_INST 。Verilog-mode向上索引最近的模板,这样对于一个子模块可以写多个模板,只需要将模板写在实例化之前即可。
/*AUTO_TEMPLATE*/:TEMPLATE中的模块名称必须与实例中的模块名称相同,并且只需列出每次实例化时不同的那些信号就好了,要遵守此格式(每行只有一个端口,并且以逗号结尾,最后以分号结尾)
/*AUTOINST*/:如果没有/*AUTO_TEMPLATE*/,则/*AUTOINST*/会自动补全信号列表,默认实例化的端口名与子模块的端口名一致,如果不一致需要手动将不一致的端口连线,/*AUTOINST*/不会重复覆盖;如果有/*AUTO_TEMPLATE*/,Verilog-mode会向上索引最近的模板,按模板的格式进行例化,这样对于一个子模块可以写多个模板,只需要将模板写在实例化之前即可。
/*AUTOINSTPARAM*/:在实例化时自动填充参数列表;
/*AUTOINPUT*/、/*AUTOOUTPUT*/:在top层中,一般只有子模块的例化,没有任何其它粘合逻辑,这也是最好的。这时top层通过 AUTOWIRE 声明了子模块的输出连线,AUTOINST 实现了子模块的实例化,其余未声明的信号,就是top模块的输入输出信号,我们可以通过 AUTOINPUT AUTOOUTPUT 完成输入输出信号的声明。特别注意Top层仅做例化,不包括其他逻辑。
1 | module submod(/*AUTOARG*/); |
1 | module submod_wrapper0(/*AUTOARG*/); |
取消输出
比如我们尝试在TOP模块做一个glue logic,将两个模块的输出接口(如a_dfx[15:0]和b_dfx[15:0])组合为一个输出接口(dfx[31:0])进行输出。如果使用/*AUTOOUTPUT*/方法,会将a_dfx[15:0],b_dfx[15:0]也放到输出口,此时我们就需要verilog-auto-output-ignore-regexp方法来对这类接口进行特殊处理,避免将其放到输出端口上。代码示例如下,其中将匹配到‘_dfx’的信号全部ignore。
1 | module abc(/*AUTOARG*/); |
正则匹配
在顶层实例化时,有大量的信号需要重新命名,使用模板的话会增加大量的注释内容,不过往往这些信号命名有特定的规律,我们可以使用正则表达式来处理;
@在正则匹配中匹配到例化名称中最前面的连续数字;建议使用 @ 来匹配例化名称中的数字,使用正则来匹配信号中的数字;
@ in the template takes the leading digits from the reference.
1 | // 提取信号中固定位置的数字 |
1 | module submod_wrapper1(/*AUTOARG*/); |
获取模块路径
现在我们可能会好奇,Verilog-Mode如何知道给定的模块声明的路径。
verillog-mode首先在当前文件中查找,以防您在那里定义了多个模块。然后它在verilog-library-extensions中查找带有每个扩展名的模块名,通常是一个’.v’。最后,它在每个定义了在verilog-library-directories的目录中搜索。
因此,如果我们有一个需要在子目录中查找子模块的顶级模块,我们需要告诉verilog-mode在子目录中查找。最好的方法是在每个需要库变量的Verilog文件的末尾定义它们:
1 | // Local Variables: |
这三个变量的作用如下:verilog-library-directories
变量verilog-library-directories
包含了要在其中查找模块的目录列表,至少应包含当前目录。verilog-library-extension
变量verilog-library-extensions
包含一个文件扩展名列表,试图将其附加到模块名以生成文件名。通常只是“.v”。verilog-library-files
变量verilog-library-files
包含一个文件列表,这些文件将完整地搜索模块。这通常是到技术文件的完整路径,其中定义了许多标准单元。
参考资料
verilog-mode还有其他实用的功能,不过掌握其20%的知识足够应付80%的场景了。其余的功能还包括缩进对齐,自动补齐复位信号等。不过还是建议只在顶层连线使用verilog-mode功能,内部的IP设计还是需要各Designer仔细检查。