本文目的
主要梳理之前学习Verilog的疏漏内容,系统了解Verilog语法。
Verilog的数据类型:
Verilog有两组主要的数据类型:网络数据类型(Net Data Type)和寄存器数据类型(Register Data Type)。其他的数据类型有:事件(Event)、参数(Parameter)和范围(Specparam)以及其他数据类型。
Verilog还是用强度值来解决数字电路中不同强度的驱动源之间的赋值冲突
如果两个具有不同强度的信号驱动同一个线网,则竞争结果值为高强度信号的值。
如果两个强度相同的信号之间发生竞争,则结果为不确定值。
整数、实数和时间寄存器类型
整数
是一种通用的寄存器数据类型,用于对数量进行操作,使用integer进行声明。
integer counter; //一般用途的变量用作计数器
initial
counter = -1; //把-1存储到寄存器中
实数:
实常量和实数寄存器数据类型使用关键字real来声明,可以用十进制或科学计数法
来表示。实数声明不能带有范围,其默认值为0.如果将一个实数赋予一个整数,
那么实数将会被取为最接近的整数。
real delta; //定义一个名为delta的实型变量
时间寄存器
仿真是按照仿真时间进行的,verilog使用一个特殊的时间寄存器数据类型来保存仿真时间。
时间变量通过使用关键字time来声明,其宽度与具体实现无关,最小为64位。通过调用系统
函数$time可以取得当前的仿真时间。
数组
Verilog中允许声明reg、integer、time、real、realtime及其向量类型的数组,对数组的维数没有
限制,即可声明任意维数的数组。线网数组也可用于连接实例的端口,数组中的每个元素都可以
作为一个标量或者向量,以同样的方式来使用,形如<数组名>[<下标>]。
Integer count[0:7]; //由八位计数变量组成的数组
reg bool [31:0]; //由32个1位的布尔寄存器变量组成的数组
wire [7:0] w_array2 [5:0]; //声明8位向量的数组
注意:不要把数组和线网或寄存器向量混淆起来。向量是一个单独的元件,它的位宽是n,数组由多个元件组成,其中每个元件的位宽为n或1.
nets型
wire型数据常用来表示用于以assign关键字指定的组合逻辑信号。
Verilog程序模块中输入输出信号类型默认为wire型。
wire型信号可以用做方程式的输入,也可以用做”assign”语句或者实例元件的输出
reg型
reg是寄存器数据类型的关键字。
寄存器是数据存储单元的抽象,通过赋值语句可以改变寄存器存储的值,其作用相当于改变触发器存储器的值。
reg型数据常用来表示always模块内的指定信号,代表触发器。
通常在设计中要由always模块通过使用行为描述语句来表达逻辑关系。在always块内被赋值的每一个信号都必须定义为reg型,即赋值操作符的右端变量必须是reg型
reg型号数据的格式:
reg[n-1:0]数据名1,数据名2,…,数据N;
//定义了N个寄存器变量,每个数据位宽为n
reg型数据的默认值是不定的。reg型数据可以为正值或负值。当一个reg型数据是一个表达式中的操作数时,它的值被当做无符号值,即正值(如果一个4位的reg型数据被写入-1,在表达式中运算时,其值被认为是+15)
reg型和wire型的区别:reg型保持最后一次的赋值,而wire型需要持续驱动。
Memory型
尽管memory型数据和reg型数据的定义格式很相似,但是要注意不同之处。Memory描绘的是深度,reg描绘的是宽度。
Reg [ n-1:0 ] rega; //一个n位寄存器
Reg mema [ n-1 ] //一个由n个一位寄存器组成的存储器组
一个n位的寄存器可以在一条赋值语句里进行赋值。而一个完整的存储器不行
Rega = 0;//合法的赋值语句
Mema=0;//非法的赋值语句
Mema[3]=0;//合法,给memory中的第三个单元赋值为0
数组
Verilog中允许声明reg、integer、time、real、realtime及其向量类型的数组,对数组的维数没有限制,即可声明任意维数的数组。线网数组也可用于连接实例的端口,数组中的每个元素都可以作为一个标量或者向量,以同样的方式来使用,形如<数组名>[<下标>]。
Integer count[0:7]; //由八位计数变量组成的数组
reg bool [31:0]; //由32个1位的布尔寄存器变量组成的数组
wire [7:0] w_array2 [5:0]; //声明8位向量的数组
=注意:不要把数组和线网或寄存器向量混淆起来。向量是一个单独的元件,它的位宽是n,数组由多个元件组成,其中每个元件的位宽为n或1.==
向量
线网和寄存器类型的数据均可声明为向量(位宽大于1)。如果在声明中没有指定位宽,则默认为标量(1位)
wire a; //标量线网变量,默认
wire [7:0] bus; //8位的总线
reg clock ; //标量寄存器,默认
reg [0:40] virtual_addr; //向量寄存器,41位宽的虚拟地址
向量通过[high#:low#]进行说明,方括号中左边的数总是代表向量的最高有效位。
可变向量域选择
Verilog的系统任务
系统任务也属于行为级建模,系统任务的调用要出现在initial与always结构中。所有的任务都已$开头。
1、 d i s p l a y , display, display,write用于信息的显示和输出。
%b或%B | 二进制 |
---|---|
%o或%O | 八进制 |
%d或%D | 十进制 |
%h或%H | 十六进制 |
%e或%E | 实数 |
%c或%C | 字符 |
%s或%S | 字符串 |
%v或%V | 信号强度 |
%t或%T | 时间 |
%m或%M | 层次实例 |
— | === |
---|---|
\n | 换行 |
\t | 制表符 |
\ | 反斜杠\ |
" | 引号” |
%% | 百分号% |
调用方式:eg:$display("%b+%b=%b",a,b,sum);
$write("%b+%b=%b",a,b,sum);
注:如果没有在指定变量的显示格式,不会输出数值。如果没有指定变量显示的位置,变量值会在字符串部分之后直接显示出来,变量之间是没有间隔的,只是一次简单的显示。
显示任务 d i s p l a y 默 认 显 示 的 格 式 是 十 进 制 的 , 还 有 display默认显示的格式是十进制的,还有 display默认显示的格式是十进制的,还有displayb, d i s p l a y o , displayo, displayo,displaybh的显示格式分别是二进制,八进制,十六进制。同理有 w r i t e , write, write,writeo, w r i t e b , writeb, writeb,writeh。
d i s p l a y 与 display与 display与write的区别是: d i s p l a y 会 在 每 次 显 示 信 息 后 自 动 换 行 , display会在每次显示信息后自动换行, display会在每次显示信息后自动换行,write不会换行.
2、$strobe探测任务
探测任务的语法和显示任务完全相同,也是把信息显示出来。也有 s t r o b e , strobe, strobe,strobeb, s t r o b e o , strobeo, strobeo,strobeh四种。
两者的区别在于: s t r o b e 命 令 会 在 当 前 时 间 部 结 束 时 完 成 ; 而 strobe命令会在当前时间部结束时完成;而 strobe命令会在当前时间部结束时完成;而display是只要仿真器看到就会立即执行。
3、$monitor监测任务
监测任务用于持续监测指定变量,只要这些变量发生了变化,就会立即显示对应的输出语句,并且在仿真中只能进行一次调用,后面的调用会覆盖前面的。
eg:
initial begin
$monitor("x=%b,y=%b,cin=%b",x,y,cin);
end
同理,有 m o n i t o r , monitor, monitor,monitorb m o n i t o r o monitoro monitoromonitorh。
可用$monitoroff,monitoeron关闭监事和打开监视。
4、 s t o p , stop, stop,finish仿真控制任务
区别: s t o p 暂 停 当 前 方 针 , stop暂停当前方针, stop暂停当前方针,finish终止值当前方针。
`define:作用 -> 常用于定义常量可以跨模块、跨文件;
范围 -> 整个工程;
parameter: 作用 -> 常用于模块间参数传递;
范围 -> 本module内有效的定义;
localparam 作用 -> 常用于状态机的参数定义;
范围 -> 本module内有效的定义,不可用于参数传递;
localparam cannot be used within the module port parameter list.
2、应用举例
`define、parameter、localparam三者的区别及举例
`define
概念:可以跨模块的定义,写在模块名称上面,在整个设计工程都有效。
一旦`define指令被编译,其在整个编译过程中都有效。例如,通
过另一个文件中的`define指令,定义的常量可以被其他文件调用,
直到遇到 `undef;
举例:定义 `define UART_CNT 10'd1024
使用 `UART_CNT
parameter
概念:本module内有效的定义,可用于参数传递;
如果在模块内部定义时无法进行参数传递,
若在模块名后照下面这样写则可以进行传递
举例:定义
module video_in
#(
parameter MEM_DATA_BITS = 64,
parameter INTERLACE = 1 // 0
)
(
input clk,
input rst_n,
output burst_finsh
);
使用 -> 调用此模块的时候可以像端口信号传递一样进行参数传递
video_in
#( .MEM_DATA_BITS ( 64 ),
.INTERLACE ( 1 )
)
u_video_in (
.clk (clk_50m),
.rst_n (rst_n),
.burst_finsh (burst_finsh)
);
localparam:
概念:本module内有效的定义,不可用于参数传递;
localparamcannot be used within the module port parameter list.
一般情况下,状态机的参数都是用localparam的。
举例:
localparam BURST_LEN = 10'd64; /*一次写操作数据长度 */
localparam BURST_IDLE = 3'd0; /*状态机状态:空闲 */
localparam BURST_ONE_LINE_START = 3'd1; /*状态机状态:视频数据一行写开始 */
localparam BURSTING = 3'd2; /*状态机状态:正在处理一次ddr2写操作 */
localparam BURST_END = 3'd3; /*状态机状态:一次ddr2写操作完成*/
localparam BURST_ONE_LINE_END = 3'd4; /*状态机状态:视频数据一行写完成*/
reg[2:0] burst_state = 3'd0; /*状态机状态:当前状态 */
reg[2:0] burst_state_next = 3'd0; /*状态机状态:下一个状态*/