武汉理工大学FPGA第2章FPGA设计基础汇总.docx
《武汉理工大学FPGA第2章FPGA设计基础汇总.docx》由会员分享,可在线阅读,更多相关《武汉理工大学FPGA第2章FPGA设计基础汇总.docx(38页珍藏版)》请在冰豆网上搜索。
武汉理工大学FPGA第2章FPGA设计基础汇总
第2章FPGA设计基础
2.1VerilogHDL基础知识
2.1.1概述
硬件描述语言HDL(HardwareDescriptionLanguage)是一种用形式化方法来描述数字电路和数字逻辑系统的硬件描述语言,有两种类型:
VHDL和VerilogHDL。
举个例子,在传统的设计方法中,对2输入的与门,我们可能需要到标准器件库中调出一个74系列的器件,但在硬件描述语言中,“&”就是一个与门的形式描述,“C=A&B”就是一个2输入与门的描述,而“and”就是一个与门器件。
VerilogHDL语言具有这样的描述能力:
设计的行为特性、设计的数据流特性、设计的结构组成以及包含响应监控和设计验证方面的时延和波形产生机制。
所有这些都使用同一种建模语言,可综合的Verilog模块可以构成一个可靠的复杂IP软核和固核模块。
VerilogHDL语言不仅定义了语法,而且对每个语法结构都定义了清晰的模拟、仿真语义。
因此,用这种语言编写的模型能够使用Verilog仿真器进行验证。
VerilogHDL语言从C编程语言中继承了多种操作符和结构。
VerilogHDL模型可以是实际电路的不同级别的抽象,主要指:
(1)系统级
(2)算法级(3)RTL级(4)门级(5)开关级,前三种属于行为级描述。
VHDL侧重于系统级描述,从而更多的为系统级设计人员所采用;Verilog侧重于电路级描述,从而更多的为电路级设计人员所采用。
Verilog非常容易掌握,只需有C语言编程基础,就可很快上手。
2.1.2Verilog与C语言的比较
虽然Verilog语言是从C语言过渡而来的,其某些语法与C语言接近,但存在如下几方面的本质区别:
1.Verilog是一种硬件语言,最终是为了产生实际的硬件电路或对硬件电路进行仿真;C语言是一种软件语言,是控制硬件来实现某些功能;
2.C语言只要是语法正确,都是可以编译执行的;而Verilog语言有可综合的限制,即在所有的verilog语句中,只有一部分可以被综合,而另外的部分则不能被综合,只能用来仿真;
3.C语言是一种软件编程语言,其基本思想是语句的循序执行,而Verilog语言的基本思想是模块的并行执行;
4.利用Verilog编程时,要时刻记得Verilog是硬件语言,要时刻将Verilog与硬件电路对应起来。
Verilog语言是在C语言的基础之上发展起来的,他们有着类似的语意,C语言和Verilog的很多关键字和运算符都可以对应起来。
表2-1中列出了常用的C与Verilog相对应的关键字与控制结构。
表2-1常用的C与Verilog相对应的关键字与控制结构
C
Verilog
sub-function
module,function,task
if-then-else
if-then-else
Case
Case
{,}
begin,end
For
For
While
While
Break
Disable
Define
Define
Int
Int
Printf
monitor,display,strobe
表2-2列出了C与Verilog相对应的运算符。
表2-2常用的C与Verilog相对应的运算符
C
Verilog
功能
*
*
乘
/
/
除
+
+
加
-
-
减
%
%
取模
!
!
反逻辑
&&
&&
逻辑且
||
||
逻辑或
>
>
大于
<
<
小于
>=
>=
大于等于
<=
<=
小于等于
==
==
等于
!
=
!
=
不等于
~
~
位反相
&
&
按位逻辑与
|
|
按位逻辑或
^
^
按位逻辑异或
~^
~^
按位逻辑同或
>>
>>
右移
<<
<<
左移
?
:
?
:
同等於if-else敘述
2.1.3采用自顶向下的设计方法
随着技术的发展,一个芯片上往往集成了几十万到几百万个器件,传统的自底向上的设计方法已不太现实。
因此,一个设计往往从系统级设计开始,把系统划分成几个大的基本的功能模块,每个功能模块再按一定的规则分成下一个层次的基本单元,如此一直划分下去。
自顶向下的设计方法可用图2-1所示的树状结构表示。
图2-1TOP-DOWN设计思想
通过自顶向下的设计方法,可实现设计的结构化,可使一个复杂的系统设计由多个设计者分工合作,还可以实现层次化的管理。
2.1.4模块
模块(module)是Verilog的基本描述单位,用于描述某个设计的功能或结构及与其他模块通信的外部端口。
模块在概念上可等同于一个器件,尤如调用通用器件(与门、三态门等)或通用宏单元(计数器、ALU、CPU)等。
因此,一个模块可在另一个模块中调用。
一个设计是由一个个模块(module)构成的,模块是以module开始,endmodule结束,模块包括模块的端口定义部分和逻辑功能描述部分。
模块的端口定义部分,即该模块的端口声明定义了该模块的管脚名,是该模块与其他模块通讯的外部接口,相当于器件的pin。
模块的内容,包括I/O说明,内部信号、调用模块等的声明语句和功能定义语句。
I/O说明定义了管脚信号的流向,信号的位宽(总线或单根信号线)。
功能描述用来产生各种逻辑(主要是组合逻辑和时序逻辑),还可用来实例化一个器件。
例如:
3位加法器的形成语言为
moduleaddr(a,b,cin,count,sum);//模块名及输入输出端口列表//
input[2:
0]a;//定义信号流向及位宽//
input[2:
0]b;
inputcin;
outputcount;
output[2:
0]sum;
assign{count,sum}=a+b+cin;//功能描述//
endmodule
2.1.5VerilogHDL基本语法
VerilogHDL的一些基本语法包括标识符、注释、格式、数字值集合、两种数据类型、运算符、表达式和一些基本的语句等。
1.标识符
(1)定义
标识符(identifier)用于定义模块名、端口名、信号名等。
VerilogHDL中的标识符可以是任意一组字母、数字、$符号和_(下划线)符号的组合,但标识符的第一个字符必须是字母或者下划线。
另外,标识符是区分大小写的。
以下是标识符的几个例子:
Count
COUNT//与Count不同
R56_68
FIVE$
(2)关键词
VerilogHDL定义了一系列关键词,但要注意只有小写的关键词才是保留字。
例如,标识符always(这是个关键词)与标识符ALWAYS(非关键词)是不同的。
下面是常用的关键字:
alwaysandassignbegincasedefaultdisable
edgeelseendendcaseendmoduleendfunction
endprimitiveendspecifyendtableendtaskeventfor
foreverforkfunctionifinitialinoutinputinteger
joinlargemodulenegedgenornotoroutput等等。
(3)书写规范建议
以下是一些书写规范的要求:
✧使用有意义的有效的名字如Sum、CPU_addr等;
✧用下划线区分词;
✧采用一些前缀或后缀,如时钟采用Clk前缀:
Clk_50,Clk_CPU;低电平采用_n后缀:
Enable_n;
✧统一一定的缩写,如全局复位信号Rst;
✧同一信号在不同层次保持一致性,如同一时钟信号必须在各模块保持一致;
✧自定义的标识符不能与保留字同名;
✧参数采用大写,如SIZE。
2.注释
VerilogHDL中有两种注释方式,一种是以“/*”符号开始,“*/”结束,在两个符号之间的语句都是注释语句,因此可扩展到多行。
如:
/*statement1,
statement2,
.....
statementn*/
以上n个语句都是注释语句。
另一种是以//开头的语句,它表示以//开始到本行结束都属于注释语句。
3.格式
VerilogHDL是区分大小写的,即大小写不同的标识符是不同的。
另外其书写格式自由,即一条语句可多行书写,一行可写多个语句。
空白(新行、制表符、空格)没有特殊意义。
如inputA;inputB;与
inputA;
inputB;
是一样的。
书写规范建议:
一个语句一行。
4.数字值集合
下面介绍VerilogHDL的值集合、常量(整型、实型、字符型)和变量等。
(1)值集合
VerilogHDL中规定了四种基本的值类型,这四类基本值组成的VerilogHDL中的常量。
0:
逻辑0或“假”;
1:
逻辑1或“真”;
X:
未知值;
Z:
高阻。
注意,这四种值的解释都内置于语言中。
如一个为z的值总是意味着高阻抗,一个为0的值通常是指逻辑0;在门的输入或一个表达式中为“z”的值通常解释成“x”;此外,x值和z值都是不分大小写的,也就是说,值0x1z与值0X1Z相同。
(2)常量
VerilogHDL中有三种常量:
整型、实型、字符串型。
下划线符号(_)可以随意用在整数或实数中,它们就数量本身没有意义,可用来提高易读性,唯一的限制是下划线符号不能用作为首字符。
下面主要介绍整型和字符串型。
1)整型
整型数可以按如下两种方式书写:
A.简单的十进制格式
这种形式的整数定义为带有一个可选的“+”(一元)或“-”(一元)操作符的数字序列。
下面是这种简易十进制形式整数的例子。
32十进制数32
-15十进制数-15
B.基数表示法
这种形式的整数格式为:
[size]'basevalue
size定义以位计的常量的位长;base为o或O(表示八进制),b或B(表示二进制),d或D(表示十进制),h或H(表示十六进制);value是基于base的值的数字序列,值x和z以及十六进制中的a到f不区分大小写。
下面是一些具体实例。
5'O375位八进制数(二进制11111)
4'D24位十进制数(二进制0011)
4'B1x_014位二进制数
7'Hx7位x(扩展的x),即xxxxxxx
4'hZ4位z(扩展的z),即zzzz
4'd-4非法:
数值不能为负
8'h2A在位长和字符之间,以及基数和数值之间允许出现空格
3'b001非法:
`和基数b之间不允许出现空格
(2+3)'b10非法:
位长不能够为表达式
注意:
x(或z)在十六进制值中代表4位x(或z),在八进制中代表3位x(或z),在二进制中代表1位x(或z)。
如果定义的长度比为常量指定的长度长,通常在左边填0补位。
但是如果数最左边一位为x或z,就相应地用x或z在左边补位。
例如:
10'b10左边添0占位,0000000010
10'bx0x1左边添x占位,xxxxxxx0x1
如果长度定义得更小,那么最左边的位相应地被截断。
例如:
3'b1001_0011与3'b011相等
5'H0FFF与5'H1F相等
2)字符串型
字符串是双引号内的字符序列,字符串不能分成多行书写。
例如:
"INTERNALERROR"
"REACHED->HERE"
用8位ASCII值表示的字符可看作是无符号整数,因此字符串是8位ASCII值的序列。
为存储字符串“INTERNALERROR”,变量需要8*14位。
reg[1:
8*14]Message;
...
Message="INTERNALERROR"
5.数据类型
VerilogHDL主要包括两种数据类型:
线网类型(nettype)和寄存器类型(regtype)。
(1)线网类型
1)wire和tri定义
线网类型主要有wire和tri两种。
线网类型用于对结构化器件之间的物理连线的建模,如器件的管脚,内部器件如与门的输出等。
线网类型代表的是物理连接线,因此它不存贮逻辑值,必须由器件所驱动,通常由assign进行赋值。
如assignA=B^C;当一个wire类型的信号没有被驱动时,缺省值为Z(高阻)。
信号没有定义数据类型时,缺省为wire类型。
2)两者区别
tri主要用于定义三态的线网。
(2)寄存器类型
1)定义
reg是最常用的寄存器类型,寄存器类型通常用于对存储单元的描述,如D型触发器、ROM等。
必须注意的是,reg类型的变量,不一定是存储单元,如在always语句中进行描述的变量必须用reg类型的变量。
reg范围定义是可选的,如果没有定义范围,缺省值为1位寄存器。
例如:
reg[3:
0]Sat;//Sat为4位寄存器。
regCnt;//1位寄存器。
2)寄存器类型的存储单元建模举例
用寄存器类型来构建两位D触发器如下:
reg[1:
0]Dout;
.....
always@(posedgeClk)
Dout<=Din;
....
3)书写规范建议
对数组类型,请按降序方式,如[7:
0]。
6、运算符和表达式
常用的算术运算符主要是:
加法(二元运算符):
“+”;
减法(二元运算符):
“-”;
乘法(二元运算符):
“*”;
(1)算术操作结果的位数长度
算术表达式结果的长度由最长的操作数决定。
在赋值语句下,算术操作结果的长度由操作符左端目标长度决定。
考虑如下实例:
reg[3:
0]Arc,Bar,Crt;
reg[5:
0]Frx;
...
Arc=Bar+Crt;
Frx=Bar+Crt;
第一个加的结果长度由Bar、Crt和Arc长度决定,长度为4位。
第二个加法操作的长度由Frx的长度决定(Frx、Bat和Crt中的最长长度),长度为6位。
(2)关系运算符
关系运算符有:
?
>(大于)?
<(小于)?
>=(不小于)?
<=(不大于)
==(逻辑相等)!
=(逻辑不等)
关系操作符的结果为真
(1)或假(0)。
如果操作数中有一位为X或Z,那么结果为X。
例如:
23>45结果为假(0),而:
52<8'hxFF结果为x。
如果操作数长度不同,长度较短的操作数在最重要的位方向(左方)添0补齐。
例如:
'b1000>='b01110等价于:
'b01000>='b01110结果为假(0)。
(3)逻辑运算符
逻辑运算符有:
&&(逻辑与)||(逻辑或)!
(逻辑非)
用法为:
(表达式1)逻辑运算符(表达式2)....
这些运算符在逻辑值0(假)或1(真)上操作。
逻辑运算的结果为0或1。
例如,
假定
Crd='b0;//0为假Dgs='b1;//1为真
那么
Crd&&Dgs结果为0(假)Crd||Dgs结果为1(真)!
Dgs结果为0(假)
(4)按位逻辑运算符
按位运算符有:
?
~(一元非):
(相当于非门运算)
?
&(二元与):
(相当于与门运算)
?
|(二元或):
(相当于或门运算)
?
^(二元异或):
(相当于异或门运算)
?
~^,^~(二元异或非即同或):
(相当于同或门运算)
这些操作符在输入操作数的对应位上按位操作,并产生向量结果。
例如:
假定
A='b0110;B='b0100;
那么
A|B结果为0110;A&B结果为0100
如果操作数长度不相等,长度较小的操作数在最左侧添0补位。
例如,
'b0110^'b10000与下式的操作相同:
'b00110^'b10000结果为'b10110。
(5)条件运算符
条件操作符根据条件表达式的值选择表达式,形式如下:
cond_expr?
expr1:
expr2
如果cond_expr为真(即值为1),选择expr1;如果cond_expr为假(值为0),选择expr2。
如果cond_expr为x或z,结果将是按以下逻辑expr1和expr2按位操作的值:
0与0得0,1与1得1,其余情况为x。
(6)连接运算符
连接操作是将小表达式合并形成大表达式的操作。
形式如下:
{expr1,expr2,...,exprN}
例如:
wire[7:
0]Dbus;
assignDbus[7:
4]={Dbus[0],Dbus[1],Dbus[2],Dbus[3]};
//以反转的顺序将低端4位赋给高端4位。
assignDbus={Dbus[3:
0],Dbus[7:
4]};//高4位与低4位交换。
由于非定长常数的长度未知,不允许连接非定长常数。
例如,下列式子非法:
{Dbus,5}//不允许连接操作非定长常数。
(7)条件语句
if语句的语法如下:
if(condition_1)
procedural_statement_1
{elseif(condition_2)
procedural_statement_2}
{else
procedural_statement_3}
如果对condition_1求值的结果为非零值,那么procedural_statement_1被执行,如果condition_1的值为0、x或z,那么procedural_statement_1不执行。
如果存在一个else分支,那么这个分支被执行。
例如:
if(Sum<60)
begin
Grade=C;
Total_C=Total_c+1;
end
elseif(Sum<75)
begin
Grade=B;
Total_B=Total_B+1;
end
else
begin
Grade=A;
Total_A=Total_A+1;
end
注意条件表达式必须总是被括起来。
(8)case语句
case语句是一个多路条件分支形式,其语法如下:
case(case_expr)
case_item_expr{,case_item_expr}:
procedural_statement
...
[default:
procedural_statement]
endcase
case语句首先对条件表达式case_expr求值,然后依次对各分支项求值并进行比较,第一个与条件表达式值相匹配的分支中的语句被执行。
可以在1个分支中定义多个分支项;这些值不需要互斥。
缺省分支覆盖所有没有被分支表达式覆盖的其他分支。
例如:
case(HEX)
4'b0001:
LED=7'b1111001;//1
4'b0010:
LED=7'b0100100;//2
4'b0011:
LED=7'b0110000;//3
4'b0100:
LED=7'b0011001;//4
4'b0101:
LED=7'b0010010;//5
4'b0110:
LED=7'b0000010;//6
4'b0111:
LED=7'b1111000;//7
4'b1000:
LED=7'b0000000;//8
4'b1001:
LED=7'b0010000;//9
4'b1010:
LED=7'b0001000;//A
4'b1011:
LED=7'b0000011;//B
4'b1100:
LED=7'b1000110;//C
4'b1101:
LED=7'b0100001;//D
4'b1110:
LED=7'b0000110;//E
4'b1111:
LED=7'b0001110;//F
default:
LED=7'b1000000;//0
endcase
书写建议:
case的缺省项必须写,防止产生锁存器。
7.例化语句
(1)例化语法
一个模块能够在另外一个模块中被引用,这样就建立了描述的层次。
模块例化语句形式为:
module_nameinstance_name(port_associations);
信号端口可以通过位置或名称关联,但是关联方式不能够混合使用。
端口关联形式如下:
port_expr//通过位置。
.PortName(port_expr)//通过名称。
例如:
moduleand(C,A,B);
inputA,B;
outputC;
andA1(T3,A,B);//实例化时采用位置关联,T3对应输出端口C,A对应A,B对应B。
andA2(//实例化时采用名字关联,.C是and器件的端口,其与信号T3相连)
.C(T3),
.A(A),
.B(B)
);
port_expr可以是以下的任何类型:
1)标识符(reg或net)如.C(T3),T3为wire型标识符。
2)位选择,如.C(D[0]),C端口接到D信号的第0bit位。
3)部分选择,如.Bus(Din[5:
4])。
4)上述类型的合并,如.Addr({A1,A2[1:
0]}。
5)表达式(只适用于输入端口),如.A(wireZire=0)。
注意:
在例化的端口映射中请采用名字关联,这样,当被调用的模块管脚改变时不易出错。
(2)悬空端口的处理
在实际例化中,可能有些管脚没用到,可在映射中采用空白处理,如:
DFFd1(
.Q(QS),
.Qbar(),
.Data(D),
.Preset(),//该管脚悬空
.Clock(CK)
);//名称对应方式。
对输入管脚悬空的,则该管脚输入为高阻Z,输出管脚被悬空的,该输出管脚废弃不用。
(3)不同端口长度的处理
当端口和局部端口表达式的长度不同时,端口通过无符号数的右对齐或截断方式进行匹配。
8.连续赋值语句
数据流的描述是采用连续赋值语句(assign)语句来实现的。
语法如下:
assignnet_type=表达式;
连续赋值语句用于组合逻辑的建模。
等式左边是wire类型的变量,等式右边可以是常量、由运算符如逻辑运算符、算术运算符参与的表达。
例如:
wire[3:
0]Z,Preset,Clear;//线网说明
assig