auto_dff说明文档
前言
代码即注释,注释即代码。
我一直比较推崇围绕寄存器进行编码的RTL风格。这种风格典型的组织方式就像这样:
//信号声明
wire [8 -1:0]cnt_d;
wire [8 -1:0]cnt_q;
wire cnt_en;
//寄存器例化
dffre #(.WIDTH(8))
u_cnt_dff(.clk(clk), .rst_n(rst_n), .d(cnt_d), .en(cnt_en), .q(cnt_q));
//可能有其他代码
...
//信号逻辑
assign cnt_en = case0 || case1;
assign cnt_d = case0 ? 8'd0 : cnt_q + 1'b1;
某天实在是不想一行一行的自己来例化寄存器了,因此就做了这个脚本auto_dff.py。
工程路径
auto_dff.py · 尼德兰的喵/myscript_python - Gitee.com
更新记录
| 时间 | 更新 | 说明 |
|---|---|---|
| 2024/1/29 | 1.分区域进行寄存器例化 2.支持always型寄存器例化方式 |
功能列表
1.通过注释进行寄存器例化;
使用说明
/*AUTO_DFF [位宽]信号名 = 初始值 寄存器类型名 END*/
-可选- --可选-- ---可选---
可选内容遵循一下规则:
说明1:位宽不写则默认为1
说明2:信号名必须写
说明3:初始值不写,则默认0
说明4:寄存器类型必须包含关键字dffre dffse dffve dffr dffe dff,推荐只维护dff dffr dffe dffre四种寄存器
说明5:寄存器类型不写默认dffre
说明6:寄存器类型名支持_crc _err _parity等后缀
脚本的使用方式为在.vimrc中添加:
command! DF :execute '%! 你的路径/auto_dff.py -f %'
command! DFD :execute '%! 你的路径/auto_dff.py -d -f %'
之后在vim打开的文件中,通过:DF和:DFD插入和删除寄存器例化文件。
使用示例
寄存器例化
对于文件中的如下注释语句:
/*AUTO_DFF [INFO_WD]axi_info0=8'd10 dffre END*/
/*AUTO_DFF [6]axi_info1 dffve END*/
/*AUTO_DFF [$clog(WDD)]axi_info2 nova_dffe END*/
/*AUTO_DFF axi_info3 END*/
/*AUTO_DFF [$clog(WDD)+2] axi_info4 my_only_dffr END*/
/*AUTO_DFF axi_info5 this_dffr END*/
/*AUTO_DFF [$clog(WDD)*$clog(WDD)]axi_info6 = 0 this_dff END*/
:DF后会展开为:
/*AUTO_DFF [INFO_WD]axi_info0=8'd10 dffre END*/
//AUTO_DFF_START
wire [INFO_WD -1:0]axi_info0_d;
wire [INFO_WD -1:0]axi_info0_q;
wire axi_info0_en;
wire [INFO_WD -1:0]axi_info0 = axi_info0_q;
dffre #(.WD(INFO_WD))
u_axi_info0_dff (
.clk (clk),
.rst_n (rst_n),
.d (axi_info0_d),
.q (axi_info0_q),
.en (axi_info0_en));
//AUTO_DFF_END
/*AUTO_DFF [6]axi_info1 dffve END*/
//AUTO_DFF_START
wire [6 -1:0]axi_info1_d;
wire [6 -1:0]axi_info1_q;
wire axi_info1_en;
wire [6 -1:0]axi_info1 = axi_info1_q;
dffve #(.WD(6), .VA({6{1'b0}}))
u_axi_info1_dff (
.clk (clk),
.rst_n (rst_n),
.d (axi_info1_d),
.q (axi_info1_q),
.en (axi_info1_en));
//AUTO_DFF_END
/*AUTO_DFF [$clog(WDD)]axi_info2 nova_dffe END*/
//AUTO_DFF_START
wire [$clog(WDD) -1:0]axi_info2_d;
wire [$clog(WDD) -1:0]axi_info2_q;
wire axi_info2_en;
wire [$clog(WDD) -1:0]axi_info2 = axi_info2_q;
nova_dffe #(.WD($clog(WDD)))
u_axi_info2_dff (
.clk (clk),
.d (axi_info2_d),
.q (axi_info2_q),
.en (axi_info2_en));
//AUTO_DFF_END
/*AUTO_DFF axi_info3 END*/
//AUTO_DFF_START
wire [1 -1:0]axi_info3_d;
wire [1 -1:0]axi_info3_q;
wire axi_info3_en;
wire [1 -1:0]axi_info3 = axi_info3_q;
dffre #(.WD(1))
u_axi_info3_dff (
.clk (clk),
.rst_n (rst_n),
.d (axi_info3_d),
.q (axi_info3_q),
.en (axi_info3_en));
//AUTO_DFF_END
/*AUTO_DFF [$clog(WDD)+2] axi_info4 my_only_dffr END*/
//AUTO_DFF_START
wire [$clog(WDD)+2 -1:0]axi_info4_d;
wire [$clog(WDD)+2 -1:0]axi_info4_q;
wire axi_info4_en;
wire [$clog(WDD)+2 -1:0]axi_info4 = axi_info4_q;
my_only_dffr #(.WD($clog(WDD)+2))
u_axi_info4_dff (
.clk (clk),
.rst_n (rst_n),
.d (axi_info4_d),
.q (axi_info4_q),
);
//AUTO_DFF_END
/*AUTO_DFF axi_info5 this_dffr END*/
//AUTO_DFF_START
wire [1 -1:0]axi_info5_d;
wire [1 -1:0]axi_info5_q;
wire axi_info5_en;
wire [1 -1:0]axi_info5 = axi_info5_q;
this_dffr #(.WD(1))
u_axi_info5_dff (
.clk (clk),
.rst_n (rst_n),
.d (axi_info5_d),
.q (axi_info5_q),
);
//AUTO_DFF_END
/*AUTO_DFF [$clog(WDD)*$clog(WDD)]axi_info6 = 0 this_dff END*/
//AUTO_DFF_START
wire [$clog(WDD)*$clog(WDD) -1:0]axi_info6_d;
wire [$clog(WDD)*$clog(WDD) -1:0]axi_info6_q;
wire axi_info6_en;
wire [$clog(WDD)*$clog(WDD) -1:0]axi_info6 = axi_info6_q;
this_dff #(.WD($clog(WDD)*$clog(WDD)))
u_axi_info6_dff (
.clk (clk),
.d (axi_info6_d),
.q (axi_info6_q),
);
//AUTO_DFF_END
always手写寄存器例化
如果不是通过例化寄存器进行搭建,也可以以手写always块的形式进行生成,此时需要修改脚本:
class DFF:
inst_type = False #True为例化形式
修改后重新生成为:
/*AUTO_DFF [INFO_WD]axi_info0=8'd10 dffre END*/
//AUTO_DFF_START
wire [INFO_WD -1:0]axi_info0_d;
wire [INFO_WD -1:0]axi_info0_q;
wire axi_info0_en;
reg [INFO_WD -1:0]axi_info0;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
axi_info0 <= 8'd10;
else if(axi_info0_en)
axi_info0 <= axi_info0_d;
end
assign axi_info0_q = axi_info0;
//AUTO_DFF_END
/*AUTO_DFF [6]axi_info1 dffve END*/
//AUTO_DFF_START
wire [6 -1:0]axi_info1_d;
wire [6 -1:0]axi_info1_q;
wire axi_info1_en;
reg [6 -1:0]axi_info1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
axi_info1 <= {6{1'b0}};
else if(axi_info1_en)
axi_info1 <= axi_info1_d;
end
assign axi_info1_q = axi_info1;
//AUTO_DFF_END
/*AUTO_DFF [$clog(WDD)]axi_info2 nova_dffe END*/
//AUTO_DFF_START
wire [$clog(WDD) -1:0]axi_info2_d;
wire [$clog(WDD) -1:0]axi_info2_q;
wire axi_info2_en;
reg [$clog(WDD) -1:0]axi_info2;
always @(posedge clk)begin
if(axi_info2_en)
axi_info2 <= axi_info2_d;
end
assign axi_info2_q = axi_info2;
//AUTO_DFF_END
/*AUTO_DFF axi_info3 END*/
//AUTO_DFF_START
wire [1 -1:0]axi_info3_d;
wire [1 -1:0]axi_info3_q;
wire axi_info3_en;
reg [1 -1:0]axi_info3;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
axi_info3 <= {1{1'b0}};
else if(axi_info3_en)
axi_info3 <= axi_info3_d;
end
assign axi_info3_q = axi_info3;
//AUTO_DFF_END
/*AUTO_DFF [$clog(WDD)+2] axi_info4 my_only_dffr END*/
//AUTO_DFF_START
wire [$clog(WDD)+2 -1:0]axi_info4_d;
wire [$clog(WDD)+2 -1:0]axi_info4_q;
reg [$clog(WDD)+2 -1:0]axi_info4;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
axi_info4 <= {($clog(WDD)+2){1'b0}};
else
axi_info4 <= axi_info4_d;
end
assign axi_info4_q = axi_info4;
//AUTO_DFF_END
/*AUTO_DFF axi_info5 this_dffr END*/
//AUTO_DFF_START
wire [1 -1:0]axi_info5_d;
wire [1 -1:0]axi_info5_q;
reg [1 -1:0]axi_info5;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
axi_info5 <= {1{1'b0}};
else
axi_info5 <= axi_info5_d;
end
assign axi_info5_q = axi_info5;
//AUTO_DFF_END
/*AUTO_DFF [$clog(WDD)*$clog(WDD)]axi_info6 = 0 this_dff END*/
//AUTO_DFF_START
wire [$clog(WDD)*$clog(WDD) -1:0]axi_info6_d;
wire [$clog(WDD)*$clog(WDD) -1:0]axi_info6_q;
reg [$clog(WDD)*$clog(WDD) -1:0]axi_info6;
always @(posedge clk)begin
axi_info6 <= axi_info6_d;
end
assign axi_info6_q = axi_info6;
//AUTO_DFF_END
分区域例化
脚本除了识别/*AUTO_DFF END*/关键字外,还会识别/*AUTO_DFF INSTWIRE*/和/*AUTO_DFF INSTREG*/关键字,这两个关键词分别指明了“信号声明区域”和“寄存器例化区域”:

因此寄存器生成的代码遵循如下的规则:
#识别的关键词为 /*AUTO_DFF [WD]dff_inst=3'd2 moon_dffre_crc END*/
#最简单模式为 /*AUTO_DFF dff_inst END*/,表示1比特复位值为0的寄存器
#如果在文档中能够找到/*AUTO_DFF INSTWIRE*/则将所有的信号声明放在此处
#如果在文档中能够找到/*AUTO_DFF INSTREG*/则把所有的寄存器例化或always块放至于此处
#/*AUTO_DFF INSTREG*/必须在/*AUTO_DFF INSTWIRE*/后面,否则编译会有问题
#如果只有/*AUTO_DFF INSTREG*/,那么信号声明也都放在这里
#如果找不到对应的,则放在/*AUTO_DFF ... END*/下面
也就是说,一个/*AUTO_DFF [WD]dff_inst=3'd2 moon_dffre_crc END*/会生成两部分代码,分别是信号声明:
wire [WD -1:0]dff_inst_d;
wire [WD -1:0]dff_inst_q;
wire dff_inst_en;
wire dff_inst_crc;
和寄存器例化:
wire [WD -1:0]dff_inst = dff_inst_q;
moon_dffre_crc #(.WD(WD))
u_dff_inst_dff (
.clk (clk),
.rst_n (rst_n),
.d (dff_inst_d),
.q (dff_inst_q),
.en (dff_inst_en),
.dff_inst_crc (dff_inst_crc)
);
生成的寄存器不会生成在/*AUTO_DFF [WD]dff_inst=3'd2 moon_dffre_crc END*/下面,因此这个注释仅仅作为提示寄存器类型和复位值的注释而存在,可以自由的写在逻辑区域或者其他任何区域,作用就是提醒有一个寄存器做逻辑的时候别忘了:
/*AUTO_DFF [WD]dff_inst=3'd2 moon_dffre_crc END*/
assign dff_inst_d = ....;
assign dff_inst_en = ....;
附加信息
有时候我们的寄存器会有附加信息,比如说带着err、parity、ecc结果等校验信息,此时在“type”区域可以写成类似moon_dffr_err的形式,moon_为调用寄存器组的前缀名,dffr为寄存器的类型,err为附加信息。此时声明时会多声明一个dff_inst_err信号并将其连接在err接口上。
如果是手写寄存器的形式,那么只声明一个dff_inst_err信号,逻辑需要后面自己去做。