Verilog编码规范.docx

上传人:b****7 文档编号:10215447 上传时间:2023-02-09 格式:DOCX 页数:31 大小:47.85KB
下载 相关 举报
Verilog编码规范.docx_第1页
第1页 / 共31页
Verilog编码规范.docx_第2页
第2页 / 共31页
Verilog编码规范.docx_第3页
第3页 / 共31页
Verilog编码规范.docx_第4页
第4页 / 共31页
Verilog编码规范.docx_第5页
第5页 / 共31页
点击查看更多>>
下载资源
资源描述

Verilog编码规范.docx

《Verilog编码规范.docx》由会员分享,可在线阅读,更多相关《Verilog编码规范.docx(31页珍藏版)》请在冰豆网上搜索。

Verilog编码规范.docx

Verilog编码规范

 

Verilog编码规范

(仅供内部使用)

拟制:

xxx

日期:

xxx

审核:

审核者

日期:

yyyy-mm-dd

批准:

批准者

日期:

yyyy-mm-dd

版权所有XX

修订记录

修订日期

修订版本

描述

修订者

 

基本原则:

简单,一致,可重用。

●简单指尽量使用简单的语句,尽量使用简单的设计,尽量使用简单的时钟,尽量使用简单的复位。

●一致指尽量保持代码风格一致,尽量保持命名一致。

●可重用指有成熟的IP尽量使用IP,设计的代码要尽量可重用。

1

命名规范

给信号命名就像给孩子取名字一样,有区别,有根源,有深度,还有一点,要简单,别冗长。

有区别指取名字不要一样,假如大家只有一个手机号码,那这个号码还能有什么用处?

有根源指取名字要能象姓氏一样,让人一看就直到是张家的后代而不是李家的。

有深度就是取名字要有涵义,张一,张二,张三虽然也是名字,但是请考虑一下被取名字人的感受。

简单点,几十个字母长的名字,打字的和看字的都累。

♦大小写规则:

只有parameter,`define和module名称才能享受大写。

♦Module名应与文件名保持一致(文件名是小写),假如不想在设计后面遇到麻烦的话。

♦不要尝试使用任何保留字,因为他们已经被保留了。

♦不要重复使用同样的名字去命名不同的数据。

♦(建议)对module名加”_LVx”的后缀,增强module名称的结构层次含义

如:

设计顶层为TOPLEVEL,即LEVEL1,命名为QTRxxxx_LV1;

时钟模块,IO_PAD,CORE,为LEVEL2,命名为CLK_PROC_LV2等等;

CORE内子模块为LEVEL3,然后以此类推。

♦对于来自同一驱动源的所有时钟信号使用相同的名字。

♦对于低电平有效的信号,应该以_n结尾。

♦模块间相连端口名称要一致。

♦(建议)使用下表所列的命名缩写方式。

全称

名称

clock

Clk

reset

rst

clear

clr

address

addr

data_in

din

data_out

dout

interruptrequest

int

readenable

rden

writeenable

wren

count

cnt

request

req

control

ctrl

arbiter

arb

pointer

ptr

segment

seg

memory

mem

register

reg

∙(建议)使用下列后缀命名方式

全称

添加后缀

activelow

_n

enable

_en

select

_sel

flag

_flg

delay

_dly

∙信号命名的两个词之间用下划线间隔,如ram_addr,cnt_ctrl等等

∙信号命名尽量不要使用孤立的、小写的英文字母L

2

代码编写规范

2.1版面

♦语句独立成行,增加可读性和可维护性。

♦行的长度

保持每行小于或等于72个字符。

因为有的终端或打印机每行不能超过80个字符。

规定72个字符是为了留出边空,提高可读性。

还有一个原因是为象vi这样的编辑器留有显示行号的地方。

用回车来分割超过72个字符的行,并且在下一行用缩进来表示该行是前一行的继续。

♦缩进。

用缩进来提高续行和嵌套循环的可读性。

缩进采用4个空格。

避免使用TAB键。

不同的编辑器或用户环境使得TAB的位置差别很大,造成缩进的混乱。

有一些工具可以将TAB替换成空格。

♦(建议)使用注释

使用注释来解释端口、信号、信号组、always块、函数等。

注释应该放在它所描述的代码的附近。

注释应该简明扼要,并足够说明问题。

避免注释杂乱。

显而易见的功能不用加注释。

注释关键是说明设计意图。

2.2编写代码规范

♦在源文件中要有文件头

在源文件、script文件的开始应包含一个文件头。

文件头至少应包含下列信息:

文件名、作者、模块的功能描述和关键特征的列表、文件产生的日期、更改记录(日期、更改者、更改的内容)。

(参见代码模板sample.v)

♦模块名称用大写,例如:

moduleMEM_CTRL。

♦端口声明时每行声明一个端口,并有注释(最好在同一行),也可对同一类型的一组端口加注释。

对于时钟,复位以及其他控制信号,需要注释有效工作沿或者有效工作值

建议用下述顺序声明端口。

//INPUTs

clocks,//posedgeactive

resets,//activehigh

enables,//activehigh

othercontrolsignals,

Dataandaddresslines,

//OUTPUTs

clocks,

resets,

enables,

othercontrolsignals,

data

♦在输入和输出两类端口之间留一个空行来提高可读性。

如上例所示。

♦端口列表之后,使用parameter定义内部信号宽度以及其他参数化设置。

♦IO信号申明和内部信号申明要单独成行,参考sample.v文件

♦使用简单的语句,一般使用if…else…和case就能满足大部分需求,不要使用复杂的语句

♦常量

(1)一位的控制信号采用二进制表达方式,如1'b0;

(2)常数位宽不可缺省;

如:

Bad:

if((rst_n==0)&&(cnt_addr==15))

Good:

if((rst_n==1'b0)&&(cnt_addr==5'd15))

∙变量

(1)NetandRegister

(a)一位位宽的wire信号的声明不可缺省

(b)一个reg变量只可以在一个always语句中赋值

(c)(建议)任一register的赋值加上单位延迟,对异步复位同样加上单位延迟;

(d)向量有效位顺序的定义采用倒序格式,如:

Data[4:

0]

(2)Memory

代码中不建议使用Memory(存储器阵列),Memory只用于Testbench中,访问存储器阵列中某一向量的某一位或几位,需要通过中间变量进行。

例:

reg[15:

0]mem[0:

255];

temp=mem[33];//tempgetsdataataddr33

3_bit_reg=temp[8:

6];//getthreebitsofaddr33

∙运算符及表达式

(1)表达式

(a)用括号来表示执行的优先级,尽管操作符本身有优先顺序,但用括号表示优先级对读者更清晰,更有意义

如:

Bad:

A+B?

C:

D;

Good:

(A+B)?

C:

D;

(b)适当使用括号

适当使用括号可以控制生成的电路结构,如Z=A+B+C+D,综合结果可能为三级加法器,而变换为Z=(A+B)+(C+D),综合结果则可能为两级加法器;

(c)注意资源共享

需要资源共享的部分一定要放在同一个模块的同一个always语句中,不同模块不同always语句之间的代码不能实现资源共享。

如:

always@(...)

if(...)d0=A+B;

elsed0=C+D;

中,DC可能只会生成一个加法器。

条件算子中不存在资源共享

如:

z=(cond==1’b1)?

(a+b):

(c+d);

必须使用两个加法器;

而等效的条件if-else语句可以资源共享,如:

if(cond==1’b1)

z=(a+b);

else

z=(c+d);

只要加法器的输入端复用,就可以实现加法器的共享,使用一个加法器实现。

(d)尽量采用公共子表达式

如:

x=a+b+c

y=d+a+b

改为:

z=a+b

x=z+c

y=d+z

(2)算符

(a)条件运算符

r1=gate?

r2:

r3;

避免使用条件嵌套:

r1=(aa=0)?

((bb==0)?

r2:

r3):

r4;or

r1={aa,bb}==0?

r2:

{aa,bb}==0?

r3:

{aa,bb}==0?

R4:

r4;

(b)逻辑操作符

在if(),while(),()?

A:

B之类的表达式中,括号中的表达式应该是一个逻辑表达式,相应的操作符应该用逻辑操作符。

如:

wirex,A,B;

(x)?

A:

B与(x==1'b1)?

A:

B

If(A&B)与if((A&&B)==1’b1)

While(A=B)与while(A==B)

操作结果相同,但显然前者不规范。

(c)乘法运算符“*”

对于一个变量data与常数constant相乘data*constant,如果常数不是2的整数次幂,建议先将其分解,如constant=53=32+16+4+1=2^5+2^4+2^2+2^0,这样乘积就可以表示为变量data移位结果的相加。

对于乘法运算符“*”,综合后通常得到的是乘法器,时延较大。

∙赋值语句

(1)不要在信号列表中进行运算操作

如:

Bad:

addr(a,b,d&e);

Good:

addr(a,b,c);c=d&e;

(2)BLOCK赋值和NON-BLOCK赋值的使用

(a)组合逻辑采用BLOCK赋值(=)

如:

always@(dat)

i_dat=dat;

(b)非组合逻辑(主要是寄存器)采用NON-BLOCK赋值并加delay以保证前仿真和后仿真的一致

如:

always@(posedegclk)

q<=#`DELd;

(3)在同一块语句中不允许同时出现阻塞赋值和非阻塞赋值

∙条件语句

(1)IF语句

(a)向量比较时,比较的向量长度要相等,同样向量和常量比较时长度也要求匹配,长度不同时要求进行显式位扩展(verilog对位数小的向量做0扩展以使它们的长度相匹配,该扩展是隐式的)

如:

reg[7:

0]abc;

reg[3:

0]def;

.......

if(abc=={4'b0,def})begin

.......

if(abc==8'h0)begin

(b)不要采用if表达式的简写形式

例如:

if(variable)等同于if(variable!

=0)

if(!

variable)等同于if(variable==0)

但后者才合乎规范

(c)每个if都应该有一个else与之相对应,如果条件为假时不进行任何操作,则用一条空语句else;避免产生latch

(d)if...elseif...elseif...else的代码书写格式如下,要注意优先级

if(...)

begin

......

end

else

begin

if(...)

......

else(...)

if(...)

else(...)

end

(d)如果变量在if-else语句中非完全赋值,则应给变量一个缺省值

如:

if(a==b)

begin

v1=2'b01;

v2=2'b10;//v3isnotassigned

end

elseif(a==c)

begin

v2=2'b10;

v3=2'b11;//v1isnotassigned

end

else//default赋值

begin

v1=2'b00;

v2=2'b00;

v3=2'b00;

end

(2)CASE语句

(a)所有的case语句都应该有一个default语句,避免产生Latch

(b)(建议)不要使用casex、casez语句,综合工具不支持

∙循环语句

(1)forever语句

(2)repeat语句

(3)while语句

(4)for语句

在可以用其它语句描述电路时,建议不要采用循环语句来描述。

∙initial语句

不要在RTL代码中出现initial块

综合会将initial块忽略,使前仿真和后仿真不一致

initial

begin

......

end

∙always语句

(1)在使用always生成组合逻辑时,敏感表要列全,敏感表中也不能包含没有用到的变量。

Rule:

Combinationalsensitivitylistsshouldinclude

1)Anysignalonrighthandsideofassignment

2)Anysignaliniforcaseexpression

Forexample:

......

modulesense_list_ex(

b,

c,

d

);

//PARAMETER

//INPUTS

inputb;

inputc;

inputd;

//OUTPUTS

//INOUTS

//SIGNALDECLARATIONS

wireb;

wirec;

wired;

rega;

//ASSIGNSTATEMENTS

//MAINCODE

always@(borcord)

if(b==1’b1)

a=c&d;

else

if(c==1’b1)a=d;

endmodule//SENSE_LIST_EX

(2)对带异步清零端的寄存器的定义模板

always@(posedgeclk_mainornegedgerst_n)

if(rst_n==1'b0)//此处统一采用rst_n==1'b0形式而不采用(!

rst_n)

begin//形式,对相关寄存器清0(采用#`u_dly<=赋值)

......

end

else

begin

//对相关寄存器赋值(采用#`u_dly<=赋值)

......

end

采用时钟上升沿触发。

∙有限状态机(FSM)

(1)组合逻辑和时序逻辑分开描述;

//PART1:

COMBINATERIALLOGICFORNEXTSTATE

always@(cur_stateorfull_new_frorfullorhave_space)

begin:

OVC_FSM_NXT_ST

case(cur_state)

STDBY:

begin

if(full_new_fr==1'b1)

nxt_state=W_BLOCK;

elseif(full==1'b1)

nxt_state=W_DSCD;

else

nxt_state=cur_state;

end

W_BLOCK:

begin

if(have_space==1'b1)

nxt_state=STDBY;

else

nxt_state=cur_state;

end

W_DSCD:

begin

if(have_space==1'b1)

nxt_state=STDBY;

else

nxt_state=W_BLOCK;

end

default:

nxt_state=STDBY;

endcase

end//OVC_FSM_NXT_ST

//PART2:

SEQUENTIALLOGICFORCURRENTSTATE

always@(posedgeclkor`RST_EDGEreset)

begin:

OVC_FSM_ST_TRANS

if(reset==`RST_VALUE)

cur_state<=`DLYW_BLOCK;

else

cur_state<=`DLYnxt_state;

end//OVC_FSM_ST_TRANS

3电路设计规则

3.1时钟

♦(建议)简单的时钟结构易于理解、分析和维护,而且容易产生好的综合结果。

最好是能够有单一的全局时钟,所有寄存器都在上升沿触发。

♦所有子模块内部使用单一时钟单一时钟沿,如条件不满足时,必须注明原因,并提出对综合以及布线的要求。

♦设计中包含内部产生的时钟时,必须将所有需要的时钟在一个单独的模块中生成。

♦如果不得不用混合的时钟沿,在综合和时序分析时确保能满足时钟精度最差情况下的占空比。

同时确保把假定的占空比写入用户手册。

在多数设计中,占空比是时钟树的函数,而时钟树的插入通常又依赖于具体的工艺。

使用Core的芯片设计者必须检查实际的占空比能够满足Core的要求,也应该了解怎样改变综合和时序分析的策略以使得Core能够满足实际的条件。

♦(建议)多数基于扫描链的测试方法要求对上升沿和下降沿触发的寄存器分开处理。

如果必须使用大量的上升沿和下降沿触发的寄存器,将上升沿和下降沿触发的寄存器分到不同的模块中是很有用的。

这样容易确定下降沿触发的寄存器,并可将它们放到不同的扫描链中。

♦(建议)避免在RTL级手工实例化时钟Buffer。

时钟Buffer通常是在综合以后在物理设计时插入的。

在可综合的RTL代码中,时钟网络通常被认为是理想的网络,没有延时。

在布局布线时,时钟树插入工具插入适当的结构,尽可能的接近理想的、平衡的时钟配布网络。

一个例外情况是在顶层模块中可以插入厂家提供的伪时钟Buffer,用于指明时钟树的源头和时钟树的参数。

♦(建议)避免在RTL级使用门控时钟或内部产生的时钟信号。

门控时钟电路依赖于具体的工艺和时序。

门控时钟不正确的时序可能导致假的时钟信号和误操作。

不同局部时钟SKEW还会导致保持时间冲突(violation)。

门控时钟还会降低电路的可测性,也使得综合的约束变得困难。

多数低功耗的电路需要门控时钟,但它们不应该出现在RTL级的编码中,象PowerCompiler这类工具可以自动去做。

如果设计中必须使用门控时钟、内部产生的时钟或复位信号,应该让产生这些信号的电路位于设计顶层的一个独立的模块中。

它将违反编码规范的地方限制在一个小的范围内,有利于对这些产生电路开发特殊的测试策略。

对于其他模块将可采用标准的时序分析和扫描链插入技术。

3.2复位

♦(建议)确保所有寄存器只被简单的复位信号所控制。

最好的情况是,复位信号是1bit寄存器的输出。

因为组合逻辑的输出会带有毛刺,对于异步复位电路,则会引起触发器的异常。

♦(建议)尽可能避免内部产生的条件复位信号。

通常模块内所有寄存器应在同一时间内被复位。

这种方式使得分析和设计更加简单和容易。

♦(建议)如果需要条件复位,设置一个单独的复位信号,并且将产生逻辑隔离于一个单独的模块。

这种方式可使代码更易读,并易于综合出好的结果。

♦如果需要内部产生异步复位信号,必须保证所产生的异步复位信号没有毛刺,最好的办法是保证异步复位信号最后为1bit触发器的输出,

例如当计数器达到一个预设值时,产生异步复位信号:

bad:

wirereset;assignreset=(count==value);

better:

regreset;

always@(posedgeclk)

reset<=(count==value);

3.3避免LATCH

描述组合逻辑的always块中,如果if语句缺乏else子句、case语句中各个条件所处理的变量不同都会在综合时推断出LATCH。

使用下述方法可避免LATCH:

♦对所有的输入条件都给出输出。

♦保证always块敏感列表完备。

敏感列表应包括:

if(…),case(…)中的条件信号;所有always块中位于赋值语句右边的信号;当信号为多bit向量时,应包括向量的所有bit而不是部分。

♦在最终优先级的分支上使用else子句,而不用elsif。

♦所有的Case应该有一个defaultcase。

♦避免使用LATCH,除非能清楚地分析相关电路的时序以及毛刺带来的影响

3.4避免组合反馈

♦在设计中避免组合反馈电路。

这种电路违背了同步设计原则,难以控制其行为,对仿真、调试和DFT都极其不利。

3.5赋值语句

♦在写可综合的代码时,在时序逻辑的always语句块中总是使用非阻塞赋值。

否则RTL级的仿真会和门级仿真的结果不一致。

♦在组合逻辑的always语句块中使用阻塞赋值。

♦同一个触发器不能在多个always块中被赋值。

3.6case语句和if-else语句

♦(建议)如果不需要有优先级的编码结构,建议使用case语句而不要使用if-else语句。

对于基于cycle的仿真器,case语句的仿真速度要比if语句的仿真速度快。

对于大的多选器,case语句也比条件赋值语句的仿真速度快。

对于综合工具,case语句也往往能产生出时序和面积更优化的电路。

▪(建议)对于条件分支为独热编码的case语句,建议采用下列语句,对于综合工具能产生较优化的电路

case(1’b1)

condition1:

statement;

condition2:

statement;

default:

statement;

endcase

3.7状态机

♦将状态机的描述分成两个always块,一个用来描述组合逻辑,一个用来描述时序逻辑。

♦(建议)用参数语句来定义状态向量。

♦(建议)将状态机的逻辑和非状态机的逻辑分成不同的模块,以便于综合工具对状态机进行单独优化。

♦必须使用default条件为状态机指定一个默认的状态,防止状态机进入死锁状态。

♦FSM提供防死锁机制,以防止限死在某个状态,特别是在异常情况下。

♦在FSM逻辑比较复杂的时候,建议使用独热编码方式,以提高时序。

//PART1:

COMBINATERIALLOGICFORNEXTSTATE

always@(cur_stateorfull_new_frorfullorhave_space)

begin:

OVC_FSM_NXT_ST

case(cur_state)

STDBY:

begin

if(full_new_fr==1'b1)

nxt_state=W_BLOCK;

elseif(full==1'b1)

nxt_state=W_DSCD;

else

nxt_state=cur_state;

end

W_BLOCK:

begin

if(have_space==1'b1)

nxt_state=STDBY;

else

nxt_state=cur_

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

当前位置:首页 > 小学教育 > 其它课程

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1