hdmFPGA知识点Word文档格式.docx
《hdmFPGA知识点Word文档格式.docx》由会员分享,可在线阅读,更多相关《hdmFPGA知识点Word文档格式.docx(46页珍藏版)》请在冰豆网上搜索。
在一个模块中的多个assign与always之间都是并行语句
`timescale1ns/100ps
此语句说明时延时间单位为1ns并且时间精度为100ps(时间精度是指所有的时延必须被限定在0.1ns内)
`timescale1ns/1ns
以反引号“`”开始的第一条语句是编译器指令,`timescale将模块中所有时延的单位设置为1ns,时间精度为1ns。
例如,在连续赋值语句中时延值#1和#2分别对应时延1ns和2ns。
2.3数据流描述方式
某个值被指派给线网变量。
assign[delay]LHS_net=RHS_expression;
时延定义了右边表达式操作数变化与赋值给左边表达式之间的持续时间。
如果没有定义时延值,缺省时延为0。
在硬件上就是用一根线连起来了,注意,assign中只能用“=”号。
连续赋值语句是并发执行的
赋值目标只能是线网,而且这种赋值行为没有任何附加的判断条件
连续赋值语句的赋值目标可以是:
·
标量线网·
向量线网·
向量的常数型位选择;
·
向量的常数型部分选择;
上述类型的任意连接结果(使用连接操作符)
等号右端操作数上的数据是始终处于被监控状态的,一旦这些数据发生变化就会引起赋值语句的执行。
跟在assign之后。
默认时延是0
连续赋值语句中的时延可以指定3类时延值:
上升时延、下降时延和关闭时延(值变为x)。
assign#(rise,fall,turn-off)LHS_target=RHS_expression;
2.4行为描述方式
3.2顺序行为建模
initial和always都不仅仅是单独的一条语句,它们引导一个“过程”或者说一个“结构”,
跟在initial和always之后的都是一段程序,所有其他顺序行为语句都必须包含在这段程序中。
可以说,initial和always的出现是顺序行为描述的开始。
这段程序会被封装成一个“程序块”,可以用begin-end(顺序语句块)封装,也可以用fork-join(并行语句块)封装。
并行语句块fork-join封装并行语句块中的语句彼此之间都是并行执行的,和书写顺序无关。
1)initial语句:
此语句只执行一次。
2)always语句:
此语句总是循环执行,或者说此语句重复执行。
所有的initial初始化语句和always语句在0时刻并发执行。
initial之后要跟多条过程语句,就要把这些语句封装为“程序块”。
顺序语句块(begin-end---{-}用来代表一个语句块)是最常使用的封装结构,在begin和end之间的过程语句是按书写顺序依次执行的
moduleFA_Seq(A,B,Cin,Sum,Cout);
inputA,B,Cin;
outputSum,Cout;
regSum,Cout;
regT1,T2,T3;
always
@(AorBorCin)
begin
Sum=(A^B)^Cin;
T1=A&
Cin;
T2=B&
T3=A&
B;
Cout=(T1|T2)|T3;
end
endmodule
parameterSIZE=1024;
reg[7:
0]RAM[0:
SIZE-1];
regRibReg;
initial
begin:
SEQ_BLK_A
integerIndex;
RibReg=0;
for(Index=0;
Index<
SIZE;
Index=Index+1)
RAM[Index]=0;
在always里面赋值语句用“<
=”(以后学深了再探讨阻塞赋值和非阻塞赋值)
always@(posedgeClkorposedgeRst)
begin
if(Rst)
Q<
=1’b0;
else
=D;
上面括号内的内容也是敏感变量,而posedge代表“上升沿“,就是信号由低到高,相应的,negedge代表“下降沿”,就是信号由高到低。
即整个always语句当敏感变量clk或rst有上升沿时执行。
因此,当Rst由0变为1时,Q被复位,在时钟上升沿时,D被采样到Q。
程序中begin之后的标识符“SEQ_BLK_A”是顺序语句块的名称,并不是所有的语句块都要求有名称。
在这段过程语句中因为出现了局部变量Index,所以要求这个程序块必须有名称标记。
时延可以细分为两种类型:
1)语句间时延:
这是时延语句执行的时延。
#4T1=A&
2)语句内时延:
这是右边表达式数值计算与左边表达式赋值间的时延。
Sum=#3(A^B)^Cin;
如果在过程赋值中未定义时延,缺省值为0时延,也就是说,赋值立即发生。
ONLY_ONCE
reg[3:
0]Pal;
//需要4位,Pal才能取值8。
for(Pal=0;
Pal<
8;
Pal=Pal+1)
{PA,PB,PCi}=Pal;
#5$display(“PA,PB,PCi=%b%b%b”,PA,PB,PCi,
“:
:
PCo,PSum=%b%b”,PCo,PSum);
初始化语句中的顺序过程(begin-end)必须标记。
在这种情况下,ONLY_ONCE是顺序过程标记。
如果在顺序过程内没有局部声明的变量,就不需要该标记。
//输出显示:
$monitor("
Attime%t,"
$time,"
TR=%b,TS=%b,TQ=%b,TQb=%b"
TR,TS,TQ,TQb);
第二条初始化语句调用系统任务$monitor。
只要参数表中指定的变量值发生变化就打印指定的字符串。
注意只有小写的关键词才是保留字。
例如,标识符always(这是个关键词)与标识符ALWAYS(非关键词)是不同的。
Always[timing_control]procedural_statement
其中,timing_control是时序控制,形式可以是时延控制(即等待一个确定的时间),或事件控制(即等待某一时间发生或某一条件为真);
procedural_statement是过程语句。
用符号“@”定义了事件控制的always语句。
事件控制还有一种是“电平敏感事件”,使用关键词“wait”定义。
3.2.2时序控制
时序控制有两种形式:
时延控制和事件控制。
(1)时延控制
语句前时延·
单独时延·
语句内时延
如果时延表达式的值是0,则称之为显示零时延,它和不定义时延是完全不同的。
后者默认时延是0,程序继续执行不受任何影响,
而显式零时延将使整个程序的运行在当前仿真时间暂停,使其处于一种“等待”状态,等待所有其他正在被执行的事件全都执行完毕后,才将整个进程唤醒继续执行,处于等待状态时,仿真时间不前进。
(2)事件控制
边沿触发事件和电平敏感事件。
边沿触发事件使用符号@定义,形式如下:
@eventprocedural_statement
其中“@”是边沿事件使用的符号,event是边沿事件,procedural_statement是过程语句。
和时延控制类似,事件控制也可以独立成为一条语句。
@event;
运行到这条语句时,整个程序在此被暂停,进入等待状态,一直等到这条语句中指定的事件发生后,才能继续执行后面的程序。
event可以是多个事件,这些事件之间只需要用关键字“or”分隔开即可。
注意:
在verilog中,上升沿不仅仅是从0到1这一种情况,下降沿也不仅仅是从1到0这种情况。
电平敏感事件
wait(condition)procedural_statementcondition就是电平敏感事件,prcedural_statement是过程语句。
执行到wait时,检查condition定义的条件是否满足,满足就接着执行随后的过程语句;
否则进入等待状态,知道条件满足为止。
3.2.4过程性赋值
过程性赋值是最常见的赋值形式:
等号左侧是赋值目标,右侧是表达式。
它有以下几个特点:
过程性赋值只出现在initial语句和always语句内;
过程性赋值只能给寄存器变量赋值;
只有寄存器类型数据能够在这两种语句中被赋值。
赋值表达式的右端可以是任何表达式。
事实上,Verilog有两种过程性赋值:
阻塞过程赋值和非阻塞过程赋值。
之前程序中出现的都是阻塞过程赋值,其标志是使用赋值符号“=”,而非阻塞过程赋值使用赋值符号“<
=”,二者在功能和特点上有很大区别。
来自always语句和initial语句(切记只有寄存器类型数据可以在这两种语句中赋值)的值能够驱动门或开关,而来自于门或连续赋值语句(只能驱动线网)的值能够反过来用于触发always语句和initial语句。
(1)阻塞过程赋值
“阻塞”是从阻塞过程赋值的工作过程中得来的。
它先计算右侧表达式的值,然后赋值给等号左端目标,而且在完成整个赋值之前不能被其它语句打断。
也就是说在某一条阻塞过程赋值语句正在执行时,处于其后的其它赋值语句都不能执行。
(2)非阻塞过程赋值
非阻塞过程赋值操作符是“<
=”,非阻塞过程赋值只能用于给寄存器赋值。
赋值过程都包括两个子过程:
过程1,计算右侧表达式的值;
过程2,给左侧目标赋值。
对阻塞过程赋值而言,这两个子过程可以视为连续完成的,而且在完成赋值之前不允许其后的其它语句执行。
而对于非阻塞赋值,所谓“在某个时刻完成赋值”,其实是在这个时刻开始执行子过程1,在这个时刻结束时执行子过程2,这两个子过程之间有一个微小的事件间隔。
在这个间隔期间,这条非阻塞赋值语句后面的其它的语句也可以执行。
(4)过程性连续赋值
连续赋值适用于线网,过程赋值适用于寄存器。
但是还有一类赋值方式,它既能对线网赋值也能对寄存器赋值(但不能是寄存器的位选择或部分选择),这种赋值方式被称为过程性连续赋值。
它属于过程性赋值而非连续赋值,所以它能出现在always和initial语句中(连续赋值语句不能出现在always和initial内)。
但是它又有连续赋值的一些特性,就是在过程性连续赋值语句中,右端表达式中操作数的任何变化都会引起赋值语句的重新执行。
过程性赋值语句有两种类型:
assign-deassign(赋值-重新赋值),force-release(强制-释放虽然它也可以用于对寄存器赋值,但主要用于对线网赋值)。
assign用于对寄存器赋值,deassign用于取消之前由assign赋给某寄存器的值。
也就是说,使用assign为寄存器赋值后,这个值将一直保存在寄存器上,直到遇到deassign为止。
看下例:
moduleDEF(D,Clr,Clk,Q);
inputD,Clr,Clk;
outputQ;
regQ;
always@(negedgeClk)在Clk的下降沿把D赋给Q
Q=D;
always@(Clr)
if(!
Clr)
assignQ=0;
如果clr为0,assign赋值使Q被赋值为0,然后Q就始终为0,
else
deassignQ;
上面这段程序中,第一个always会在Clk的下降沿把D赋给Q,但是第二个always内的过程性连续赋值语句会使第一个always的赋值失效。
在第二个always中,如果clr为0,assign赋值使Q被赋值为0,然后Q就始终为0,而不管Clk如何变化,即Clk和D对Q无效;
等到Clr变为1时,deassign被执行,对Q的过程性连续赋值被取消。
之后Clk才能对Q产生影响。
换句话说,在Clr为0是,Q一直被assign所霸占,只有Clr为1时,才会把Q释放掉,从而才会受到D的影响。
force-release
当force语句应用于寄存器时,寄存器的当前值被force语句的值覆盖;
当release语句应用于寄存器时,寄存器中的当前值将保持不变,直到被赋予新值。
来看两段代码:
reg[2:
0]Colt;
Colt=2;
forceColt=1;
//Colt被强制赋值为1
releaseColt//对Colt的强制赋值被取消,从现在起Colt将保持值为1
assignColt=5;
forceColt=3;
//被强制赋值为3
releaseColt;
//对Colt的强制赋值被取消,assignColt=5重新生效,使得Colt的值变为5
用force语句对线网赋值时,将为线网替换所有驱动源,直到那个线网上执行release语句为止。
wireprt;
or#1(prt,Std,Dzx);
//定义一个或门,线网prt的驱动源由或门的结构给出
forceprt=Dzx&
Srd;
#5;
releaseprt;
上面的程序中,执行force语句将替换ptr原来的驱动源,ptr将一直保持这些驱动源,直到release把对prt的强制赋值取消之后,或门的prt驱动源才重新生效。
3.2.5if语句
对于if语句,如果某个分支内的过程语句多于一条,就应该把它们放在begin-end块内。
这里的begin-end相当于C中的{}。
3.2.6case语句
在比较条件表达式和分支表达式的值时,如果值的某些位出现了x或z,那么这些x和z和0、1一样有意义,所以只要在两个值的相同位置出现x或z,就认为这两个值相同。
casex和casez
在casez语句中,出现在条件表达式和任意分支项表达式中的值z被认为是无关值,出现z的那个位在比较时被忽略。
在casex语句中,值x和z都被认为是无关位。
所谓无关位即在比较时可以忽略的位,只要其它的位相匹配即可。
3.2.7循环语句
(1)forever是个永远循环执行的语句,不需要声明任何变量,其语法格式如下:
Foreverprocedural_statement
其中,procedural_statement是过程语句,若过程语句多于一条,则应该放在begin-end块内。
forever语句中必须有某种形式的时序控制,否则forever会在0时延后永远重复执行过程语句。
forever的最主要用途是产生周期性的波形作为仿真测试信号。
(2)repeat循环语句
repeat循环是个重复执行若干次数的语句,带有一个控制循环次数的常量或者变量,其语法形式如下:
repeat(loop_count)procedural_statement
其中loop_count是控制循环次数的常数或变量;
如果控制循环次数的常数或变量的值不确定(为x或z)时,那么循环次数按0处理。
Sum=repeat(Count)@(posedgeClk)Sum+1;
//首先计算出Sum+1的值,然后等待Clk上升沿,最后为左端赋值
repeat(4)@(negedgeClockz);
//等待Clockz上出现4次下降沿之后,才能继续执行repeat之后的语句
(3)while循环语句
(4)for循环语句
4.case语句
case语句是一个多路条件分支形式(相当于c语言的switch语句)用法如下:
case(case_expr)
case_item_expr{,case_item_expr}:
procedural_statement
...
[default:
procedural_statement]
endcase
case语句首先对条件表达式case_expr求值,第一个与条件表达式值相匹配的分支中的语句被执行。
case(HEX)
4'
b0001:
LED=7'
b1111001;
//1
b0010:
b0100100;
//2
default:
LED=7'
b1000000;
//0
endcase
case的缺省项必须写,防止产生锁存器。
以$字符开始的标识符表示系统任务或系统函数。
系统任务提供了一种封装行为的机制。
这种机制可在设计的不同部分被调用。
任务可以返回0个或多个值。
系统函数除只能返回一个值以外与任务相同。
此外,函数在0时刻执行,即不允许延迟,而任务可以带有延迟。
系统任务和系统函数可以分成以下几类:
1.输出控制类系统函数:
模拟过程的状态信息以及模拟结果的输出都必须按一定的格式进行表示,:
$display,$write,$minitor等。
2.模拟时标类系统函数:
比如$time和$realtime等。
3.进程控制类系统任务:
对模拟进程控制,有$finish,$stop等。
4.文件读写类系统任务:
用于控制对数据文件读写方式,如$readmem。
5.其他类:
比如随机数产生系统函数$random。
$display(“格式控制输出和字符串”,输出变量名表);
输出结束后自动换行,
$write(“格式控制输出和字符串”,输出变量名表);
后者不会。
系统任务$monitor
和$display与$write一样,同属于输出控制类,它的调用形式可以有以下三种:
$monitor(“格式控制输出和字符串”,输出变量名表);
$monitoron;
$monitoroff;
以上第一种的格式和上面的$display完全一致,不同点是,$display每调用一次执行一次,$monitor则一旦被调用,就会随着对输出变量名表中的每一个变量检测,如果发现其中任何一个变量在模拟过程中发生了变化,就会按照$monitor中的格式,产生一次输出。
通常情况下$monitor的输出中会用到一个系统函数$time,如:
$monitor($time,,“signal1=%bsignal2=%b”,signal1,signal2);
对$time的返回值也可以进行格式控制,如:
$monitor(“%dsignal1=%bsignal2=%b”,$time,signal1,signal2);
由于$monitor一旦被调用后就会启动一个后台进程,因而不可能在有循环性质的表达中出现,如always过程块或者其他高级程序循环语句,$monitor通常位于initial过程块的最开始处,
格式说明符
输出格式
%h或%H
以十六进制的格式输出
%d或%D
以十进制的格式输出
%o或%O
以八进制的格式输出
%b或%B
以二进制的格式输出
%c或%C
以ASCII字符形式输出
%s或%S
以字符串方式输出
%v或%V
输出连线型数据的驱动强度
%t或%T
输出模拟系统所使用的时间单位
%m或%M
输出所在模块的分级名
%e或%E
将实型量以指数方式显示
%f或%F
将实型量以浮点方式显示
%g或%G
将实型量以上面两种种较短的方式显示
3.4.3系统函数$time和$realtime
$time和$realtime属于模拟时标类系统函数,将返回从模拟程序开始执行到被调用时刻的时间,不同之处在于$time返回的是64位整数,而$realtime返回的是一个实型数。
例`timescale10ns/1ns
moduletime_demo;
regar;
parameterdelay=1.6
initial
begin
$display(“timevalue”);
$monitor($time,,,“var=%b”,var);
//$time换成$realtime,没有四舍五入误差的问题。
#delayvar=1;
#delayvar=0;
#1000$finish;
end
endmodule
系统时间定标为10ns为计时单位,所以delay=1.6实际代表的时间是16ns,
输出时间应该是1.6和3.2,可是实际输出时2和3,这是因为$time在返回时间变量时进行了四舍五入。
显示结果如下:
timevalue
1var=x
2var=1
3var=0
4.
3.4.4系统任务$finish和$stop
控制模拟进程。
$finish;
$finish(n);
它的作用就是中止仿真器的运行,结束仿真过程。
可以带上一个参数,参数n只能取以下三个值:
0:
不输出任何信息。
1:
输出结束仿真的时间和模拟文件的位置。
2:
在1的基础上增加对CPU时间、机器内存占用情况等统计结果的输出。
如果$finish不指明参数时,默认为1。
$stop的调用方式相同和$finish相同,参数也相同。
不同的是,$stop的作用只相当于一个pause的暂停语句,,这是设计人员可以输入相应的命令,对模拟过程进行交互控制,比如用force/release语句,对某些信号实行强制性修改,在不退出仿真进程的前提下,进行模拟调试。
3.4.6系统任务$random产生一个随机数,
$random%b,b>
0,它将产生一个范围在(-b+1)到(b-1)之间的随机数。
测试模块提供随机脉冲序列,如:
0]ran_num;