VerilogHDL verilog hdl语言

先记下来:
1、不使用初始化语句;
2、不使用延时语句;
3、不使用循环次数不确定的语句,如:forever,while等;
4、尽量采用同步方式设计电路;
5、尽量采用行为语句完成设计;
6、always过程块描述组合逻辑,应在敏感信号表中列出所有的输入信号;
7、所有的内部寄存器都应该可以被复位;
8、用户自定义原件(UDP元件)是不能被综合的。
一:基本
Verilog中的变量有线网类型和寄存器类型。线网型变量综合成wire,而寄存器可能综合成WIRE,锁存器和触发器,还有可能被优化掉。
二:verilog语句结构到门级的映射
1、连续性赋值:assign
连续性赋值语句逻辑结构上就是将等式右边的驱动左边的结点。因此连续性赋值的目标结点总是综合成由组合逻辑驱动的结点。Assign语句中的延时综合时都将忽视。
2、过程性赋值:
过程性赋值只出现在always语句中。
阻塞赋值和非阻塞赋值就该赋值本身是没有区别的,只是对后面的语句有不同的影响。
建议设计组合逻辑电路时用阻塞赋值,设计时序电路时用非阻塞赋值。
过程性赋值的赋值对象有可能综合成wire,latch,和flip-flop,取决于具体状况。如,时钟控制下的非阻塞赋值综合成flip-flop。
过程性赋值语句中的任何延时在综合时都将忽略。
建议同一个变量单一地使用阻塞或者非阻塞赋值。
3、逻辑操作符:
逻辑操作符对应于硬件中已有的逻辑门,一些操作符不能被综合:===、!==。
4、算术操作符:
Verilog中将reg视为无符号数,而integer视为有符号数。因此,进行有符号操作时使用integer,使用无符号操作时使用reg。
5、进位:
通常会将进行运算操作的结果比原操作数扩展一位,用来存放进位或者借位。如:
Wire[3:0]A,B;
Wire[4:0]C;
AssignC=A+B;
C的最高位用来存放进位。
6、关系运算符:
关系运算符:<,>,<=,>=
和算术操作符一样,可以进行有符号和无符号运算,取决于数据类型是reg,net还是integer。
7、相等运算符:==,!=
注意:===和!==是不可综合的。
可以进行有符号或无符号操作,取决于数据类型
8、移位运算符:
VerilogHDL verilog hdl语言
左移,右移,右边操作数可以是常数或者是变量,二者综合出来的结果不同。
9、部分选择:
部分选择索引必须是常量。
10、BIT选择:
BIT选择中的索引可以用变量,这样将综合成多路(复用)器。
11、敏感表:Always过程中,所有被读取的数据,即等号右边的变量都要应放在敏感表中,不然,综合时不能正确地映射到所用的门。
12、IF:
如果变量没有在IF语句的每个分支中进行赋值,将会产生latch。如果IF语句中产生了latch,则IF的条件中最好不要用到算术操作。Case语句类似。Case的条款可以是变量。
如果一个变量在同一个IF条件分支中先赎值然后读取,则不会产生latch。如果先读取,后赎值,则会产生latch。
13、循环:
只有for-loop语句是可以综合的。
14、设计时序电路时,建议变量在always语句中赋值,而在该always语句外使用,使综合时能准确地匹配。建议不要使用局部变量。
15、不能在多个always块中对同一个变量赎值
16、函数
函数代表一个组合逻辑,所有内部定义的变量都是临时的,这些变量综合后为wire。
17、任务:
任务可能是组合逻辑或者时序逻辑,取决于何种情况下调用任务。
18、Z:
Z会综合成一个三态门,必须在条件语句中赋值
19、参数化设计:
优点:参数可重载,不需要多次定义模块
四:模块优化
1、资源共享:
当进程涉及到共用ALU时,要考虑资源分配问题。可以共享的操作符主要有:关系操作符、加减乘除操作符。通常乘和加不共用ALU,乘除通常在其内部共用。
2、共用表达式:
如:C=A+B;
D=G+(A+B);
两者虽然有共用的A+B,但是有些综合工具不能识别.可以将第二句改为:D=G+C;这样只需两个加法器.
3、转移代码:
如循环语句中没有发生变化的语句移出循环.
4、避免latch:
两种方法:1、在每一个IF分支中对变量赋值。2、在每一个IF语句中都对变量赋初值。
5:模块:
综合生成的存储器如ROM或RAM不是一种好方法,只是成堆的寄存器,很费资源。最好用库自带的存储器模块。
五、验证:
1、敏感表:
在always语句中,如果敏感表不含时钟,最好将所有的被读取的信号都放在敏感表中。
2、异步复位:
建议不要在异步时对变量读取,即异步复位时,对信号赋以常数值。

Averilog的流行,有两方面的原因;
Bverilog与VHDL相比的优点
C典型的verilog模块
Dverilog语法要点

A)verilog的流行,有两方面的原因:
1它是cadence的模拟器verilog-XL的基础,cadence的广泛流行使得verilog在90年代深入人心;
2它在硅谷获得广泛使用;
B)verilog与VHDL相比的优点二者的关系仿佛C与FORTRAN,具体而言:
1verilog的代码效率更高:
比较明显的对比:
VHDL在描述一个实体时采用entity/architecture模式,
verilog在描述一个实体时只需用一个"module/edumodule"语句块.
此外verilog的高效性还在很多地方体现出来;
2verilog支持二进制的加减运算:
VHDL在进行二进制的加减运算时使用conv_***函数或者进行其他的定义,总之必须通知编译器;verilog直接用形如"c=a+b"的表示二进制的加减运算;
3综合时可控制性好:
VHDL对信号不加区分地定义为"signal",
而verilog区分为register类型的和wire类型的;
但是也有人支持VHDL,认为verilog和VHDL的关系仿佛C和C++.C)典型的verilog模块
讨论以下典型电路的verilog描述:
*与非门;
*加法器;//即全加器
*D触发器;
*计数器;/
d=e|c;
end
Example2:
inputa,b,c;
rege,d;
always@(aorborcord)
begin
e=d&a&b;
d=e|c;
end
2,条件的描述完备性
如果if语句和case语句的条件描述不完备,也会造成不必要的锁存器。
Example1:
if(a==1'b1)q=1'b1;//如果a==1'b0,q=?q将保持原值不变,生成锁存器!
Example2:
if(a==1'b1)q=1'b1;
elseq=1'b0;//q有明确的值。不会生成锁存器!
Example3:
reg[1:0]a,q;
....
case(a)
2'b00:q=2'b00;
2'b01:q=2'b11;//如果a==2'b10或a==2'b11,q=?q将保持原值不变,锁存器!
endcase
Example4:
reg[1:0]a,q;
....
case(a)
2'b00:q=2'b00;
2'b01:q=2'b11;
default:q=2'b00;//q有明确的值。不会生成锁存器!
endcase
Verilog中端口的描述
1,端口的位宽最好定义在I/O说明中,不要放在数据类型定义中;
Example1:
moduletest(addr,read,write,datain,dataout)
input[7:0]datain;
input[15:0]addr;
inputread,write;
output[7:0]dataout;//要这样定义端口的位宽!
wireaddr,read,write,datain;
regdataout;
Example2:
moduletest(addr,read,write,datain,dataout)
inputdatain,addr,read,write;
outputdataout;
wire[15:0]addr;
wire[7:0]datain;
wireread,write;
reg[7:0]dataout;//不要这样定义端口的位宽!!
2,端口的I/O与数据类型的关系:
端口的I/O端口的数据类型
module内部module外部
inputwirewire或reg
outputwire或regwire
inoutwirewire
3,assign语句的左端变量必须是wire;直接用"="给变量赋值时左端变量必须是reg!
Example:
assigna=b;//a必须被定义为wire!!
********
begin
a=b;//a必须被定义为reg!
end
VHDL中STD_LOGIC_VECTOR和INTEGER的区别
例如A是INTEGER型,范围从0到255;B是STD_LOGIC_VECTOR,定义为8位。A累加到255时,再加1就一直保持255不变,不会自动反转到0,除非令其为0;而B累加到255时,再加1就会自动反转到0。所以在使用时要特别注意!
以触发器为例说明描述的规范性
1,无置位/清零的时序逻辑
always@(posedgeCLK)
begin
Q<=D;
end
2,有异步置位/清零的时序逻辑
异步置位/清零是与时钟无关的,当异步置位/清零信号到来时,触发器的输出立即被置为1或0,不需要等到时钟沿到来才置位/清零。所以,必须要把置位/清零信号列入always块的事件控制表达式。
always@(posedgeCLKornegedgeRESET)
begin
if(!RESET)
Q=0;
else
Q<=D;
end
3,有同步置位/清零的时序逻辑
同步置位/清零是指只有在时钟的有效跳变时刻置位/清零,才能使触发器的输出分别转换为1或0。所以,不要把置位/清零信号列入always块的事件控制表达式。但是必须在always块中首先检查置位/清零信号的电平。
always@(posedgeCLK)
begin
if(!RESET)
Q=0;
else
Q<=D;
end
结构规范性
在整个芯片设计项目中,行为设计和结构设计的编码是最重要的一个步骤。它对逻辑综合和布线结果、时序测定、校验能力、测试能力甚至产品支持都有重要的影响。考虑到仿真器和真实的逻辑电路之间的差异,为了有效的
进行仿真测试:
1,避免使用内部生成的时钟
内部生成的时钟称为门生时钟(gatedclock)。如果外部输入时钟和门生时钟同时驱动,则不可避免的两者的步调不一致,造成逻辑混乱。而且,门生时钟将会增加测试的难度和时间。
2,绝对避免使用内部生成的异步置位/清零信号
内部生成的置位/清零信号会引起测试问题。使某些输出信号被置位或清零,无法正常测试。
3,避免使用锁存器
锁存器可能引起测试问题。对于测试向量自动生成(ATPG),为了使扫描进行,锁存器需要置为透明模式(transparentmode),反过来,测试锁存器需要构造特定的向量,这可非同一般。
4,时序过程要有明确的复位值
使触发器带有复位端,在制造测试、ATPG以及模拟初始化时,可以对整个电路进行快速复位。
5,避免模块内的三态/双向
内部三态信号在制造测试和逻辑综合过程中难于处理.

近日读J.Bhasker的<verilogsynthesispracticalprimer>,受益匪浅,理清了不少基础电路知识,记下一些tips:
1.过程赋值(always中触发赋值)的变量,可能会被综合成连线或触发器或锁存器.
2.综合成锁存器的规则:
a.变量在条件语句(if或case)中,被赋值.
b.变量未在条件语句的所有分支中被赋值.
c.在always语句多次调用之间需要保持变量值.
以上三个条件必须同时满足.
3.综合成触发器的规则:
变量在时钟沿的控制下被赋值。
例外情况:变量的赋值和引用都仅出现在一条always语句中,则该变量被视为中
间变量而不是触发器。
4.对于无时钟事情的always语句(即组合逻辑建模),其时间表应包括该alwa语
句引用的所有变量,否则会出现RTL与Netlist的不一致
芯片外部引脚很多都使用inout类型的,为的是节省管腿。一般信号线用做总线等双向数据传输的时候就要用到INOUT类型了。就是一个端口同时做输入和输出。inout在具体实现上一般用三态门来实现。三态门的第三个状态就是高阻'Z'。当inout端口不输出时,将三态门置高阻。这样信号就不会因为两端同时输出而出错了,更详细的内容可以搜索一下三态门tri-state的资料.
1使用inout类型数据,可以用如下写法:
inoutdata_inout;
inputdata_in;
regdata_reg;//data_inout的映象寄存器
reglink_data;
assigndata_inout=link_data?data_reg:1’bz;//link_data控制三态门
//对于data_reg,可以通过组合逻辑或者时序逻辑根据data_in对其赋值.通过控制link_data的高低电平,从而设置data_inout是输出数据还是处于高阻态,如果处于高阻态,则此时当作输入端口使用.link_data可以通过相关电路来控制.
2编写测试模块时,对于inout类型的端口,需要定义成wire类型变量,而其它输入端口都定义成reg类型,这两者是有区别的.
当上面例子中的data_inout用作输入时,需要赋值给data_inout,其余情况可以断开.此时可以用assign语句实现:assigndata_inout=link?data_in_t:1’bz;其中的link,data_in_t是reg类型变量,在测试模块中赋值.
另外,可以设置一个输出端口观察data_inout用作输出的情况:
Wiredata_out;
Assigndata_out_t=(!link)?data_inout:1’bz;
else,inRTL
inoutuseintopmodule(PAD)
dontuseinout(tri)insubmodule
也就是说,在内部模块最好不要出现inout,如果确实需要,那么用两个port实现,到顶层的时候再用三态实现。理由是:在非顶层模块用双向口的话,该双向口必然有它的上层跟它相连。既然是双向口,则上层至少有一个输入口和一个输出口联到该双向口上,则发生两个内部输出单元连接到一起的情况出现,这样在综合时往往会出错。
对双向口,我们可以将其理解为2个分量:一个输入分量,一个输出分量。另外还需要一个控制信号控制输出分量何时输出。此时,我们就可以很容易地对双向端口建模。
例子:
CODE:
moduledual_port(
....
inout_pin,
....
);
inoutinout_pin;
wireinout_pin;
wireinput_of_inout;
wireoutput_of_inout;
wireout_en;
assigninput_of_inout=inout_pin;
assigninout_pin=out_en?output_of_inout:高阻;
endmodule
可见,此时input_of_inout和output_of_inout就可以当作普通信号使用了。
在仿真的时候,需要注意双向口的处理。如果是直接与另外一个模块的双向口连接,那么只要保证一个模块在输出的时候,另外一个模块没有输出(处于高阻态)就可以了。
如果是在ModelSim中作为单独的模块仿真,那么在模块输出的时候,不能使用force命令将其设为高阻态,而是使用release命令将总线释放掉
很多初学者在写testbench进行仿真和验证的时候,被inout双向口难住了。仿真器老是提示错误不能进行。下面是我个人对inout端口写testbench仿真的一些总结,并举例进行说明。在这里先要说明一下inout口在testbench中要定义为wire型变量。
先假设有一源代码为:
modulexx(data_inout,........);
inoutdata_inout;
........................
assigndata_inout=(!link)?datareg:1'bz;
endmodule
方法一:使用相反控制信号inout口,等于两个模块之间用inout双向口互连。这种方法要注意assign语句只能放在initial和always块内。
moduletest();
wiredata_inout;
regdata_reg;
reglink;
initialbegin
..........
end
assigndata_inout=link?data_reg:1'bz;
endmodule
方法二:使用force和release语句,但这种方法不能准确反映双向端口的信号变化,但这种方法可以反在块内。
moduletest();
wiredata_inout;
regdata_reg;
reglink;
#xx;//延时
forcedata_inout=1'bx;//强制作为输入端口
...............
#xx;
releasedata_inout;//释放输入端口
endmodule
很多读者反映仿真双向端口的时候遇到困难,这里介绍一下双向端口的仿真方法。一个典型的双向端口如图1所示。

其中inner_port与芯片内部其他逻辑相连,outer_port为芯片外部管脚,out_en用于控制双向端口的方向,out_en为1时,端口为输出方向,out_en为0时,端口为输入方向。

用Verilog语言描述如下:
modulebidirection_io(inner_port,out_en,outer_port);
inputout_en;
inout[7:0]inner_port;
inout[7:0]outer_port;
assignouter_port=(out_en==1)?inner_port:8'hzz;
assigninner_port=(out_en==0)?outer_port:8'hzz;
endmodule

用VHDL语言描述双向端口如下:
libraryieee;
useIEEE.STD_LOGIC_1164.ALL;
entitybidirection_iois
port(inner_port:inoutstd_logic_vector(7downto0);
out_en:instd_logic;
outer_port:inoutstd_logic_vector(7downto0));
endbidirection_io;
architecturebehavioralofbidirection_iois
begin
outer_port<=inner_portwhenout_en='1'else(OTHERS=>'Z');
inner_port<=outer_portwhenout_en='0'else(OTHERS=>'Z');
endbehavioral;

仿真时需要验证双向端口能正确输出数据,以及正确读入数据,因此需要驱动out_en端口,当out_en端口为1时,testbench驱动inner_port端口,然后检查outer_port端口输出的数据是否正确;当out_en端口为0时,testbench驱动outer_port端口,然后检查inner_port端口读入的数据是否正确。由于inner_port和outer_port端口都是双向端口(在VHDL和Verilog语言中都用inout定义),因此驱动方法与单向端口有所不同。
验证该双向端口的testbench结构如图2所示。

这是一个self-checkingtestbench,可以自动检查仿真结果是否正确,并在Modelsim控制台上打印出提示信息。图中Monitor完成信号采样、结果自动比较的功能。
testbench的工作过程为
1)out_en=1时,双向端口处于输出状态,testbench给inner_port_tb_reg信号赋值,然后读取outer_port_tb_wire的值,如果两者一致,双向端口工作正常。
2)out_en=0时,双向端口处于输如状态,testbench给outer_port_tb_reg信号赋值,然后读取inner_port_tb_wire的值,如果两者一致,双向端口工作正常。

用Verilog代码编写的testbench如下,其中使用了自动结果比较,随机化激励产生等技术。

`timescale1ns/10ps
moduletb();
reg[7:0]inner_port_tb_reg;
wire[7:0]inner_port_tb_wire;
reg[7:0]outer_port_tb_reg;
wire[7:0]outer_port_tb_wire;
regout_en_tb;
integeri;

initial
begin
out_en_tb=0;
inner_port_tb_reg=0;
outer_port_tb_reg=0;
i=0;
repeat(20)
begin
#50
i=$random;
out_en_tb=i[0];//randomizeout_en_tb
inner_port_tb_reg=$random;//randomizedata
outer_port_tb_reg=$random;
end
end

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

5.同一个层次的所有语句左端对齐;Initial、always等语句块的begin关键词跟在本行的末尾,相应的end关键词与Initial、always对齐;这样做的好处是避免因begin独占一行而造成行数太多;
例:
always@(posedgeSysClkornegedgeSysRst)begin
if(!SysRst)DataOut<=4'b0000;
elseif(LdEn)begin
DataOut<=DataIn;
End
elseDataOut<=DataOut+4'b0001;
end
6.不同层次之间的语句使用Tab键进行缩进,每加深一层缩进一个Tab;
8.在endmodule,endtask,endcase等标记一个代码块结束的关键词后面要加上一行注释说明这个代码块的名称;
9.在task名称前加tsk以示标记。在function的名称前加func以示标记。例如:
tasktskResetSystem;
……
endtask//oftskResetSystem
五.小结:
以上列出的代码编写规范无法覆盖代码编写的方方面面,还有很多细节问题,需要在实际编写过程中加以考虑。并且有些规定也不是绝对的,需要灵活处理。并不是律条,但是在一个项目组内部、一个项目的进程中,应该有一套类似的代码编写规范来作为约束。
总的方向是,努力写整洁、可读性好的代码


二.reg型
在“always”块内被赋值的每一个信号都必须定义成reg型。
reg型数据的缺省初始值是不定值。
reg型只表示被定义的信号将用在“always”块内,理解这一点很重要。并不是说reg型信号一定是寄存器或触发器的输出。虽然reg型信号常常是寄存器或触发器的输出,但并不一定总是这样。

三.memory型
memory型数据是通过扩展reg型数据的地址范围来生成的。其格式如下:

reg[n-1:0]存储器名[m-1:0];
或 reg[n-1:0]存储器名[m:1];

在这里,reg[n-1:0]定义了存储器中每一个存储单元的大小,即该存储单元是一个n位的寄存器。存储器名后的[m-1:0]或[m:1]则定义了该存储器中有多少个这样的寄存器。
reg[7:0]mema[255:0];

这个例子定义了一个名为mema的存储器,该存储器有256个8位的存储器。该存储器的地址范围是0到255。注意:对存储器进行地址索引的表达式必须是常数表达式。
尽管memory型数据和reg型数据的定义格式很相似,但要注意其不同之处。如一个由n个1位寄存器构成的存储器组是不同于一个n位的寄存器的。见下例:

reg[n-1:0]rega;//一个n位的寄存器
regmema[n-1:0];//一个由n个1位寄存器构成的存储器组

一个n位的寄存器可以在一条赋值语句里进行赋值,而一个完整的存储器则不行。见下例:

rega=0; //合法赋值语句
mema=0; //非法赋值语句

如果想对memory中的存储单元进行读写操作,必须指定该单元在存储器中的地址。下面的写法是正确的。
mema[3]=0; //给memory中的第3个存储单元赋值为0。
3.3.1.基本的算术运算符

在VerilogHDL语言中,算术运算符又称为二进制运算符,共有下面几种:
1)+(加法运算符,或正值运算符,如rega+regb,+3)
2)-(减法运算符,或负值运算符,如rega-3,-3)
3)×(乘法运算符,如rega*3)
4)/(除法运算符,如5/3)
5)%(模运算符,或称为求余运算符,要求%两侧均为整型数据。如7%3的值为1)

注意: 在进行算术运算操作时,如果某一个操作数有不确定的值x,则整个结果也为不定值x。
1)~//取反
2)&//按位与
3)|//按位或
4)^//按位异或
5)^~//按位同或(异或非)
在VerilogHDL语言中存在三种逻辑运算符:
1)&&逻辑与
2)||逻辑或
3)!逻辑非

关系运算符共有以下四种:

a<ba小于b
a>ba大于b
a<=ba小于或等于b
a>=ba大于或等于b

3.3.5.等式运算符
3.3.6.移位运算符
3.3.7.位拼接运算符(Concatation)
3.3.10.关键词
在VerilogHDL中,所有的关键词是事先定义好的确认符,用来组织语言结构。关键词是用小写字母定义的,因此在编写原程序时要注意关键词的书写,以避免出错。下面是VerilogHDL中使用的关键词(请参阅附录:Verilog语言参考手册):

always,and,assign,begin,buf,bufif0,bufif1,case,casex,casez,cmos,deassign,default,defparam,disable,edge,else,end,endcase,endmodule,endfunction,endprimitive,endspecify,endtable,endtask,event,for,force,forever,fork,function,highz0,highz1,if,initial,inout,input,integer,join,large,macromodule,medium,module,nand,negedge,nmos,nor,not,notif0,notifl,or,output,parameter,pmos,posedge,primitive,pull0,pull1,pullup,pulldown,rcmos,reg,releses,repeat,mmos,rpmos,rtran,rtranif0,rtranif1,scalared,small,specify,specparam,strength,strong0,strong1,supply0,supply1,table,task,time,tran,tranif0,tranif1,tri,tri0,tri1,triand,trior,trireg,vectored,wait,wand,weak0,weak1,while,wire,wor,xnor,xor



(1).非阻塞(Non_Blocking)赋值方式(如b<=a;)
1)块结束后才完成赋值操作。
2)b的值并不是立刻就改变的。
3)这是一种比较常用的赋值方法。(特别在编写可综合模块时)

(2).阻塞(Blocking)赋值方式(如b=a;)
1)赋值语句执行完后,块才结束。
2)b的值在赋值语句执行完后立刻就改变的。
3)可能会产生意想不到的结果。

一.顺序块
顺序块有以下特点:
1)块内的语句是按顺序执行的,即只有上面一条语句执行完后下面的语句才能执行。
2)每条语句的延迟时间是相对于前一条语句的仿真时间而言的。
3)直到最后一条语句执行完,程序流程控制才跳出该语句块。
顺序块的格式如下:
begin
语句1;
语句2;
......
语句n;
end
其中:
块名即该块的名字,一个标识名。其作用后面再详细介绍。
块内声明语句可以是参数声明语句、reg型变量声明语句、integer型变量声明语句、real型变量声明语句。

二.并行块
并行块有以下四个特点:
1)块内语句是同时执行的,即程序流程控制一进入到该并行块,块内语句则开始同时并行地执行。
2)块内每条语句的延迟时间是相对于程序流程控制进入到块内时的仿真时间的。
3)延迟时间是用来给赋值语句提供执行时序的。
4)当按时间时序排序在最后的语句执行完后或一个disable语句执行时,程序流程控制跳出该程序块。

并行块的格式如下:
fork
语句1;
语句2;
.......
语句n;
join
其中:
•块名即标识该块的一个名字,相当于一个标识符。
•块内说明语句可以是参数说明语句、reg型变量声明语句、integer型变量声明语句、real型变量声明语句、time型变量声明语句、事件(event)说明语句。

在fork_join块内,各条语句不必按顺序给出,因此在并行块里,各条语句在前还是在后是无关紧要的。见下例:

三.块名
在VerilgHDL语言中,可以给每个块取一个名字,只需将名字加在关键词begin或fork后面即可。这样做的原因有以下几点。
1)这样可以在块内定义局部变量,即只在块内使用的变量。
2)这样可以允许块被其它语句调用,如被disable语句。
3)在Verilog语言里,所有的变量都是静态的,即所有的变量都只有一个唯一的存储地址,因此进入或跳出块并不影响存储在变量内的值。
基于以上原因,块名就提供了一个在任何仿真时刻确认变量值的方法。





casez语句用来处理不考虑高阻值z的比较过程,casex语句则将高阻值z和不定值都视为不必关心的情况。

如果用到if语句,最好写上else项。如果用case语句,最好写上default项。遵循上面两条原则,就可以避免发生这种错误,使设计者更加明确设计目标,同时也增强了Verilog程序的可读性。
3.6.循环语句

在VerilogHDL中存在着四种类型的循环语句,用来控制执行语句的执行次数。

1)forever连续的执行语句。
2)repeat连续执行一条语句n次。
3)while执行一条语句直到某个条件不满足。如果一开始条件即不满足(为假),
则语句一次也不能被执行。
4)for通过以下三个步骤来决定语句的循环执行。
a)先给控制循环次数的变量赋初值。
b)判定控制循环的表达式的值,如为假则跳出循环语句,如为真则执行指定的语句后,转到第三步。
c)


#1:当为时序逻辑建模,使用“非阻塞赋值”。
#2:当为锁存器(latch)建模,使用“非阻塞赋值”。
#3:当用always块为组合逻辑建模,使用“阻塞赋值”
#4:当在同一个always块里面既为组合逻辑又为时序逻辑建模,使用“非阻塞赋值”。
#5:不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”。
#6:不要在两个或两个以上always块里面对同一个变量进行赋值。
#7:使用$strobe以显示已被“非阻塞赋值”的值。
#8:不要使用#0延迟的赋值。
#9:在VERILOG语法中,if...elseif...else语句是有优先级的,一般说来第一个IF的优先级最高,最后一个ELSE的优先级最低。如果描述一个编码器,在XILINX的XST综合参数就有一个关于优先级编码器硬件原语句的选项PriorityEncoderExtraction.而CASE语句是"平行"的结构,所有的CASE的条件和执行都没有“优先级”。而建立优先级结构会消耗大量的组合逻辑,所以如果能够使用CASE语句的地方,尽量使用CASE替换IF...ELSE结构。
#10:XILINX的底层可编程硬件资源叫SLICE,由2个FF和2个LUT组成。FF触发器LUT查找表
ALTERA的底层可编程硬件资源叫LE,由1个FF和1个LUT组成。
#11:慎用锁存器(latch),同步时序设计要尽量避免使用锁存器,综合出非目的性latch的主要原因在于不完全的条件判断句。另外一种情况是设计中有组合逻辑的反馈环路(combinatorialfeedbackloops)。
#12:状态机的一般设计原则,Biary,gray-code编码使用最少的触发器,较多的组合逻辑。而one-hot编码反之。所以CPLD多使用GRAY-CODE,而FPGA多使用ONE-HOT编码。另一方面,小型设计使用GRAY-CODE和BINARY编码更有效,而大型状态机使用ONE-HOT更有效。
#13:业界主流CPLD产品是lattice的LC4000系列和ALTERA的MAX3000系列。
#14:复位使初始状态可预测,防止出现禁用状态。FPGA和CPLD的复位信号采用异步低电平有效信号,连接到其全局复位输入端,使用专用路径通道,复位信号必须连接到FPGA和CPLD的全局复位管脚。。
#15:不要用时钟或复位信号作数据或使能信号,也不能用数据信号作为时钟或复位信号,否则HDL综合时会出现时序验证问题。信号穿过时钟的两半个周期时,要在前后分别取样;防止出现半稳定状态。
#16:fpga设计中不要使用门时钟(don'tusegatedclock)。时钟信号必须连接到全局时钟管脚上。
#17:不要使用内部三态信号,否则增加功耗。
#18:只使用同步设计,不要使用延时单元。
#19:避免使用负延触发的双稳态多谐振荡器(flipflop)。
#20:不要使用信号和变量的默认值(或初始值),用复位脉冲初始化
信号和变量。
#21:不要在代码中使用buffer类型的端口读取输出数据;要使用out类型,再增加另外变量或信号,以获取输出值。
这是因为buffer类型的端口不能连接到其他类型的端口上,因此buffer类型就会在整个设计的端口中传播下去。
#22:对变量要先读后写;如果先写后读,就会产生长的组合逻辑和锁存器(或寄存器)。这是因为变量值是立即获取的。
#23:在组合逻辑进程中,其敏感向量标中要包含所有要读取得信号;这是为了防止出现不必要的锁存器。

近期,在stephenBrown的一本书数字逻辑基础与verilog设计一书中看到关于触发器电路的时序分析。以前一直没有搞明白这个问题,现在觉得豁然开朗。怕忘记了,特地摘抄与此与edacn网友分享。
触发器电路的时序分析:
图7-84给出了一个使用D触发器的简单电路。我们想要计算该电路能正常工  

爱华网本文地址 » http://www.aihuau.com/a/25101015/244131.html

更多阅读

语言栏不显示怎样处理? 显示语言栏不见了

语言栏不显示怎样处理?——简介语言栏不显示怎样处理?语言栏不显示怎样处理?——方法/步骤语言栏不显示怎样处理? 1、任务栏输入法(语言栏)不显示 桌面任务栏右侧的输入法状态(也就是语言栏)不

易语言让进度条动起来怎么写 易语言进度条怎么用

易语言让进度条动起来怎么写——简介最近在写一个下载工具,需要获取下载进度,也就是需要使进度条动起来,然而到底如何才能使进度条动起来呢?这里分享一下!易语言让进度条动起来怎么写——方法/步骤易语言

语言表达技巧攻略 语言表达技巧包括

语言表达技巧攻略——简介谈话虽然有许多技巧,但是人们在讲话中,总有着一种恐惧心理。担心在谈话中出丑或者谈话失败,这种恐惧心理极大地影响着人的表达能力。实际上,恐惧心理不是一个人的专利,而是普遍存在的现象不过,经过长期的努力。就

吐槽是什么意思——几种网络语言解释 膜是什么意思网络语言

吐槽是什么意思——几种网络语言解释——简介现在网络生活越来越显得重要了,一段时间不上网,别人在网上说的话你可能都听不懂了,这些语言在我们平时生活中很少用。但是随着网络的发展,这些语言也进入了我们的生活当中。下面就几个常用网

怎样练好口才语言表达能力 如何练口才和表达能力

怎样练好口才(语言表达能力)——简介在就业压力日益增大的今天,应聘的时候不再是单单看一个人的内在的能力,而是多种素质综合在一起的比拼。其中最重要的一个方面就是自己的口才。初次见面,别人不可能一眼能看出你的能力,这时候就是你的口

声明:《VerilogHDL verilog hdl语言》为网友慢慢坚强分享!如侵犯到您的合法权益请联系我们删除