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编辑器下使用,举例如下:

自动补全

  1. 不想写入数据的输入输出信号列表、组合逻辑的敏感信号列表;

/*AUTOARG*/:自动生成模块参数表;

/*AUTOSENSE*/ or /*AS*/:自动生成组合逻辑的敏感信号列表;(在verilog 2000中,已经对语法做出了简化,比如使用@(*)来代替敏感信号列表,但是需要EDA工具的支持。不过现在EDA工具都已经支持verilog 2005了,可以将敏感信号列表直接写为@(*)即可,所以AUTOSENSE功能可以不使用。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
module auto_arg_as(/*AUTOARG*/);
input a;
input b;
input c;
output d;
/*AUTOREG*/

always @(/*AS*/)
d = a ^ b ^ c;
endmodule

//After Auto
module auto_arg_as(/*AUTOARG*/
// Outputs
d,
// Inputs
a, b, c
);
input a;
input b;
input c;
output d;
/*AUTOREG*/
// Beginning of automatic regs (for this module's undeclared outputs)
reg d;
// End of automatics

always @(/*AS*/a or b or c)
d = a ^ b ^ c;

endmodule
  1. 不想写内部的wire、reg数据类型定义;

/*AUTOWIRE*/:自动补全wire的定义,在重复例化模块时使用,会将内部连线的信号自动生成wire定义;(注意只在重复例化的时候使用,设计功能模块时勿用)

/*AUTOREG*/:会将reg类型的output信号补全reg的定义;

自动例化与连线

  1. 不想例化重复的模块;

如果一个模块被实例化多次,你可以使用verilog的generate语法,也可以使用Emacs verilog-mode的AUTO_TEMPLATEAUTO_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
2
3
4
5
6
7
8
9
10
module submod(/*AUTOARG*/);
input [1:0] in_a;
input [1:0] in_b;
output [1:0] out_a;
output [1:0] out_b;

assign out_a = in_a ^ in_b;
assign out_b = in_a | in_b;

endmodule
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
module submod_wrapper0(/*AUTOARG*/);
/*****************************\
I/O
\*****************************/
/*AUTOINPUT*/
/*AUTOOUTPUT*/

/*****************************\
Signal
\*****************************/
/*AUTOWIRE*/

/*****************************\
Inst
\*****************************/
/*submod AUTO_TEMPLATE(
.in_\(.*\)(din_\1[]),
.out_\(.*\)(dout_stage1_\1[]),
);*/
submod u_submod_0(/*AUTOINST*/);

/*submod AUTO_TEMPLATE(
.in_\(.*\)(dout_stage1_\1[]),
.out_\(.*\)(dout_stage2_\1[]),
);*/
submod u_submod_1(/*AUTOINST*/);

/*submod AUTO_TEMPLATE(
.in_\(.*\)(dout_stage2_\1[]),
.out_\(.*\)(dout_\1[]),
);*/
submod u_submod_2(/*AUTOINST*/);

endmodule
//Local Variables:
//verilog-library-directories:(".")
//End:

//**********************************************************
// After Auto
//**********************************************************
module submod_wrapper0(/*AUTOARG*/
//Outputs
dout_b, dout_a,
//Inputs
din_b, din_a
);
/*****************************\
I/O
\*****************************/
/*AUTOINPUT*/
//Beginning of automatic inputs (from unused autoinst inputs)
input [1:0] din_a; // To u_submod_0 of submod.v
input [1:0] din_b; // To u_submod_0 of submod.v
//End of automatics
/*AUTOOUTPUT*/
//Beginning of automatic outputs (from unused autoinst outputs)
output [1:0] dout_a; // To u_submod_2 of submod.v
output [1:0] dout_b; // To u_submod_2 of submod.v
//End of automatics

/*****************************\
Signal
\*****************************/
/*AUTOWIRE*/
wire [1:0] dout_stage1_a; //From u_submod_0 of submod.v
wire [1:0] dout_stage1_b; //From u_submod_0 of submod.v
wire [1:0] dout_stage2_a; //From u_submod_1 of submod.v
wire [1:0] dout_stage2_b; //From u_submod_1 of submod.v
//End of automatics

/*****************************\
Inst
\*****************************/
/*submod AUTO_TEMPLATE(
.in_\(.*\)(din_\1[]),
.out_\(.*\)(dout_stage1_\1[]),
);*/
submod u_submod_0(/*AUTOINST*/
// Outputs
.out_a (dout_stage1_a[1:0]), // Templated
.out_b (dout_stage1_b[1:0]), // Templated
// Inputs
.in_a (din_a[1:0]), // Templated
.in_b (din_b[1:0]) // Templated
);

/*submod AUTO_TEMPLATE(
.in_\(.*\)(dout_stage1_\1[]),
.out_\(.*\)(dout_stage2_\1[]),
);*/
submod u_submod_1(/*AUTOINST*/
// Outputs
.out_a (dout_stage2_a[1:0]), // Templated
.out_b (dout_stage2_b[1:0]), // Templated
// Inputs
.in_a (dout_stage1_a[1:0]), // Templated
.in_b (dout_stage1_b[1:0]) // Templated
);

/*submod AUTO_TEMPLATE(
.in_\(.*\)(dout_stage2_\1[]),
.out_\(.*\)(dout_\1[]),
);*/
submod u_submod_2(/*AUTOINST*/
// Outputs
.out_a (dout_a[1:0]), // Templated
.out_b (dout_b[1:0]), // Templated
// Inputs
.in_a (dout_stage2_a[1:0]), // Templated
.in_b (dout_stage2_b[1:0]) // Templated
);
endmodule
//Local Variables:
//verilog-library-directories:(".")
//End:

取消输出

比如我们尝试在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
2
3
4
5
6
7
8
9
10
11
module abc(/*AUTOARG*/);
/*AUTOINPUT*/
/*AUTOOUTPUT*/
/*AUTOWIRE*/
assign dfx = {a_dfx[15:0], b_dfx[15:0]};
module u_1(/*AUTOINST*/);
module u_2(/*AUTOINST*/);
endmodule
//Localvariables:
//verilog-auto-output-ignore-regexp:("_dfx")
//END:

正则匹配

在顶层实例化时,有大量的信号需要重新命名,使用模板的话会增加大量的注释内容,不过往往这些信号命名有特定的规律,我们可以使用正则表达式来处理;

@在正则匹配中匹配到例化名称中最前面的连续数字;建议使用 @ 来匹配例化名称中的数字,使用正则来匹配信号中的数字;

@ in the template takes the leading digits from the reference.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 提取信号中固定位置的数字
.pci_req\([0-9]+\)_j (pci_req_jtag_[\1]),
.pci_req12_j (pci_req_jtag_[12]),
//---------------------------------------------
// 删除末尾下划线内容
.\(.*\)_j (\1[]),
.pci_req_j (pci_req[7:0]),
//---------------------------------------------
// 对信号矢量化处理(将@改为\([0-9]+\)也是可以的)
.\(.*[^0-9]\)@ (\1[\2]),
.pci_req0 (pci_req[0]),
.pci_req1 (pci_req[1]),
.pci_req2 (pci_req[2]),
.pci_req3 (pci_req[3]),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
module submod_wrapper1(/*AUTOARG*/);
/*****************************\
I/O
\*****************************/
/*AUTOINPUT*/
/*AUTOOUTPUT*/

/*****************************\
Signal
\*****************************/
/*AUTOWIRE*/

/*****************************\
Inst
\*****************************/
/*submod AUTO_TEMPLATE(
.in_\(.*\)(din_\1_@[]),
.out_\(.*\)(dout_\1_@[]),
);*/
submod u_submod_0(/*AUTOINST*/);

/*submod AUTO_TEMPLATE(
.in_\(.*\)(din_\1_@[]),
.out_\(.*\)(dout_\1_@[]),
);*/
submod u_submod_1(/*AUTOINST*/);

/*submod AUTO_TEMPLATE(
.in_\(.*\)(din_\1_@[]),
.out_\(.*\)(dout_\1_@[]),
);*/
submod u_submod_2(/*AUTOINST*/);

endmodule
//Local Variables:
//verilog-library-directories:(".")
//End:

//**********************************************************
// After Auto
//**********************************************************
module submod_wrapper1(/*AUTOARG*/
//Outputs
dout_b_2, dout_b_1, dout_b_0, dout_a_2, dout_a_1, dout_a_0,
//Inputs
din_b_2, din_b_1, din_b_0, din_a_2, din_a_1, din_a_0
);
/*****************************\
I/O
\*****************************/
/*AUTOINPUT*/
//Beginning of automatic inputs (from unused autoinst inputs)
input [1:0] din_a_0; // To u_submod_0 of submod.v
input [1:0] din_a_1; // To u_submod_1 of submod.v
input [1:0] din_a_2; // To u_submod_2 of submod.v
input [1:0] din_b_0; // To u_submod_0 of submod.v
input [1:0] din_b_1; // To u_submod_1 of submod.v
input [1:0] din_b_2; // To u_submod_2 of submod.v
//End of automatics
/*AUTOOUTPUT*/
//Beginning of automatic outputs (from unused autoinst outputs)
output [1:0] dout_a_0; // To u_submod_0 of submod.v
output [1:0] dout_a_1; // To u_submod_1 of submod.v
output [1:0] dout_a_2; // To u_submod_2 of submod.v
output [1:0] dout_b_0; // To u_submod_0 of submod.v
output [1:0] dout_b_1; // To u_submod_1 of submod.v
output [1:0] dout_b_2; // To u_submod_2 of submod.v
//End of automatics

/*****************************\
Signal
\*****************************/
/*AUTOWIRE*/

/*****************************\
Inst
\*****************************/
/*submod AUTO_TEMPLATE(
.in_\(.*\)(din_\1[]),
.out_\(.*\)(dout_stage1_\1[]),
);*/
submod u_submod_0(/*AUTOINST*/
// Outputs
.out_a (dout_a_0[1:0]), // Templated
.out_b (dout_b_0[1:0]), // Templated
// Inputs
.in_a (din_a_0[1:0]), // Templated
.in_b (din_b_0[1:0]) // Templated
);

/*submod AUTO_TEMPLATE(
.in_\(.*\)(dout_stage1_\1[]),
.out_\(.*\)(dout_stage2_\1[]),
);*/
submod u_submod_1(/*AUTOINST*/
// Outputs
.out_a (dout_a_1[1:0]), // Templated
.out_b (dout_b_1[1:0]), // Templated
// Inputs
.in_a (din_a_1[1:0]), // Templated
.in_b (din_b_1[1:0]) // Templated
);

/*submod AUTO_TEMPLATE(
.in_\(.*\)(dout_stage2_\1[]),
.out_\(.*\)(dout_\1[]),
);*/
submod u_submod_2(/*AUTOINST*/
// Outputs
.out_a (dout_a_2[1:0]), // Templated
.out_b (dout_b_2[1:0]), // Templated
// Inputs
.in_a (din_a_2[1:0]), // Templated
.in_b (din_b_2[1:0]) // Templated
);
endmodule
//Local Variables:
//verilog-library-directories:(".")
//End:

获取模块路径

现在我们可能会好奇,Verilog-Mode如何知道给定的模块声明的路径。

verillog-mode首先在当前文件中查找,以防您在那里定义了多个模块。然后它在verilog-library-extensions中查找带有每个扩展名的模块名,通常是一个’.v’。最后,它在每个定义了在verilog-library-directories的目录中搜索。

因此,如果我们有一个需要在子目录中查找子模块的顶级模块,我们需要告诉verilog-mode在子目录中查找。最好的方法是在每个需要库变量的Verilog文件的末尾定义它们:

1
2
3
4
5
// Local Variables:
// verilog-library-directories:("." "subdir" "subdir2")
// verilog-library-files:("/some/path/technology.v" "/some/path/tech2.v")
// verilog-library-extensions:(".v" ".h")
// End:

这三个变量的作用如下:
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仔细检查。

Verilog-Mode User Guide

Verilog-Mode使用方法总结