原创 Verilog代码编写规范

2009-10-11 10:31 3173 6 6 分类: FPGA/CPLD

一. 强调Verilog代码编写风格的必要性。
强调Verilog代码编写规范,经常是一个不太受欢迎的话题,但却是非常有必要的。
每个代码编写者都有自己的编写习惯,而且都喜欢按照自己的习惯去编写代码。与自己编写风格相近的代码,阅读起来容易接受和理解。相反和自己编写风格差别较大的代码,阅读和接受起来就困难一些。
曾有编程大师总结说,一个优秀的程序员,能维护的代码长度大约在1万行数量级。代码的整洁程度,很大程度上影响着代码的维护难度。
遵循代码编写规范书写的代码,很容易阅读、理解、维护、修改、跟踪调试、整理文档。相反代码编写风格随意的代码,通常晦涩、凌乱,会给开发者本人的调试、修改工作带来困难,也会给合作者带来很大麻烦。
(实际上英文Coding Style有另一层涵义,更偏重的是,某一个电路,用那一种形式的语言描述,才能将电路描述得更准确,综合以后产生的电路更合理。本文更偏重的是,编写Verilog代码时的书写习惯。)


二. 强调编写规范的宗旨。
缩小篇幅
提高整洁度
便于跟踪、分析、调试
增强可读性,帮助阅读者理解
便于整理文档
便于交流合作

三. 变量及信号命名规范。
1. 系统级信号的命名。
系统级信号指复位信号,置位信号,时钟信号等需要输送到各个模块的全局信号;系统信号以字符串Sys开头。

2. 低电平有效的信号后一律加下划线和字母n。如:SysRst_n;FifoFull_n;

3. 经过锁存器锁存后的信号,后加下划线和字母r,与锁存前的信号区别。如CpuRamRd信号,经锁存后应命名为CpuRamRd_r。
低电平有效的信号经过锁存器锁存后,其命名应在_n后加r。如CpuRamRd_n信号,经锁存后应命名为CpuRamRd_nr
多级锁存的信号,可多加r以标明。如CpuRamRd信号,经两级触发器锁存后,应命名为CpuRamRd_rr。

4. 模块的命名。
在系统设计阶段应该为每个模块进行命名。命名的方法是,将模块英文名称的各个单词首字母组合起来,形成3到5个字符的缩写。若模块的英文名只有一个单词,可取该单词的前3个字母。各模块的命名以3个字母为宜。例如:
Arithmatic Logical Unit模块,命名为ALU。
Data Memory Interface模块,命名为DMI。
Decoder模块,命名为DEC。


5. 模块之间的接口信号的命名。
所有变量命名分为两个部分,第一部分表明数据方向,其中数据发出方在前,数据接收方在后,第二部分为数据名称。
两部分之间用下划线隔离开。
第一部分全部大写,第二部分所有具有明确意义的英文名全部拼写或缩写的第一个字母大写,其余部分小写。
举例:CPUMMU_WrReq,下划线左边是第一部分,代表数据方向是从CPU模块发向存储器管理单元模块(MMU)。下划线右边Wr为Write的缩写,Req是Request的缩写。两个缩写的第一个字母都大写,便于理解。整个变量连起来的意思就是CPU发送给MMU的写请求信号。
模块上下层次间信号的命名也遵循本规定。
若某个信号从一个模块传递到多个模块,其命名应视信号的主要路径而定。

6. 模块内部信号:
模块内部的信号由几个单词连接而成,缩写要求能基本表明本单词的含义;
单词除常用的缩写方法外(如:Clock->Clk, Write->Wr, Read->Rd等),一律取该单词的前几个字母( 如:Frequency->Freq, Variable->Var 等);
每个缩写单词的第一个字母大写;
若遇两个大写字母相邻,中间添加一个下划线(如DivN_Cntr);
举例:SdramWrEn_n;FlashAddrLatchEn;

四. 编码格式规范。

1. 分节书写,各节之间加1到多行空格。如每个always,initial语句都是一节。每节基本上完成一个特定的功能,即用于描述某几个信号的产生。在每节之前有几行注释对该节代码加以描述,至少列出本节中描述的信号的含义。

2. 行首不要使用空格来对齐,而是用Tab键,Tab键的宽度设为4个字符宽度。行尾不要有多余的空格。

3. 注释。
使用//进行的注释行以分号结束;
使用/* */进行的注释,/*和*/各占用一行,并且顶头;
例:
// Edge detector used to synchronize the input signal;

4. 空格的使用:
不同变量,以及变量与符号、变量与括号之间都应当保留一个空格。
Verilog关键字与其它任何字符串之间都应当保留一个空格。如:
Always @ (……)
使用大括号和小括号时,前括号的后边和后括号的前边应当留有一个空格。
逻辑运算符、算术运算符、比较运算符等运算符的两侧各留一个空格,与变量分隔开来;单操作数运算符例外,直接位于操作数前,不使用空格。
使用//进行的注释,在//后应当有一个空格;注释行的末尾不要有多余的空格。
例:
assign SramAddrBus = { AddrBus[31:24], AddrBus[7:0] };
assign DivCntr[3:0] = DivCntr[3:0] + 4'b0001;
assign Result = ~Operand;


5. 同一个层次的所有语句左端对齐;Initial、always等语句块的begin关键词跟在本行的末尾,相应的end关键词与Initial、always对齐;这样做的好处是避免因begin独占一行而造成行数太多;
例:
always @ ( posedge SysClk or negedge SysRst ) begin
if( !SysRst ) DataOut <= 4'b0000;
else if( LdEn ) begin
DataOut <= DataIn;
End
else DataOut <= DataOut + 4'b0001;
end

6. 不同层次之间的语句使用Tab键进行缩进,每加深一层缩进一个Tab;

8. 在endmodule,endtask,endcase等标记一个代码块结束的关键词后面要加上一行注释说明这个代码块的名称;

9. 在task名称前加tsk以示标记。在的名称前加func以示标记。例如:
task tskResetSystem;
……
endtask //of tskResetSystem

五.小结:
以上列出的代码编写规范无法覆盖代码编写的方方面面,还有很多细节问题,需要在实际编写过程中加以考虑。并且有些规定也不是绝对的,需要灵活处理。并不是律条,但是在一个项目组内部、一个项目的进程中,应该有一套类似的代码编写规范来作为约束。
总的方向是,努力写整洁、可读性好的代码。


Motorala推荐的Verilog代码规范.(Verilog).(翻译)


一. 文件命名


规则 1: 每个文件中只包含一个设计单元
理由: 便于修正.


规则 2: 文件命名协定
<设计单元名称>.<扩展名>
理由: 便于理解设计单元constructs及文件内容.
如: spooler.v    // spooler模块的同步Verilog代码描述



规则 3. 模拟和数字Verilog文件
每个单一文件必须包含: (1)模拟(analog)Verilog(用.va文件后缀); 或(2)数字Verilog(用.v文件后缀); 或(3)清晰的模\数混合Verilog(用.va文件后缀).
理由: 模拟的编译器也许不能操纵数字的架构; 反之亦然.



二. HDL编码项目命名


规则 4: 允许的字符集
命名中必须包含字母, 数字或下划线[A-Z, a-z, _] (见 规则5)
例外: 不允许使用连续的下划线.
理由: 双下划线在硬件竞争中是不工作的.



规则 5: 命名的首字符

命名必须以字母开头, 而不是数字或下划线 (见 规则4)
理由: 以数字或下划线开头的命名, 可能会引起工具冲突.



规则 6: 所有命名必须是唯一的无关项

理由:  在Verilog(语法敏感)和VHDL(语法不敏感)中的设计转换中, 很可能受语法不敏感的设计风格影响.



规则 7: 信号的一致性

理由: 和VHDL相比, Verilog和许多支持VHDL的工具都一样是语法敏感. 及时辨别信号的类型(如: 低电平有效的信号或时钟)有助于调试.



规则 8: 常量, 参数和标签区块要大写

理由: 提供了一种可以辨别那些在仿真中不需要经过数据转换的实体的机制.



规则 9: 信号, constructs和实例化标签要小写

理由: 从在仿真中数据不变化的实体中区别信号和constructs, 以及在设计中保持一致的视觉和感觉.



规则 10: 有意义的信号和变量名称

小写的名称必须包含信号和变量的意图.
理由: 描述是什么, 而不是怎样去做, 有助于理解设计.
如: data_bus, set_priority



规则 11: 有意义的常量名称

常量名称一定要描述常量的意图. 根据意图可以很明显地看出常量的类型, 及不是端口的名称. 常量需大写.
理由: 有意义的名称对于常量来说, 非常重要.
如: 好的名称: SBUS_DATA_BITS, MEMORY_WIDTH, CLK_PERIOD
如: 差的名称: ADDRESS_SIZE 并不明晰, 当它指的是数的位宽或地址空间的长度



规则 12: 有意义的construct名称

construct的名称如functions, modules, tasks等,必须根据它们要做什么而不是怎么样去做来命名. construct名称必须小写.
理由: 描述是什么, 而不是怎样去做, 有助于理解设计.



规则 13: 有意义的实例化标签

实例化标签要根据construct指定的要实现什么, 而不是怎样去做来命名.
理由: 描述是什么, 而不是怎样去做, 有助于理解设计.
如: addr_decode, bit_stuff, sbus_if



规则 14: 用下划线分隔包含许多单词的名称

理由: 增加可读性
如: ram_addr



规则 15: 低电平有效信号命名使用 _b

理由: 有意义, 一致性的名称, 有助于理解设计.
如: enable_data_b, reset_b



规则 16: 时钟信号命名使用 _clk

理由: 有意义, 一致性的名称, 有助于理解设计.
如: fifo_transmit_clk
例外: 明显包含时钟的信号名称(如: system_clock, clk32m).



规则 17: 未连接(Unconnected)的输出信号命名使用 _uc

理由: 当关于未连接信号的警告出现时, 如果该名称以_nc结尾, 这样该信号就会很明显地被知道是未连接, 而不是存在的错误.



规则 18: 信号捆绑(bundling)

不相干的信号不能被捆绑成总线.
理由: 易于理解module.



方针 19: 三态信号命名使用 _z

理由: 有意义, 一致性的名称, 有助于理解设计.
如: ram_data1_z



方针 20: 状态机信号命名使用 _next

理由: 有意义, 一致性的名称, 有助于理解设计.
如: transmit_next



方针 21: 测试模块信号命名使用 _test

理由: 有意义, 一致性的名称, 有助于理解设计.
如: parallel_clk_test



方针 22: 扫描(scan)使能(enable)信号命名使用 _se

理由: 有意义, 一致性的名称, 有助于理解设计.



方针 23: 模拟信号命名使用 _ana

理由: 当区别模拟信号时, 有助于理解设计. 尤其是当使用图像查看器时, 更加有用.



方针 24: 信号名称的多后缀优先性

从高到低:
1. _b
2. _z
3. _clk
4. _next
最高优先级的后缀被推荐为信号名称的最靠后的后缀.
如: ram_data1_z_b, receive_clk_b



方针 25: 参数化变量命名使用 _PP

理由: 有意义, 一致性的名称, 有助于理解设计.
如: NUM_COLUMNS_PP



方针 26: 信号名称的长度应该小于28个字符

短的名称可读性较强. 但28个字符不包括层次(hierarchy).
理由: 长的名称可能会引起工具的冲突.



方针 27: 避免缩略语除了常用已知的缩略语

理由: 使用有意义的名称.
例外: 一般所知的缩略语, 如RAM,和loop counters. loop counters可能被命名为单个字母, 如I或N, 因为它们代表索引. 一些后端工具会连接所有层次的名称, 以及给予有限的名称总长度. 在那种情况下, 缩略语是多层次名称所必须的. 但这些缩略语必须以注释的方式来阐明.



方针 28: 文档和附加命名协议

任何在module中使用的缩略语要记录在档案中是被推荐的. 任何在module中使用的协议都要添加到必要的协议中, 或推荐的SRS, 然后记录到档案中.
理由: 对于原始设计者来说,很明显的缩略语, 当再次被使用到时, 有可能不再那么清晰.



方针 29: 层次间名称的一致性

层次间或整个IP中, 信号或设计单元的名称应该保持一致.推荐关联多个实例化的名称因该有名称索引.
理由: 增加可读性, 消除歧义, 在综合中避免缓冲器置入.



方针 30: Verilog名称应该与档案中的名称相同

理由: 便于理解HDL模块.


 

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
6
关闭 站长推荐上一条 /3 下一条