模块
模块介绍
模块是Verilog HDL语言的基本单元,数字系统是用模块的形式来描述。
模块是描述某个设计的功能、结构和其他模块通信的外部端口。
Verilog HDL中的各个模块是并行运行的
模块可以调用其他模块的实例
模块结构
1 | module <模块名>(<端口列表>) |
语句模块描述方式
Verilog有三种建模方式,分别是
结构化描述方式
数据流描述方式
行为描述方式
结构型描述
通过实例进行描述的方法,将预定义的基本原件实例嵌入语言中,监控实例的输入,一单任何一个发生变化便重新运算并输出。
数据流型描述
是一种描述组合逻辑功能的方法,用assign
连续赋值语句来实现。
连续赋值语句完成如下的组合功能:等式右边的所有变量受持续监控,每当这些变量中有任何一个发生变化,整个表达式被重新赋值并送给等式左端。
行为级描述
是通过描述行为特性来实现,关键词是always
,含义是一单敏感变量发货时能变化,就重新一次进行赋值,有无限循环之意。这种描述方法常用来实现时序电路,也可用来描述组合功能。
Tip
用户可以混用上述三种描述方法,但需要说明的是,模块中门的实例、模块实例语句、assign语句和always语句是并发执行的,即执行顺序和书写次序无关。
Verilog HDL区分大小写
Verilog HDL关键字一般为小写
其中数据流描述方式经常使用连续赋值语句,某个值被赋给某个网线变量。
assign [delay] net_name = expression;
注意在各assign 语句之间,是并行执行的,即各语句的执行与语句之间的顺序无关。
行为描述方式经常使用always、initial语句赋值。使用reg进行寄存器的声明。always是指一直在重复运行,由always后面括号的变量变化时触发。在always以及end之间是串行顺序执行的。
数据流型描述是一种描述组合逻辑功能的方法,用assign连续赋值语句来实现。
常量
数字
语法:
<位宽>'<进制><数值>
其中位宽是指对应二进制的位数
需要注意的是,尾款小于相应数值的实际位数时,相应的高位部分被忽略。4'D61
与4'B1101
相同。因为十进制下61==111101
,这里要求二进制4bit,所以是1101
.
parameter
语法:
1 | parameter 参数名1=表达式,参数名2=表达式,......; |
使用常量的目的:
便于阅读
便于修改
变量
- 网络型 nets type
指硬件电路中的各种连接,输出始终根据输入的变化更新其值的变化。
- 寄存器型 register type
常指硬件中电路中具有状态保持作用的器件,如触发器、寄存器等等。
nets type中最主要的就是wire型变量,常用来表示用assign语句赋值的组合逻辑信号。可以取值为0,1,x(不定值),z(高阻)
注意,Verilog HDL模块中的输入输出信号类型缺省时,自动定义为wire型变量。
语法:
wire 数据1,数据2,……数据n.
例子:
1 | wire a,b,c //定义了三个wire型变量 |
这里记录下register type中的reg型,常用的寄存器型变量
语法: reg 数据1,数据2,数据3……;
例子:
1 | reg a,b; |
常用语句
赋值 连续赋值语句、过程赋值语句
条件语句 if-else语句、case语句
循环语句 forever、repeat、while、for语句
结构说明语句 initial、always、task、function语句
编译预处理语句 ‘define ‘include ‘timescale语句
过程块
- always过程块
模板:
1 | always @(<敏感信号表达式>) |
当敏感信号表达式的值改变时候,就执行一遍块内语句。同时always过程块是不能够嵌套使用的。
关键字posedge
与negedge
关键字分别是上升沿以及下降沿
例如:同步时序电路的时钟信号为clk,clear为异步清零信号。敏感信号可写为:
1 | //上升沿触发,高电平清0有效 |
例如当negedge clear
表示当clear==0
时
1 | always @(posedge clk or negedge clear) |
- initial过程块
initial模板:
1 | initial |
对变量和存贮器初始化
1 | initial |
initial语句主要面向功能模拟,通常不具有可综合性。
模拟0时刻开始执行,只执行一次
同一模块内的多个initial过程块,模拟0时刻开始并行执行。
initial与always语句一样,是不能嵌套使用的。即在initial语句中不能再次嵌套initial语句块。
赋值语句
- 连续赋值语句assign常用于对wire型变量进行赋值
1 | input a,b; |
a,b信号的任何变化,都将随时反映到c上来。
- 过程赋值语句常用于对reg型变量进行赋值
一般分为两种方式:
非阻塞赋值:一条非阻塞赋值语句的执行是不会阻塞下一条语句的执行,也就是收本条非阻塞赋值语句的执行完毕之前,下一条语句也可以开始执行。非阻塞赋值语句在块结束时才一同完成赋值操作,在一块内非阻塞赋值语句并行执行。赋值符号
<=
阻塞赋值:该语句结束时就完成赋值操作,前面的语句没有完成之前,后面的语句是不能执行的,在一块内非阻塞赋值语句顺序执行。赋值符号
=
非阻塞赋值:
1 | module non_block (c, a,b,clk); |
由于是非阻塞赋值,bc在clk上升沿同时进行状态变化。所以b<=a;c<=b;
语句在同时执行,所以c的值是b上一个上升沿的值,b的值被赋值为a上一个上升沿的值。
阻塞赋值:
1 | module block (c, a,b,clk); |
顺序执行有c==b
条件语句
if-else语句块
pass
case语句
1 | case (<敏感表达式>) |
Tips
若没有列出所有条件分支,编译器认为条件不满足时,会引进触发器保持原值。
时序电路可利用上述特性来保持状态。
组合电路必须列出所有条件分支,否则会产生隐含触发器。
8bit二进制乘法器 integer
1 | module mult_for (outcome,a,b); |
task、function
task
定义格式:
1 | task<任务名> |
调用格式:
<任务名>(port1,port2,port3,......)
函数function
定义格式:
1 | function <返回值位宽或类型> <函数名> |
调用格式:
<函数名> (<输入表达式1>,<输入表达式2>,<输入表达式3>,......)
1 | module function_example(i,out); |
Verilog中函数function声明,在声明的最后一句一般都是对结果赋值。也就是对函数名赋值。如上例中,函数名gefun,在声明最后一句就是对gefun的赋值。
编译预处理
`define
`include
`timescale
编译指令以”`”反引号开头。不同于整数的单引号,预编译使用的是反引号,一般在键盘的左上角。
顺序与并行
assign语句
之间
:并行执行(同时执行)过程块
之间
(always、initial):并行执行assign语句与过程块
之间
:并行执行过程块(initial、always)内部
串行块(begin-end):顺序执行—非阻塞语句类似于并行语句
并行快(fork-join):并行执行