Verilog编码规范.docx
《Verilog编码规范.docx》由会员分享,可在线阅读,更多相关《Verilog编码规范.docx(26页珍藏版)》请在冰豆网上搜索。
Verilog编码规范
Verilog语言编码规范
版本
修改内容
修改人
时间
1.0
初始文档
赵文哲
2011-08
维护人:
赵文哲
E-mail:
*********************
1.关于verilog语言编码规范
本编码规范由西安交通大学人机所电视组全体学生和创芯公司全体员工共同编写和维护。
以此来维护DTV系列芯片的verilog源码的可读性,健壮性和易维护性。
该文档主要致力于verilog语言的编码标准化,同时也适用于其他相似的硬件描述语言,如VHDL等。
使代码易于管理的方法之一是增强代码的一致性,让别人读懂自己的代码是非常重要的事情。
因此,保持自己的代码符合统一的规范是一个编码者的基本素质。
如果自己的编码风格与本文档的规定实在不同,无法忍受,请与维护者联系,在组内会议上统一讨论解决方案。
此外,如其他人对该编码规范有任何建议和批评,欢迎联系该规范的维护者。
维护者的联系方式详见首页的维护列表。
关于本文档读者,文档主要规范了verilog语言的写法和格式,并不介绍verilog语言的语法。
请读者自己学习verilog语言的基础知识。
2.项目文件组织形式
一般而言,项目的文件需要统一的存放在一个统一的文件夹下。
根据各自功能不同,分门别类的存放。
以项目proj-xx为例,其文件存储方式如表1所示。
表1项目文件组织
proj-xx
|--doc
|--datasheet
|--specification
|--inc
|--ip
|--sim_utility
|--altera_utility
|--xilinx_utility
|--dc_utility
|--rtl
|--sim
|--proj_sim
|--subproj_sim
|--softcode
|--adc
|--dac
|--ddr
|--probe
|--dc
|--pt
|--fp
|--pr
|--synplify
doc:
存放项目相关的文档,包括该项目用到的datasheet,芯片规格书(specification)等等。
inc:
存放项目所用到的头文件。
主要是整个项目所用到的整体的定义。
ip:
存放商业上购买的成熟IP以及项目中一些成熟的可复用的代码。
一般的,ip分为sim、FPGA以及DC版本。
rtl:
存放项目的rtl代码。
这是项目的核心,文件名与module名称应当一致,同时按照模块的层叠关系进行存放,必要时也可以采用平行结构存放。
sim:
存放项目的仿真代码。
一般在sim中建立一级子项目,使得sim文件恰恰在项目的两级子目录下。
仿真文件的寻址需要上朔两级从根目录开始,这样当文件发生变动时,文件的寻址不需要多么大的改动。
softcode:
存放芯片设置之外,但是仿真平台需要的模块。
仅仅用在sim之中。
一般存放有仿真用adc、dac、片外ram等等。
dc:
存放synopsysDC综合后的结果。
pt:
存放synopsysPT检查的时序报表。
fp:
存放后端工具floorplan后的网表和延时文件。
pr:
存放后端工具place&route后的网表和延时文件。
synplify:
存放synplify工具综合后的网表。
单单看上面的介绍,可能还是无法直观的看出我们所编写的代码的存放位置。
图1直观的给出了文件的组织形式。
rtl文件夹中存放有芯片内部的数字逻辑;ip文件夹中存放有芯片内部的模拟部分和可复用部分。
softcode文件夹中存放有芯片外部的供仿真平台(testbench)使用的模块。
而testbench文件则直接存放在sim文件夹之中。
图1文件组织形式框图
3.verilog文件内部组织形式
Verilog文件主要有以下几个部分组成:
开头声明,文件引用,时间单位定义,moduleIO声明,宏定义,moduleIO定义,wire®定义,parameter定义,module具体实现。
关于这几个部分的组织形式,请参考附录里的”sample.v”文件。
本章节主要介绍除module具体实现之外的其他环节的规定和注意事项。
而关于module具体实现部分将根据该module是否可综合,分为之后的两个章节来分别阐述。
1)开头声明
每一个verilog文件的开头部分,都必须有一段声明的文字。
该段文字如表2所示,声明文件的版权,作者,修改日期以及修改内容介绍等等。
表2文件的开头声明
//************************Declaration***************************************//
//ThisVerilogfilewasdevelopedbyTheInstituteofArtificialIntelli-//
//genceandRobotics,Xi'anJiaotongUniversity.Thisfilecontainsinfor-//
//mationconfidentialandproprietarytoTheInstituteofArtificial//
//IntelligenceandRobotics,Xi'anJiaotongUniversity.Itshallnotbe//
//reproducedinwhole,orinpart,ortransferredtootherdocuments,or//
//disclosedtothirdparties,orusedforanypurposeotherthanthatfor//
//whichitwasobtained,withoutthepriorwrittenconsentofTheInstitute//
//ofArtificialIntelligenceandRobotics,Xi'anJiaotongUniversity.This//
//noticemustaccompanyanycopyofthisfile.//
////
//Copyright(c)1986--2011TheInstituteofArtificialIntelligenceand//
//Robotics,Xi'anJiaotongUniversity.Allrightsreserved//
////
//Filename:
//
//Author:
venturezhao//
//Date:
2011-01-0100:
00//
//VersionNumber:
1.0//
//Abstract:
//
////
//Modificationhistory:
(includingtime,version,authorandabstract)//
//2011-01-0100:
00version1.0venturezhao//
//Abstract:
Initial//
////
//*********************************end**************************************//
该段注释可以用vim,emacs等高级编辑器直接生成,见附录的vim配置文件。
也可以直接拷贝该段文字,再进行修改,见附录ultraedit文件夹中的”sample.v”。
如果对该文件进行了修改,请在开头声明中添加以下语句,如表3所示。
表3文件的修改声明
//Modificationhistory:
(includingtime,version,authorandabstract)//
//2011-01-0100:
00version1.0venturezhao//
//Abstract:
Initial//
////
//Modificationhistory:
(includingtime,version,authorandabstract)//
//2011-01-0200:
00version1.1venturezhao//
//Abstract:
modifiedtheclockfield.//
////
2)module之前的声明
在文件的开头声明之后,接下来需要包含(include)本文件所需的头文件,以及声明本文件内的时间单位(timescale)。
如表4所示。
表4module之前的声明
//INCLUDEFILE
`include"../../inc/sample.vh"
//MODULEDECLARATION
`timescale1ns/1ps
补充:
之所以将timescale放在include之后,是因为有时候头文件中也会定义时间单位,从而造成时间单位混淆的隐含错误。
3)IO口定义规范
module的IO口定义格式有很多种,本文档目前规定采用verilog-95的格式,在module中声明,在module内部定义的方式。
如表5所示。
表5module的IO口定义规范
//------------------------------------------------------------------------------
//INDEX:
Module
//------------------------------------------------------------------------------
modulemoudule_name(
//reset&clock
ngreset,
clk,
//inputsignal
vi_video,
//outputsignal
vo_video,
//parameter
TYPE
);
//------------------------------------------------------------------------------
//INDEX:
2.Interface
//------------------------------------------------------------------------------
//reset&clock
inputngreset;
inputclk;
//inputsignal
input[9:
0]vi_video;
//outputsignal
output[9:
0]vo_video;
//parameter
input[7:
0]TYPE;
4)wire与reg的定义位置
一个module中的变量声明可以集中放在一起,有时也可以随时使用随时定义。
但是对于稍微复杂的模块而言,随时使用随时定义的方式将会产生前后混淆的问题,同时代码维护时的复杂度也高于集中放在一起的情况。
因此,本文档规定,将reg与wire的定义放在紧跟着module的IO定义之后。
5)变量定义的对齐方式
为了代码的移植方便,同时增加可读性。
本文档规定采用统一的空格方式。
关键字input,output,inout,reg,wire等应当与0列对齐;总线(bus)的位宽声明应当与7列对齐;IO口,变量名称应当与17列对齐。
如果总线位宽过长,导致变量名称无法对齐至17列,则需要以内容为单位,将局部的一些变量统一以4为单位后移。
如表6所示。
表6变量定义的对齐标准
inputngreset;
inputclk;
input[9:
0]vi_video;
output[9:
0]vo_video;
input[7:
0]TYPE;
reg[4:
0]cur_state,next_state;
reg[7:
0]TYPE_D,TYPE_R;
wire[9:
0]video_calc;
wirecontrl;
reg[9:
0]vo_video;
6)变量的命名方式
变量的命名方式有很多种,本规范规定采用如下方式:
变量定义时,根据意义将单词简写成3-8个字符,然后之间用下划线连接。
变量的大小写也有一定的规定:
对于芯片的全局寄存器列表及其相关变量,本规范规定采用全部大写的形式;对于define或parameter,一般采用大写的形式。
而对于其他的变量尽量采用小写或者大小写混合的形式。
表7变量的命名方式
inputngreset;//reset
inputclk;//clock
input[9:
0]vi_video;//一般的变量,采用缩写加下划线形式
output[9:
0]vo_video;
input[7:
0]TYPE;//全局寄存器
reg[4:
0]cur_state,next_state;
reg[7:
0]TYPE_D,TYPE_R;//全局寄存器相关变量
wire[9:
0]video_calc;
wirecontrl;
reg[9:
0]vo_video;
parameterINIT=5'h1;//parameter
parameterSTART=5'h2;
7)always模块的对齐格式
在module的具体实现中,begin和end的对齐方式有很多种。
本文档规定以下两种允许使用的格式。
第一种格式如表8所示,begin跟在判断行之后,当碰见endelsebegin时,正好可以放在一行。
每一次的包含关系时,语句向后递推4列,以形成层叠关系。
第二种格式如表9所示,begin重新开一行,end与begin对齐。
这种方式在出现endelsebegin时,会占用额外的两行。
论行数,这与C语言的行数一样,但是纯字母的方式毕竟没有大括号直观。
其层叠关系与第一种方式一致。
总的来说,这两种格式都可以,但本文档还是推荐使用第一种格式。
除此以外的别的方式,如依次空三列等方式被禁止使用。
表8always模块的一种对齐格式
always@(posedgeclkornegedgengreset)begin
if(!
ngreset)begin
cur_state<=INIT;
endelsebegin
cur_state<=next_state;
end
end
表9always模块的另一种对齐格式
always@(posedgeclkornegedgengreset)
begin
if(!
ngreset)
begin
cur_state<=INIT;
end
else
begin
cur_state<=next_state;
end
end
8)空格与tab之争
代码中表示层叠关系时,空格和tab是两种常见的实现形式。
其优劣众说纷纭。
有人说空格好,因为空格的适用范围将比较广,在各个编辑器下都可以无差别的打开。
也有人说使用tab表示层叠关系时,编写代码时将会比较方便,因为按一个tab会比按4个空格的速度要快。
本规范规定代码中必须使用空格。
首先是器适用范围较广,其次是在vim或者别的高级文字编辑器下,将tab直接转化成空格的使用方法十分方便,根本察觉不出使用空格时的麻烦。
最重要的是,统一使用空格会使得代码维护工作变得简单和统一。
9)行内与行间限制
一行的字符不得超过第80列。
这样便于代码的浏览,不需要将文件横向滚动,或者因为编辑器自动换行而使得结构凌乱。
换行后,可以在当前行向后递推4列处开始,也可以按照语义来进行对齐。
一行的末尾不能是空格。
首先,这样的空格毫无意义;其次,行尾的空格对于文档编辑时,会造成操作上的不便。
行间的空格不可超过2行。
过多的空行没有任何意义。
表10行内与行间限制
always@(posedgeclkornegedgengreset)begin
if(!
ngreset)begin
cur_state<=INIT;
endelsebegin
cur_state<=next_state;//[error]行尾不得有空格
end
end
//<-空行不得超过2行
always@(posedgeclkornegedgengreset)begin
if(!
ngreset)begin
cur_cnt<=INIT;
endelsebegin
cur_cnt<=(cur_state==TRANSFER)?
(test_cnt<<4)+addr[3:
0]:
(test_cnt<<8)+addr[7:
4];//[ok]超过80列时,换行继续
end
end
10)注释
对源代码注释时,不得直接翻译语法。
应当指出当前语句的目的或者意义。
如表11所示,cur_count和cur_cnt分别依次加1。
如果注释仅仅是说明这一点,那么这个注释可以直接删掉,这没有任何意义。
正确的做法是,注释中说明该变量的含义,以及加1的目的。
另外,错误的注释比没有注释更糟糕。
因此,在维护代码时,首先考虑更新的,应该是代码的注释。
当代码注释无法清晰的表达出该段代码的意义时,建议使用另外的文档来说明,并在代码处注明链接关系。
表11注释
always@(posedgeclkornegedgengreset)begin
if(!
ngreset)begin
cur_count<=INIT;
endelsebegin
cur_count<=next_count+1;//cur_count递增//[error]没有必要
end
end
always@(posedgeclkornegedgengreset)begin
if(!
ngreset)begin
cur_cnt<=INIT;
endelsebegin
cur_cnt<=next_cnt+1;//cur_cnt记录DDR2SDRAMAF的递增时间//[ok]可以
end
end
4.可综合verilog语言设计
可综合verilog语言由时序逻辑和组合逻辑两部分构成。
verilog语言的综合就是对这两种逻辑进行匹配嵌套和翻译的过程。
因此,在书写可综合verilog语言时,为了得到简单的,可控的硬件实现,一般在代码结构上使用特定的结构,而不会写一些较为怪异的结构,使得综合结果变差。
由于编写可综合verilog语言的目的是将其综合成硬件电路,因此在编写可综合verilog语言时,首先应想到该代码所代表的硬件电路是什么,只有这样才不会犯一些低级错误,比如说将两个输出接到一个输入上,组合逻辑出现了死循环等等。
值得注意的是,verilog语言的变量类型,wire和reg,与当前电路是时序逻辑还是组合逻辑没有任何关系,它们仅仅是从verilog语法上定义的两种类型。
时序逻辑与组合逻辑的实现只取决于verilog代码的结构。
由于时序逻辑和组合逻辑本身的特点,其赋值类型也分为了阻塞赋值与非阻塞赋值两种。
为综合方便,不得在两种逻辑中混用这两种赋值。
锁存器是一种比较省资源的ASIC逻辑,但由于现在的综合工具没有很好的办法对其进行约束,并且在FPGA下的实现过于复杂。
同时它的出现多数是由于编码者的疏忽而产生的,因此一般情况下不得使用锁存器来实现逻辑。
当遇到跨时钟域的情况时,一般有双跳技术或者通过异步SRAM等方式进行隔离。
同时复位也是一个非常重要的问题。
关于上述话题请查阅相关的资料,下面主要介绍可综合verilog语言的编码规范。
1)时序逻辑与组合逻辑
同步数字电路有时序逻辑和组合逻辑组成。
在可综合verilog语言中,这两种逻辑都有着固定的写法,表12所示为时序逻辑的写法;表13所示为组合逻辑的几种写法。
对于变量的定义而言,寄存器型的变量是指在always模块中被赋值的变量,如reg、integer、time等等;网型的变量是指在assign模块中被赋值的变量,如wire,tri等等。
需要注意的是,这两种类型的变量与verilog的语法有关,而与综合后的硬件实现无关。
请参考表12和表13的定义。
并不是说综合后是连线的变量在定义时就一定是网型的,只能说verilog语言本身还需要发展和完善。
当对综合后的连线有属性说明时,请人为的将其赋值到网型变量上,以便指明属性。
表12时序逻辑
reg[7:
0]cur_count;
//...
always@(posedgeclkornegedgengreset)begin
if(!
ngreset)begin
cur_count<=INIT;
endelsebegin
cur_count<=next_count+1;
end
end
表13组合逻辑
reg[7:
0]cur_count;
wirecontrl;
//...
always@(*)begin
cur_count=next_count+1;
end
assigncontrl=(cur_state==START)?
1'b0:
1'b1;
2)阻塞赋值与非阻塞赋值
verilog语言的赋值方式有两种,阻塞赋值”=”和非阻塞赋值”<=”。
之所以会出现这两种赋值方式,是由时序逻辑和组合逻辑的特性所决定的。
由于在组合逻辑中,由于所有的赋值操作都在当前clock内完成,因此我们认为其赋值操作会立即生效。
阻塞赋值的含义正好与其吻合。
在时序逻辑中,由于所有的赋值操作都在下一个clock内生效,因此我们认为其赋值操作在当前时刻不能生效。
这与非阻塞赋值的含义相同。
有鉴于此,任何在组合逻辑中使用非阻塞赋值,或者在时序逻辑中使用阻塞赋值的写法都是有悖于硬件实现的。
在可综合verilog语言中,这种混用方式是被禁止的。
3)同步跨时钟域的信号
在verilog具体实现的最开始,应当首先处理需要通过双跳方式同步的信号。
将这些信号进行两级flip-flop延时。
注意内部的大小写关系和命名方法,具体的请参见表14。
表14同步跨时钟域信号
//synchronizedsignal
always@(posedgeclkornegedgengreset)begin
if(!
ngreset)begin
{TYPE_D,TYPE_R}<=2'h0;
{v_ctrl_d,v_ctrl_r}<=2'h0;
endelsebegin
{TYPE_D,TYPE_R}<={TYPE,TYPE_D};
{v_ctrl_d,v_ctrl_r}<={v_ctrl,v_ctrl_d};
end
end
补充:
对于可以用双跳方式同步的信号,一般而言是变化比较缓慢的单根信号。
或者几乎不变的一组信号。
对于递增或递减的信号,应当采取格雷码编码的方式。
4)有限状态