Verilog HDL 简介与基本语法(1)

FPGA学习 阶段一:Verilog HDL 简介与基本语法(1)

FPGA硬件开发路线学习规划:

由于想学习FPGA的相关内容,同时给数字电路打点基础,于是借助着B站上西安电子科技大学的视频开始学习Verilog HDL这门硬件描述语言,在学完语言后,将按照**《FPGA设计技巧与案例开发详解》**这本书上的相关内容在一块核心为Altera Cyclone IV的开发板上完成基本例程,之后在网上寻找相关项目学习、开发

1. Verilog的基本概念

  • Verilog是一门类C语言,语法非常接近C,将电路设计转变为程序设计
  • Verilog的代码对应的是现实中的一个硬件,这个过程叫“综合”,类似于C语言的编译
  • Verilog的代码是并行执行的

2. Verilog HDL基础知识

2.1 Verilog HDL语言要素

2.1.1 空白符

空白符包括空格符\b、制表符\t、换行符\n、和换页符\f。在编译和综合时,空白符被忽略:

1
2
3
4
5
6
7
8
9
initial begin a = 3'b100; b = 3'b010; end

equals to:

initial
begin
a = 3'b100;
b = 3'b010;
end

2.1.2 注释符

注释与C语言相同,单行注释用//,多行注释用/* */

2.1.3 标识符

标识符命名规则与C语言基本相同,只允许出现字母、数字、$符号和_ 下划线,第一个字符必须是字母或 _下划线

2.1.4 数值

  • Verilog HDL有四种基本的逻辑数值状态:
状态 含义
0 低电平、逻辑0、“假”
1 高电平、逻辑1、“真”
x或X 不确定或未知的逻辑状态
z或Z 高阻态

备注:高阻态High impedance表示电路中的某个节点具有相对电路中其他点相对更高的阻抗
电路分析时高阻态可做开路理解。可以把它看作输出(输入)电阻非常大。它的极限状态可以认为悬空(开路)。

当门电路的输出上拉管导通而下拉管截止时,输出为高电平;反之就是低电平;如上拉管和下拉管都截止时,输出端就相当于浮空(没有电流流动),其电平随外部电平高低而定,即该门电路放弃对输出端电路的控制 。

  • 整数及其表示

+/-(size>'<base_format><number>

数制 基数符号 合法标识符
二进制 b或B 0、1、x、X、z、Z、?、_
八进制 o或O 0~7、x、X、z、Z、?、_
十进制 d或D 0~9、_
十六进制 h或H 09、af、A~F、x、X、z、Z、?、_
1
2
3
4
5
6
7
8
9
10
11
//正确的表示
8'b10001101 //位宽为8位的二进制数10001101
8'ha6 //位宽为8位的十六进制数a6
5'o35 //位宽为5位的八进制数35
4'd6 //位宽为4位的十进制数6
4'b1x_01 //位宽为4位的二进制数1x01

//错误的表示
4'd-4 //数值不能为负,有负号应放在最左边
3' b001 //'和基数b之间不能有空格
(4+4)'b11 //位宽不能是表达式
  • 实数及其表示
    和C语言相同:
    a.十进制表示法,小数点两边必须都有数字
    b.科学计数法
1
2
3
4
5
6
2.7                 //十进制计数法
5.2e8 //科学计数法
3.5E-6 //e和E意义相同
5_4582.2158_5896 //使用下划线可提高可读性
6. //非法表示
.3e5 //非法表示

2.2 物理数据类型

物理数据类型有连线型、寄存器型和储存器型

2.2.1 信号强度

信号强度表示数字电路中不同强度的驱动源,用来解决不同驱动强度存在下的赋值冲突

标记符 名称 类型 强弱程度
supply 电源级驱动 驱动 最强
strong 强驱动 驱动
pull 上拉级驱动 驱动 较强
large 大容性 存储 中强
weak 弱驱动 驱动
medium 中性驱动 存储 较弱
small 小容性 存储
highz 高容性 高阻 最弱

2.2.2 连线型

连线型数据类型 功能说明
wire,tri 标准连线(缺省视为该类型)
wor,trior 多重驱动时,具有线或特性的连线型
wand,trand 多重驱动时,具有线与特性的连线型
trireg 具有电荷保持特性的连线型数据(特例)
tri1 上拉电阻
tri0 下拉电阻
supply1 电源线,用于对电源建模,为高电平1
supply0 电源线,用于对“地”建模,为低电平0
  • 单连线之间的逻辑wire和tri
wire/tri 0 1 x z
0 0 x x 0
1 x 1 x 1
x x x x x
z 0 1 x z
  • 含有线或时连线之间的逻辑
wor/trior 0 1 x z
0 0 1 x 0
1 1 1 1 1
x x 1 x x
z 0 1 x z

2.2.3 连续型数据类型的声明

<net_declar><drive_strength><range><delay><list_of_variables>;

  • net_declar:包括wire、tri等等连线型中的任意一种
  • dirve_strength:用来表示连线变量的驱动强度
  • rang:用来指定数据位标量或矢量。若该项默认,表示数据类型为1位的标量,超过1位则为矢量
  • delay:指定仿真延迟时间
  • list_of_variables:变量名称,一次可定义多个名称,用逗号分开

2.2.4 寄存器型

reg型是数据储存单元的抽象类型,其对应的硬件电路原件具有状态保持作用,能够存储数据,如触发器、锁存器等
reg型变量常用于行为级描述,由过程赋值语句对其进行赋值

1
2
3
4
5
6
7
8
//reg型变量简单例子:
reg a; //定义一个1位的名为'a'的reg变量
reg[3:0]b; //定义一个4位的名为'b'的reg变量
reg[8:1]c,d,e; 定义三个名称分别为c、d、e的8位reg型变量

//reg型变量一般为无符号数,若将一个负数赋给reg型变量,则自动转换成二进制补码形式。例如:
reg signed[3:0]rega;
rega = -2; //rega的值为1110(14),是2的补码

2.2.5 寄存器型数据类型的声明

reg<range><list_of_register_variables>;

  • range:可选项,它指定了reg型变量的位宽,缺省时为1位
  • list_of_register_variables:为变量名称列表,一次可以定义多个名称,之间用逗号分开

2.2.6 物理数据类型声明的例子

1
2
3
4
5
6
7
8
9
10
11
12
reg rega;           //定义一个1位寄存器型变量
reg[7:0] regb; //定义一个8位的寄存器型变量
tri[7:0] tribus; //定义一个8位的三态总线
tri0[15:0] busa; //定义一个16位连线型,处于三态时为下拉电阻
tri1[31:0] busb; //定义一个32位连线型,处于三态时为上拉电阻
reg scalared[1:4] b;//定义一个4位的标量型储存器矢量
wire(pull1,strong0) c = a + b; //定义一个1和0的驱动强度不同的1位连线型变量c
trireg(large) storeline; //定义一个具有大强度的电荷存储功能的存储线

/*在数字电路中,三态逻辑(英语:Three-state logic)允许输出端在0和1两种逻辑电平之外呈现高阻态,等效于将输出的影响从后级电路中移除。
这允许多个电路共同使用同一个输出线,例如总线结构
在总线连接的结构上。总线上挂有多个设备,设备与总线以高阻的形式连接。这样在设备不占用总线时自动释放总线,以方便其他设备获得总线的使用权.*/

2.2.7 存储器型

储存器型变量可以描述RAM型、ROM型储存器以及reg文件
储存器变量的一般声明格式为:
reg<range1><name_of_register><range2>

  • range1和range2都是可选项,缺省都为1
  • range1:表示存储器当中寄存器的位宽,格式为[msb:lsb]
  • range2:表示寄存器的个数,格式为[msb:lsb],即拥有msb-lsb+1个
  • name_of_registers:变量名称,一次可以定义多个名称,之间用逗号隔开
1
2
3
4
5
reg[7:0] mem1[255:0];           //定义了一个有256个8位寄存器的存储器mem1
//地址范围是0到255
reg[15:0] mem2[127:0],reg1,reg2;//定义了一个具有128个16位寄存器的储存器mem2和两个16位寄存器reg1和reg2
reg[n-1:0] a; //表示一个n位寄存器a
reg mem1[n-1:0] //表示一个由n个1位寄存器构成的存储器mem1

2.3 抽象数据类型

抽象数据类型主要包括整型integer、时间型time、实型real及参数型parameter

2.3.1 整型

integer<list—of—register—variables>;

1
2
integer index;          //简单的32位有符号整数
integer i[31:0]; //定义了整型数组,它有32个元素

2.3.2 时间型

时间型数据与整型数据相似,只是它是64位无符号数。时间型数据主要用于对模拟时间的储存与计算处理,常与系统函数$time一起使用。
声明格式:
time<list_of_register_variables>

1
time a,b;           //定义了两个64位的时间型变量

2.3.3 实数型

实数型数据在机器码表示法中是浮点型数值,可用于对延迟时间的计算
声明格式:
real<list_of_variables>

1
real stime;         //定义了一个实数型数据

####2.3.4 参数型
属于常量,在仿真开始之前就被赋值,在仿真过程中保持不变,以提高程序的可读性和维护性
声明格式:
parameter<name_of_parameter>=<value>

1
2
3
parameter length = 32,weight = 16;
parameter PI = 3.14,LOAD = 4'b1101;
parameter DELAY=(BYTE+BIT)/2;

2.3 运算符和表达式

运算符

2.3.1 算术运算符

算术运算符包含加法+;减法-;乘法*;除法/;取模%

  1. 算术操作结果的算术表达式的结果由最长的操作数决定。在赋值语句下,算术操作结果的长度由操作左端目标长度决定
1
2
3
4
reg[3:0] A,B,C;
reg[5:0] D;
A = B + C; //4位
D = B + C; //6位
  1. 有符号数和无符号数的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module arith_tb;
reg[3:0] a;
reg[2:0] b;
initial
begin
a = 4'b1111; //15
b = 3'b011; //3
$display("%b",a*b) //乘法运算,结果等于4'b1101,高位被舍去
$display("%b",a/b); //除法运算,结果为4'b0101
$display("%b",a+b); //加法运算,结果为4'b0010
$diaplay("%b",a-b); //减法运算,结果为4'b0010
$diaplay("%b",a%b); //取模运算,结果为4'b0000
end
endmodule
/*注意:二进制的减法先补全位数,使两个数同位,然后再取补码,
正二进制数的补码是它本身,
负二进制数的补码是它取反后加1*/

2.3.2 相等关系运算符

  1. 等于==、不等于!=、全等===、非全等!==
  2. 比较的结果有三种,真1、假0、不定值x
  • ==真值表
== 0 1 x z
0 1 0 x x
1 0 1 x x
x x x x x
z x x x x
  • ===真值表
=== 0 1 x z
0 1 0 0 0
1 0 1 0 0
x 0 0 1 0
z 0 0 0 1

2.3.3 条件运算符

表达形式如下:
条件表达式? 表达式1 : 表达式2
条件表达式的计算结果只有1,0x三种,结果为1时执行表达式1;结果为0时执行表达式2

1
2
3
4
5
6
7
8
module mux2(in1,in2,sel,out);
input [3:0] in1,in2;
input sel;
output [3:0] out;
reg [3:0] out;
assign out = (sel!)?in1:in2;
//sel为0时out等于in1,反之等于in2
endmodule

2.3.4 连接和复制运算符

  • 连接运算符
1
{信号1的某几位,信号2的某几位,...,信号n的某几位}
  • 复制操作符
    将一个表达式放入双重花括号中,复制因子放在第一层括号中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module con_rep_tb;
reg [2:0] a;
reg [3:0] b;
reg [7:0] c;
reg [4:0] d;
reg [5:0] e;
initial
begin
a = 3'b101;
b = 4'b1110;
c = {a,b};
d = {a[2:1],b[2:0]};//d取了a的第3位到2位与b的第3位到1位拼接
e = {2{a}};
$display("%b",c); //8'b01011110
$display("%b",d); //5'b10110
$display("%b",e); //6'b101101
end
endmodule

2.4 模块

2.4.1 模块的基本概念

  1. 模块(module)是Veril HDL语言的基本单元,它代表一个基本的功能块,用于描述某个设计的功能或结构以及与其他模块通信的外部接口
    3DOVPI.png
  2. 一个模块主要包括:
  • 模块的开始与结束:以关键词module开始,以关键词endmodule结束的一段程序,其中模块开始语句必须要以分号结束
  • 端口定义:用来定义端口列表里的变量哪些是输入(input)、输出(output)和双向端口(inout)以及位宽的说明。
  • 数据类型说明:数据类型在语言上包括wire、reg、memory和parameter等类型,用来说明模块中所用到的内部信号、调用模块等的声明语句和功能定义语句
  • 逻辑功能描述:用来产生各种逻辑(主要是组合逻和时序逻辑)。主要包括以下部分:initial语句、always语句、其它子模块实例化语句、门实例化语句、用户自定义原句(UDP)实例化语句、连续赋值语句(assign)、函数(function)和任务(task)
    例:上升沿D触发器
1
2
3
4
5
6
7
module dff(din,clk,q);
input din,clk;
output q;
reg q;
always@(posedge clk) //posedge指D触发器上升沿触发
q <= din;
endmodule

2.4.2 端口

端口的定义:
模块的端口可以是输入口input、输出端口output或双向端口inout
模块的端口声明了模块的输入输出口。其格式如下:
module 模块名(口1,口2,…)
模块的端口表示的是模块的输入和输出口名,也就是说,它与别的模块联系端口的标识。
在模块被引用时,在引用的模块中,有些信号要输入到被引用的模块中,有的信号需要从被引用的模块中取出来。在引用模块时其端口可以用两种方法连接:

  1. 在引用时严格按照模块定义的端口顺序来连接,不用标明原模块定义时规定的端口名,例如:
    模块名(连接端口1信号名,连接端口2信号名,...)
  2. 在引用时用“.”符号,标明原模块是定义时规定的端口名,例如:
    模块名(.端口1名(连接信号1名),.端口2名(连接信号2名),...)
    这样的好处在于可以用端口名与被引用模块的端口相对应,而不必严格按端口顺序对应,提高了程序的可读性和可移植性。
作者

Hank.Gan

发布于

2020-03-01

更新于

2021-08-10

许可协议

评论