verilog语法
综合:编译RTL代码,从库里选择用到的门器件,把这些器件按照“逻辑”搭建成电路
不可综合:找不到对应的器件来实现相应的代码
在设计的时候要确保所写的代码是可以综合的,
下面是不可综合或者不推荐使用的代码
代码 | 要求 |
---|---|
initial | 严禁在设计中使用,只能在测试文件中使用 |
task/function | 不推荐在设计中使用,在测试文件中可用 |
for | 在设计中、测试文件中均可使用。但在设计中多数会将其用错,所以建议在初期设计时不使用,熟练后按规范使用 |
while/repeat/forever | 严禁在设计中使用,只能在测试文件中使用,产生激励信号 |
integer(整数) | 不推荐在设计中使用 |
三态门 | 内部模块不能有三态接口,三态门只有顶层文件中才使用。三态门目的是为了节省管脚 |
casex/casez | (设计代码内部不能有X态和Z态,因此casez、casex设计时不使用)待定 |
force/wait/fork | 严禁在设计中使用,只能在测试文件中使用 |
#n(延迟多久) | 严禁在设计中使用,只能在测试文件中使用 |
推荐使用的代码及其说明
代码 | 备注 |
---|---|
reg/wire | 设计中所有的信号类型定义,只有reg和wire,always中使用reg assign中使用wire |
parameter | 设计代码中所有的位宽、长度、状态机命名等、建议都用参数表示,阅读方便并且修改容易 使用方式如下: module a (#parameter SIZE = 6,WIDTH = 8)(input .. ,output ..); endmodule |
assign/always | 程序块主要部分 组合逻辑格式为: always@(*) begin end 或者用 assign 时许逻辑格式为: always@(posedge clk or negedge rst) begin if(rst==1’b0) begin /*CODE*/ end else begin /*CODE*/ end end 时许逻辑中,敏感列表一定是clk的上升沿和复位的下降沿,最开始必须判断复位 |
if else 和case | alway里面的语句,使用if else 和case两种方法用来作选择判断,可以完成全部设计 |
算术运算符 (+、-、*、/、%) |
可以直接综合出相对应的电路。但除法和求余数运算的电路面积一般比较大,不建议直接使用除法和求余数 乘法电路是由多个加法电路实现的,乘法电路比加法电路更大 |
赋值运算符 (=,<=) |
时序逻辑用“<=”,组合逻辑用“=”(时序逻辑也可以用,但是会阻碍),其他情况不存在 |
关系运算符 (==,!=,>,<,>=,<=) |
|
逻辑运算符 (&&, |
|
位运算符 (~,|,^,&) |
|
移位运算符 (<<,>>) |
|
拼接运算符 ({}) |
模块
模块(module)是verilog的基本描述单元,模块相当于一个原理图,例化相当于制造这个模块
例化模块:推荐按名字关联,按位置关联
1 | module A(...); |
1 |
|
信号类型
verilog HDL的信号类型有很多种,主要包括两种数据类型:线网类型、寄存器类型。在进行工程设计的过程也只会使用到这两个类型的信号。
信号位宽
wire [7:0] aaa;//位宽为8
线网类型 wire
通常用assign进行赋值
如果没有定义线宽范围,缺省值为1
信号数据类型没有定义,默认wire类型
寄存器类型reg
wire和reg的区别
reg 型信号并不一定生成寄存器。如下面代码1所示;
可以这么使用,在模块中使用·always设计的信号都定义为reg型,其他信号都定义为wire型;
1 | always @(\*)begin |
综合成组合电路,对X进行设计,综合出的电路里面是没有寄存器的,但是他在always中使用,所以还是要reg定义,虽然没有生成寄存器,但是还是要使用寄存器类型,这也是语法识别出来必须这样子。
1 | ass ass1( |
代码2是例化代码,out是例化模块输出,由于顶层模块out不是always产生的,而是例化产生的,因此顶层out要定义成wire型
程序语句
描述功能语句就会使用 assign always
assign语句
assign 语句是连续赋值语句,一般是将一个变量的值不间断的赋值给另一个变量,两个变量之间就类似于被导线链接在了一起,习惯上当做连线用。
assign与assign之间是并行的,独立的
always(总是)语句
always语句是条件循环语句,执行机制是:对一个称为敏感变量表(敏感列表)发生条件满足事件后驱动程序执行
当敏感事件的条件满足时,就执行一次程序语句,敏感列表中信号,一定会包含当前整个always用到的输入信号。
当always很复杂,有很多敏感信号,可能会遗漏,为了避免这种情况可以用“*”来代替,这个“*”是指“程序语句”中所有的条件信号,如下代码,“*”代表 a、b、d、sel,不包含c
1 | always @(*) begin |
这种条件信号变化结果称为“组合逻辑”
always有时钟信号,由1变0或者由0变1的瞬间执行一下程序代码,输出才变化,其他时间输出不变的,称为“时序逻辑电路”
信号边沿触发,既信号上升沿或下降沿才变化的always,被称为“时序逻辑”
注意:识别信号是不是时钟不是看名字,而是看这个信号放在哪里 ,只有放在敏感列表并且是边沿出发的才是时钟。而信号rst是复位信号,同样也不是看名字来判断,而是放在敏感列表中并且同样边沿出发。
设计时需要注意以下几点(有待参考)
组合逻辑的always语句中敏感变量必须写全,或者用“*”代替。
组合逻辑器件的赋值采用阻塞赋值“=”,时许逻辑器件赋值语句采用非阻塞赋值“<=”,原因 阻塞赋值和非阻塞赋值
数字进制
verilog中数字表示方式,最常用的格式是:<位宽>’<基数><数值>,如 4'b1001
;
位宽:描述数值转换成二进制数后存储需要占用的位数,位宽是一个十进制的整数,例如4'b1001
中4就是位宽。如果没有该项可以通过常量值的进行推断,例如 'b10010
可推断位宽为 5。
基数:表示数值是用多少进制数显示出来。 基数的表达形式有: b、B、o、O、d、D、h、H
分别表示 二进制、十进制、八进制、十六进制。如果没有此项默认为十进制数,例如,二进制的4'b1001
可以写成十进制 4'd9
等,还可以不写基数直接写成9
。 只要底层二进制相同,无论携程十进制、八进制、十六进制都是表示同样的数字
数值:是由基数(表示用什么进制表示)所决定的表示常量真实值的一串ASCII码
如果基数定义为b或B,数值可以是0、1、x、X(不定态)、z、Z(高阻态)
如果基数定义为o或O,数值可以是0、1、2、3、4、5、6、7
如果基数定义为h或H,数值可以是0、1、2、3、4、5、6、7、9、A、B、C、D、E、F、a、b、c、d、e、f
如果基数是d或D,数值符可以是任意的十进制数0到9,但不可以是x或z
如果出现位宽小于数值,那么数据大小以位宽为准,例如:4'b001001 =>4'b1001
如果出现位宽大于数值,则高位补0 例如:32'h 12 => 32'h00000012
二进制是基础
二进制表示小数,转到其他文章
不定态
数字电路只有高电平和低电平,分别是1和0.但代码中经常能看到x和z,如1’bz。x和z是什么电平,没有实际的电平来对应两者,x和z更多地是用来表示设计者的意图或者用于仿真目的** (转换成实际电路就会变成1或0)。告诉仿真器和综合器如何解释这段代码。
X态:不定态,常用于判断条件,告诉综合工具设计者不关系它的电平是多少,0或1都可以
1 | always(posedge clk or negedge rst) begin |
1 | always(posedge clk or negedge rst) begin |
仿真过程中有些信号产生了不定态,设计者就要认真分析这个不定态是否合理。如果真的不关心是0还是1,那么可以不解决。建议所有信号都处于已知状态。可以减少思考时间
高阻态
Z态,高阻态。表示不驱动该信号(既不给0也不给1)(FPGA内部信号不会这样,内部一般都是固定值),通常用于三态门接口当中。
三态门做的接口,既做输入也做输出,是双向接口,一般硬件电路中会将该线接上一个上拉电阻(弱上拉)或下拉电阻(弱下拉)。
FPGA设计中是如何做到“不驱动”这一行为呢?FPGA内部有三态门。
在verilog中三态门代码实现
1 | assign data = (wr_en == 1)?wr_data:1'bz; |
综合器看到这两行代码则知道要合成三态门了,高阻z的作用在在于此,硬件上用三态门是为了减少管脚,而在FPGA内部没有必要减少连线,所以使用三态信号是没有意义的。
建议设计时不要在FPGA内部使用高阻态“z”,没有必要给自己添加“思考”麻烦。如果设计中使用了高阻态也不会报错,可以实现功能。
高阻态“z”表示不驱动总线,实际电路就是高电平或者低电平。
高阻态在语法上表现一个行为,硬件上有对应电路
算术运算符
分类 | 功能 | 代码 | 电路示意图 | 备注 |
---|---|---|---|---|
加法器 | 加 | always@(*) begin c = a+b; end |
逻辑运算都是有与门、或门等门电路搭建起来的电路, 1位加法器 C = A ^ B cout(进位) = A && B |
|
减法器 | 减 | always@(*) begin c = a-b; end |
C = A & B | |
乘法器 | 乘 | always@(*) begin c = a*b; end |
二进制运算中,乘法运算实质上就是加法运算, 例 1111\*111 = (1111) + (11110) + (1111000) 所以乘法器会比加法器消耗的资源多 |
|
除法器 | 除 | always@(*) begin c = a/b; end |
二进制运算中,除法和求余涉及到加法、减法和移位等运算, 所以除法和求余数电路资源都非常大,设计时要经历避免除法和求余。 一定要用到除法,尽量让除数为2的N次方,如2、4、8、16等。 |
|
a/2就是a向右移动1位 a/4就是a向右移动2位 a/8就是a向右移动3位 移位运算是不消耗资源的 |
||||
求余器 | 余数 | always@(*) begin c = a%b; end |
可以直接写的算术运算符:+
、-
、*
(对应硬件电路比较小);除法和求余就不能直接写,对应得硬件电路比较大;慎用除法
加法运算符号
verilog代码中可以直接使用符号“+”;组合逻辑
1 | assign C = A + B; |
减法运算符
1 | C = A - B; |
乘法运算符
1 | C = A *B |
多位二进制的乘法与十进制计算相同
1 | 1 1 |
求余和除法运算
没有直接的除法器,仿真测试中,是可以使用除法和求余,不用综合成电路,实际设计中,尽量不写
信号位宽
写代码时。需要注意信号的位宽,最终的结果取决于 “=” 号左边信号的位宽,保存低位,丢弃高位。
1 | wire c; |
信号c的位宽为 1 位,运算结果最终保留 1 位,c = 1'b0
;
信号d的位宽为 2 位,运算结果最终保留 2 位,d = 2'b10
;
信号e的位宽为 3 位,运算结果最终保留 3 位,e = 3'b010
;
“1”默认是32位,1 + 1 的结果也是32位,由于f的位宽只有3位,所以运算的结果可以保留低3位,f = 3'b010
;
减法运算也是相同道理
1 | wire c; |
0 - 1
得到的二进制是1111111...
,保存结果取决 ”=“ 号左边信号的位宽。
c的位宽有1位,保留低1位,c的值1'b1
;
d的位宽有2位,保留低2位,d的值2'b11
;
e的位宽有3位,保留低3位,e的值3'b111
;
f的位宽有4位,保留低4位,e的值4'b1111
;
乘法也是如此,结果取决于等号左边信号的位宽,保存低,丢弃高位。
1 | wire [4:0] h; |
2'b11 * 3'b101
值是4'b1111
h该信号有5位,4'b1111
赋给5位信号,结果高位补0,结果位5'b01111
补码由来
加法出现进位的时候,计算的结果是不正确的,只有保留了进位计算的结果才是正确的
二进制减法
1 | 、 、 |
二进制减法结果是负数结果,就要用取反加一变成正数,得到负数也是取反加1,负数的表示方式是从:1111
:-11110
:-21101
:-31100
:-41011
:-51010
:-61001
:-71000
:-810111
:-910110
:-10
逻辑运算符
类型 | 情况 | 功能 | verilog代码 | 电路示意图 | 备注 |
---|---|---|---|---|---|
与门 | 1位逻辑与 (符号:&&) |
A和B都为1, C位1,否则0 |
reg A,B; always@(*)begin end |
以后换成链接 | 注意:FPGA支持多输入的与门, 例如四输入与门,输入可为ABCD, 输出为E,当ABCD同时为1时, E为1。 |
与门 | 多位逻辑与 (符号&&) |
A或B都不为0时 C为1,否则为0; |
reg [2:0] A,B,C; always@(*) begin end 综合电路会是如下操作: A是多位,会先对A的多位 进行逻辑或,B也是如此 然后再对A和B进行与运算 |
以后换成链接 | 多位信号之间的逻辑与, 很容易引起歧义,设计最好不要用 多位数的逻辑与。 如果要实现上面功能, 建议代码改为如下: always@(*)begin end |
或门 | 1位逻辑或 (符号||) |
A和B其中1个为1, C为1;否则C为0; |
reg A,B; always@(*) begin end |
注意:FPGA支持多输入的或门 例如四输入或门,输入可为ABCD, 输出为E,当ABCD任意一个输入 为1时,E为1; |
|
或门 | 多位逻辑或 (符号:||) |
A和B其中1个非0, C为1;否则C为0; |
reg[2:0]A,B,C; always@(*)begin end |
verilog HDL语言中逻辑运算符,分别是:
&&
:逻辑与;||
:逻辑或;!
:逻辑非。
与、或:分为1位的逻辑与、或和多位的逻辑与、或;