verilog语言代码设计规范.docx

上传人:b****2 文档编号:11613448 上传时间:2023-06-01 格式:DOCX 页数:36 大小:49.01KB
下载 相关 举报
verilog语言代码设计规范.docx_第1页
第1页 / 共36页
verilog语言代码设计规范.docx_第2页
第2页 / 共36页
verilog语言代码设计规范.docx_第3页
第3页 / 共36页
verilog语言代码设计规范.docx_第4页
第4页 / 共36页
verilog语言代码设计规范.docx_第5页
第5页 / 共36页
verilog语言代码设计规范.docx_第6页
第6页 / 共36页
verilog语言代码设计规范.docx_第7页
第7页 / 共36页
verilog语言代码设计规范.docx_第8页
第8页 / 共36页
verilog语言代码设计规范.docx_第9页
第9页 / 共36页
verilog语言代码设计规范.docx_第10页
第10页 / 共36页
verilog语言代码设计规范.docx_第11页
第11页 / 共36页
verilog语言代码设计规范.docx_第12页
第12页 / 共36页
verilog语言代码设计规范.docx_第13页
第13页 / 共36页
verilog语言代码设计规范.docx_第14页
第14页 / 共36页
verilog语言代码设计规范.docx_第15页
第15页 / 共36页
verilog语言代码设计规范.docx_第16页
第16页 / 共36页
verilog语言代码设计规范.docx_第17页
第17页 / 共36页
verilog语言代码设计规范.docx_第18页
第18页 / 共36页
verilog语言代码设计规范.docx_第19页
第19页 / 共36页
verilog语言代码设计规范.docx_第20页
第20页 / 共36页
亲,该文档总共36页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

verilog语言代码设计规范.docx

《verilog语言代码设计规范.docx》由会员分享,可在线阅读,更多相关《verilog语言代码设计规范.docx(36页珍藏版)》请在冰点文库上搜索。

verilog语言代码设计规范.docx

verilog语言代码设计规范

verilog语言代码设计规范

 

2011年12月

 

一、规范适用范围4

1.1项目适用范围4

1.2人员适用范围4

1.3编码设计的成果形式4

二、代码书写规范5

2.1模块说明书写规范5

2.1模块注释书写规范5

2.3变量名称书写规范6

2.4代码结构书写规范7

三、使用verilog语言的语法范围8

3.1设计RTL代码的语法范围8

3.2设计仿真代码的语法范围10

四、使用verilog语言的结构范围11

4.1系统设计文件的形式与使用方法11

4.2模块结构划分的标准12

4.3组合逻辑的代码风格13

4.4时序逻辑的代码风格21

4.5仿真代码的代码风格27

五、使用受限范围内的语法或结构要进行的申请过程32

5.1受限的语法与结构32

5.2批准使用的程序32

二、代码书写规范

2.1模块说明书写规范

在开始子模块设计时,必须对子模块的基本信息给予说明。

说明的位置一般在设计的开头,使用注释的形式用(/**/)说明该设计的作者、编写日期、版本号、在系统中的层次位置、基本功能描述等。

其形式如下所示:

/********************************************************

*author:

abc*

*date:

2005-5-12*

*version:

1.0*

*hiberarchy:

mcu-alu-adder*

*describe:

16bitadder*

********************************************************/

 

说明的内容要简洁清晰。

使用/**/对将说明部分括起来是为了与普通注释相区别。

2.1模块注释书写规范

注释对项目团队关于设计的交流至关重要,好的设计总是会在恰当的地方对语句或变量予以说明,没有注释的设计不是真正的工业级设计,通篇的注释同没有注释一样糟糕,会将代码淹没在无用的注释之中。

这一节给出书写注释的规范。

如果设计中出现了一个新的变量,那么必须对这个新变量给予注释,对变量的注释应该放在变量的定义之前,注释应该说明变量的物理意义或作用。

其形式如下:

………

//inputport

//clearresetsingalofsystem

//Accontrolsingalofaccumulation

inputclear,Ac;

………

 

 

如果设计中的某块结构属于作者的创新或设计中很关键的部分,作者应该对这种结构的物理含义予以简要说明。

注释在语句或结构的前一行开始写如:

………

//wallacetreemultiplication

………

 

2.3变量名称书写规范

verilog语言规定了各种类型的标志符的格式,作为规范我们对用户自定义的各种变量的命名方法及书写格式加以约束。

变量一般指模块(或设计)名、端口名、连线名、参照名、单元名以及内部寄存器名。

首先变量名必须能表达实际的物理意义,如果需要几个单词来表示,那么单词之间用一个“_”分隔。

变量名不宜过长,一般不要超过16个字符否则书写的效率会下降,因此变量名应该尽量使用单词的缩略写法,完整的含义应在注释中给予说明。

我们规定常量参数一律使用大写字母表示,变量的名称一律用小写字母a~z、数字0~9或下划线_表示,变量首字符一律用字母。

模块名(或设计名)应该与文件名一致,一个文件只应包含一个模块。

它是模块功能的缩略表达。

端口名应与该端口实际的物理意义相一致。

连线是对内部单元(实例)引脚间进行连接的物理线或是对组合逻辑输出端口、组合逻辑单元输出端进行赋值运算的输入线。

连线名应该有确定的连接对象或是有确定的信号物理意义,所以针对单元引脚连接的连线它的名称应该表明所连接单元的名称,如timing_alu表示时序发生器发出的控制信号连接ALU部件的控制端。

针对为输出端做赋值运算的输入连线它的名字应该表达相应的物理意义,绝不要使用通用名如:

a,b,c这样的名字。

这类连线适当的名称如:

add_a,add_b。

参照名就是一个单元或实例参考的库标准单元或原始设计名,所以它的名字与库单元或模块名相同。

单元与实例在synopsys的DC工具中是不加区分的,这里也等同看待,它的名字可以用参照名为头后加数字予以标识。

如alu1、acc1等。

如果设计的内部有中间级寄存器,那么寄存器以实际的物理意义进行命名,比如在乘法器中为分割关键路径引入的中间级寄存器可以命名为pipeline1_out等。

2.4代码结构书写规范

好的代码结构可以清晰的看出设计的层次关系,进而使结构与设计者所要表达的逻辑意图一致,方便纠错和交流。

代码最基本的结构有平行结构和层次结构,他们反映了代码的隶属关系,我们规定注释语句与语句块是一个层次的,这意味着对模块的注释行(以//开头)必须在一行的顶头开始写。

其他语句的层次低于模块定义和注释,那么其他语句至少要向后缩退四个空格。

语句块中的语句低于语句块的定义,例如:

always@(posedgeclk)

begin

out<=in;

end

//endalways

语句块中的语句块和其他语句是同级的。

语句块结束应该有注释说明结束的语句块的名称

例如:

always@(posedgeclk1negedgeclear)

begin

if(!

clear)

begin

out<=6'b000000;

end

else

begin

case(addr)

`SFR_A:

begin

out<=in;

end

……

endcase

end

end

三、使用verilog语言的语法范围

3.1设计RTL代码的语法范围

verilog语言是一种通用的HDL(硬件描述语言),它的语法范围包括了用户各种设计层级的需求,虽然语言本身对这种层级并没有细致的区分,但因为设计者编写的代码最终要使用具体的综合工具综合成实际电路,所以语言层级的划分不可避免的具有针对某种工具的特点,我们这里的划分依据是根据DesignCompile综合工具来定的。

在论述的过程中我们常使用左值和右值的称谓,一般我们将赋值运算符左侧的变量称作左值,右侧的变量称作右值,因此左值具有位置属性而右值具有数值属性。

右值的任何地方禁止出现“x”,左值在任何情况下不可以为常量。

在有些环境中也将左值称做写数据,右值称做读数据。

在一个语句块中不可对同一个左值赋两次值,因为这样会引起数据冲突(三态门除外)。

verilog语言可以描述四个设计层级的语法范畴,依次是:

系统行为级、模块寄存器传输级(RTL)、电路门级、晶体管开关级。

层级之间的转换我们称之为综合。

目前业界的流行的做法是前端设计将RTL级综合为门级网表。

行为级到门级的直接综合还不成熟,另外,数字电路中的晶体管我们一般当作开关来对待,因此对它们具体的器件特性描述,并不是针对数字电路设计的verilog语言所能及的。

系统行为级描述一般作为系统设计的辅助手段或建立系统仿真环境的语言实现手段,在系统设计时,设计人员一般使用行为级描述来建立系统数据流的直观表述,以助选定设计方案,划分系统层次、确定模块接口等。

这个级别是设计的最高抽象层,它不考虑设计的具体实现,只是确定实现的可行性,和估计实现的规模,因此系统设计时系统行为级描述不限制语言的种类,可以用verilog也可用SystemC甚至C语言,它们只是帮助系统设计人员最终得到系统各个模块的端口列表和层级子模块明确的功能定义。

有些项目使用层级关系的原理图来说明系统结构,子模块使用黑盒来代替。

一些小项目这个过程可以省略直接由编码人员来实现。

行为级描述也可以建立系统的仿真环境,它也是实际工作中行为级最重要的作用。

这时一般将被仿真的模块作为实例,仿真环境为实例提供各种输入向量,设计人员观察输出结果分析被测实例的功能正确与否,如有错误可以定位错误以便修正。

HDL的寄存器传输级(RTL)一般用于对电路子模块的描述,它一般由四种基本结构组成:

寄存器、计数器、选择器、算术逻辑运算单元。

其中寄存器对应always语句块,选择器对应if…else…语句块,算术运算符加(+)、减(-)可以作为RTL代码直接综合,乘(*)、除(/)如果没有特殊的性能要求也可以受限使用,逻辑运算符与、或、非是RTL代码。

计数器一般作为各种状态标志的产生器。

我们规定RTL的抽象级别是逻辑抽象,所以它应该反应的是各种器件的逻辑行为和逻辑结构,而不是器件的物理组成(门级连接),另外它对应的是具体的物理实体,而不是理想的逻辑关系(没有实物对应的逻辑抽象和理想的信号时空顺序)。

在逻辑抽象级我们一般将组合电路看作连线之间的算术逻辑赋值运算,将时序电路看作由寄存器存储组合逻辑产生结果的结构。

所以RTL级的四种基本结构的组合变化足以构建各种器件千变万化的逻辑结构。

我们规定对wire型变量的赋值使用连续赋值语句(assign),并且一律使用阻塞型赋值(=),组合逻辑中always块中的reg型变量一律使用阻塞型赋值,在时序逻辑中使用非阻塞型赋值(<=)。

RTL代码中要求不使用循环语句,所有涉及循环赋值的部分一律将循环打散采用显式赋值。

代码中不要出现函数、任务的相关语句块,所有需要出现的地方使用module编写模块,再调用实例。

没有实物对应的initial语句块以及具有理想信号时空顺序的语句,如显式指定延迟语句不能在RTL代码中出现。

如果有实例调用那么实例的引脚连接采用端口对应禁止使用位置对应。

因为综合工具没有与“x”对应的状态,所以设计中任何地方的值禁止出现“x”,如果对总线的某几位不加考虑,那么位选择时只出现要考虑的部分。

对于重要的有含义的常量不要直接在设计中使用具体值,而应该用参数定义使常量参数化。

为了便于查找我们将综合工具不能综合的关键字列举如下:

●initial

●循环语句:

Ørepeat

Øforever

Øwhile

Øfor的非结构用法

●数据类型

Øevent

Øreal

Øtime

ØUDPs

●fork…join块

●wait

●过程连续赋值语句

Øassign和deassign

Øforce和release

●部分操作符

Ø===

Ø!

==

以上的关键字,综合工具不支持,它们也不是我们设计RTL代码所必须的,我们规定设计的RTL代码一定不能含有上面列出的条目。

3.2设计仿真代码的语法范围

在前面已经简单谈及了仿真代码的编写问题,阐明了仿真代码实际上是HDL行为级描述的一种应用。

它是一种不考虑实际硬件的实现只为产生仿真向量的理想的逻辑抽象,它所产生的信号可以具有理想的时空顺序。

它的逻辑范畴不受具体物理器件实现的限制。

我们规定仿真代码中有关信号时间顺序的安排应放在initial语句块中,被测模块的实例引脚连接使用端口对应风格。

仿真代码可以使用循环结构、可以使用函数、任务、用户定义原语。

仿真代码产生的仿真向量要具有典型型,向量的覆盖面要大,对于逻辑边界一定要有对应的仿真向量,对于可能的数据路径也要找出相应的输入向量遍历它。

仿真代码包括两个部分,分别是,虚拟器件建模或仿真环境建立。

虚拟器件一般指ROM、RAM存储器件或没有用RTL代码实现的行为级抽象器件的建模。

仿真环境一般包括各器件的实例调用、时钟的模型、和各信号的时空安排。

为了对信号进行特定的时空安排我们可以在verilog语法范围内使用对信号的数值和出场顺序进行任意逻辑的处理,并且我们可以对信号的状态进行记录、观测、调整,仿真工具一般为我们内置了不少系统调用,常用的调用我们列举如下:

1)$time//找到当前的仿真时间

2)$display,$monitor//显示和监视信号值的变化

3)$stop//暂停仿真

4)$finish//结束仿真

5)$readmemb//从外部文件向内部寄存器读数据

6)$dumpfile//打开记录数据变化的数据文件

7)$dumpvars;//选择需要记录的变量

8)$dumpflush;//把记录在数据文件中的资料转送到硬盘保存

系统调用的具体用法,和其他系统调用,请参见具体工具的使用手册。

关于时钟的建模部分请参见4.5节,我们一般将信号的时空顺序和信号的初始化操作放在initial语句块中表达。

四、使用verilog语言的结构范围

4.1系统设计文件的形式与使用方法

系统设计文件是编码设计人员的设计依据,一般由系统设计人员确定。

它的内容至少应该包括子模块的功能定义,模块的端口定义、模块的时序要求、和项目的进度安排,无论项目开发采用增量式开发模式还是采用流水线开发模式,对于编码人员来说都是不需要关心的,因为增量式最小原形法与普通的流水线模式最大的不同是每次增量需要设计人员考虑的设计规模是不同的,一般增量式开发过程每次增量的设计目标都是在可以把握的范围之内的。

需要设计的子模块功能在一次增量的过程中是不变的,当然不同的增量级别和迭代级别要考虑的功能和性能要求又是不同的。

系统设计文件中对子模块的描述可以用层级式的原理图方式也可以用verilog语言配合文字给予说明的文本文件,编码设计人员拿到系统设计文件后,应该可以明确自己工作的职责,如明确需要设计的是什么功能模块、明确模块的输入输出端口是什么,模块与外部模块的耦合关系、是否本模块与其他模块有编写的顺序问题、模块需不需要再有层级关系以及模块必须交付的时间等。

编码人员在充分理解了以上这些问题之后再开始具体的编码。

4.2模块结构划分的标准

模块结构的划分不仅在系统级而且在编码级都必须仔细考虑,一般编码级的划分是对子模块的再次细分。

因为模块的结构直接影响了综合后电路的质量,所以划分结构必然要考虑综合工具对各种结构的综合效率。

verilog语言可以用以下结构创建层级。

一般module语句定义了新层,参照的实例在模块中创建了一个次层级,实例间具有层级的边界。

算术运算符(+,-,…)暗指的电路能创建一个次层级。

always语句不创建层级关系。

划分必须按一定的准则去做,我们将这些准则列举如下:

●尽量不要将组合逻辑划分成一个独立的模块。

因为DC必须保留端口的定义。

逻辑优化不能穿越设计的边界。

因此综合优化的效率可能会很低,电路的质量会很差。

除非电路是纯组合逻辑的特殊情况,否则我们一般以寄存器为输出边界。

●消除不必要的层级,避免粘连逻辑。

我们将实例与实例通过门单元进行连接的逻辑叫做粘连逻辑。

因为DC必须保留端口的定义,所以DC综合时不能将粘连逻辑吸收到任何模块内部进行优化,好的划分应该尽量消除不必要的层级,使优化充分发挥作用。

●尽量平衡各逻辑块的尺寸和模块内部的数据传输路径长度。

划分的太细不利于优化,但也不是划分的块越大越好,如果逻辑块太大,综合运行的时间可能非常长,模块内部的关键路径如果出现时序异常将很难修正,模块的维护和团队的交流将出现巨大障碍。

●设计至少应该有三个层级。

三个层级依次为:

顶层、中间级、内核级。

一般顶层只包含实例的调用,中间级是对一些可复用子模块的调用和相关处理,内核级一般是最底层,他包括设计的核心处理部件、项目可复用的模块等。

●如果设计中有异步电路一般应该将异步电路单独设计,并且一个时钟对应一个模块,模块与模块之间的握手连接在异步电路的顶层处理。

按照以上准则得到的划分结构可能不止一个,设计人员必须在不同的划分中进行选择得到最佳的方案。

4.3组合逻辑的代码风格

这一节和下一节将对具体器件用verilog语言实现的代码风格进行规范。

所谓代码风格就是具体硬件使用verilog语言实现的算法。

这里需要强调的是设计人员必须用硬件的思维来编写代码。

组合逻辑是可以认为是算术逻辑的赋值运算。

一般情况下组合逻辑在连续赋值语句中实现,因此它的赋值对象不能是reg类型的变量,另外不能使用非阻塞型的赋值符号。

由于与非、或非的物理实现要比与、或简单,因此考虑到速度与最终芯片的面积我们优先选择与非、或非的逻辑。

如果必须用语句块实现组合逻辑电路那么,组合逻辑也可以放到always的语句块中,在always语句块中的左值必须是reg类型,组合逻辑的always语句块一般使用阻塞型赋值,如果一定要在电路中体现数据传输路径,那么在使用阻塞型赋值时必须考虑中间变量(既充当左值又充当右值的变量)的传输路径问题。

在非阻塞赋值中,因为读变量的时间发生在变量值改变之前,所以每出现一个中间变量则传输路径会多一级,中间变量会被保留,因此中间变量也要在always事件列表中指明。

但是使用阻塞型赋值时,读变量发生在变量值改变之后,所以中间变量常常是被前级变量替代了或与出现中间变量的位置直接与前级变量相连传输路径不再保留中间变量,为防止电路出现仿真结果在综合前后不一致的现象,在always语句块中使用阻塞型赋值时右值部分的变量和条件控制信号在always事件列表中要显式指明,但是中间变量不必在列表中出现。

在任何情况下都不要在一个语句块中对同一个左值多次赋值。

因为这样会因为竞争冒险而产生数据冲突。

为了将代码的编写规范化我们将选择器、寄存器、计数器的结构分别给予规范。

选择器:

在always语句块中如果出现选择结构,那么它对应的是一个多路选择器,当我们使用if…else语句实现这种结构时,一般情况下我们推荐if和else要匹配。

以下三种结构她们在逻辑上是等价的我们要求使用第一种。

always@(aorborsel)

begin

if(sel)

begin

out=a;

end

else

begin

out=b;

end

end

always@(aorborsel)

begin

out=b;

if(sel)

begin

out=a;

end

end

always@(aorborsel)

begin

out=(sel)?

a:

b;

end

if语句本身会引入电路的优先级结构,不同的编写风格会出现不同的优先级结果,我们分别给予解释。

always@(sel_aorsel_borsel_cor

aorborcord)

begin

out=d;

if(sel_a)

begin

out=a;

end

if(sel_b)

begin

out=b;

end

if(sel_c)

begin

out=c;

end

end

这种结构因为逻辑执行的先后问题,结果的产生发生在所有if语句块判断完成后,因此优先级是从下往上的。

既最后完成判断的语句块它的路径离输出端是最近的优先级也最高。

always@(sel_aorsel_borsel_coraorborcord)

begin

if(sel_a)

begin

out=a;

end

elseif(sel_b)

begin

out=b;

end

elseif(sel_c)

begin

out=c;

end

else

begin

out=d;

end

end

这种结构只要有一个条件发生匹配if语句立即完成,跳至整个if结构的出口,因此它的优先级是从上往下的。

我们要求使用第二种优先结构的写法。

当遇到不需要优先级的设计时我们一般选择case结构,这是一种在语法逻辑上没有优先级的结构,在case选择分支中的所有结构块他们在地位上是平行的。

其结构如下所示。

always@(seloraorborcord)

begin

case(sel)

2'b00:

out=a;

2'b01:

out=b;

2'b10:

out=c;

2'b11:

out=d;

endcase

end

使用case语句应该注意以下几个问题。

首先,case语句的状态表达式不同于if语句的布尔表达式,case语句状态表达式中的包含的各种状态在它的状态项中都应该有明确的对应,如果状态项中的状态少于状态表达式包含的状态,那么综合后的电路可能会引入锁存,另一方面如果状态表达式中包含的状态少于状态项中的状态那么意味着状态表达式的一个值会开启多个状态项中的语句块这种情况叫状态项重叠。

发生状态项重叠的case语句不再具有无优先级的特性,这使多个开启的状态项语句块依据编写的顺序具有从上而下的优先级,具有和if

…else…语句一样的效用。

其结构如下所示:

always@(aorborcord)

begin

case(1'b1)

sel_a:

out=a;

sel_b:

out=b;

sel_c:

out=c;

default:

out=d;

endcase

end

它和if…else…结构是等价的。

if(sel_a)

begin

out=a;

end

elseif(sel_b)

begin

out=b;

end

elseif(sel_c)

begin

out=c;

end

else

begin

out=d;

end

因此为了使case语句无优先级结构,并且不出现锁存,我们必须使状态项中的状态与状态表达式所能包含的状态完全一致。

其中最后一个状态或几个状态可以不显式出现而在default分支中表达。

具有这种结构的case语句我们称做全case语句,我们要求在描述没有优先级结构的多路选择器时使用全case语句。

一般情况下我们不使用casex和casez这样的语句,禁止出现“?

”、“x”

这种不确定状态的表达,因为综合工具没有与不确定状态相对应的值。

因此我们要求语句中的状态一定要明确只能为“0”“1”“z”(“z”只在三态门中出现)。

循环结构虽然具有简化编写工作量的优势,但是当使用阻塞赋值或非阻塞赋值时会增加其逻辑传输级的复杂度,并且综合工具在综合以前会将循环部分打散再执行综合过程这样综合效率会降低,因此我们要求RTL文件不允许使用循环结构,其循环部分一律手工打散逐项输入。

但是对行为级的描述我们并不加这种限制。

需要说明的是,对于仿真代码中的循环体一定要注意循环的边界必须是可以算出的具体值,循环不能使用边沿触发的事件列表。

函数、任务结构也是我们在RTL代码中禁止使用的,因为综合时它只是将相关的结构放在调用的地方,把函数的输出作为右值进行赋值。

这并不引入物理的层级边界,与逻辑上的函数是有层级边界的相矛盾,因此为了逻辑与物理的一致性我们取消对他们的使用。

当设计中需要双向总线,选择输出或异步设计的时候设计人员往往需要借助三态门来实现,三态门具有“0”、“1”、“z”三种状态,因为三态门中“z”状态与其他状态逻辑运算后结果不变,因此,三态门是电路描述中唯一可以在一个语法层次为同一个左值多次赋值的结构,综合工具不会检查这种情况下的数据冲突问题。

为了不引起综合前后仿真结果的不同我们规定,三态门一律使用如下的连续赋值语句:

assignout=(sel_a)?

a:

1'bz;

并且遇到为同一个左值多次赋值的描述时使用如下结构:

assignout=(sel_a)?

a:

1'bz;

assignout=(!

sel_b)?

b:

1'bz;

使用这种结构设计人员一定要小心。

设计中双向端口往往是不能避免的,双向端口具有既能输入又能输出的特点,要让双向端口不会发生数据冲突是设计人员的责任。

常用的双向端口有两种基本的结构,一种是输入与输出是互斥的,另一种是输入常通,输出选通。

这两种

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 人文社科 > 法律资料

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2