起因
之前的小插曲1、2、3都是讲的FPGA项目经验,这次回归基础,讲一讲FPGA的编程语言,我用的时Verilog HDL。我将从定义开始讲解Verilog 语言。
Verilog HDL语言
总体结构
(1),端口定义
module 模块名(端口1,端口2,端口3……)
例如:
module test(a,b,c)
表示该模块一共有3个端口
(2),输入输出口定义
输入端口:input [n:1]端口1,端口2,端口3……端口n
输出端口:output [n:1]端口1,端口2,端口3……端口n
其中[n:1]表示端口的位宽为n,也可以写成[n-1:0]。如果没有写位宽,则默认为1,例如
input a;
output b;
a,b默认为1位
(3),内部信号声明
例如wire a\reg c;
(4),逻辑功能实现
用assign、always语句描述,其中
assign用于给wire型变量赋值
always用于给reg型变量赋值
(5)模块例化
<模块名字> <例化后的模块名字>(端口对应);
endmoudle
概念图如下:
词法
数值表达式
Verilog的数值并不是直接写1,2,3。因为编译器默认1,2,3为十进制数,而大部分用到的都是二进制和十六进制,所以必须将数值加以区分:格式为<位宽>‘<基数><数值>。
位宽:表示数值一共有几位;
基数:表示是什么进制,b(二进制)、d(十进制)、h(十六进制)、o(八进制)
数值:表示大小,范围根据基数来定:其中x为不定态,?/z为高阻态(不用管)
基数为b:可选0.1.x.z
基数为o:可选2.3.4.5.6.7
基数为d:可选1~9
基数为h:8.9.a.b.c.d.e.f
数据类型
1.parameter:parameter定义一个标识符代表一个常量,例如:
parameter data=1,address=8'b111111111;
2.线网——wire型数据
相当于定义了一个接口,用于连线。输入输出信号默认定义为wire型,但要对其赋值,只能用assign语句。
定义举例:wire[32:1] emx1,emx2,emx3;
表示emx1,emx2,emx3均有32位
wire[8:0]a;表示a为9位变量
3.寄存器——reg型数据
也相当于一个接口,一般用于寄存器的输出
reg w1 w2;表示定义w1和w2均是1位变量
运算符
算术运算符
+、-、*、/、%表示加减乘除余,和C语言中一样
关系运算符
>=、<=、<、>表示大于等于、小于等于、小于、大于。如果为真,则返回1,如果为假,则返回0。
逻辑运算符
&&、||、!表示与或非。如果为真,则返回1,如果为假,则返回0。
位运算符
&、|、~表示按位的与或非,比如两个数1111,0000。对它们两个进行一位一位的与或非。
等式运算符
==:两个等于是对两个数进行按位比较,若有对应位不相同,则返回0。若两个数的对应位均为不定态x,则直接判错。
===:若两个数中的对应位均为x,不判错。其余用法和“==”相同。
缩减运算符
缩减运算符是单目运算符,只是在位运算符的基础上做了些更改,位运算符是两个值一位一位的运算。而缩减运算符是自身的第一位与第二位运算,结果再与第三位运算,接着再与第四位运算,以此类推。
若A=4'b1001
&A=0//只有当A的每一位都为1时才为1
|A=1//只有当A的每一位都为0时才为0
并接运算符
表示将某些信号的某几位组合在一起
output [5:0]a;
input[3:0]b;
assign ab={a,[1,b[1:0]}//表示将a的6位和b的低两位拼接在一起
a为高位,b[0]为最低位
描述语句
assign
assign sum=add1+add2
assign语句不可以重复赋值
assign sum=a-b;
assign sum=a+b;//会导致程序报错
分支语句
if-else
if()
begin
……
end
else
begin
……
end
case
case(敏感信号)
敏感信号的值:情况;
敏感信号的值:情况;
敏感信号的值:情况;
敏感信号的值:情况;
敏感信号的值:情况;
default:情况;
endcase
测试仿真——testbench代码编写
如果我们身边没有FPGA的实物,但我们又想看一下实验效果,这时就可以利用Modesim进行软件仿真,仿真的代码称为——testbench代码。代码总体结构如下:
1.时间尺度指令
`timescale 1ns/100ps
2.模块名字
module 模块名字(一般是程序代码+_top);
在testbench中不需要定义输入input和输出output端口,因为仿真代码仅仅是用来给程序设置电平信号的。
3.信号的声明和定义
在程序中如果定义的是输出reg,则在这里定义成wire;
在程序中如果定义的是输出wire,则在这里定义成reg.
这就像是32中的TX-RX、RX-TX
4.初始化
在仿真代码里面如果要模拟时钟信号、输入信号,则就在初始化里面定义
Initual begin
#5 A=1;//5个时间单位后执行A为高电平指令
#5 A=0;//5个时间单位后,执行A为低电平指令
……
end
5.定时赋值
手动设置时钟的翻转,通常是用always来实现
always #5 clk=~clk;
//每隔5个时间单位就翻转一次时钟