Emacs verilog-mode 的使用

本文

主要介绍使用emacs verilog-mode 编辑verilog的神奇操作。

版本 说明
0.1 初版发布
0.2 添加快捷键插入内容

背景

  • 主机: Thinkpad S2
  • 系统: Deepin GNU/Linux 15.11
  • 内核: Debian 6.3.0-18+deb9u1
  • emacs版本:Linux GNU Emacs 26.3

参考

什么是verilog mode

verilog-mode是Emacs的一种编辑模式,主要面对verilog的开发环境,拥有很方便的自动缩进机制和AUTO机制。AUTO机制是Emacs verilog-mode中一些自动化实现的脚本功能,比如自动填充模块参数列表、自动完成模块例化、自动声明连线等等。

为什么使用verilog-mode

verilog语法中有很多内容是冗余的,模块中必须出现却起不到什么功能作用,列举如下:

  • 模块参数列表和模块端口声明input/output
  • reg语句和已经声明为输出的信号
  • 子模块实例化的连线声明
  • 子模块的实例化语句和子模块的端口声明
  • 组合逻辑always语句的敏感信号表(不过已经可以使用*来代替了)

可见verilog语法中的垃圾信息还是不少的,不过这是语法规则导致的,是语言本身的缺陷,作为使用者只能遵守语法规则。这些冗余信息中比如参数列表和模块例化连线,不仅需要花费时间去编写,而且还特别容易出错,给RTL编写以及后续的修改维护都带来很多问题。那么如果解决这些问题,会带来什么效果呢?个人认为有以下几点:

  • 代码整洁,便于阅读
  • 提高编码效率,尤其是顶层实例化
  • 减少拼写错误
  • 便于维护,比如修改、增加和删除端口,无需修改参数列表,比如修改、增加和删除子模块端口,无需修改顶层实例化

AUTO机制的使用

Emacs verilog-mode的AUTO机制,就是在代码中相应的位置写一些/*AUTO*/类似的注释,verilog-mode可以自动替换为所需的内容。Emacs编辑器和verilog-mode的AUTO机制结合,可以很方便的看到AUTO的效果,而且AUTO是以注释形式添加到verilog文件,在语法上本身是合法的,不会影响EDA工具的使用。举例如下:

  • 原代码
    • top module
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module top_md(/*AUTOARG*/);
input           din1;
input [1:0]     din2;

output          dout1;
output [1:0]    dout2;

/*AUTOREG*/
/*AUTOWIRE*/

wire            din_a;
wire            din_b;

always@(/*AUTOSENSE*/)
    dout1 = din1 | din2[1];

assign din_a = din2[0];
assign din_b = din2[1];

sub_md i_sub_md(/*AUTOINST*/);

assign dout2[1:0] = dout_a[1:0];
endmodule // top_md
  • sub_module
1
2
3
4
5
6
7
8
9
module sub_md(/*AUTOARG*/);
input           din_a;
input           din_b;
output [1:0]    dout_a;

wire [1:0]      dout_a;

assign dout_a[1:0] = {din_b,din_a};
endmodule // sub_md
  • AUTO之后
 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
module top_md(/*AUTOARG*/
// Outputs
dout1, dout2,
// Inputs
din1, din2
);
input           din1;
input [1:0]     din2;

output          dout1;
output [1:0]    dout2;

/*AUTOREG*/
// Beginning of automatic regs (for this module's undeclared outputs)
reg                     dout1;
// End of automatics
/*AUTOWIRE*/
// Beginning of automatic wires (for undeclared instantiated-module outputs)
wire [1:0]              dout_a;                 // From i_sub_md of sub_md.v
// End of automatics

wire            din_a;
wire            din_b;

always@(/*AUTOSENSE*/din1 or din2)
    dout1 = din1 | din2[1];

assign din_a = din2[0];
assign din_b = din2[1];

sub_md i_sub_md(/*AUTOINST*/
                // Outputs
                .dout_a                 (dout_a[1:0]),
                // Inputs
                .din_a                  (din_a),
                .din_b                  (din_b));

assign dout2[1:0] = dout_a[1:0];
endmodule // top_md

使用 AUTOSENSE 自动生成敏感表

在Verilog 2000中,已经对语法做出了简化,比如使用 @(×) 来代替敏感信号列表,但是需要EDA工具的支持。不过现在EDA工具都已经支持verilog 2005了,可以将敏感信号列表直接写为 @(×) 即可,所以 AUTOSENSE 功能可以不使用。

1
2
3
4
always @ (/*AUTOSENSE*/) begin
   outin = ina | inb;
   out = outin;
end

快捷键 C-c C-a 之后,实现AUTO机制,快捷键 C-c C-k 可取消AUTO效果。AUTO效果如下:

1
2
3
4
always @ (/*AUTOSENSE*/ina or inb) begin
   outin = ina | inb;
   out = outin;
end

使用AUTOARG自动生成模块参数表

1
2
3
4
module ex_arg (/*AUTOARG*/);
   input i;
   output o;
endmodule

快捷键 C-c C-a 之后,实现AUTO机制,快捷键 C-c C-k 可取消AUTO效果。AUTO效果如下:

1
2
3
4
5
6
7
8
9
module ex_arg (/*AUTOARG*/
  // Outputs
  o,
  // Inputs
  i);

   input i;
   output o;
endmodule

不支持带 `ifdefs 等条件限制的端口声明,如有此需求,可以将其写在 AUTOARG 之前, AUTOARG 不会对其进行重新声明。不过不建议这种方式,因为在 AUTOINST 中会引入更多的 `ifdefs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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

  ...

使用AUTOINST自动实例化

子模块:

1
2
3
4
5
module fanout (o,i)
   input i;
   output [31:0] o;
   wire [31:0] o = {32{i}};
endmodule

顶层模块:

1
2
3
4
5
module ex_inst (o,i)
   input i;
   output [31:0] o;
   fanout fanout (/*AUTOINST*/);
endmodule

快捷键 C-c C-a 之后,实现AUTO机制,快捷键 C-c C-k 可取消AUTO效果。AUTO效果如下:

1
2
3
4
5
6
7
8
9
module ex_inst (o,i)
   output o;
   input i;
   fanout fanout (/*AUTOINST*/
                    // Outputs
                    .o (o[31:0]),
                    // Inputs
                    .i (i));
endmodule

注意,子模块需要与顶层例化模块保存在同一文件目录,否则会索引不到子模块,而且实例化的端口名与子模块的端口名,默认是一致的。一般来讲,提倡实例化的端口名与子模块的端口名一致,这样对综合及维护都会带来好处,但是由于某些原因无法使其一致,那最简单的方法就是在 AUTOINST 之前指定实例化的端口名,所有在 AUTOINST 之前定义的端口不会再次生成,但是你最好加上 //Inputs //Outputs 注释,否则 AUTOWIRE 不知道信号方向。举例如下:

1
2
3
4
5
6
fanout fanout (
               // Inputs
               .i          (my_i_dont_mess_with_it),
               /*AUTOINST*/
               // Outputs
               .o          (o[31:0]));

如果你被端口名不一致所困扰,可以参考我的另一篇帖子 Emacs Verilog Perl 的使用中vrename的使用章节

使用AUTO_TEMPLATE创建模板

如果一个模块被实例化多次,你可以使用verilog的 generate 语法,也可以使用Emacs verilog-mode的 AUTO_TEMPLATEAUTOINST 。Verilog-mode向上索引最近的模板,这样对于一个子模块可以写多个模板,只需要将模板写在实例化之前即可。

1
2
3
4
/* psm_mas AUTO_TEMPLATE (
        .PTL_MAPVALIDX          (PTL_MAPVALID[@]),
        .PTL_BUS                (PTL_BUSNEW[]),
        ); */

TEMPLATE中的模块名称必须与实例中的模块名称相同,并且只需列出每次实例化时名字不同的那些信号就好了。如上文的例子,要遵守此格式(每行只有一个端口,并且以逗号结尾,最后以分号结尾,就像AUTOINST产生的一样)。实际上,最简单的方法是完成一个AUTOINST,并将其修改后复制到AUTO_TEMPLATE。

1
psm_mas ms2 (/*AUTOINST*/);

AUTO效果:

psm_mas ms2 (/*AUTOINST*/
    // Outputs
    .INSTDATAOUT     (INSTDATAOUT),
    .PTL_MAPVALIDX   (PTL_MAPVALID[2]),   // Templated

    .PTL_BUS         (PTL_BUSNEW[3:0]),   // Templated
    ....

@ 字符非常有用,他将被实例名的末尾数字代替(不限于1位数字),比如ms2,@替换为2。AUTO机制把模板中的 [] 替换为子模块中该信号真实的字段范围,比如 PTL_BUSNEW[] 替换成了 PTL_BUSNEW[3:0] ,因为子模块中PTL_BUSNEW信号声明就是 [3:0]。这样写的好处就是子模块的修改自动响应到顶层模块。(其实如果端口信号位宽一致,实例化端口也可以不指明位宽字段,不过为了便于维护和减少出错,建议指明位宽字段)

神奇的 @ 不仅支持数字,还可以支持字母,这需要在模板做以下操作(模板支持正则表达式):

1
2
3
4
5
6
7
/* InstModule AUTO_TEMPLATE "_\([a-z]+\)" (
                .ptl_mapvalidx		(@_ptl_mapvalid),
                .ptl_mapvalidp1x	(ptl_mapvalid_@),
                );
        */
        InstModule ms2_FOO (/*AUTOINST*/);
        InstModule ms2_BAR (/*AUTOINST*/);

AUTO效果如下:

1
2
3
4
5
6
7
8
InstModule ms2_FOO (/*AUTOINST*/
            // Outputs
            .ptl_mapvalidx		(FOO_ptl_mapvalid),
            .ptl_mapvalidp1x		(ptl_mapvalid_FOO));
        InstModule ms2_BAR (/*AUTOINST*/
            // Outputs
            .ptl_mapvalidx		(BAR_ptl_mapvalid),
            .ptl_mapvalidp1x		(ptl_mapvalid_BAR));

在模板中使用Lisp

在AUTO_TEMPLATE中指定简单的线名称并不能够解决所有需求,尤其是在多个实例化中使用同一信号不同数据位时。因此,Verilog-Mode允许你编写一个程序来进行计算并为实例化端口进行命名,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* InstModule AUTO_TEMPLATE (
     .a(in[@"(+ (* 8 @) 7)":@"(* 8 @)"]),
     );*/

InstModule u_a0 (/*AUTOINST*/
                 // Inputs
                 .a                     (in[7:0]));               // Templated
InstModule u_a1 (/*AUTOINST*/
                 // Inputs
                 .a                     (in[15:8]));              // Templated
InstModule u_a2 (/*AUTOINST*/
                 // Inputs
                 .a                     (in[23:16]));             // Templated
InstModule u_a3 (/*AUTOINST*/
                 // Inputs
                 .a                     (in[31:24]));             // Templated

这里对上述Lisp代码部分解释一下, 也就是 @"(+ (* 8 @) 7)":@"(* 8 @)"

  • 基础结构为 @"" 代表@为输入参数,引号内为计算处理
  • 每个括号内容为一步计算,形如 (* 8 @) ,意思是 @参数乘8
  • (+ (* 8 @) 7) 就是将 (* 8 @) 的结果加7

AUTOINSTPARAM的使用

如何在实例化时自动填充参数列表?AUTOINSTPARAM可以做到,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
module InstModule;
   parameter PARAM1 = 1;
   parameter PARAM2 = 2;
endmodule

module ModnameTest;
   InstModule #(/*AUTOINSTPARAM*/
         // Parameters
         .PARAM1  (PARAM1),
         .PARAM2  (PARAM2))
     instName
       (/*AUTOINST*/
        ...);

verilog-mode中如何使用正则表达式

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

  • 提取信号中固定位置的数字
1
2
3
.pci_req\([0-9]+\)_j   (pci_req_jtag_[\1]),

.pci_req12_j   (pci_req_jtag_[12]),
  • 删除末尾下划线内容
1
2
3
.\(.*\)_j        (\1_[]),

.pci_req_j   (pci_req[7:0]),
  • 对信号矢量化处理(\1内容代表提取末尾数字之前的内容,\2内容代表提取末尾数字)(将@改为\([0-9]+\)也是可以的)
1
2
3
4
5
6
.\(.*[^0-9]\)@  (\1[\2]),

.pci_req0   (pci_req[0]),
.pci_req1   (pci_req[1]),
.pci_req2   (pci_req[2]),
.pci_req3   (pci_req[3]),

AUTOWIRE 的使用

顶层实例化时,需要将连线进行声明, AUTOWIRE 可以完成此项任务。 AUTOWIRE 将声明所有子模块输出的连接线。这对于在两个子模块之间互连但未在顶部模块中使用的信号特别有用。

1
2
3
4
5
6
7
8
9
module top (o,i)
   output o;
   input i;

   /*AUTOWIRE*/

   inst inst   (/*AUTOINST*/);
   other other (/*AUTOINST*/);
endmodule

快捷键 C-c C-a 之后,实现AUTO机制,快捷键 C-c C-k 可取消AUTO效果。AUTO效果如下:

 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
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

AUTOREG的使用

如果模块输出来自寄存器,则需要将信号声明为寄存器和输出端口。 AUTOREG 将完成输出信号的寄存器声明,并且如果输出来自非寄存器,则不会添加reg声明。

1
2
3
4
5
6
7
8
module ex_reg (o,i)
   output o;
   input i;

   /*AUTOREG*/

   always o = i;
endmodule

快捷键 C-c C-a 之后,实现AUTO机制,快捷键 C-c C-k 可取消AUTO效果。AUTO效果如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
module ex_reg (o,i)
   output o;
   input i;

   /*AUTOREG*/
   // Beginning of automatic regs
   reg    o;
   // End of automatics

   always o = i;
endmodule

AUTOINPUT、AUTOOUTPUT的使用

在top层中,一般只有子模块的例化,没有任何其他粘合逻辑,这也是最期望的。这时top层通过 AUTOWIRE 声明了子模块的输出连线, AUTOINST 实现了子模块的实例化,其余未声明的信号,就是top模块的输入输出信号,我们可以通过 AUTOINPUT AUTOOUTPUT 完成输入输出信号的声明。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
module top_md(/*AUTOARG*/);
/*AUTOINPUT*/
/*AUTOOUTPUT*/

/*AUTOREG*/
/*AUTOWIRE*/

sub_md i_sub_md112(/*AUTOINST*/);
endmodule // top_md


module sub_md(/*AUTOARG*/);
input           din_a;
input  [1:0]    din_b;
output [2:0]    dout_a;

assign dout_a[2:0] = {din_b,din_a};
endmodule // sub_md

快捷键 C-c C-a 之后,实现AUTO机制,快捷键 C-c C-k 可取消AUTO效果。AUTO效果如下:

 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
module top_md(/*AUTOARG*/
// Outputs
dout_a,
// Inputs
din_b, din_a
);
/*AUTOINPUT*/
// Beginning of automatic inputs (from unused autoinst inputs)
input                   din_a;                  // To i_sub_md112 of sub_md.v
input [1:0]             din_b;                  // To i_sub_md112 of sub_md.v
// End of automatics
/*AUTOOUTPUT*/
// Beginning of automatic outputs (from unused autoinst outputs)
output [2:0]            dout_a;                 // From i_sub_md112 of sub_md.v
// End of automatics

/*AUTOREG*/
/*AUTOWIRE*/

sub_md i_sub_md112(/*AUTOINST*/
                   // Outputs
                   .dout_a              (dout_a[2:0]),
                   // Inputs
                   .din_a               (din_a),
                   .din_b               (din_b[1:0]));
endmodule // top_md

AUTORESET的使用

寄存器类型的变量往往需要赋初值。描述时序逻辑的always时,最好对复位条件下进行寄存器变量的初始化;描述组合逻辑的always时,最好对寄存器变量赋缺省值,这样不会产生latch。对于多信号的always语句,手动添加不仅费时,而且容易出错,此时我们可以使用 AUTORESET 自动完成。这里默认初始化赋值为0,如需其他值,需要将其手动添加到 AUTORESET 之前,AUTO不会对其重复添加。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
reg        dout_a;
reg  [1:0] dout_b;
reg  [2:0] dout_c;
reg        dout_x;
reg  [1:0] dout_y;
reg  [2:0] dout_z;

always@(*)
    /*AUTORESET*/
if (sig1==1'b1)begin
    dout_a      = din0;
    dout_b[1:0] = {2{din1}};
    dout_c[2:0] = {3{din2}};
end

always@(posedge clk or negedge rst_n)
if (!rst_n)begin
    /*AUTORESET*/
end
else begin
    dout_x      <= din0;
    dout_y[1:0] <= {2{din1}};
    dout_z[2:0] <= {3{din2}};
end

快捷键 C-c C-a 之后,实现AUTO机制,快捷键 C-c C-k 可取消AUTO效果。AUTO效果如下:

 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
reg        dout_a;
reg  [1:0] dout_b;
reg  [2:0] dout_c;
reg        dout_x;
reg  [1:0] dout_y;
reg  [2:0] dout_z;

always@(*)
    /*AUTORESET*/
    // Beginning of autoreset for uninitialized flops
    dout_a = 1'h0;
    dout_b = 2'h0;
    dout_c = 3'h0;
    // End of automatics
if (sig1==1'b1)begin
    dout_a      = din0;
    dout_b[1:0] = {2{din1}};
    dout_c[2:0] = {3{din2}};
end

always@(posedge clk or negedge rst_n)
if (!rst_n)begin
    /*AUTORESET*/
    // Beginning of autoreset for uninitialized flops
    dout_x <= 1'h0;
    dout_y <= 2'h0;
    dout_z <= 3'h0;
    // End of automatics
end
else begin
    dout_x      <= din0;
    dout_y[1:0] <= {2{din1}};
    dout_z[2:0] <= {3{din2}};
end

注意,要先声明reg, AUTORESET 才会自动加上位宽,如果仍然不能自动将位宽加上,请在.emacs或.emacs.d/init.el文件添加以下设置:

1
(setq verilog-auto-reset-widths t)

如果对时序逻辑always语句中的复位赋值需要添加延迟,请在.emacs或.emacs.d/init.el文件添加以下设置(注意:#1后必须有空格):

1
(setq verilog-assignment-delay "#1 ")

AUTOREGINPUT的使用

在写测试激励时,往往将设计模块(也可以叫dut)作为一个子模块在testbench中进行实例化,这事需要定义一些reg类型的信号,作为dut的输入激励。 AUTOREGINPUT 可以帮你完成此任务。

 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
module testbench;
/*AUTOREGINPUT*/

initial begin
    #0  begin din1 = 1'b0; din2 =1'b0; end
    #10 begin din1 = 1'b0; din2 =1'b1; end
    #10 begin din1 = 1'b1; din2 =1'b0; end
    #10 begin din1 = 1'b1; din2 =1'b1; end
end

dut i_dut(/*AUTOINST*/);
endmodule // testbench

module dut(/*AUTOARG*/
// Outputs
dout,
// Inputs
din1, din2
);
input din1;
input din2;
output dout;

assign dout = din1 & din2;
endmodule

快捷键 C-c C-a 之后,实现AUTO机制,快捷键 C-c C-k 可取消AUTO效果。AUTO效果如下:

 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
module testbench;
/*AUTOREGINPUT*/
// Beginning of automatic reg inputs (for undeclared instantiated-module inputs)
reg                     din1;                   // To i_dut of dut.v
reg                     din2;                   // To i_dut of dut.v
// End of automatics

initial begin
    #0  begin din1 = 1'b0; din2 =1'b0; end
    #10 begin din1 = 1'b0; din2 =1'b1; end
    #10 begin din1 = 1'b1; din2 =1'b0; end
    #10 begin din1 = 1'b1; din2 =1'b1; end
end

dut i_dut(/*AUTOINST*/
          // Outputs
          .dout                         (dout),
          // Inputs
          .din1                         (din1),
          .din2                         (din2));
endmodule // testbench

module dut(/*AUTOARG*/
// Outputs
dout,
// Inputs
din1, din2
);
input din1;
input din2;
output dout;

assign dout = din1 & din2;
endmodule

其他使用技巧

多目录问题(如果文件在同一目录,可忽略)

我们的项目工程会有很多代码,存放在不同的文件目录里,而Verilog-mode进行 AUTOINST 只会自动查找当前目录,这时候就会遇到问题了。那我们就来讲一讲verilog-mode是如何查找的吧。首先如果您在单文件中定义了多个模块,则会优先选择当前文件中的模块,然后在当前文件所在目录查找,如果定义了verilog-library-extensions,则继续查找附加扩展名的文件中查找模块,最后,它在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:

define问题(如果AUTO相关内容不涉及define,可忽略)

使用AUTOINST或AUTOSENSE时,有些场景中信号会带有define,这时候需要verilog-mode将define信息读进来,才可以正确完成AUTO功能。使用如下:

1
2
3
4
// Local Variables:
// eval:(verilog-read-defines)
// eval:(verilog-read-defines "group_standard_includes.v")
// End:

第一行是读取当前文件中的define,第二行是读取define文件。

include file问题(如果AUTO相关内容不涉及include,可忽略)

出于速度原因,Verilog-Mode不会自动读取include文件。这意味着include文件中定义的常量将不被作为AUTOSENSE的常量。解决方法如下:

1
2
3
// Local Variables:
// eval:(verilog-read-includes)
// End:

verilog-mode 的AUTO使用方法

上文也看到了,可以通过打开emacs,使用快捷键来完成AUTO和取消AUTO,除此之外还可以当做脚本来处理,使用方法如下:

1
emacs --batch --no-site-file -l verilog-mode.el filename.v -f verilog-auto -f save-buffer

由此可见,我们可以通过脚本进行批量处理代码文件,完成AUTO机制,并且可以通过将不同目录文件复制其软连接到同一目录,再通过脚本实现AUTO机制,这样解决了不同目录对 AUTOINST 的影响。

emacs的配置(关于verilog-mode)

以下是我的关于verilog-mode的emacs配置内容,“;;”代表注释符号。(所有设置参数均已添加注释,查看其他参数可以在verilog-mode模式下快捷键C-h m,查看verilog-mode.el)

 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
;;my verilog mode config
(add-to-list 'auto-mode-alist' ("\\.vp\\'" . verilog-mode))   ;;open *.vp file with verilog-mode
(add-to-list 'auto-mode-alist' ("\\.svp\\'" . verilog-mode))  ;;open *.svp file with verilog-mode

(setq verilog-date-scientific-format t)   ;;dates are written in scientific format (e.g.  1997/09/17)

(setq-default indent-tabs-mode nil)       ;;false tab indent mode
(setq verilog-indent-level 4)             ;;Indentation of Verilog statements with respect to containing block
(setq verilog-indent-level-module 0)      ;;Indentation of Module level Verilog statements (eg always, initial)
(setq verilog-indent-level-declaration 0) ;;Indentation of declarations with respect to containing block
(setq verilog-case-indent 4)              ;;Indentation for case statements
(setq verilog-cexp-indent 4)              ;;Indentation of Verilog statements split across lines
(setq verilog-indent-lists t)             ;;Indentation of lists, look the under content.
;;  "How to treat indenting items in a list.
;;If t (the default), indent as:
;;	always @( posedge a or
;;                reset ) begin
;;
;;If nil, treat as:
;;	always @( posedge a or
;;	   reset ) begin"

(setq verilog-indent-level-behavioral 4)  ;;Absolute indentation of first begin in a task or function block
(setq verilog-indent-level-directive 4)   ;;Indentation to add to each level of \\=`ifdef declarations
(setq verilog-auto-indent-on-newline t)   ;;Non-nil means automatically indent line after newline
(setq verilog-tab-always-indent t)        ;;Non-nil means TAB should always re-indent the current line.
                                          ;;A nil value means TAB will only reindent when at the beginning of the line.

(setq verilog-indent-begin-after-if t)    ;;Non-nil means indent begin statements following if, else, while, etc. Otherwise, line them up.
(setq verilog-auto-newline t)             ;;Non-nil means automatically newline after semicolons and the punctuation mark after an end
(setq verilog-auto-endcomments t)         ;;Non-nil means a comment /* ... */ is set after the ends which ends cases, tasks, functions and modules.  The type and name of the object will be set between the braces.
(setq verilog-auto-reset-widths t)        ;;True means AUTORESET should determine the width of signals
(setq verilog-assignment-delay "#1 ")     ;;Text used for delays in delayed assignments.  Add a trailing space if set
(setq verilog-auto-lineup 'all)           ;;
;; "Type of statements to lineup across multiple lines.
;;If `all' is selected, then all line ups described below are done.
;;
;;If `declarations', then just declarations are lined up with any
;;preceding declarations, taking into account widths and the like,
;;so or example the code:
;;	reg [31:0] a;
;;	reg b;
;;would become
;;	reg [31:0] a;
;;	reg        b;
;;
;;If `assignment', then assignments are lined up with any preceding
;;assignments, so for example the code
;;	a_long_variable <= b + c;
;;	d = e + f;
;;would become
;;	a_long_variable <= b + c;
;;	d                = e + f;

有些参数目前我也不清楚具体用法和含义,如有其他好用的配置参数,欢迎推荐给我。

忘了C-c C-a怎么办

有时候我们会忘记 C-c C-a ,显然这样会导致代码错误,如果是全部未实现AUTO还好,很容易及时发现,但是如果是在修改代码,并且仅仅是很小的改动,在功能仿真时不能及时发现,那就成了隐藏的bug,后果还是很严重的。比如组合逻辑always块的 AUTORESET ,功能仿真时并不容易被发现。 所以为了防止忘记 C-c C-a ,这里提供两种方案:

  • 将verilog-auto与保存文件的 C-x C-s 绑定(注意,只有使用 C-x C-s 才有效,功能栏点击保存不会实现AUTO),请将以下代码添加到.emacs或.emacs.d/init.el(只在verilog-mode下有效,不会影响其他模式)
1
2
3
4
5
6
7
(add-hook 'verilog-mode-hook
    '(lambda ()
        (local-set-key (kbd "C-x C-s")
            '(lambda ()
                (interactive)
                (verilog-auto)
                (save-buffer)))))
  • 使用脚本处理,将自己的源文件通过脚本处理后生成新的文件,比如我将原始文件命名为 *.vp 或 *.svp,最终处理后的文件为 *.v 或 *.sv,这样可以避免忘记完成verilog-auto。以脚本的形式进行处理还有一个好处,那就是对vim以及其他编辑器使用者比较友好,因为并非所有开发者都使用emacs。脚本处理命令如下:
1
emacs --batch --no-site-file -l verilog-mode.el filename.v -f verilog-auto -f save-buffer

快捷键插入内容

 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
C-c C-t a  Insert an always @(AS) begin .. end block.
   C-c C-t b  Insert a begin .. end block.
   C-c C-t c  Insert a case block, prompting for details.
   C-c C-t f  Insert a for (...) begin .. end block, prompting for details.
   C-c C-t g  Insert a generate .. endgenerate block.
   C-c C-t h  Insert a header block at the top of file.
   C-c C-t i  Insert an initial begin .. end block.
   C-c C-t j  Insert a fork begin .. end .. join block.
   C-c C-t m  Insert a module .. (/*AUTOARG*/);.. endmodule block.
   C-c C-t o  Insert an OVM Class block.
   C-c C-t u  Insert an UVM Object block.
   C-c C-t U  Insert an UVM Component block.
   C-c C-t p  Insert a primitive .. (.. );.. endprimitive block.
   C-c C-t r  Insert a repeat (..) begin .. end block.
   C-c C-t s  Insert a specify .. endspecify block.
   C-c C-t t  Insert a task .. begin .. end endtask block.
   C-c C-t w  Insert a while (...) begin .. end block, prompting for details.
   C-c C-t x  Insert a casex (...) item: begin.. end endcase block, prompting for details.
   C-c C-t z  Insert a casez (...) item: begin.. end endcase block, prompting for details.
   C-c C-t ?  Insert an if (..) begin .. end block.
   C-c C-t :  Insert an else if (..) begin .. end block.
   C-c C-t /  Insert a comment block.
   C-c C-t A  Insert an assign .. = ..; statement.
   C-c C-t F  Insert a function .. begin .. end endfunction block.
   C-c C-t I  Insert an input declaration, prompting for details.
   C-c C-t O  Insert an output declaration, prompting for details.
   C-c C-t S  Insert a state machine definition, prompting for details.
   C-c C-t =  Insert an inout declaration, prompting for details.
   C-c C-t W  Insert a wire declaration, prompting for details.
   C-c C-t R  Insert a register declaration, prompting for details.
   C-c C-t D  Define signal under point as a register at the top of the modul

文章原创,可能存在部分错误,欢迎指正,联系邮箱 cao_arvin@163.com。