FPGAVerilog语法入门

Posted 独独白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FPGAVerilog语法入门相关的知识,希望对你有一定的参考价值。

学习板:ZYNQ7020

一、可编程逻辑器件

1、概念

对于常见的89C51、STM32、Arduino等等单片机来讲,内部电路连接是固定的,即内部的逻辑功能是固定不变的,要想改变它们的逻辑功能,就必须重新设计电路,改变内部各单元电路的连接。

而对于可编程逻辑器件(Programmable Logic Device)(PLD),用户可以自行修改内部连接,这种PLD内部电路结构可以通过写入编程数据来设置,当然写入后也可以擦除重写。用户可以根据自己的需求来设置内部电路的逻辑功能。

2、常见的可编程逻辑器件

常见的可编程逻辑器件有:

(1)CPLD

复杂可编程逻辑器件(Complex Programmable Logic Device)

(2)FPGA

现场可编辑门阵列(Field Programmable Gate Array)

俩者的差异本质差异在于电路结构不同

CPLD:基于乘积项的与或逻辑阵列

FPGA:基于“查找表”的CLB阵列

3、FPGA与单片机的区别

FPGA是一种可通过编程来改变其逻辑功能的数字集成电路;而对单片机编程并不能改变其电路结构。

通过FPGA编程后,生成电路,通过逻辑功能达到我们的目的;而对单片机编程后,会转换指令,单片机处理软件指令:取指、译码、执行…取指、译码、执行…一步步执行下去

4、HDL

HDL(Hardware Description Language),硬件描述语言。用于描述数字电路结构和功能的语言。

HDL 可以在不同的层次对数字电路的结构、功能和行为进行描述。它所描述的电路可以通过综合工具(如Vivado软件)将其转换为门级电路网表,然后将门级电路网表与基本元件一一对应起来,再通过布局布线工具转换为电路布线结构。

5、Verilog与C的区别

Verilog语言是硬件描述语言,编译下载到FPGA后,会生成电路,而不同模块的电路执行可以独立执行,所以可以说verilog语言是并行执行的。

C语言是软件编程语言,编译下载到单片机后,生成的是指令。单片机必须通过:取指、译码、执行…取指、译码、执行…来完成功能,所以可以理解为是串行执行。

即可以说Verilog语言是并行执行,也可以说FPGA是并行执行;既可以C语言是串行执行,也可以说单片机是串行执行。这既是Verilog与C的区别,也是FPGA与单片机的区别。

二、逻辑值

逻辑 0:表示低电平,也就是对应我们电路的 GND;
逻辑 1:表示高电平,也就是对应我们电路的 VCC;
逻辑 X:表示未知,有可能是高电平,也有可能是低电平;
逻辑 Z:表示高阻态,外部没有激励信号时是一个悬空状态。

三、符号

Verilog程序由各种符号流构成,这些符号包括:空白符(White Space)、运算符(Operator)、数字(Number)、字符串(String)、注释(Comment)、标识符(Identifier)、关键字(Key Word)等

1、空白符(White Space)

Verilog语言中的空白符包括:空格、Tab、换行符

verilog语言可以不分行,例如:

initial begin ina=3'b001;inb=3'b011;inc=3'b111;end

等效为:

initial 
    begin 
        ina=3'b001;
        inb=3'b011;
        inc=3'b111;
	end

空白符只是为了使代码错落有致,阅读起来更加方便,在代码被综合时空白符会被忽略。

2、注释(Comment)

注释与c语言一样:
(1)单行注释
以“//”开始,到本行结束

(2)多行注释
以“/*”开始,到 “ */”结束

3、标识符(Identifier)

标识符用于用户在编程时给Verilog对象、模块、端口和实例起名字

标识符由 字母数字、“$”、和"_"(下划线)组成。并且第一个字符必须是字母或者下划线。标识符最长可包含1023个字符 。

还有一种标识符为转义标识符,它与C语言的转义标识符一样,以反斜杠“\\”开头,空白符结尾。

4、关键字(Key Word)

Verilog的关键字有很多,1995年发布标准后,在2001年又进行了修订,目前最常见关键字有

关键字含义
module模块开始定义
input输入端口定义
output输出端口定义
inout双向端口定义
parameter信号的参数定义
wire线性变量定义
reg寄存器型数据定义
always产生reg信号语句的关键字
assign产生wire信号语句的关键字
begin语句的起始标志
posedge/negedge时序电路的标志
caseCase语句起始标记
defaultCase语句的默认分支标志
endcaseCase语句结束标记
if/elseif/else语句标记
forfor语句标记
end语句的结束标志
endmodule模块结束定义

5、运算符(Operator)

与C语言类似(本博八中详细总结)

四、常量

Verilog的常量主要有三种:整数、实数、字符串

1、整数(Integer)

整数的书写格式:

(+或-)(位宽)’(进制符)(值)

进制符与C语言一致:
①二进制:b或B
②八进制:o或O
③十进制:d或D
④十六进制:h或H

例如:

8'b11000111     //位宽为8的二进制数11000111
8'Hd5           //位宽为8的十六进制数d5     变为二进制:11010101
5'O23           //位宽为5的八进制数23   变为二进制:10011
5'd6            //位宽为5的十进制数6,变为二进制:00110

位宽'之间、进制数数值之间允许出现空格,格式:

(+或-)(位宽)<空格>’(进制符)<空格>(值)

例如上面的例子,中间插了空格

8 'b 11000111     //位宽为8的二进制数11000111
8 'H d5           //位宽为8的十六进制数d5     变为二进制:11010101
5 'O 23           //位宽为5的八进制数23   变为二进制:10011
5 'd 6            //位宽为5的十进制数6,变为二进制:00110

其它地方出现空格是非法的!

书写整型变量时,注意以下几条规则:

(1)较长的数值可以用下划线分开,不要求每隔4位加一个下划线,可以任意加在某几个位置,它本身没有意义,只是为了代码的可读性(注意第一位不能是下划线)。例:

16'B 1001_1011_1111_0000
13'b 1_0011_0100_1110
13'b 1_001_101_001_110

(2)如果没有定义位宽,则默认为32位,例:

'b 111   //为二进制 0000 0000 0000 0000 0000 0000 0000 0111
'd 123  //10进制数,变为二进制  0000 0000 0000 0000 0000 0000 0111 1011

(3)如果定义的位宽比数值的长度长,需要在前面补零。例:

5'b 111  //为二进制 00111
10'd 123  //为二进制 00 0111 1011

(4)如果定义的位宽比数值长度短,则左边的部分被截掉。例:

5'b 101_1111     //为二进制 11111
3'd123          //为二进制 011

(5)未知态x、高阻态z在二进制中代表1位x或z;在八进制中代表三位x或z;在16进制中代表四位x或z。例:

5'b111zz  //为二进制111zz
8'O23z    //为二进制10011zzz
18'O12z3z  //为二进制 000 001 010 zzz 011 zzz
16'Hz5Ax    //为二进制 zzzz 1001 1010 xxxx

(6)整数可以带正负号,并且正负号必须写在最前面。负数通常表示为二进制补码的形式

(7)当位宽和进制符默认时,认为是十进制、32位数。例:

32  //为十进制数32,二进制数:0000 0000 0000 0000 0000 0000 0010 0000
123 //为十进制数123 ,二进制数:0000 0000 0000 0000 0000 0000 0111 1011

(8)在位宽'之间、进制数数值之间允许出现空格,格式:

(+或-)(位宽)<空格>’(进制符)<空格>(值)

上面总结过了。

(9)在2001修订后,扩展了带符号的整数定义。例:

8'sh3a //一个十六进制带符号整数 3a

2、实数(real)

rerilog不像c语言那般,有float、double类型。对于浮点型数据,都是定义为实数(real)类型。
例:

real a,b;  
 initial begin                                            
    a=16/10;                                              
    b=16/1.1;
end

实数的表示方法有以下几种:
(1)十进制表示法

例如:

    1.0
    3.1415
    0.1		

需要注意的是小数点俩边都必须有数字(与c语言不同),如以下表示是错的:

3.
0.
.3
//三者都是错误表示法

(2)科学计数法

    12_345.56E3  //值为123456.0
    314159E-5   //值为3.14159

3、字符串(String)

字符串是双引号内的字符序列,字符串不能分成多行书写。

Verilog中采用reg类型变量来储存字符串,例如:

reg [8*12:1] stringval;                                                                                
initial begin 
  stringval="Hello world!" ;                                              
end 

4、字符串中的一类特殊字符

特殊字符含义
\\n换行
\\tTab键
\\符号\\
"符号"
\\ddd八进制数ddd的ASCII字符

例如:

\\123    //八进制数123对应的ASCII字符,即字符S

五、数据类型

Verilog中主要有两种数据类型:net型和Variable

1、net型

net型数据相当于电路中的各种物理连接,其特点是输出的值随着输入的值的变化而变化。

net型数据不能储存值,它的值由驱动它的元件所决定。net型变量有两种驱动方式:
①在结构描述中将其连接到一个门元件或模块的输出端;
②用持续赋值语句assign对其进行赋值。

如果没有驱动元件连接到ne类型的变量上,该变量的值为高阻Z(trireg除外

常用的net型变量:

类型功能可综合性
wire、 tri连线功能可综合
wor、trior具有线或线特性的多重驱动连线
wand、triand具有线或线特性的多重驱动连线
tri1、tir0分别为上拉电阻和下拉电阻
supply1、supply0分别为电源(逻辑1)和地(逻辑0)
trireg具有电荷保持作用的连线可综合

着重总结一下最常用的两种net型数据类型

(1)wire型

Verilog模块中的输入和输出信号没有明确指定数据类型时都被默认为wire型。

wire型信号可用作任何表达式的输入,也可以用作assign语句和实例元件的输出。

wire型取值可为:0、1、x、z,如果wire没有连接到驱动,其值为高阻态z。

wire型变量定义格式:

wire 数据名1,数据名2....数据名n;

例如定义两个位宽为1的wire型变量a,b:

wire  a,b;

如果定义多位宽的wire型变量,例如总线,则格式:

wire[n-1:0] 数据名1,数据名2....数据名n;

或者

wire[n:1] 数据名1,数据名2....数据名n;

例:

wire[7:0]  databus;  //定义位宽为8的数据总线
wire[15:0]  addrbus  //定义位宽为16的地址总线

wire[8:1]  databus;  //定义位宽为8的数据总线
wire[16:1]  addrbus  //定义位宽为16的地址总线

(2)tri型

tri与wire功能和使用方法完全一致。对应Verilog的综合器来讲,对tri型数据和wire型数据的处理是完全相同的。定义为tri型只是为了增加程序的可读性,可以更清楚地表示该信号综合后的电路连接具有三态的功能。

2、variable型

variable型变量必须放在过程语句中,如initial、always,通过赋值语句进行赋值。而且在initial、always等过程块内被赋值的信号必须定义为variable型。在综合器综合时,根据其被赋值的具体情况确定是映射连线还是映射成为存储元件(触发器、寄存器)

常见的variable型变量:

类型功能可综合性
reg寄存器型变量可综合
integer32位带符号整型变量可综合
real64位带符号实型变量不可综合
time64位带无符号时间变量不可综合

上表中的real、time变量是纯数学的描述,不对应具体的硬件电路,不可被综合。

time主要用于对模拟时间的存储和处理,real表示实数寄存器,主要用于仿真。

(1)reg型

reg型变量定义格式:

reg 数据1,数据2,…数据n;

如定义两个位宽为1的reg型变量 a、b:

reg a,b;

定义多位宽的reg型变量:

reg[n-1:0] 数据1,数据2,…数据n;
reg[n:1]数据1,数据2,…数据n;

例如定义位宽为8的reg型变量a:

reg[7:0] a;

reg[8:1] a;

(2)integer型

integer型变量定义与reg型变量相同,integer型变量多用于表示循环变量,用来表示循环次数等。

如下定义位宽为1的integer型变量a、b和位宽为32的integer型变量c、d:

integer a,b;
integer[31:0] c;
integer[32:1] d;

六、参数

1、参数parameter

用参数parameter来定义一个常亮,通常用来表示时延和变量的宽度,并且只能被赋值一次 。类似于C语言中的#define宏定义。

parameter型参数声明格式:

parameter 参数名1=表达式1,参数名2=表达式2,…参数名n=表达式n;

注意有等于号!与c语言的宏定义区分 。并且参数名最好用大写字母。

如:

parameter SUM=100,SIZE=100;
parameter NUM=10, SIZE=NUM*4;
parameter PI=3.14159;

2、参数localparam

localparam用于定义局部参数,作用范围仅限于本模块内,不能用于参数传递。即在实例化是不能通过层次引用来重新定义,常用于状态机参数的定义。

localparam参数的定义与parameter参数的定义相同。

七、向量

1、标量与向量

宽度为1的变量称为标量,如果在变量声明中没有指定位宽,则默认为标量。

如:

wrie a;   //线型变量a,为标量
reg clk;  //reg型标量clk,位宽为1,为标量

位宽大于1的变量称为向量,格式是[MSB:LSB],MSB表示最高有效位,LSB表示最低有效位。

如:

wire[7:0] bus;//位宽为8的wire型变量 bus,为向量
reg[7:0] a;//位宽为8的reg型变量a,最高有效位和最低有效位分别是a[7]、a[0]
reg[0:7] b;//位宽为8的reg型变量a,最高有效位和最低有效位分别是b[0]、b[7]

2、位选择和域选择

在表达式中任意选中向量的一位称为位选择,选中向量的多位称为域选择。

如:

a=A[2];//位选择  将A的位2赋值给a变量
b=B[5:3] //域选择  将B的位5:3赋值给b变量
reg[7:0] a,b;
reg[3:0] c;
reg d;
c=a[3:0]+b[7:4];//域选择
d=a[7]&b[7];	//位选择

上面代码的c=a[3:0]+b[7:4];//域选择等效为:

c[3]=a[3]+b[7];
c[2]=a[2]+b[6];
c[1]=a[1]+b[5];
c[0]=a[0]+b[4];

如果不想向量支持位选择和域选择,只作为一个统一的整体进行操作,则在定义时用关键字vectored说明,此时向量变为向量类向量

如果未做说明,则默认为标量类向量,可以进行位选择和域选择。当然也可以用关键字scalared说明 。

如:

wire vectored [7:0] databus; //向量类向量,不能进行位选择和域选择

3、储存器

储存器是由一组宽度相同的寄存器构成的阵列,定义储存器时需要定义储存器的容量和字长。

容量表示储存器储存单元的数据量
字长表示每个储存单元的数据宽度

如:

reg[5:0] mymenory[63:0];  //mymenory表示容量为64,前面的部分表示字长为6
//能存储64个数,每个数的位宽为6

八、运算符

Verilog的运算符与C语言的基本相同。

1、算术运算符

符号含义
+
-
*
/
%取模

2、逻辑运算符

符号含义
&&逻辑与
||逻辑或
!逻辑非

3、位运算符

符号含义
~按位取反
&按位与
|按位或
^按位异或
^~ 或者~^(等价)按位同或

4、关系运算符

符号含义
<小于
<=小于或等于
>大于
>=大于或等于

5、等式运算符(有个重要知识点

符号含义
==等于
!=不等于
===全等
!==不全等

相等和全等的区别:

相等比较(==)参与比较的两个操作数必须逐位相等,结果才能为1。但是如果某些位为不定态x和高阻态z,则比较的结果为不定值。

而全等比较(===)则对这些不定值x或高阻值z也进行比较,两个操作数必须完全一致,其结果才为1 。

所以等于(==)一般用于比较不含不定态的操作数,全等( ===)可用于比较含不定态的操作数。

下面是等于(==)的真值表:

等号左边\\结果\\等号右边01xz
010xx
101xx
xxxxx
zxxxx

下面是全等(===)的真值表:

等号左边\\结果\\等号右边01xz
01000
10100
x0010
z0001

例:

a=5'b 11xz0;
b=5'b 11xz0;
//a==b的值为不定值x
//a===b的值为1

6、缩减运算符

符号含义
&
~&与非
|
~|或非
^异或
^~、 ~^同或

缩减运算符与位运算符的逻辑一样,但缩减运算符是对单个操作数进行与、或、非的递推运算,它放在操作数的前面,将向量缩减为一个标量

例:

reg[3:0] a;
b=&a;  //等效为b=(a[0]&a[1]&a[2]&[3])
A=5'b11001;
//则有以下结果:
&A=0;	//1&1&0&0&1=0
~&A=1; //!(1&1&0&0&1)=1;
|A=1;	//1|1|0|0|1=1
~|A=0;  //!(1|1|0|0|1)=0
^A=1;    //1^1^0^0^1=1;
~^A=1;   //1~^1~^0~^0~^1=1;

7、移位运算符

符号含义
>>右移
<<左移
>>>算术右移
<<<算术左移

A>>n 可以把操作数A右移n位,A<<n可以把操作数A左移n位。

如:

A=6'b 101101;
//A>>2的值为6'b 001011
//A<<2的值为6'b 110100

算术左移和算术右移是为了区分带符号的操作数,有以下规则:

左移:
算术左移与逻辑左移不论操作数有无符号,两者结果都一样,都是低位补0。

右移:
操作数为正整数时,算术右移与逻辑右移一样,都是高位补零。
操作数为负整数时,算术右移高位补1,逻辑右移高位补0 。
也就是常说的:有符号算术右移,高位需依次补足符号位的数。

例:

A=6'sb 101101;  //定义6位的有符号操作数
//A<<2的值为6'b 110100
//A<<<2的值为6'b 110100
//两者一样


//A>>2的值为6'b 001011
//A>>>2的值为6'b 111011
//两者不一样,注意>>>时,符号位为1,所以高2位都要补1

8、指数运算符

指数运算符用两个星号“**”表示,一般底数为2。

例:

parameter  WIDTH=16;
parameter  DEPTH=8;
reg[WIDTH-1:0] memory[0:(2**DEPTH)-1]
//定义一个储存器,该储存器的每个元素的位宽都为16位
//可储存2^8个元素(256)

9、条件运算符

这个与c语言一样,用符号?:表示。

用法:

值=条件?表达式1:表达式2
条件成立则:值=表达式1
条件不成立则:值=表达式2

例:

integer a=1,b=2,c;
c=a>b?1:0;
//结果为c=0

10、位拼接运算符

通过两个花括号来进行位拼接,花括号里面的部分用逗号隔开用法:

信号1的某几位,信号2的某几位…信号n的某几位

例:

 ina=3'b 111;
 inb=4'b 1011;
 inc=5'b 11001;
 output[7] sum;
 sum=ina[2:0],inb[2:0],inc[0];
 //拼接后,sum=111_011_1

拼接运算符可以嵌套。例:

 ina=3'b 111;
 inb=4'b 1011;
 inc=5'b 11001;
ina,inb,inc,ina//结果:111_1011_11001_111

4ina,inb[2:0],2inc[4:3],inb[2]
//等价与:ina,ina,ina,ina,inb[2:0],inc[4:3],inb[2],inc[4:3],inb[2]
//结果为:111_111_111_111_011_11_0_11_0

可以用位拼接来实现移位操作

我们知道一个整数乘2,就是整体左移1位;乘4就是整体左移2位;除2就是整体右移1位…

注意:

不是移位运算,移位运算位数不变,这里整体左移2位,则低位补2个零,总位数+2;整体右移2位,则舍去低位的两个0(整除2,则原来数的位1:0一定都是0),可以在高位补2个0让位数保持不变,或者可以不要管,直接让位数-2 。
则有:

input[5:0] a,b;
f=a*4+a/8;
//则可以用以下方法实现:
f=a,2'b00+3'b000,a[5:3]

11、运算优先级

优先级从高到底排序:

类别运算符
单目运算符+、-、!、~ 、&、~ &、|、~ |、^、~ ^、 ^~
指数运算**
算术运算符* 、/ 、%
移位运算符<< 、 >> 、 <<< 、>>>
关系运算符<、<=、>、>=
等式运算符==、!=、 = ==、!= =
位运算符& 、|
逻辑运算符&&、||
条件运算符?:
位拼接运算符

以上是关于FPGAVerilog语法入门的主要内容,如果未能解决你的问题,请参考以下文章

FPGAVerilog语法入门

FPGAVerilog语法入门

FPGAVerilog语法入门

FPGAVerilog语法入门

FPGAverilog实现的i2c接口控制

FPGAVerilog:时序电路应用 | 序列发生器 | 序列检测器