RTL Coding Style
RTL Coding Style
标准的文件头
在每一个版块的开头一定要使用统一的文件头,其中包括作者名,模块名,创建日期,概要,更改记录,版权等必要信息。
建议使用以下的文件头
1 | // ************************************************************** |
标准的module格式
对于模块的书写采用统一的格式便于项目内部成员的理解和维护,其内容解释如下:
- 端口声明时,定义顺序以功能区分,不以方向区分。时钟和复位信号端口必须定义在其他信号之前。
- 端口定义必须采用ANSI风格(在模块端口位置完全声明端口名称、类型和方向)
- 模块名、模块例化名统一,例化名前加小写u_以区分 ( 多次例化另加标识 ),三者关系:
- 文件名 :xxx .v (小写)
- 模块名 :xxx (小写)
- 例化名 :u_xxx
- 例化模块时,每行只例化一个端口,且不能使用表达式,所有输入端口都必须有驱动,未使用的输出接口必须例化,但可以不连接。
- 消除顶层模块的胶连逻辑(集成各功能单元的
top.v
位置),各功能单元的top.v
中允许存在合理的胶连逻辑。
一致的排版
一致的缩排
统一的缩排取4个空格宽度(注意尽量别使用TAB键缩进,不同的IDE对TAB缩进的解释可能不同)
输入输出信号的宽度定义与关键字之间,信号名与宽度之间要用空格分开;所有宽度定义对所有信号名对齐,代码风格统一如下:
1
2
3
4
5
6input [3:0] input_a ; // *****
input input_b ; // *****
…
output [127:0] output_a ;
output [15:0] output_b ;
output output_c ;
一致的begin end书写方式
always中,一定要用begin end区分,格式和代码风格统一如下:
1 | always @ (postedge clk or negedge rst_n) begin |
if else中仅有一个语句行时,不要使用begin end;如果有多个语句行时,注意缩进四个空格。
一致的信号命名风格
全称 | 缩写 | 中文含义 |
---|---|---|
acknowledge | ack | 应答 |
address | addr(ad) | 地址 |
arbiter | arb | 仲裁 |
check | chk | 校验,如CRC校验 |
clock | clk | 时钟 |
config | cfg | 配置信息 |
control | ctrl | 控制 |
count | cnt | 计数 |
data in | din(di) | 数据输入 |
data out | dout(do) | 数据输出 |
decode | de | 译码 |
decrease | dec | 减一 |
delay | dly | 延迟 |
disable | dis | 不使能 |
error | err | 错误(指示) |
enable | en | 使能 |
frame | frm | 帧 |
generate | gen | 生成,如CRC生成 |
grant | gnt | 申请通过 |
increase | inc | 加一 |
input | in(i) | 输入信号 |
length | len | (帧、包)长 |
output | out(o) | 输出信号 |
priority | pri | 优先级 |
pointer | ptr | 指针 |
rd enable | ren | 读使能 |
read | rd | 读(操作) |
ready | rdy | 应答信号或准备好 |
receive | rx | (帧数据)接收 |
request | req | (服务、仲裁)请求 |
reset | rst | 复位信号 |
souce | scr | 源(端口) |
ststistics | stat | 统计 |
timer | tmr | 定时器 |
temporary | tmp | 临时 |
transmit | tx | 发送(帧数据)相关 |
valid | vld(v) | 有效、校验正确 |
wr enable | wen | 写使能 |
write | wr | 写操作 |
- 端口、信号、变量名的所有字母小写;函数名、宏定义、参数定义用大写;
- 使用简称、缩略词(加上列表);
- 基于含义命名(避免以数字命名的简单做法),含义可分段(最多分三段),每一小段之间加下划线”_”,如tx_data_val;命名长度一般限制在20个字符以内;
- 低电平有效信号,加后缀”_n”,如 rst_n;
- 无条件寄存的寄存信号在原信号上加dly1、dly2… 如原信号 data_in,寄存一拍data_in_dly1,寄存两拍data_in_dly2;
- 不能用 ”reg”作为最后的后缀名,因为综合工具会给寄存器自动加上reg后缀, 如果命名里就用reg作为后缀名则扰乱了网表的可读性。
统一的表达式书写
括号的使用
如果一个表达式的分组情况不是很明显时,加上括号有助于理解。
例如下面的代码加上括号就清晰很多。
if (&a==1’b1&&!flag==1’b1||b==1’b1)
改为:
if ((&a==1’b1)&&(!flag==1’b1)||(b==1’b1))
适当的使用空格
一般表达式在运算符的两侧要各留出一个空格,但定义比较长的表达式,去掉优先级高的运算符前的空格,使其与运算对象紧连在一起,可以更清晰的显示表达式结构。
还是上面的例子
if ((&a==1’b1)&&(!flag==1’b1)||(b==1’b1))
改为
if ( (&a==1’b1) && (!flag==1’b1) || (b==1’b1) )
”<=”, ”=”,运算符前后都要加空格。
赋值要指明比特宽度
赋值或者条件判断时要注明比特宽度,注意表达式的位宽匹配。如:
reg [4:0] signal_a;
错误情况:
1 | signal_a <= 5; |
正确情况:
1 | signal_a <= 5'd5; |
因为工具默认是32位宽,如果不注明位宽,工具检查会报warning,而且这样增加了设计的严谨性。
(tips:参数化代码设计中可直接加上不指定位宽的常数)
书写规范
每行只写一条语句,便于后续验证中行覆盖率的检查。
统一的语句书写―条件判断结构书写方式
条件的完整性
if else搭配使用,对于缺省的条件要写”else;”;
if else的条件判别式要全面,比如”if(a == 1’b0)”;
case中的缺省条件要写”default”;
”if else”结构:适用于复杂条件判断的语句
对于复杂的条件判断,使用” ? : “如果不仔细分析条件的每一条路径,就让读代码的人搞不清它是到底要做什么。例如:
C = (!Ic&&!rc)?1'b0:(Ic?rc:Ic);
改为:
1 | always @(Ic or rc) begin //if else |
简单的条件判断,我们可以使用三目符;当涉及复杂的条件判断,使用IF-ELSE结构以获得清晰的结构便于理解和维护。
IF-ELSE结构 VS CASE结构
IF-ELSE综合的结果可能是与或非门,也可能是一组多路选择器;而CASE综合结果一般会是多路选择器,但对于可以优化的CASE综合工具会综合出更简单的结构。
所有对于可以写出平行结构的条件,优先写成case结构,例如地址译码等。条件之间有重复和嵌套的情况则是写成IF-ELSE结构。
Finite State Machine
不允许有模糊不清的状态机模式,所有的状态机必须清晰明了。
要求将状态机的时序部分和组合逻辑部分分开,建议采用三段式结构。
状态机的状态名必须使用参数编码,并以“ST_”做前缀命名,状态信号必须用fsm_<cs/ns/ls>命名。(注:cs为current state,ns为next state,ls为last state)
统一格式的always程序块的书写
always 中的变量的赋值方式―阻塞与非阻塞赋值
当进行时序逻辑建模时,always块中使用非阻塞赋值,“<=”;
参考如下代码:
1 | always @(posedge clk or negedge rst_n) begin |
当进行组合逻辑建模时,always块中使用阻塞赋值,“=”;
1 | always @(addr) begin |
always中变量赋值的唯一性
组合always块一定要注意敏感量列表中的触发项完整且不冗余;如果不是这样,综合的电路会与实际设计不符合,会报warning;这边建议使用always @(*)
的写法描述组合always块。
不要再多个always模块中对同一个reg型变量进行赋值;
建议不要在一个always块里给多个变量赋值。如果将一组条件相同的变量写在一个always块中更有利于可读性的提高和功能的实现时候,可有例外情况,但请尽量多加注释,以增加可读性,并注意在组合always块中不要出现LATCH;
always复位的书写
异步复位和同步复位模块内要统一,异步复位的条件表达式及命名要和always敏感列表中的描述相统一,所有的复位有效电平必须统一。
合理的注释
- 代码中建议采用英文作详细的注释;
- 注释应该与代码一致,修改程序的时候一定要修改相应的注释;
- 注释不应重复代码已经表明的内容,而是简明地点明程序的突出特征;
- 注释应该提取程序的线索和关键词,它整合程序中分散的信息并它帮助理解程序中不能表明的部分。
- 注释中可以加入TODO、FIXME等标签来提示代码中的待办事项;
重用化设计
层次结构与模块划分
- 层次设计的原理以简单为主―尽量避免不必要的层次;层次结构设计得好,在综合中就不需要太多的优化过程;
- 模块的划分根据层次设计来决定―模块化对于布线有很大帮助,模块化的设计中要尽量减少全局信号的使用;
- 通用的部分尽量提取出来作为一个共用模块;
参数传递
- 需要传递参数的模块,在多次例化的时候统一都传递参数,不要例化同一个模块,有的传参数,有的不传。
- 大模块间信号加前缀:“模块A缩写”_“模块B缩写”表示模块A到模块B的信号。
模块划分的技巧
- 将不同的时钟域分离开来;
- 按照不同的设计目标划分成块,分块时应在数据流方向上进行切分;
- 在同一模块中实现逻辑资源和算术资源的共享;