verilog基础
Verilog HDL基础
硬件描述语言HDL(Hardware Description Language)是硬件设计人员和电子自动化(EDA)工具之间的接口,其主要目的是用来编写设计文件,建立电子系统行为级的仿真模型。
硬件描述语言利用计算机的强大功能对用HDL建模的复杂数字逻辑进行仿真,然后自动综合以生成符合要求且再电路结构上可以实现的数字逻辑网表(Netlist),根据网表和某种工艺的器件自动生成具体电路然后生成该工艺条件下这种具体电路的延时模型。仿真验证无误后用于制造ASIC芯片或写入CPLD和FPGA器件中。
这种特殊结构能够:
1 |
|
HDL主要有两种:Verilog 和 VHDL
1 |
|
行为级的抽象:
1 |
|
对于这些,较适合使用Verilog设计;对于特大型(千万门级以上)的系统级设计,则VHDL比较合适。
Verilog的设计方法
- 自顶向下(top-down)(与模拟电路自底向上相对)

模块设计流程:
需求分析
工作人员需要对用户提出的功能要求进行分析理解,做出电路系统的整体规划,形成详细的技术指标,确定初步方案。例如,要设计一个电子屏,需要考虑供电方式、工作频率、产品体积、成本、功耗等,电路实现采用 ASIC 还是选用 FPGA/CPLD 器件等。
功能划分
正确地分析了用户的电路需求后,就可以进行逻辑功能的总体设计,设计整个电路的功能、接口和总体结构,考虑功能模块的划分和设计思路,各子模块的接口和时序(包括接口时序和内部信号的时序)等,向项目组成员合理分配子模块设计任务。
文本描述
可以用任意的文本编辑器,也可以用专用的 HDL 编辑环境,对所需求的数字电路进行设计建模,保存为 .v 文件。
功能仿真(前仿真)
对建模文件进行编译,对模型电路进行功能上的仿真验证,查找设计的错误并修正。
此时的仿真验证并没有考虑到信号的延迟等一些 timing 因素,只是验证逻辑上的正确性。
逻辑综合
综合(synthesize),就是在标准单元库和特定的设计约束的基础上,将设计的高层次描述(Verilog 建模)转换为门级网表的过程。逻辑综合的目的是产生物理电路门级结构,并在逻辑、时序上进行一定程度的优化,寻求逻辑、面积、功耗的平衡,增强电路的可测试性。
但不是所有的 Verilog 语句都是可以综合成逻辑单元的,例如时延语句。
布局布线
根据逻辑综合出的网表与约束文件,利用厂家提供的各种基本标准单元库,对门级电路进行布局布线。至此,已经将 Verilog 设计的数字电路,设计成由标准单元库组成的数字电路。
时序仿真(后仿真)
布局布线后,电路模型中已经包含了时延信息。利用在布局布线中获得的精确参数,用仿真软件验证电路的时序。单元器件的不同、布局布线方案都会给电路的时序造成影响,严重时会出现错误。出错后可能就需要重新修改 RTL(寄存器传输级描述,即 Verilog 初版描述),重复后面的步骤。这样的过程可能反复多次,直至错误完全排除。
FPGA/CPLD 下载或 ASIC 制造工艺生产
完成上面所有步骤后,就可以通过开发工具将设计的数字电路目标文件下载到 FPGA/CPLD 芯片中,然后在电路板上进行调试、验证。
如果要在 ASIC 上实现,则需要制造芯片。一般芯片制造时,也需要先在 FPGA 板卡上进行逻辑功能的验证。
基础语法
多路选择器
(没有考虑延时问题)
1 |
|

结构级描述(描述中含有传输延时)
1 |
|
三位加法器
1 |
|
两位比较器
1 |
|
三、模块
模块分为两部分:一、描述接口;二、描述逻辑功能
声明输入输出口:module 模块名(口1,口2,口3,……)
1.I/O说明:
输入口:input[信号位宽-1:0]端口名
输出口:output[信号位宽-1:0]端口名
输入/输出口:inout[信号位宽-1:0]端口名
也可以卸载端口声明中:module module_name(input port1, input port2,output port1, output port2……)
2.内部信号说明:
在模块内用到的和与端口有关的wire和 reg 类型变量的声明。
如reg[width-1:0]R变量1,R变量2……;
wire[width-1:0]W变量1,W变量2……;
3.功能定义
(1)assign
声明,如assign a = b & c;
(2)用实例元件,如and #2 ul(q,a,b);
(ul是与门的名,#2指输出延迟两个单位时间,输入a,b,输出q)
(3)always
块,如
1 |
|
注意:上述3种例子是同时执行的(并发),顺序不会影响功能的实现;但是在always模块内,逻辑是按照指定顺序执行


数据类型及其常量和变量




变量:
wire型
1
2
3wire a; //定义一个1位wire数据
wire[7:0]b; //定义一个8位wire数据
wire[4:1]c,d; //定义两个4位wire数据reg型
1
2
3reg rega; //定义一个1位reg数据
reg[7:0]regb; //定义一个8位reg数据
reg[4:1]regc,regd; //定义两个4位reg数据memory型
1
reg[7:0] mema[255,0]; //一个名为mema的存储器,有256个8位的寄存器
区别:
1
2reg[n-1:0] rega; //一个n位寄存器
reg mema [n-1:0]; //一个n个1位寄存器构成的存储器
四、运算符

Verilog特殊运算符
===
和!==

位拼接运算符
1
2
3
4
5{a,b[3:0],w,3'b101}
等于:{a,b[3],b[2],b[1],b[0],w,1'b1,1'b0,1'b1}
{4{w}} //等同于{w,w,w,w}
{b,3{a,b}} //可嵌套,等同于{b,a,b,a,b,a,b}缩减运算符
1
2
3
4
5reg[3:0] B;
reg C;
C = &B;
//相当于
C = ((B[0]&B[1])&B[2])&[3]
赋值语句
非阻塞赋值
a <= b
(1)在语句块中,上述语句所赋的变量值不能立即为下面的语句所用
(2)块结束之后才能完成本次操作,而所赋的值是上次赋值得到的
(3)在编写可综合的时序逻辑模块时,这是最常用的赋值方法
阻塞赋值
a = b
(1)赋值语句执行完后,块才结束
(2)b的值在赋值语句执行完后,立刻就能改变
(3)在时序逻辑中使用,可能会出现意想不到的效果(贬义)
简单理解
1.非阻塞赋值:
1 |
|
- 阻塞赋值:
1 |
|

块语句
顺序块
1
2
3
4
5begin
语句1;
语句2;
……
end1
2
3
4
5begin:块名
语句1;
语句2;
……
end并行块
1
2
3
4
5fork
语句1:
语句2:
……
join1
2
3
4
5fork:块名
语句1:
语句2:
……
join起始时间和结束时间
顺序块:起始时间就是第一条语句开始被执行的时间,结束时间就是最后一条语句执行完的时间
并行块:起始时间对于所有块来说是相同的,结束时间是按时间排序在最后的语句执行结束的时间。
分支与循环
if 没啥好说的
case:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15reg[15:0] rega;
reg[9:0] result;
case(rega)
16'd0: result = 10'b0111_1111_11
16'd1: result = 10'b1011_1111_11
16'd2: result = 10'b1101_1111_11
16'd3: result = 10'b1110_1111_11
16'd4: result = 10'b1111_0111_11
16'd5: result = 10'b1111_1111_11
16'd6: result = 10'b1111_1111_11
16'd7: result = 10'b1111_1111_11
16'd8: result = 10'b1111_1111_11
16'd9: result = 10'b1111_1111_11
default: result = 10'bx
endcaseforever(多用于产生周期性波形)
1
2
3
4
5
6forever 语句;
或:
forever
begin
多条语句
endrepeat(后常接常量表达式)
1
2
3
4
5
6repeat(表达式) 语句;
或:
repeat(表达式)
begin
多条语句
endwhile
1
2
3
4
5
6while(表达式) 语句;
或:
while(表达式)
begin
多条语句
endfor 没啥好说的