注释、分隔符、数字、字符串、标识符、关键字……;逻辑值集合和数据类型(线网、寄存器、向量、数字、仿真时间、数组、参数、存储器、字符串……);用于显示和监视信息、暂停和结束仿真的系统任务;宏定义、文件包含的基本编译指令
3.1词法约定
- 空白符:空格( \b )、制表符( \t )、换行符。
- 仅用于分隔标识符,编译时忽略(字符串中的空白符除外)
- 注释:单行注释( //… )、多行注释( /* … */)。
- 单行注释可以嵌套在多行注释中多行注释不可嵌套。即( /* …此处可用//… */ )。
- 操作符:单目操作符、双目操作符、三目操作符。
- 单目操作符优先级>操作数;三目操作符含两个单独操作符,用以分隔3个操作数。
a = ~ b ; // 单目操作符 ~
a = b && c ; // 双目操作符 &&
a = b ? c : d ; // 三目操作符 ?:
- 数字声明
- 指明位数的数字( <size>'<base format><number>):<size>用十进制数表示位宽;<base format>基数格式包括十进制()、十六进制()、二进制()、八进制();<number> 0~1、a/A~f/F 。
- 不指明位数的数字:不指定基数格式时默认为十进制数;默认位宽与计算机有关(最小32位)。
4'b1111 // 4位二进制数
12'habc // 12位十六进制数
16'd255 // 16位十进制数
23456 // 32位十进制数
'hc3 // 32位十六进制数
'021 // 32位八进制数
- X和Z值:不确定值X、高阻值Z。
- X和Z代表的位数:十六进制--4位;八进制--3位;二进制--1位
- 数的最高位是X、Z或0时,自动复制扩展剩余更高位;数的最高位是1时,用0扩展剩余更高位。
12'h12x // 12位十六进制数,四个最低位不确定
6'hx // 6位十六进制数,所有位都不确定
32'bz // 32位高阻值
- 负数:位宽前加负号表示常数负数;用一个可选的带符号说明符表示带符号算术运算的负数。
-6'd3 // 6位 用二进制补码形式储存的 (3)10,表示负数
-6'sd3 // 6位 用于带符号算数运算的负数
4'd-2 // 非法
- 下划线和问号:下划线( _ )可出现在数字中任何位置(第一个字符除外),提高可读性,编译时忽略;问号()同 Z,表示高阻抗,增强casez和casex语句的可读性(表示“不必关心”的情况)。
12'b1111_0000_1010 // 提高可读性
4'b10?? // 相当于 4'b10zz
- 字符串:字符串()相当于一个单字节ACSII字符队列。必须一行写完,不能包含回车符。
"a / b"
- 关键字和标识符:关键字(全小写)是Verilog中预留的定义语言结构的特殊标识符,包括关键字、系统任务和编译指令。标识符(区分大小写)是程序代码中对象的名字。
- 标识符由字母、数字字符、下划线_、美元符$组成。首字符不能是数字或$。$开始的标识符为系统函数。
reg value; // reg是关键词;value是标识符
imput clk; // input是关键字;clk是标识符
- 转义标识符:“\”开始,空白符(空格、制表符、换行符)结束。
- \...空白符之间的字符逐个处理,可为所有可打印字符,\和空白符除外。
\a+b-c //同a+b-c
\**my_name** //同**my_name**
3.2数据类型
- 四值电平逻辑:0(假)、1(真)、x(不确定)、z(高阻/浮动) 按值的级别排序
- 强度值:supply(驱动)、strong(驱动)、pull(驱动)、large(存储)、weak(驱动)、medium(存储)、small(存储)、highz(高阻) 按程度由强到弱排序
- 各类线网中,只有trireg类型可以存储强度(large、medium、small三等级)
- 强度值解决不同强度驱动源之间的赋值冲突。
- 不同强度信号驱动同一个线网,结果服从高强度信号。同强度多个信号竞争,结果为不确定值。
- 线网net:硬件单元间的连接。由连接器件输出端连续驱动。
- net不是关键词。包括wire、wand、wor、tri、triand、trior、trireg…数据类型。
- 线网用wire声明,值由驱动源决定。默认值为z(trireg类型线网默认值为x),默认位宽为1。
wire a; // 声明a是wire类型
wire b, c; // 声明b和c是wire类型
wire d = 1'b0; // 声明时赋值:逻辑值0
- 寄存器register:存储元件,被改写前保持原数值。
- Verilog的寄存器是一个保存数值的变量,不需要驱动源和时钟信号。仿真中随时可以赋值改变。
- 寄存器用reg声明,默认值为x。声明为带符号类型的变量时可用于带符号的算术运算。
reg reset; // 声明保持数值的变量reset
initial
begin
reset = 1'b1; // 初始化,使数字电路复位
#100 reset = 1'b0; // 100个时间单位后,reset置逻辑0
end
reg signed [63:0] m; // 64位带符号的值
integer i; // 32位带符号的值。integer为整数
- 向量:线网和寄存器类型数据默认位宽为1,是标量;可声明为向量(位宽大于1)。
- 向量用[high#:low#]或[low#:high#]来说明。:左边为向量的最高有效位。不能直接对整个数组赋值。
wire a; // 标量线网变量,默认
wire [7:0] bus; // 8位总线
wire [31:0] busA, busB, busC; // 3条32位宽的总线
reg clock; // 标量寄存器,默认
reg [0:40] virtual_addr; // 向量寄存器,41位宽的虚拟地址
- 向量域选择:可指定向量的某一位或若干相邻位。
busA[7] // busA的第7位
bus[2:0] // bus的最低三位
virtual_addr[0:1] // 向量virtual_addr的两个最高位
- 可变的向量域选择:可通过for循环动态选取向量的各个域。
- [<starting_bit.+ : width] 从起始位(可为变量)开始递增,位宽为width(常量)
- [<starting_bit.- : width] 从起始位(可为变量)开始递减,位宽为width(常量)
reg [255:0] data1; // data1[255]是最高有效位
reg [0:255] data2; //data2[0]是最高有效位
reg [7:0' byte;
//用变量选择向量的一部分
byte = data1[31-:8]; //从第31位算起,宽度为8位,相当于data1[31:24]
byte = data1[24+:8]; //从第24位算起,宽度为8位,相当于data1[31:24]
byte = data2[31-:8]; //从第31位算起,宽度为8位,相当于data2[31:24]
byte = data2[24+:8]; //从第31位算起,宽度为8位,相当于data2[31:24]
//起始位可以为变量,宽度必须为常数。可通过可变域选择
//用循环语句选取一个很长的向量的所有位
for (j=0; j<=31; j=j+1)
byte = data1[(j*8)+:8]; //
//初始化向量的一个域
data1[(byteNum*8)+:8] = 8'b0; // 若byteBum = 1,共有8位被清零,[15:8]
- 整数:一种通用的寄存器数据类型,用integer声明。
- 默认位宽是宿主机的位数,域具体实现有关,最小32位。
- reg类型寄存器变量为无符号数,整数类型变量为有符号数
integer counter; // 一般用途的变量没座位计数器
initial
counter = -1; // 把-1存储到计数器中
- 实数:用real声明,用十进制或科学计数法(eg. 3e6代表3000000)表示。
- 实数不带范围,默认值为0。
- 将实数赋值给整数时,取整为最接近的整数。
real delta; //定义一个实型变量delta
initial
begin
delta = 4e10; // delta被赋值40000000000
delta = 2.13; // delta被赋值2.13
end
integer i; //定义一个整型变量i
initial
i = delta; // i被赋值2
- 时间寄存器:用以保存仿真时间的寄存器数据类型。
- 时间寄存器用time来声明,宽度与具体实现有关,最小为64位。
- 调用系统函数$time得到当前的仿真时间。
time save_sim_time; //定义时间类型变量save_sim_time
initial
save_sim_time = $time; //记录当前仿真时间
- 数组:Verilog中可声明reg、integer、time、real、realtime及其向量类型的数组,任意维数均可。
- 线网数组可用于连接实例的端口,其每个元素可作为一个标量或向量。<数组名>[<下标>]。
- 对于多维数组要说明每一维的索引。
integer count[0:7]; //8个计数变量组成的数组
reg bool[31:0]; //32个boolean布尔寄存器变量组成的数组
time chk_point[1:100]; //100个时间检查变量组成的数组
reg [4:0] port_id[0:7]; //8个 位宽为5的端口标识变量组成的数组
integer matrix[4:0][0:255]; //二维整数型数组
reg [63:0] array_4d [15:0][7:0][7:0][255:0]; //四维64位寄存器型数组
wire [7:0] w_array2 [5:0]; //6个 8位线型向量的数组
wire w_array1[7:0][5:0]; //1位线型变量的二维数组
count[5] = 0; //count数组中第5个整数型单元(32位)复位
chk_point[100] = 0; //chk_point数组中的第100个时间型单元(64位)复位
port_id[3] = 0; //port_i数组中第3个寄存器型单元(5位)复位
matrix[1][0] = 33559; //数组中第1行第0列整数型单元(32位)置为33559
array_4d[0][0][0][0][15:0] = 0; //思维数组中索引号位[0][0][0][0]的寄存器型单元的0~15位置0
port_id = 0; //非法,不能写入整个数组
matrix[1] = 0; //非法,不能写入整行(matrix[1][0]~matrix[1][255])
- 注意分辨数组和线网/寄存器向量。向量是一个单独元件,位宽为n;数组是多个元件,每个元件位宽为n或1。
- 存储器:用寄存器的一维数组表示存储器。
- 每个元素称为一个元素或一个字(word),位宽为1位或多位,由一个数组索引来指定。
- n个1位寄存器不等于一个n位寄存器
reg mem1bit[0:1023]; //1K的1位存储器mem1bit
reg [7:0] membyte[0:1023]; //1K的字节(8位)存储器membyte
membyte[511] //取
出存储器membyte中地址511处存储的字节
- 参数:用关键字parameter在模块内定义常数。
- 参数代表常数,每个模块实例的参数值可在编译阶段被重载,其类型和范围可以定义(不能像变量一样赋值)。
- 通过模块实例化或使用defparam语句来改变参数值(重定义)
- 局部参数:用localparam来定义,不能改变值,不能用参数重载语句(defparam)或通过有序参数列表或命名参数赋值来直接修改。
parameter port_id = 5; //定义常数port_id为5
parameter cache_line_width =256; //定义告诉缓冲器总线宽度为常数256
parameter signed[15:0] WIDTH; //参数WIDTH有正负号,16位
localparam state1 = 4'b0001,
state2 = 4'b0010,
state3 = 4'b0100,
state4 = 4'b1000; //定义状态机的状态编码为局部参数,避免被意外修改
- 字符串:保存在reg类型的变量中,每个字符占8位(一个字节)。(寄存器变量的宽度应足够大,稍宽于字符串位长)
- 寄存器变量的宽度大于字符串的位宽,则用0填补左边的空余位。
- 寄存器变量的宽度小于字符串的位宽,则截去字符串的最左位。
reg [8*18:1] string_value; //声明18个字节的变量string_value
initial
string_value = "Hello Verilog Worlk"; //字符串可存储在变量中
- 在字符串中显示特殊字符时必须加前缀转义字符:
转义字符 | 显示内容 |
\n | 换行 |
\t | tab(制表空格) |
%% | % |
\\ | \ |
\" | " |
\ooo | 1~3个八进制数字字符 |
3.3系统任务和编译指令
$display("Hello Verilog World"); //显示:Hello Verilog World
$display($time); //显示:当前仿真时间
//在时间为200的时刻显示41位虚拟地址1fe0000001
reg [0:40] virtual_addr;
$display("At time %d virtual address is %h", $time, virtual_addr); //显示:At time 200 virtual address is 1fe0000001
//用二进制数显示port_id 5
$display("ID of the port is %b", port_id); //显示:ID of the port is 00101
//显示x字符;用二进制数显示4位总线bus的信号10xx
reg [3:0] bus;
$display("Bus value is %b", bus); //显示:Bus value is 10xx
//在名为top的最高层模块中显示在盖层被调用的实例p1的层次名
$display("This string is diaplayed from %m level of hierachy"); //显示:This string is diaplayed from top.p1 level of hierachy
//显示特殊字符:换行和%号
$diaplay("This is a \n multiline string with a %% sign"):
//显示:
//This is a
//multiline string with a % sign
格式 | 显示 |
%d或%D | 用十进制表示变量 |
%b或%B | 用二进制表示变量 |
%s或%S | 显示字符串 |
%h或%H | 用十六进制表示变量 |
%c或%C | 显示ASCII字符 |
%m或%M | 显示层次名 |
%v或%V | 显示强度 |
%o或%O | 用八进制表示变量 |
%t或%T | 显示当前时间格式 |
%e或%E | 用科学计数法格式显示实数(eg. 3e10) |
%f或%F | 用十进制浮点数格式显示实数 |
%g或%G | 用科学计数法或十进制格式显示实数(取较短的格式) |
//监视时钟和复位信号的时间和值
//时钟每5个时间单位翻转一次,复位信号10个时间单位后变低
initial
begin
$monitor($time, "Value of signals clock = %b reset = %b', clock, reset);
end
/*监视语句部分输出:
0 Value of signals clock = 0 reset = 1
5 Value of signals clock = 1 reset = 1
10 Value of signals clock = 0 reset = 0
initial
begin
clock = 0;
reset = 1;
#100 $stop;
#900 $finish;
end
//规定字长的文本宏
//用'WORD_SIZE表示
'define WORD_SIZE 32
//别名。用'S来代替$stop
'define S $stop;
//定义经常使用的字符串
'define WORD_REG reg [31:0]
//之后可用'WORD_REG 32 来定义一个32位寄存器变量
'include "header.v"
...
<design.v文件中的Verilog代码>
...
- 系统任务(系统函数):$<keyword>形式;包括屏幕显示、线网值丰台监视、暂停、结束仿真……
- 详见《IEEE Standard Verilog Hardware Description Language》
- 显示信息$display:显示变量、字符串、表达式……;
- 用法:$display(p1, p2, p3, … , pn);
- p1, p2, p3, … , pn 是双引号括起来的变量、字符串、表达式;字符串结尾自动插入换行符(参数列表位空时光标换行;x和z以“x”“z”的形式显示。
- 字符串格式说明:
- 监视信息$monitor:对信号值变化进行动态监视
- 用法:$monitor(p1, p2, p3, … , pn);
- p1, p2, p3, … , pn 是变量、信号名或双引号括起来的字符串,
- 暂停仿真$stop: 暂停仿真,进入交互模式,方便设计者检查和调试。
- 结束仿真$finish
- 编译指令:用法:'<keyword>
- 'define:定义文本宏。编译时,编译器遇到'<宏名>时,用预定义的文本宏进行替换(类似于C中的#define)。使用时加前缀'
- 'include:将一个Verilog源文件包含在另一个Verilog文件中(类似于C中的#define)。常用语将内含全局或公用定义的头文件包含在设计文件中。
- 'ifdef
- 'timescale