当前位置 博文首页 > zhoujianjayj的博客:Verilog代码规范(一)
入行挺久的,但是一直没有写过一些技术文档之类的文章。几年前有过类似的想法,但是觉得自己道行太浅,难免班门弄斧。
最近因为某些原因在学习和整理一些内容,也顺便发出来和大家一起交流。希望可以和大家一起学习,大家也可以提供宝贵的意见。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
长长的分割线,下面是正文
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码规范很多都是team的统一要求,有些并非是强制,team内遵循某个约定,以方便阅读、管理。
代码规范的好处:
1. 信号命名常用前后缀
2. define和parameter定义的常量和参数使用大写;
3. 所有的模块名,信号名,变量名和端口名**小写;**但类似于FlopClockConstant,以单词开头大写的命名规则可以参考;
?
1. 文件名为"<模块名>.v“,即文件名与设计单元(模块名)相同,都用小写;(强制)
2. 顶层文件要加上_top后缀。而testbench文件则加上_tb后缀;
?
1. 使用u_xxx或者xxx_inst,多次例化可以使用xxx_inst0,等
module counter (mclk,rst_n,cnt);
input mclk;
input rst_n;
output [7:0] cnt;
counter u_counter0();
counter counter_inst0();
2. 元件连接使用端口名连接,不使用端口顺序连接;(强制)
//端口名连接
counter8 counter8_inst(
.clk(ref_clk),
.rst_n(rst_n),
.carry_in(ci),
.carry_out(ci_o),
.dout(data_out)
);
//端口顺序连接,不推荐使用,不便理解,容易犯错,非常不便于维护;
counter8 counter8_inst(ref_clk,rst_n,ci,ci_o,data_out);
3. 模块的参数例化也需要列写完整;(强制)
?
1. 两个空格或者四个空格;不建议使用tab键进行缩进,因为不同的vim设置,tab所代表的space值不同,有的tab=4space,有的tab=2space
个人做法:在vim设置中加上如下设置,则tab自动转换成4个space;
set expandtab #将tab替换成space
set tabstop=4 #将tab设置成4个space
?2. 对齐有利于修改,维护,阅读和理解:
(1)有利于修改:如果现在需要例化这个模块,并且端口完全上浮上去。那么对于端口对齐的模块的端口信息转换成模块例化就非常简单,只需要进行简单的复制粘贴即可,如下代码段;
input clk_csr ;
input rst_csr_n;
input clk_rx ;
input rst_rx_n ;
input test_mode;
相反对于第二个模块的端口信息,想要做成模块例化就很难,如下的代码;
output pread;
output [31:0] paddr;
output pwrite;
output [31:0] pecsr_wdata;
output [3:0] pbyte_en;
?(2)?阅读和理解:缩进便于理解代码层次,如下代码段所示:
always @(posedge clk_rx or negedge rst_rx_n)
begin
if(~rst_rx_n) begin
o_rx_bypass <= 1'b0;
end
else begin
case (o_rx_bypass)
2'b00:begin
o_rx_bypass <= o_rx_bypass;
end
2'b01:begin
if(en) beign
o_rx_bypass <= o_rx_bypass_sync;
end
else begin
o_rx_bypass <= o_rx_bypass;
end
end
2'b10:begin
o_rx_bypass <= o_rx_bypass;
end
2'b11:begin
o_rx_bypass <= o_rx_bypass;
end
default:;
endcase
end
end
反例,如果是if else的嵌套结构,则不缩进更加没法看;(这个例子比较好的一点是其end后面加了很多注释,有利于理解if...else的结构)
always @(*)
begin
case(valid)
2'b00:
begin if(flag) valid_data = data[0];
else valid_data = 1'b0;
end
2'b01: begin if(flag) valid_data = data[1];
else valid_data = 1'b0;
end
2'b10: begin if(flag) valid_data = data[2];
else valid_data = 1'b0;
end
2'b11: begin if(flag) valid_data = data[3];
else valid_data = 1'b0;
end
defalut: valid_data = 1'b0;
endcase
end
为什么要注释?别人好阅读+自己好阅读
1. 一定要写好注释,注意逻辑分段等,方便后期阅读;常见的注释有:
(1) 文件头说明,包括:版权声明,作者信息说明,版本号,日期,修改记录,模块功能(越详细越好,甚至可以放上时序图,状态机),等等;
(2) 信号说明,包括信号分段描述,信号基本功能描述(如复位低电平)等;
信号基本功能描述有如:
(3) 代码分段说明,包括代码块起点/结束描述,代码段基本功能描述等;
(4) 尽量去掉确实不需要的注释代码,过多冗余代码不利于阅读
?
一般的代码结构:
1. 文件头声明
2. 端口信号声明
3. 参数声明(parameter)
4. 内部信号声明
5. 逻辑块+例化模块
两种方式比较建议:
?
同一个宏定义需要在多个文件里调用的话,**建议使用调用文件。把”`define”定义放在一个单独的文件里面。**自己也不可以随便添加宏定义;
(1)方便宏定义的统一管理和修改;
(2)防止不同地方定义的宏的值不一样,可能会造成冲突和覆盖;
(3)尽量避免使用不必要的宏定义;
?
在做项目的时候,一个大的系统会被分割成很多细小的部分,由不同的人负责,设计完成后上传到具有版本管理功能的服务器上。
有时候有的人忘记在上传代码之前进行严格测试,或者根本传错了版本,就会造成其他人仿真报错。
最好的办法就是将这些有问题的模块临时替换成dummy模块。
dummy模块不仅可以隔离问题模块,还可以显著加速仿真过程,可谓一举两得。一般有两种方式传统上大家在完成设计之后会另外建立一个只有接口代码的空文件,例如dummy_sync_fifo.v,当需要将sync_fifo变成dummy的时候,就将文件清单中的文件名改掉,但这样的方式会增加文件,容易造成管理的混乱,反复修改文件清单显然也不是一个好的做法。
推荐的方式是在模块的顶层文件中写一个宏控制的综合控制逻辑,当DUMMY_SYNC_FIFO宏被定义的时候,综合工具就只会将整个模块综合成没有任何逻辑的dummy模块了。
cs