ci the verilog-mode
This commit is contained in:
parent
ca899e84a2
commit
6d5b209ba5
|
@ -0,0 +1,111 @@
|
|||
@[TOC](verilog-mode中的AUTO_TEMPLATE)
|
||||
对于Verilog-mode中的模块例化,在使用AUTOINST机制时,一般都不是单独使用的,因为它默认是端口名和连接的名字都是一样的;
|
||||
但是我们基本是有所改动的,所以需要使用AUTO_TEMPLATE来进行基于正则表达式的替换;
|
||||
|
||||
# AUTO_TEMPLATE
|
||||
## 正则表达式匹配
|
||||
### $.*$(即\(.*\))--需要进行转义一下
|
||||
被$xx$包裹的部分会被记录,后续通过\1,\2来引用;
|
||||
```
|
||||
自动连接未明确列出的端口,保留原始名称。
|
||||
示例:
|
||||
若模板为 .$.*$ (\1[]),则:
|
||||
|
||||
端口 .data_in → 生成 .data_in(data_in[])
|
||||
端口 .enable → 生成 .enable(enable[])
|
||||
```
|
||||
|
||||
Demo:
|
||||
```verilog
|
||||
/* mcu_crg AUTO_TEMPLATE (
|
||||
.i_scan_mode (1'b0),
|
||||
.i_scan_rst_n (1'b1),
|
||||
.o_\(.*\) (\1[]), //这样是方便是捕获得到的 \1
|
||||
);*/
|
||||
|
||||
```
|
||||
|
||||
### \1
|
||||
尽量便面硬编码,自动匹配;
|
||||
在正则表达式 $ $ 中,\1 表示引用 第一个捕获组 匹配到的内容,\2 表示第二个捕获组,以此类推;
|
||||
| 输入字符串 | 正则$.*$ | 捕获组\1内容 |
|
||||
|-----------|---------|------------ |
|
||||
| “data_in” | $.*$ | data_in |
|
||||
| "addr_bus" | $.*$_bus | addr |
|
||||
|"ctrl[3]" | $.*$$$.*$$ | ctrl |
|
||||
|
||||
Demo:
|
||||
```verilog
|
||||
/* xxx AUTO_TEMPLATE (
|
||||
.hclk (mcu_mtrx_clk),
|
||||
.hresetn (mcu_mtrx_rst_n),
|
||||
.hgrant (1'b1),
|
||||
.hbusreq (),
|
||||
.hlock (),
|
||||
.hresp1 ({1'b0,hresp_ahb_m5}),
|
||||
.hresp2 ({1'b0,hresp_ahb_m6}),
|
||||
.$h.*$1 (\1_ahb_m5[]),
|
||||
.$h.*$2 (\1_ahb_m6[]),
|
||||
.hsel (hselx_ahb_s7),
|
||||
.hready_resp (hreadyout_ahb_s7),
|
||||
.$h.*$ (\1_ahb_s7[]),
|
||||
.debug_.* (),
|
||||
.int_.* (),
|
||||
); */
|
||||
|
||||
```
|
||||
**.debug_.* () 和 .int_.* () 的作用就是明确不连接所有调试和中断信号**。这是Verilog-mode中处理未使用端口的标准化做法,既避免冗余代码,又保证工具链解析的清晰性。
|
||||
* 正则捕获组:
|
||||
$h.*$:匹配以 h 开头的信号名(如 haddr),通过 \1 动态引用。
|
||||
示例:.$h.*$1 (\1_ahb_m5[]) → 若匹配 haddr1,则生成 .haddr1(haddr_ahb_m5[])
|
||||
|
||||
* 空连接与常量
|
||||
hgrant (1'b1):直接绑定常量高电平。
|
||||
hbusreq ():悬空不连接。
|
||||
|
||||
* 通配符忽略
|
||||
debug_.* ():忽略所有 debug_ 开头的信号。
|
||||
int_.* ():忽略所有 int_ 开头的信号。
|
||||
|
||||
|
||||
### [0-9]
|
||||
|
||||
```verilog
|
||||
.gather_info_$[0-9]+$ (gather_node_y_\1_info[]),
|
||||
.gather_intr_mask_$[0-9]+$ (gather_node_y_\1_intr_mask[]),
|
||||
```
|
||||
进行映射连接
|
||||
|
||||
### 输入接0,输出悬空
|
||||
```verilog
|
||||
.noc_ndma_r1_* (*if (equal vl-dir "input") "'0\"" "\"")")
|
||||
```
|
||||
匹配目标: 所有以 .noc_ndma_r1_ 开头的端口(如 .noc_ndma_r1_data、.noc_ndma_r1_en)。
|
||||
条件逻辑: 如果端口方向是 input,则将其连接为 **常量 '0**(即接地)。否则(output 或 inout),悬空不连接("")。
|
||||
```verilog
|
||||
/* 提取信号名前缀并复用 */
|
||||
.$.*$_req (\1_ack[]) // 将 "mem_req" 映射为 ".mem_req(mem_ack[])"
|
||||
```
|
||||
|
||||
```verilog
|
||||
.spi_\(.*\) ( @"(if(equal vl-dir \\"input\\") \\"xxx_spi_s_\1[]\\" \\"xxx_spi_s_\1[]\\" )" )
|
||||
```
|
||||
匹配规则:当是input端口时候,匹配什么;当是output端口时候,匹配什么;
|
||||
|
||||
|
||||
## AUTO_TEMPLATE 联合AUTOINST使用
|
||||
记住AUTO_TEMPLATE只是基于正则表达式的字符替换,一定是要出发AUTOINST才能进行例化模块使用;
|
||||
给出示例模板:
|
||||
```verilog
|
||||
//==S1
|
||||
/* moduleNmae AUTO_TEMPLATE "u_moduleName"(
|
||||
//==表达式规则
|
||||
); */
|
||||
|
||||
//==S2
|
||||
moduleNmae u_moduleName (/*AUTOINST*/);
|
||||
|
||||
//==S3
|
||||
按键"\a" 看看有无报错;
|
||||
|
||||
```
|
Binary file not shown.
After Width: | Height: | Size: 156 KiB |
Binary file not shown.
After Width: | Height: | Size: 155 KiB |
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
Binary file not shown.
After Width: | Height: | Size: 149 KiB |
Binary file not shown.
After Width: | Height: | Size: 134 KiB |
|
@ -0,0 +1,400 @@
|
|||
@[TOC](verilog_mode_UG)
|
||||
|
||||
# 1 what is verilog-mode
|
||||
verilog-mode原本是Emacs的一种编辑模式,面向Verilog的开发环境,主要是提供自动缩进机制和AUTO机制。其中AUTO机制是verilog-mode最重要的核心。AUTO机制主要提供自动模块参数列表、自动完成模块例化和自动声明连线等;
|
||||
我们在写rtl时候,尤其是在top顶层集成的时候,经常会不可避免的来进行例化模块连接信号,这非常繁琐和容易出错。这也是veirlog-mode产生的背景;
|
||||
# 1.1 收益
|
||||
* 自动缩进,代码整洁,便于阅读等;
|
||||
* 提高编码效率和准确性,尤其是在顶层实例化;
|
||||
* 便于维护,比如修改、增加和删除端口,无需修改参数列表,比如修改、增加和删除子模块端口,无需修改顶层实例化;
|
||||
|
||||
# 2 How to use--AUTO机制
|
||||
所有的都是添加/*AUTOxxx*/,最后使用\m来自动化生成;
|
||||
## 2.1 AUTOSENSE来自动生成敏感列表
|
||||
编写时:
|
||||
```verilog
|
||||
always @ (/*AUTOSENSE*/) begin
|
||||
dout_a = {din_a,din_b};
|
||||
end
|
||||
```
|
||||
\a生成后:
|
||||
```verilog
|
||||
always @(/*AUTOSENSE*/din_a or din_b) begin
|
||||
dout_a = {din_a,din_b};
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
## 2.2 AUTOARG来自动生成模块输入输出参数列表
|
||||
编写时:
|
||||
```verilog
|
||||
module sub_md(/*AUTOARG*/);
|
||||
input din_a;
|
||||
input din_b;
|
||||
output reg[1:0] dout_a;
|
||||
|
||||
endmodule:sub_md
|
||||
```
|
||||
\a生成后:
|
||||
```verilog
|
||||
module sub_md(/*AUTOARG*/
|
||||
// Outputs
|
||||
dout_a,
|
||||
// Inputs
|
||||
din_a,din_b
|
||||
);
|
||||
input din_a;
|
||||
input din_b;
|
||||
output reg[1:0] dout_a;
|
||||
|
||||
always @ (/*AUTOSENSE*/) begin
|
||||
dout_a = {din_a,din_b};
|
||||
end
|
||||
endmodule:sub_md
|
||||
```
|
||||
|
||||
## 2.3 AUTOINST来自动实现例化
|
||||
编写时:
|
||||
```verilog
|
||||
# sub_md.v
|
||||
|
||||
module sub_md(/*AUTOARG*/
|
||||
// Outputs
|
||||
dout_a,
|
||||
// Inputs
|
||||
din_a,din_b
|
||||
);
|
||||
input din_a;
|
||||
input din_b;
|
||||
output reg[1:0] dout_a;
|
||||
|
||||
always @ (/*AUTOSENSE*/) begin
|
||||
dout_a = {din_a,din_b};
|
||||
end
|
||||
endmodule:sub_md
|
||||
|
||||
# top_md.v
|
||||
module top_md(/*AUTOARG*/);
|
||||
input din1;
|
||||
input wire[1:0] din2;
|
||||
|
||||
output dout1;
|
||||
output [1:0] dout2;
|
||||
|
||||
/*AUTOREG*/
|
||||
/*AUTOWIRE*/
|
||||
|
||||
wire din_a;
|
||||
wire din_b;
|
||||
|
||||
always @(/*AUTOSENSE*/) beign
|
||||
dout1 = din1 | din2[1];
|
||||
end
|
||||
|
||||
assign din_a = din2[0];
|
||||
assign din_b = din2[1];
|
||||
|
||||
sub_md u_sub_md(/*AUTOINST*/);
|
||||
|
||||
assign dout2[1:0] = dout_a[1:0];
|
||||
|
||||
endmodule: top_md
|
||||
|
||||
```
|
||||
|
||||
\a生成后:
|
||||
```verilog
|
||||
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
|
||||
```
|
||||
这样上述做的需要的约束或者前提是:
|
||||
1.子模块与顶层例化模块需保存在同一文件目录;否则会索引不到子模块;
|
||||
2.实例化的端口名与子模块的端口名默认是一致的;一般来讲,是提倡实例化的端口名与子模块的端口名保持一致;
|
||||
3.但是经常连线是必须涉及到实例化端口名和被实例化端口名不一致的情况;
|
||||
---1.在/*AUTOINST*/之前进行直接指定该端口名,放心/*AUTOINST*/之前的端口名不会再次生成,但是最后加上Inputs/Outputs注释,否则AUTOWIRE不知道信号方向;
|
||||
比如如下:
|
||||
```verilog
|
||||
fanout fanout (
|
||||
//.i端口事先声明;
|
||||
// Inputs
|
||||
.i (my_i_dont_mess_with_it),
|
||||
/*AUTOINST*/
|
||||
// Outputs
|
||||
.o (o[31:0]));
|
||||
|
||||
```
|
||||
## 2.4 AUTOWIRE来自动实现连线
|
||||
顶层实例化时,需要将线进行声明,AUTOWIRE特别适合两个子模块之间互连且没在顶部模块中使用到的信号;
|
||||
约束前提:子模块间互联的信号不是已经在顶层上使用过的信号;
|
||||
编写时:
|
||||
```verilog
|
||||
# src.v
|
||||
module src(/*AUTOARG*/);
|
||||
input des2src_a;
|
||||
input des2src_b;
|
||||
output [1:0] src2des;
|
||||
|
||||
always @(/*AUTOSENSE*/) begin
|
||||
src2des = {des2src_b,des2src_a};
|
||||
end
|
||||
endmodule:src
|
||||
|
||||
# des.v
|
||||
module des(/*AUTOARG*/);
|
||||
output des2src_a;
|
||||
output des2src_b;
|
||||
input [1:0] src2des;
|
||||
|
||||
always @(/*AUTOSENSE*/) begin
|
||||
{des2src_b,des2src_a} = src2des;
|
||||
end
|
||||
|
||||
endmodule:des
|
||||
|
||||
# top.v
|
||||
module top(/*AUTOARG*/);
|
||||
input a;
|
||||
output b;
|
||||
|
||||
/*AUTOWIRE*/
|
||||
|
||||
/*AUTOREG*/
|
||||
|
||||
src u_src(/*AUTOINST*/);
|
||||
|
||||
des u_des(/*AUTOINST*/);
|
||||
|
||||
endmodule:top
|
||||
|
||||
```
|
||||
\a生成:因为代码比较多,在这里就不展示了;
|
||||
|
||||
## 2.5 AUTOREG
|
||||
若是模块的输出来自寄存器,会自动为该输出信号名声明为寄存器;AUTOREG将完成输出信号的寄存器声明,若是输出来自非寄存器,则不会添加reg声明;
|
||||
编辑时:
|
||||
```verilog
|
||||
module src(/*AUTOARG*/);
|
||||
input des2src_a;
|
||||
input des2src_b;
|
||||
output [1:0] src2des;
|
||||
output test_auto_reg;
|
||||
output test_auto_wire;
|
||||
|
||||
/*AUTOREG*/
|
||||
|
||||
/*AUTOWIRE*/
|
||||
|
||||
always @(/*AUTOSENSE*/) begin
|
||||
src2des = {des2src_b,des2src_a};
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
test_auto_reg = 1'b0;
|
||||
end
|
||||
|
||||
assign test_auto_wire = 1'b1;
|
||||
|
||||
endmodule:src
|
||||
```
|
||||
|
||||
\a生成后:
|
||||

|
||||
|
||||
## 2.6 AUTOINSTPARAM来自动实现例化时填充参数
|
||||
这个一般使用场景比较少,因为parameter较少;
|
||||
编辑时:
|
||||
```verilog
|
||||
# autoinstparam.v
|
||||
module autoinstparam #(
|
||||
parameter para_1,
|
||||
parameter para_2
|
||||
)(
|
||||
/*AUTOARG*/
|
||||
);
|
||||
|
||||
input a;
|
||||
input b;
|
||||
output c;
|
||||
|
||||
assign c = a ^ b;
|
||||
endmodule: autoinstparam
|
||||
|
||||
# topparam.v
|
||||
module topparam(/*AUTOARG*/);
|
||||
|
||||
input test_in;
|
||||
output test_out;
|
||||
|
||||
parameter para_1 = 0,
|
||||
para_2 = 1;
|
||||
|
||||
wire a = 0;
|
||||
wire b = 1;
|
||||
|
||||
autoinstparam #(/*AUTOINSTPARAM*/
|
||||
|
||||
) u_autoinstparam(/*AUTOINST*/);
|
||||
|
||||
endmodule: topparam
|
||||
```
|
||||
|
||||
\a生成后:
|
||||

|
||||
|
||||
## 2.7 AUTOINPUT、AUTOOUTPUT来自动实现top层的输入输出
|
||||
如果在top层,一般只有子模块的例化,没有任何其他粘合逻辑,当然这也是某种最理想的情况;
|
||||
这时top层通过AUTOWIRE声明子模块的例化连线,AUTOREG来声明未声明的输出信号寄存器;AUTOINST来实现子模块的例化;其余未声明的信号,就是top模块的输入输出信号,可以通过AUTOINPUT,AUTOOUTPUT完成输入输出信号的声明;
|
||||
|
||||
编辑时:
|
||||
```verilog
|
||||
# sub_md.v
|
||||
module sub_md(
|
||||
/*AUTOARG*/
|
||||
);
|
||||
input din_a;
|
||||
input din_b;
|
||||
|
||||
output [1:0] dout;
|
||||
|
||||
assign dout = {din_b,din_a};
|
||||
|
||||
endmodule: sub_md
|
||||
|
||||
# top_md.v
|
||||
module top_md(
|
||||
/*AUTOARG*/
|
||||
);
|
||||
/*AUTOINPUT*/
|
||||
|
||||
/*AUTOOUTPUT*/
|
||||
|
||||
sub_md u_sub_md(/*AUTOINST*/);
|
||||
|
||||
endmodule: top_md
|
||||
|
||||
```
|
||||
|
||||
\a生成后:
|
||||

|
||||
|
||||
## AUTORESET来自动实现时序逻辑的初始化
|
||||
寄存器声明的变量往往需要赋初值。描述时序逻辑的always时,最好对复位条件下进行寄存器变量的初始化;描述组合逻辑的always时,最好对寄存器变量赋缺省值,这样不会产生latch。
|
||||
AUTORESET模式初始化赋值是0,若是其他值,需手动添加到AUTORESET之前,这样就不会重复添加;
|
||||
|
||||
编辑时:
|
||||
```verilog
|
||||
module autoreset(
|
||||
/*AUTOARG*/
|
||||
);
|
||||
|
||||
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
|
||||
|
||||
endmodule: autoreset
|
||||
|
||||
```
|
||||
特别注意:要事先声明reg变量,AUTORESET才会自动加上位宽。
|
||||
\a生成后:
|
||||

|
||||
|
||||
## AUTOREGINPUT来自动实现打激励
|
||||
尤其适用于搭建testbench时,将设计模块(dut)作为一个子模块在TB中进行例化,因此需要一些reg类型的信号,作为dut的输入激励。
|
||||
|
||||
编辑时:
|
||||
```verilog
|
||||
module dut(
|
||||
/*AUTOARG*/
|
||||
);
|
||||
input din1;
|
||||
input din2;
|
||||
output dout;
|
||||
|
||||
assign dout = din1 & din2;
|
||||
|
||||
endmodule: dut
|
||||
|
||||
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
|
||||
|
||||
```
|
||||
\a生成后:
|
||||

|
||||
|
||||
# 3 AUTO_TEMPLATE创建模板
|
||||
## 3.1 AUTOTEMPLATE
|
||||
|
||||
|
||||
|
||||
## 在模板中使用Lisp
|
||||
|
Loading…
Reference in New Issue