always |
ifnone |
rnmos |
and |
incdir |
rpmos |
assign//标记赋值 |
include |
rtran |
automatic |
initial |
rtranif0 |
begin |
inout |
rtranif1 |
buf |
input//输入 |
scalared |
bufif0 |
instance |
showcancelled |
bufif1 |
integer |
signed |
case |
join |
small |
casex |
large |
specify |
casez |
liblist |
specparam |
cell |
library |
strong0 |
cmos |
localparam |
strong1 |
config |
macromodule |
supply0 |
deassign |
medium |
supply1 |
default |
module |
table |
defparam |
nand |
task |
design |
negedge//下降沿 |
time |
disable |
nmos |
tran |
edge |
nor |
tranif0 |
else//相反 |
noshowcancelled |
tranif1 |
end |
not |
tri |
endcase |
notif0 |
tri0 |
endconfig |
notif1 |
tri1 |
endfunction |
or |
triand |
endgenerate |
output//输出 |
trior |
endmodule |
parameter |
trireg |
endprimitive |
pmos |
unsigned |
endspecify |
posedge//上升沿 |
use |
endtable |
primitive |
vectored |
endtask |
pull0 |
wait |
event |
pull1 |
wand |
for//循环 |
pulldown |
weak0 |
force |
pullup |
weak1 |
forever |
pulsestyle_onevent |
while |
fork |
pulsestyle_ondetect |
wire |
function |
rcmos |
wor |
generate |
real |
xnor |
genvar |
realtime |
xor |
highz0 |
reg//寄存器变量 |
|
highz1 |
release |
|
if、、条件 |
repeat |
Verilog HDL语法基础
1 Verilog是大小写相关的,其中的关键字全部为小写。
2 空白符由空格、制表符、和换行符组成。
3 单行注释以“//”开始,verilog将忽略此处到行尾的内容。多行注释以“/*”开始,以“*/”结束。多行注释不允许嵌套
4 操作符有三种:单目操作符、双目操作符和三目操作符。
5 数字声明
Verilog中有两种数字生命:指明位数的数字和不指明位数的数字
指明位数的数字表示形式:<size>’<base format><number>
Size用来指明数字位宽度,只能用十进制整数表示
Base format包括十进制(’d或’D),二进制(’b或’B),八进制(‘o或’O),十六进制(‘h或’H)
例如
4’b1111 //4位2进制数
12’h3ac //12位16进制数
不指明位数的数字:如果数字说明中没有指定基数,那么默认表示为十进制数。如果没有指定位宽,则默认的位宽度与仿真器和使用的计算机有关(最小为32位)。
‘o21 //32位八进制数
X值和Z值:不确定值用X表示,高阻用Z值表示。在八进制数中代表3位,十六进制中代表4位。
12’h12X //这是一个12位16进制数,其中低四位不确定
负数: 在表示位宽的数字前面增加一个减号来表示它是一个负数。
-6’d3 //一个6位的用二进制补码形式存储的十进制数3,表示负数
-6’sd3 //一个6位的带符号算数运算的负数
下划线符号和问号:
除了第一个字符,下划线“_”可以出现在数字中的任何位置,它的作用只是提高可读性,在编译阶段会被忽略掉
问号“?”是z的另一种表示,使用问号的目的在于增强casex和casez语句的可读性。在这两条语句中,“?”表示不必关心的情况。
12’B1111_0011_1110 // 增强可读性
4’b10?? //相当于4’b10zz
6 字符串是双引号括起来的一个字符队列。对于字符串的限制是,它必须在一行中书写完,不可书写在多行中,也不能包含回车符。Verilog将字符串当作一个单字节的ASCII字符队列。
“Hello Verilog world” //是一个字符串
7 标识符和关键字
关键字是语言中预留的用于定义语言结构的特殊标识符。Verilog中关键字全部小写。
标识符是程序代码中对象的名字,程序员使用标识符来访问对象。Verilog中标识符由字母数字字符、下划线和美元符号组成,区分大小写。其第一个字符必须是数字字符或下划线。
reg value; //reg是关键字;value是标识符
8 转义标识符
转义标识符以“/”开始,以空白符结束。Verilog将反斜线和空白符之间的字符逐个进行处理。所有的可打印字符均可包含在转义字符中,而反斜线和表示结束的空白符不作为标识符的一部分。
系统任务
Verilog为某些常用操作提供了标准的系统任务(也叫系统函数)这些操作包括屏幕显示、线网值动态监视、暂停和结束仿真等。所有的系统任务都具有$<keyword>的形式。
显示信息 $display(p1,p2,p3,…,pn);
$display会自动在字符串的结尾处插入一个换行符,因此如果参数列表为空,则display的效果是现实光标移动到下一行
监视信息 $monitor(p1,p2,p3,…,pm);
系统函数$monitor对其参数列表中的变量值或者信号值进行不间断的监视,当其中任何一个发生变化的时候,显示所有参数的数值。$monitor只需调用一次即可在整个仿真过程中生效。
编译指令
Verilog提供了一些编译指令供用户使用,其使用方式为’<keyword>
‘define 用于定义verilog中的文本宏。类似于c中的#define.
‘define WORD_SIZE 32
‘include 在编译期间将一个verilog源文件包含在另一个verilog文件中,类似于c中的#i nclude结构。
‘include header.v
Verilog的数据类型
1 整数、实数和时间寄存器类型
整数是一种通用的寄存器数据类型,用于对数量进行操作,使用integer进行声明。
integer counter; //一般用途的变量用作计数器
initial
counter = -1; //把-1存储到寄存器中
实数:实常量和实数寄存器数据类型使用关键字real来声明,可以用十进制或科学计数法来表示。实数声明不能带有范围,其默认值为0.如果将一个实数赋予一个整数,那么实数将会被取为最接近的整数。
real delta; //定义一个名为delta的实型变量
时间寄存器:仿真是按照仿真时间进行的,verilog使用一个特殊的时间寄存器数据类型来保存仿真时间。时间变量通过使用关键字time来声明,其宽度与具体实现无关,最小为64位。通过调用系统函数$time可以取得当前的仿真时间。
2 数组
Verilog中允许声明reg、integer、time、real、realtime及其向量类型的数组,对数组的维数没有限制,即可声明任意维数的数组。线网数组也可用于连接实例的端口,数组中的每个元素都可以作为一个标量或者向量,以同样的方式来使用,形如<数组名>[<下标>]。
Integer count[0:7]; //由八位计数变量组成的数组
reg bool [31:0]; //由32个1位的布尔寄存器变量组成的数组
wire [7:0] w_array2 [5:0]; //声明8位向量的数组
注意:不要把数组和线网或寄存器向量混淆起来。向量是一个单独的元件,它的位宽是n,数组由多个元件组成,其中每个元件的位宽为n或1.
3 存储器
Verilog中使用寄存器一维数组来表示存储器。数字的每个元素成为一个元素或一个字(word),由一个数组索引来指定。每个字的位宽为1位或者多位。注意n个1位寄存器和一个n位寄存器是不同的。如果需要访问存储器中的一个特定的字,则可通过子的地址作为数组的下标来完成。
reg mem1bit[0:1023]; //1k的1位存储器
reg [7:0] membyte [0:1023]; //1k的字节(8位)存储器membyte
membyet[511] //取出membyte中地址511所处的字节
4 参数
Verilog使用关键字parameter在模块内定义常数。参数代表常数,不能像变量那样赋值,但是每个模块实例的参数值可以在编译阶段被重载。通过参数重载使得用户可以对模块实例进行定制。除此之外还可以对参数的类型和范围进行定义。
parameter port_id = 5; //定义常数port_id 为5
5 字符串
字符串保存在reg类型的变量中,每个字符占用8位(一个字节),因此寄存器变量的宽度应足够大,以保证容纳全部字符。如果寄存器变量的宽度大于字符串的大小,则verilog用0来填充左边的空余位。如果寄存器变来那个的宽度小于字符串的大小,则verilog截去字符串最左边的位。
1
值的种类
四值电平逻辑
值的级别 |
硬件电路中的条件 |
0 |
逻辑0,条件为假 |
1 |
逻辑1,条件为真 |
X |
逻辑值不确定 |
Z |
高阻,浮动状态 |
除了逻辑值外,Verilog还是用强度值来解决数字电路中不同强度的驱动源之间的赋值冲突。
强度等级 |
类型 |
程度 |
Supply |
驱动 |
最强 |
Strong |
驱动 |
|
Pull |
驱动 |
|
Large |
存储 |
|
Weak |
驱动 |
|
Medium |
存储 |
|
Small |
存储 |
|
Highz |
高阻 |
最弱 |
如果两个具有不同强度的信号驱动同一个线网,则竞争结果值为高强度信号的值。
如果两个强度相同的信号之间发生竞争,则结果为不确定值。
2 线网
线网(net)表示硬件单元之间的连接。线网一般使用关键字wire进行声明。如果没有显式的说明为向量,则默认线网的位宽为1。线网的默认值为Z,(trireg类型线网例外,其默认值为X)。其值由驱动源确定,如果没有驱动源则线网的值为Z
Net并不是一个关键字,它代表了一组数据类型,包括wire,wand,wor,tri,triand,trior以及trireg等。
3 寄存器
寄存器用来表示存储元件,它保持原有的数值,直到被改写。注意:不要将这里的寄存器和实际电路中由边沿触发器构成的硬件寄存器混淆。在Verilog中,术语register仅意味着一个保持数值的变量。与线网不同,寄存器不需要驱动源,而且也不像硬件寄存器那样需要时钟信号。在仿真过程中的任意时刻,寄存器的值都可以通过赋值来改变。
寄存器的数据类型通过关键字reg来声明,默认值为X。
4 向量
线网和寄存器类型的数据均可声明为向量(位宽大于1)。如果在声明中没有指定位宽,则默认为标量(1位)
wire a; //标量线网变量,默认
wire [7:0] bus; //8位的总线
reg clock ; //标量寄存器,默认
reg [0:40] virtual_addr; //向量寄存器,41位宽的虚拟地址
向量通过[high#:low#]进行说明,方括号中左边的数总是代表向量的最高有效位。
向量域选择
对于上面例子中声明的向量,我们可以指定它的某一位或者若干个相邻位。
Verilog基础——模块端口
模块
模块的定义以关键字module开始,模块名、端口列表、端口声明和可选的参数声明必须出现在其他部分的前面,endmodule语句必须为模块的最后一条语句。端口是模块与外部环境交互的通道,只有在模块有端口的情况下才需要有端口列表和端口声明。模块内部的5个组成部分是:变量声明、数据流语句、低层模块实例、行为语句块以及任务和函数。
端口
端口是模块与外界环境交互的接口。对外部环境来讲,模块内部是不可见的,对模块的调用只能通过其端口进行。这种特点为设计者提供了很大的灵活性:只要接口保持不变,模块内部的修改并不会影响到外部环境。常将端口成为终端。(terminal)
端口列表:
在模块的定义中包括一个可选的端口列表。如果模块和外部环境没有交换任何信号,则可以没有端口列表。
端口声明
端口列表中的所有端口必须在模块中进行声明,verilog中的端口具有以下三种了类型:input、output、和inout。在verilog中,所有的端口隐含地声明为wire类型,因此如果希望端口具有wire数据类型,将其声明为三种类型之一即可:如果输出类型的端口需要保存数值,则必须将其显式的声明为reg数据类型。
不能将input和inout类型的端口声明为reg数据类型,这是因为reg类型的变量是用于保存数值的,而输入端口只反映与其相连的外部信号的变化,并不能保存这些信号的值。
注意,在verilog中,也可以使用ANSI C风格进行端口声明。这种风格的声明的优点是避免了端口名在端口列表和端口声明语句中的重复。如果声明中未指明端口的数据类型,那么默认端口具有wire数据类型。
如:
module fulladd4(output reg [3:0] sum,
output reg c_out,
input [3:0] a,b,
input c_in);
……
……
endmodule
端口连接规则
将一个端口看成由相互链接的两个部分组成,一部分位于模块内部,另一部分位于模块外部。当在一个模块中调用(实例引用)另一个模块时,端口之间的连接必须遵守一些规则。
输入端口:从模块内部来讲,输入端口必须为线网数据类型,从模块外部来看,输入端口可以连接到线网或者reg数据类型的变量。
输出端口:从模块内部来讲,输出端口可以是线网或者reg数据类型,从模块外部来看,输出必须连接到线网类型的变量,而不能连接到reg类型的变量。
输入/输出端口
从模块内部来讲,输入/输出端口必须为线网数据类型;从模块外部来看,输入/输出端口也必须连接到线网类型的变量。
位宽匹配
在对模块进行调用的时候,verilog允许端口的内、外两个部分具有不同的位宽。一般情况下,verilog仿真器会对此警告。
未连接端口
Verilog允许模块实例的端口保持未连接的状态。例如,如果模块的某些输出端口只用于调试,那么这些端口可以不与外部信号连接。
端口与外部信号的连接
在对模块调用的时候,可以使用两种方法将模块定义的端口与外部环境中的信号连接起来:按顺序连接以及按名字连接。但两种方法不能混合在一起使用。
顺序端口连接:
需要连接到模块实例的信号必须与模块声明时目标端口在端口列表中的位置保持一致。
Verilog基础 门级建模
与门(and)和或门(or):
与门、或门都有一个标量输出端和多个标量输入端。门的端口列表中的第一个端口必是输出端口,其后为输入端口。当任意一个输入端口的值发生变化时,输出端的值立即重新计算。
verilog中可以使用的属于与/或门类的术语包括:
and nand or nor xor xnor
例:与门/或门的实例引用
wire OUT,IN1,IN2;
and a1(OUT,IN1,IN2); //基本门的实例引用
nand na1_3inp(OUT,IN1,IN2,IN3); //输入端超过两个,三输入与非门
and (OUT,IN1,IN2); //合法的门实例引用,不给实例命名
缓冲器/非门
与and/or门相反,buf/not门具有一个标量输入和多个标量输出。端口列表中的最后一个终端连接至输入端口,其他终端连接至输出端口。对于多个输出端的buf/not门,所有输出端的值都是相同的。
verilog提供了两种基本的门: buf not
在verilog中可以实例引用这些门,注意:buf和not门可以具有多个输出端口,但只能具有一个输入端口,这个输入端口必须是实例端口列表的最后一个。例:
buf b1(OUT1,IN); //基本门的实例引用
not n1(OUT1,IN);
buf b1_2out(OUT1,OUT2,IN); //输出端多于两个
not (OUT1,IN); //实例引用门时,不给实例命名
带控制端的缓冲器/非门(bufif/notif)
bufif1 bufif0 notif1 notif0
这四类门只有在控制信号有效的情况下才能传递数据;如果控制信号无效,则输出为高阻抗Z。
bufif1 b0(out,in,ctrl);
在控制信号有效的情况下,这些门才能传递信号。在某些情况下,例如当一个信号由多个驱动源驱动时,这样设计驱动源:让它们的控制信号的有效时间相互错开,从而避免一条信号线同时被两个源驱动,这时就需要用带控制端的缓冲器/非门来搭建电路。
门延迟
Verilog中允许用户通过门延迟来说明逻辑电路中的延迟,此外用户还可以指定端到端的延迟。
在Verilog门级原语中,有三种从输入到输出的延迟。
1 上升延迟:在门的输入发生变化的情况下,门的输出从0,x,z变化到1所需的时间成为上升延迟。
2 下降延迟:下降延迟是指门的输出从1,x,z变化到0所需的时间。
3 关断延迟:门的输出从0,1,x变化为高阻Z所需的时间。
另外,如果值变化到不确定值x,则所需的时间可以看成是以上三种延迟值中最小的那个。
Verilog中有三种不同的方法来说明门的延迟。如果用户只确定了一个延迟值,那么所有类型的延迟都是用这个延迟值,如果用户指定了两个延迟值,
则他们分别代表上升延迟和下降延迟,两者中小者为关断延迟,如果用户指定了三个延迟值,则他们分别代表上升延迟、下降延迟和关断延迟。如果为制定延迟值,
那么默认延迟值为0.
延迟声明的例子
and #(3,4,5) b1 (out,in,control); //上升延迟为3,下降延迟为4,关断延迟为5
最小/典型/最大延迟
Verilog中,用户除可以指定上面所述的三种类型的延迟以外,对每种类型的延迟还可以指定其最小值、最大值和典型值。
- 最小值 设计者预期逻辑门所具有的最小延迟
- 典型值 设计者预期逻辑门所具有的典型延迟
- 最大值 设计者预期逻辑门所具有的最大延迟
Verilog assign和always 注意事宜
assign 用于描述组合逻辑
[email protected](敏感事件列表) 用于描述时序逻辑
敏感事件 上升沿 posedge,下降沿 negedge,或电平
敏感事件列表中可以包含多个敏感事件,但不可以同时包括电平敏感事件和边沿敏感事件,也不可以同时包括同一个信号的上升沿和下降沿,这两个事件可以合并为一个电平敏感事件。
在新的verilog2001中“,”和“or”都可以用来分割敏感事件了,可以用“*”代表所有输入信号,这可以防止遗漏。
合法的写法:
[email protected] *
[email protected] (posedge clk1,negedge clk2)
[email protected] (a or b)
`timescale 100ns/100ns //定义仿真基本周期为100ns
always #1 clk=~clk //#1代表一个仿真周期即100ns
所有的assign 和 always 块都是并行发生的!
并行块、顺序块
将要并行执行的语句写在
fork
//语句并行执行
join
将要顺序执行的语句写在
begin
//语句顺序执行
end
并行块和顺序块都可以写在
initial 或 [email protected] 之后,也就是说写在块中的语句是时序逻辑的
对assign之后不能加块,实现组合逻辑只能用逐句的使用assign
组合逻辑如果不考虑门的延时的话当然可以理解为瞬时执行的,因此没有并行和顺序之分,并行和顺序是针对时序逻辑来说的。值得注意的是所有的时序块都是并行执行的。initial块只在信号进入模块后执行1次而always块是由敏感事件作为中断来触发执行的
FPAG故障一例
最近在弄FPGA LFXP2-5E的代码
在编译过程中老出现
module spd ignored due to previous errors
问题
经过几次总结一下这个故障的原因:
1.在模块中begin 与end不对应
2.语句结束时没有;或将;写成;
3.使用错误的变量名称。
verilog中reg和wire类型的区别和用法
wire表示直通,即只要输入有变化,输出马上无条件地反映;reg表示一定要有触发,输出才会反映输入。
不指定就默认为1位wire类型。专门指定出wire类型,可能是多位或为使程序易读。wire只能被assign连续赋值,reg只能在initial和always中赋值。wire使用在连续赋值语句中,而reg使用在过程赋值语句中。
在连续赋值语句中,表达式右侧的计算结果可以立即更新表达式的左侧。在理解上,相当于一个逻辑之后直接连了一条线,这个逻辑对应于表达式的右侧,而这条线 就对应于wire。在过程赋值语句中,表达式右侧的计算结果在某种条件的触发下放到一个变量当中,而这个变量可以声明成reg类型的。根据触发条件的不 同,过程赋值语句可以建模不同的硬件结构:如果这个条件是时钟的上升沿或下降沿,那么这个硬件模型就是一个触发器;如果这个条件是某一信号的高电平或低电 平,那么这个硬件模型就是一个锁存器;如果这个条件是赋值语句右侧任意操作数的变化,那么这个硬件模型就是一个组合逻辑。
输入端口可以由wire/reg驱动,但输入端口只能是wire;输出端口可以使wire/reg类型,输出端口只能驱动wire;若输出端口在过程块中 赋值则为reg型,若在过程块外赋值则为net型。用关键词inout声明一个双向端口, inout端口不能声明为reg类型,只能是wire类型;输入和双向端口不能声明为寄存器类型。
简单来说硬件描述语言有两种用途:1、仿真,2、综合。
在 设计中,输入信号一般来说你是不知道上一级是寄存器输出还是组合逻辑输出,那么对于本级来说就是一根导线,也就是wire型。而输出信号则由你自己来决定 是寄存器输出还是组合逻辑输出,wire型、reg型都可以。但一般的,整个设计的外部输出(即最顶层模块的输出),要求是寄存器输出,较稳定、扇出能力 也较好。