赞
踩
二、FPGA学习笔记(二)Verilog语法初步学习(语法篇1)
四、FPGA学习笔记(四)通过数码管学习顶层模块和例化的编写
五、FPGA学习笔记(五)Testbench(测试平台)文件编写进行Modelsim仿真
六、FPGA学习笔记(六)Modelsim单独仿真和Quartus联合仿真
七、FPGA学习笔记(七)verilog的深入学习之任务与函数(语法篇3)
FPGA学习笔记(二)Verilog语法初步学习( 语法篇1)
FPGA数字电子技术复习笔记(一)verilog语法规则补充(语法篇2)
FPGA学习笔记(七)verilog的深入学习之任务与函数(语法篇3)
作为HDL语言,有两种基本的用途:系统仿真和设计实现。所有的HDL描述都可用于仿真,但并非所有的HDL描述都可综合。
参考:FPGA学习笔记—Verilog HDL 可综合语句和不可综合语句汇总
在FPGA设计里面,我们有多种设计方式,如原理图设计方式、编写描述语言〈代码〉等方式。一开始很多工程师对原理图设计方式很钟爱,这种输入方式能够很直观的看到电路结构并快速理解,但是随着电路设计规模的不断增加,逻辑电路设计也越来越复杂,这种设计方式已经越来越不满足实际的项目需求了.这个时候Verilog 语言就取而代之了,目前Verilog已经在FPGA开发/IC设计领域占据绝对的领导地位。
标识符(identifier)用于定义模块名、端口名和信号名等。Verilog 的标识符可以是任意一组字母、数字、$和_(下划线)符号的组合,但标识符的第一个字符必须是字母或者下划线。另外,标识符是区分大小写的。
不建议大小写混合使用,普通内部信号建议全部小写,参数定义建议大写,另外信号命名最好体现信号的含义。
‘前面表示的是位数,后面表示的是进制类型以及数字。
例如下面几种等效变化:字母前面那个数字时钟指明的是二进制的位数
寄存器类型表示一个抽象的数据存储单元,它只能在 always 语句和initial语句中被赋值,并且它的值从一个赋值到另一个赋值过程中被保存下来。如果该过程语句描述的是时序逻辑,即 always 语句带有时钟信号,则该寄存器变量对应为寄存器;如果该过程语句描述的是组合逻辑,即 always语句不带有时钟信号,则该寄存器变量对应为硬件连线;寄存器类型的缺省值(默认选项)是x(未知状态)。
寄存器数据类型有很多种,如reg、integer、real等,其中最常用的就是reg类型
默认是wire类型:
线网表示Verilog 结构化元件间的物理连线。它的值由驱动元件的值决定,例如连续赋值或门的输出。如果没有驱动元件连接到线网,线网的缺省值为z(高阻态)。线网类型同寄存器类型一样也是有很多种,如tri和 wire 等,其中最常用的就是wire类型。
在verilog HDL中,参数可以重新定义:
我们再来看下参数类型,参数其实就是一个常量,常被用于定义状态机的状态、数据位宽和延迟大小等,由于它可以在编译时修改参数的值,因此它又常被用于一些参数可调的模块中,使用户在实例化模块时,可以根据需要配置参数。在定义参数时,我们可以一次定义多个参数,参数与参数之间需要用逗号隔开。这里我们需要注意的是参数的定义是局部的,只在当前模块中有效。
符号常量
大家要注意下,Verilog 实现乘除比较浪费组合逻辑资源,尤其是除法。一般2的指数次幂的乘除法使用移位运算来完成运算,详情可以看移位运算符章节。非﹖的指数次幂的乘除法一般是调用现成的IP,Quartus/Vivado等工具软件会有提供,不过这些工具软件提供的P也是由最底层的组合逻辑(与或非门等)搭建而成的。
返回值是0或者1;所有的关系运算符有着相同的优先级别,关系运算符的优先级别低于算术运算符的优先级别。
在等式运算符中还有区别:
===与!==将x/z按照普通元素看待,进行比较;
==不能识别x/z,其余位相同时,因为存在x/z位无法判断比较结果,输出x;反之,其余位不同,可以直接判断比较结果不等,输出为0;
!=不能识别x/z,其余位相同时,因为存在x/z位无法判断比较结果,输出x;反之,其余位不同,可以直接判断比较结果不等,输出为1;
还有按位同或,例如:(~a)^b
逻辑运算符一般用在条件判断上,位运算符一般用在信号赋值上。
两种移位运算符都用О来填补移出的空位。
假设a有8bit 数据位宽,那么a<<2,表示a左移2bit,a还是8bit 数据位宽,a的最高2bit 数据被移位丢弃了,最低2bit 数据固定补0。如果a是3(二进制: 00000011),那么3左移2bit,3<<2,就是12(二进制: 00001100)。一般使用左移位运算代替乘法,右移位运算代替除法,但是这种也只能表示2的指数次幂的乘除法。
这个不是循环左移,100000左移一位就变成000000了
循环左移的巧解:{}也算实时一个移位运算符
逻辑移位运算符:
Verilog中有一个特殊的运算符是c语言中没有的,就是位拼接运算符。用这个运算符可以把两个或多个信号的某些位拼接起来进行运算操作。
例子:一个典型的三八译码器
还有重复拼接操作:{2{2‘b10}}—>4’b1010
或者{2{2‘b10},3{2‘b01}}---->10‘b1010010101
慎用%和/,如果读者所使用的FPGA 有内嵌硬件乘法器或者除法器的话,那么这些乘法器和除法器就会被消耗。如果没有,资源逻辑的消耗会很大。如果是乘或除2的倍数次,可以用<<和>>代替。
下面代码是一个简易的乘法结构:,Mper 是乘数的暂存器,Mcand 是被乘数的暂存器,Sum 是累加空间。
always @ ( posedge CLK or negedge RSTn )
if( !RSTn )
begin
i <= 4'd0;
Mper <= 8'd0;
Mcand <= 8'd0;
Sum <= 8'd0;
end
else
case( i )
0:begin
Mper <= Mper_Sig;
Mcand <= Mcand_Sig;
Sum <= 8'd0;
i <=i + 1'b1;
end
1:
if( Mcand == 0 )
i <= i + 1'b1;
else begin
Sum <= Sum + Mper;
Mcand <= Mcand - 1'b1;
end
......
endcase
注释和c语言一样,有/…/ 和//两种注释方法。
常用关键字:
注意是小写的
链接: Verilog入门精简教程
适合初学的小白掌握如何编写Verilog
inout用法:见文章末尾
在同一个always中,一条阻塞赋值语句如果没有执行结束,那么该语句后面的语句就不能被执行,即被""阻塞”。也就是说always 块内的语句是一种顺序关系,这里和C语言很类似。符号“=”用于阻塞的赋值(如:b=a;)。
符号“<=”用于非阻塞赋值(如:b <= a;),非阻塞赋值是由时钟节拍决定,在时钟上升到来时,执行赋值语句右边,然后将begin-end之间的所有赋值语句同时赋值到赋值语句的左边,且一个时钟只执行一次,属于并行执行语句。
assign语句使用时不能带时钟。
always 语句可以带时钟,也可以不带时钟。在always不带时钟时,逻辑功能和assign完全一致,都是只产生组合逻辑。
这里对always的”仿顺序操作“有了新想法:
always @ ( posedge CLK or negedge RSTn ) //经典的仿单片机顺序操作模型
if( !RSTn )
begin
i <= 4'd0;
.......
end
else
case( i )
0:
.......
endcase
always@(*)则常用下面的例子
always @ ( * ) A = 4'd9; // 常数赋值
always @ ( * ) // 选择器,选择执行
if( Start_Sig[0] ) rQ = U1_Q;
else if ( Start_Sig[1] ) rQ = U2_Q;
else Q = 1'bx;
上面链接也详细讲了:
(1)forever:连续的执行语句;
(2)repaet:连续执行一条语句N次;
(3)while:执行一条语句直到某个条件不满足。如果一开始条件即不满足(为假),则语句一次也不能被执行。
(4)for通过以下3个步骤来决定语句的循环执行:
a)先给控制循环次数的变量赋初值;
b)判定控制循环的表达式的值,如果为假则跳出循环语句,如果为真则执行指定的语句后,转到第3步;
c)执行一条赋值语句来修正控制循环变量次数的变量的值,然后返回第2步。
latch是指锁存器,是一种对脉冲电平敏感的存储单元电路。锁存器和寄存器都是基本存储单元,锁存器是电平触发的存储器,寄存器是边沿触发的存储器。两者的基本功能是一样的,都可以存储数据。锁存器是组合逻辑产生的,而寄存器是在时序电路中使用,由时钟触发产生的。
latch的主要危害是会产生毛刺(glitch),这种毛刺对下一级电路是很危险的。并且其隐蔽性很强,不易查出。因此,在设计中,应尽量避免latch的使用。
代码里面出现latch 的两个原因是在组合逻辑中,if或者case语句不完整的描述,比如if缺少else分支,case缺少default分支,导致代码在综合过程中出现了latch。解决办法就是if必须带 else分支,case必须带default分支。
大家需要注意下,只有不带时钟的always语句if或者case语句不完整才会产生latch,带时钟的语句if或者case 语句不完整描述不会产生 latch。
下面为缺少else分支的带时钟的always语句和不带时钟的 always语句,通过实际产生的电路图可以看到第二个是有一个latch 的,第一个仍然是普通的带有时钟的寄存器。
组合逻辑的输出不仅取决于当前状态,还取决于输入状态
组合逻辑的输出只取决于当前状态
这里有点不懂,等以后实例操作的时候回来再看。
输入输出定义:
parameter定义:
wire/reg定义:
信号的命名:
always块描述方式:
assign块描述方法:
TAB和空格:
建议全使用空格
2022.9.29更新
高阻态的实质:电路分析时高阻态可做开路理解。你可以把它看作输出(输入)电阻非常大。他的极限可以认为悬空。感觉就是没有电流的感觉,相当开路。
在总线中的解释为:在总线连接的结构上。总线上挂有多个设备,设备于总线(这里和下面解释不同,这里相当是输出)以高阻的形式连接。这样在设备不占用总线时自动释放总线,以方便其他设备获得总线的使用权。
在lcd彩条实验中:
即在数据传输使能时,把输出的像素数据给lcd_rgb,此时是输出。而不使能时,将 lcd_rgb 的引脚方向切换成输入。代码中将高阻状态“Z”赋值给 lcd_rgb 的引脚,表示此时 lcd_rgb 的引脚电平由外围电路决定,此时可以读取 lcd_rgb 的引脚电平,从而获取到 LCD 屏的 ID。
2022.10.12更新
具体解释:
芯片外部引脚很多都使用 inout 类型的,为的是节省管腿。一般信号线用做总线等双向数据传输的时候就要用到 inout类型了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。