Verilog语言学习初步—基本概念

  1. 词法约定

module <name>(端口列表);
a
b
c
end module 
//ji既可以进行行为级描述,也可以进行结构描述
行为或算法级  数据流级  门级  开关级
Verilog允许使用四种不同层次对各个模块进行描述。在经过综合工具综合后,综合结果一般是门级结构描述。
Verilog允许在一个模块中混用多个抽象层次
实例化  实例instance    每一个实例的名字必须 是唯一的
一个模块声明中不允许嵌套模块
模块间调用是通过实例引用来完成的

完成测试功能的块称为激励块   测试台testbench
激励快设计两种模式
①激励块中调用(实例引用)并直接驱动设计块
②在一个虚拟顶层模块中实例引用激励块和设计块。激励块和设计块之间使用接口进行交互   顶层模块作用只是实例引用设计块和激励块


基本语法
1.空白符 \b  \t 和换行符
2.注释  ① //单行注释
②/*多行
    注释*/
     多行注释不允许嵌套,但是单行注释可以嵌套在多行注释中。
3.操作符
单目   a=~b;         //~ 是单目操作符
双目   a=b && c     //&& 是双目操作符
三目   a=b?c:c;    //?: 是三目操作符

4.数字声明
①<size位宽度>'<base format基数格式><number>
十进制 'd或'D      二进制  'b或'B
八进制  'O        十六进制   'H

4'B1111
12'habc
16'd255

②不指明位数的数字  没有指定基数默认十进制数;没有指定位宽度默认位宽度与仿真器和使用的计算机有关(最小32位)
'o21  //这是一个32位的八进制数

③不确定值 x      高阻值 z

④负数
-6'd3  //这是一个6位的用二进制补码形式存储的十进制数3,表示负数

⑤下划线符号和问号
下划线符号_ 除了第一个字符,可以出现在数字中任何位置,作用只是提高可读性
Verilog语言约定的常数中,问号? 是z 的另一种表示  
问号增强casex和casez语句可读性 这两条语句中问号表示 /*不必关心*/的意思

⑥字符串   由双引号括起来的一个字符队列,必须在一行中书写完,不能书写多行中,即不能包含回车符
Verilog将字符串当成一个单字节的ACSII字符队列
  "a/bhello"  //← 这是一个字符串
module <name>(端口列表);
a
b
c
end module 
//ji既可以进行行为级描述,也可以进行结构描述
行为或算法级  数据流级  门级  开关级
Verilog允许使用四种不同层次对各个模块进行描述。在经过综合工具综合后,综合结果一般是门级结构描述。
Verilog允许在一个模块中混用多个抽象层次
实例化  实例instance    每一个实例的名字必须 是唯一的
一个模块声明中不允许嵌套模块
模块间调用是通过实例引用来完成的

完成测试功能的块称为激励块   测试台testbench
激励快设计两种模式
①激励块中调用(实例引用)并直接驱动设计块
②在一个虚拟顶层模块中实例引用激励块和设计块。激励块和设计块之间使用接口进行交互   顶层模块作用只是实例引用设计块和激励块


基本语法
1.空白符 \b  \t 和换行符
2.注释  ① //单行注释
②/*多行
    注释*/
     多行注释不允许嵌套,但是单行注释可以嵌套在多行注释中。
3.操作符
单目   a=~b;         //~ 是单目操作符
双目   a=b && c     //&& 是双目操作符
三目   a=b?c:c;    //?: 是三目操作符

4.数字声明
①<size位宽度>'<base format基数格式><number>
十进制 'd或'D      二进制  'b或'B
八进制  'O        十六进制   'H

4'B1111
12'habc
16'd255

②不指明位数的数字  没有指定基数默认十进制数;没有指定位宽度默认位宽度与仿真器和使用的计算机有关(最小32位)
'o21  //这是一个32位的八进制数

③不确定值 x      高阻值 z

④负数
-6'd3  //这是一个6位的用二进制补码形式存储的十进制数3,表示负数

⑤下划线符号和问号
下划线符号_ 除了第一个字符,可以出现在数字中任何位置,作用只是提高可读性
Verilog语言约定的常数中,问号? 是z 的另一种表示  
问号增强casex和casez语句可读性 这两条语句中问号表示 /*不必关心*/的意思

3.1.5字符串   由双引号括起来的一个字符队列,必须在一行中书写完,不能书写多行中,即不能包含回车符
Verilog将字符串当成一个单字节的ACSII字符队列
  "a/bhello"  //← 这是一个字符串
  
3.1.6标识符和关键字
Verilog中关键字全部小写,而标识符是区分大小写。
标识符是程序代码中对象的名字,使用标识符来访问对象。
Verilog中标识符由字母 数字 下划线_ 美元字符$组成;
Verilog标识符的第一个字符必须是字母字符或下划线,不能以数字或美元符开始
    以美元符开始的是为系统函数保留的
3.1.7转义标识符
转义标识符以反斜线\开始,空白符(空格、制表符和换行符)结束
Verilog将反斜线和空白符间的字符逐个处理 所有可打印字符均可包含在转义字符中,而反斜线和(表示结束的)空白符不作为标识符的一部分。
    \**my_name**     //若作为标识符则与**my_name**等同

3.2数据类型
四值逻辑 八种信号强度
//  0假   1真   x不定   z高阻

/*八种信号强度
supply       驱动      最强
strong       驱动
pull         驱动
large        存储
weak         驱动
medium       存储
small        存储
highz        高阻      最弱 */
    若有两个不同信号强度的信号驱动同一个线网,则竞争结果值为高强度信号的值;
    若有两个相同信号强度的信号驱动同一个线网,则竞争结果值为不确定值
各种类型的线网中,只有trireg类型的线网可以具有存储强度,强度分为large,medium和small三个等级
3.2.2线网
线网net表示硬件单元之间的连接。线网由其连接器件的输出端连续驱动。
线网一般使用关键字wire进行声明。若没有显式地说明为向量,则默认线网位宽为1.
线网的值由驱动源决定,没有驱动源则线网的值为z。
线网的默认值为z(trireg类型的线网例外,其默认值为x)
注意,net并不是一个关键字,它代表了一组数据类型,包括wire,wand,wor,tri,triand,trior,trireg等,其中wire类型线网最为常用。

3.2.3寄存器  P21 verilog HDL本科教学版
寄存器用来表示存储元件,它保持原有的数值,直到被改写。
//注意,这里的寄存器与实际数字电路的硬件寄存器不要混淆
Verilog中术语register仅仅意味着一个保存数值的变量。
与线网不同,寄存器不需要驱动源,而且也不想硬件寄存器那样需要时钟信号。
仿真过程中的任意时刻,寄存器的值都可以通过赋值来改变。
寄存器数据类型一般通过使用关键字reg来声明。

①寄存器的声明和使用
    reg reset;  //声明能保持数值的变量reset 
    initial 
    begin 
    reset = 1'b1; //把reset初始化为1,使数字电路复位
    #100 reset = 1'b0; //经过100个时间单位后,reset置逻辑0
    end 
②带符号寄存器声明
寄存器也可以声明为带符号(signed)类型的变量,这样寄存器就可以用于带符号的算术运算。
    reg signed[63:0] m;  //64位带符号的值
    integer i;           //32位带符号的值

3.2.4向量
线网和寄存器都可以声明为向量(位宽大于1)
若在声明中没有指定位宽,则默认为标量(1位)
向量通过[hign:low]或[low:high]进行说明,方括号中左边的数总是代表向量的最高有效位
 reg [0:40] virtual_addr;  //向量寄存器,41位宽虚拟地址 其中向量virtual_addr的最高有效位是它的第0位
向量域选择 
      virtual_addr[0:1]  //向量virtual_addr的两个最高位
Verilog HDL还允许指定可变的向量域选择。下面是动态域选择的两个专用操作符
    ①[<starting_bit>+:width]  从起始位开始递增,位宽为width
    ②[<starting_bit>+:width]  从起始位开始递减,位宽为width
/*起始位可以是一个变量,但是位宽必须是一个常量。*/

    reg [255:0] data1;   //datal[255]是最高位
    reg [0:255] data2;   //datal[0]是最高位
    reg [7:0] byte;
    //用变量选择向量的一部分
    byte = datal[31-:8];  //从第31位算起,宽度为8位,相当于datal[31:24]
   byte = datal[24+:8];  //从第24位算起,宽度为8位,相当于datal[31:24]
    byte = data2[31-:8];  //从第31位算起,宽度为8位,相当于datal[24:31]
    byte = data2[24+:8];  //从第24位算起,宽度为8位,相当于datal[24:31]
    //起始位可以是变量,但位宽必须是常数。因此可以通过可变域选择。
    //用循环语句选取一个很长的向量的所有位
    for (j=0;j<=31;j=j+1)
      byte = datal[(j*8)+:8];  //
      datal[(byteNum*8)+:8];   //如果byteNum = 1,共有8位被清零,[15:8]
3.2.5整数、实数和时间寄存器数据类型
①整数integer
整数默认位宽为宿主机的字的位数,与具体实现有关,但最小应为32位。
reg类型的寄存器变量为无符号数,而整数类型的变量则为有符号数

②实数real 
实常量和实数寄存器数据类型使用关键字real来声明,可以用十进制或科学计数法来表示。
实数声明不能带有范围,其默认值为0.若将一个实数赋给一个整数,那么实数将被取整为最接近的整数。
    real delta;  //定义实型变量delta
    initial
    begin
        delta = 4e10;//delta被赋值,用科学计数法表示
         delta = 2.13;//delta被赋值为2.13
    end 
    integer i;     //定义整型变量i
    initial
        i = delta;  // i被赋值为2
        
③时间寄存器time 
 仿真是按照仿真时间来进行的,Verilog使用一个特殊的时间寄存器数据类型来保存仿真时间
 时间变量通过关键字time来声明,其宽度与具体实现有关,最小为64位。
 通过调用系统函数$time可以得到当前的仿真时间。例如:
    time save_sim_time;      //定义时间类型变量save_sim_time
    initial
        save_sim_time = $time; //把当前仿真时间记录下来
        
3.2.6数组
Verilog中允许声明reg,integer,time,real,realtime及其向量类型的数组,可以声明任意维数的数组
线网数组也可用于连接实例的端口,数组中的每个元素都可以作为一个标量或向量形如<数组名>[<下标>]
对于多维数组来讲,用户需要说明其每一维的索引:
    integer count[0:7]; //由8个计数变量组成的数组
    reg bool[31:0]; //由32个1位布尔(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];//声明8位向量的数组
    wire w_array1[7:0][5:0];//声明1位线型变量的二维数组
/*注意,不要将数组和线网或寄存器向量混淆起来。向量是一个单独元件,位宽为n;
数组由多个元件组成,其中的每一个元件的位宽为n或1*/
对数组元素的赋值:
    count[5] = 0;  //把count数组中的第5个整数型单元(32位)复位
    chk_point[100] = 0; //把chk_point数组中的第100个时间型单元(64位)复位
    port_id[3] = 0; //把port_id数组中的第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;  //非法,企图写数组的整个第2行

3.2.7存储器
Verilog中使用寄存器的一维数组来表示存储器,数组的每个元素称为一个元素或一个字(word),
由一个数组索引来指定,每个字的位宽为1位或多位。
如果需要访问存储器中的一个特定的字,则可以通过将字的地址作为数组的下标来完成。
    reg memlbit[0:1023];       //1K的1位存储器memlbit
    reg [7:0] membyte[0:1023]; //1K的字节(8位)存储器membyte
    membyte[511]               //取出membyte中地址511处所存的字节

3.2.8参数
Verilog允许使用关键字parameter在模块内定义常数。
参数代表常数,不能像变量那样赋值,但是每个模块实例的参数值可以在编译阶段被重载。
通过重载使得用户可以对模块实例进行定制
 parameter port_id = 5;
 parameter cache_line_width = 56;
 parameter signed [15:0] WIDTH;
 用户不但可以根据参数来定义模块,还可以通过参数值重定义来改变模块的行为:
 通过模块实例化或使用defparam语句改变参数值。在参数定义时注意避免使用硬编码。
 Verilog中的局部参数使用关键字localparam来定义,其作用等同于参数,区别在于
 它的值不能改变,不能通过参数重载语句(defparam)或通过有序参数列表或命名参数赋值来直接修改。
 例如,状态机的状态编码是不能被修改的,为了避免被意外更改,应当将其定义为局部参数。
 
3.2.9字符串
字符串保存在reg类型的变量中,每个字符占用8位(一个字节)因此寄存器变量的宽度应足够大,以保证容纳全部字符。
若寄存器变量的宽度大于字符串的大小(位)则Verilog使用0来填充左边的空余位;
若寄存器变量的宽度小于字符串的大小(位)则Verilog截去字符串最左边的位
因此声明保存字符串的reg变量时,其位宽应当比字符串的位长稍大。
    reg [8*18:1] string_value;  //声明变量string_value,其宽度为18个字节
    initial
        string_value = "hello verilog world"; //字符串可以存储在变量中
        
\n     换行
\t     tab指标空格
%%     %
\\     \
\"     "
\ooo   1到3个八进制数字字符

3.3系统任务和编译指令
3.3.1系统任务
所有的系统任务都具有$<keyword>的形式。
①显示信息 P26
$display是用于显示变量、字符串或表达式的主要系统任务。
用法:$display(p1,p2,p3,...,pn);
其中,p1,p2,p3,...,pn是双引号括起来的字符串、变量或表达式,$display会自动在字符串的结尾处
插入一个换行符,因此如果参数列表为空,则$display的效果是显示光标移到下一行。

②监视信息
通过系统函数$monitor,Verilog为用户提供了信号值变化进行动态监视的手段。
用法:$monitor(p1,p2,p3,...,pn);
其中,参数p1,p2,p3,...,pn可以是变量、信号名或双引号括起来的字符串。
系统函数$monitor对其参数列表中的变量值或信号值进行不间断的监视,当其中任何一个发生变化的时候,显示所有参数的数值。
$monitor只需要调用一次即可在整个仿真过程中生效。正由于$monitor在整个仿真过程中有效,因此在任意仿真时刻只有一个监视列表有效;
如果用户在源描述中调用多个$monitor,则只有最后一个调用生效,前面的调用会被覆盖。
Verilog还提供两个控制监视的系统任务:$monitoron $monitoroff
仿真开始时仿真器的默认状态是允许监视,调用$monitoroff暂停监视;调用$monitoron允许监视任务的执行。
在$monitor中使用系统函数$time来获取系统仿真时间。
系统任务$stop用于暂停仿真   系统任务$finish用于结束仿真
    //仿真时刻为100单位时暂停仿真,检查结果
    //仿真时刻为1000单位时结束仿真
    initial
    begin
    clock = 0;
    reset = 1;
    #100 $stop;
    #900 $finish;
    end
    
3.3.2编译指令  注意这里的符号不是单引号,而是键盘左上角的撇号
①`define
编译指令`define用于定义Verilog中的文本宏。在编译阶段,当编译器遇到`<宏名>时,使用预定义的文本进行替换,
类似于C语言中的#define结构。在使用预定义的常数或文本宏时,在宏名前加上前缀名"`" ----键盘左上角的撇号
    //规定字长的文本宏
    //在代码中使用`word_size表示
    `define word_size 32
    
    `define s $stop;  //定义别名,可以用`s来代替 $stop
    `define word_reg reg32
  //就可以用`word_reg reg32 来定义一个32位的寄存器变量
②`include
可以在编译期间将一个Verilog源文件包含在另一个Verilog文件中,作用类似C语言中的#include结构。
该指令通常用于将内含全局或公用定义的头文件包含在设计文件中
    //包含header.v文件,在该文件中有主Verilog文件design.v需要的内容
    `include header.v  //译者注,原文错,文件名两边忘加双引号
    ...
    <design.v>文件中的Verilog代码
    ...
    
③`ifdef  `timescale  见第9章

第4章模块和端口
4.1 模块
模块定义以关键字module开始,而模块名、端口列表、端口声明和可选参数声明必须出现在其他部分的前面,
并且endmodule语句必须为模块的最后一句。端口是模块与外部环境交互的通道,只有在模块有端口的情况下
才需要有端口列表和端口声明。
模块内部的5个组成部分是:变量声明、数据流语句、低层模块实例、行为语句块以及任务和函数
module <name>(端口列表);
a
b
c
end module 
//ji既可以进行行为级描述,也可以进行结构描述
行为或算法级  数据流级  门级  开关级
Verilog允许使用四种不同层次对各个模块进行描述。在经过综合工具综合后,综合结果一般是门级结构描述。
Verilog允许在一个模块中混用多个抽象层次
实例化  实例instance    每一个实例的名字必须 是唯一的
一个模块声明中不允许嵌套模块
模块间调用是通过实例引用来完成的

完成测试功能的块称为激励块   测试台testbench
激励快设计两种模式
①激励块中调用(实例引用)并直接驱动设计块
②在一个虚拟顶层模块中实例引用激励块和设计块。激励块和设计块之间使用接口进行交互   顶层模块作用只是实例引用设计块和激励块


基本语法
1.空白符 \b  \t 和换行符
2.注释  ① //单行注释
②/*多行
    注释*/
     多行注释不允许嵌套,但是单行注释可以嵌套在多行注释中。
3.操作符
单目   a=~b;         //~ 是单目操作符
双目   a=b && c     //&& 是双目操作符
三目   a=b?c:c;    //?: 是三目操作符

4.数字声明
①<size位宽度>'<base format基数格式><number>
十进制 'd或'D      二进制  'b或'B
八进制  'O        十六进制   'H

4'B1111
12'habc
16'd255

②不指明位数的数字  没有指定基数默认十进制数;没有指定位宽度默认位宽度与仿真器和使用的计算机有关(最小32位)
'o21  //这是一个32位的八进制数

③不确定值 x      高阻值 z

④负数
-6'd3  //这是一个6位的用二进制补码形式存储的十进制数3,表示负数

⑤下划线符号和问号
下划线符号_ 除了第一个字符,可以出现在数字中任何位置,作用只是提高可读性
Verilog语言约定的常数中,问号? 是z 的另一种表示  
问号增强casex和casez语句可读性 这两条语句中问号表示 /*不必关心*/的意思

3.1.5字符串   由双引号括起来的一个字符队列,必须在一行中书写完,不能书写多行中,即不能包含回车符
Verilog将字符串当成一个单字节的ACSII字符队列
  "a/bhello"  //← 这是一个字符串
  
3.1.6标识符和关键字
Verilog中关键字全部小写,而标识符是区分大小写。
标识符是程序代码中对象的名字,使用标识符来访问对象。
Verilog中标识符由字母 数字 下划线_ 美元字符$组成;
Verilog标识符的第一个字符必须是字母字符或下划线,不能以数字或美元符开始
    以美元符开始的是为系统函数保留的
3.1.7转义标识符
转义标识符以反斜线\开始,空白符(空格、制表符和换行符)结束
Verilog将反斜线和空白符间的字符逐个处理 所有可打印字符均可包含在转义字符中,而反斜线和(表示结束的)空白符不作为标识符的一部分。
    \**my_name**     //若作为标识符则与**my_name**等同

3.2数据类型
四值逻辑 八种信号强度
//  0假   1真   x不定   z高阻

/*八种信号强度
supply       驱动      最强
strong       驱动
pull         驱动
large        存储
weak         驱动
medium       存储
small        存储
highz        高阻      最弱 */
    若有两个不同信号强度的信号驱动同一个线网,则竞争结果值为高强度信号的值;
    若有两个相同信号强度的信号驱动同一个线网,则竞争结果值为不确定值
各种类型的线网中,只有trireg类型的线网可以具有存储强度,强度分为large,medium和small三个等级
3.2.2线网
线网net表示硬件单元之间的连接。线网由其连接器件的输出端连续驱动。
线网一般使用关键字wire进行声明。若没有显式地说明为向量,则默认线网位宽为1.
线网的值由驱动源决定,没有驱动源则线网的值为z。
线网的默认值为z(trireg类型的线网例外,其默认值为x)
注意,net并不是一个关键字,它代表了一组数据类型,包括wire,wand,wor,tri,triand,trior,trireg等,其中wire类型线网最为常用。

3.2.3寄存器  P21 verilog HDL本科教学版
寄存器用来表示存储元件,它保持原有的数值,直到被改写。
//注意,这里的寄存器与实际数字电路的硬件寄存器不要混淆
Verilog中术语register仅仅意味着一个保存数值的变量。
与线网不同,寄存器不需要驱动源,而且也不想硬件寄存器那样需要时钟信号。
仿真过程中的任意时刻,寄存器的值都可以通过赋值来改变。
寄存器数据类型一般通过使用关键字reg来声明。

①寄存器的声明和使用
    reg reset;  //声明能保持数值的变量reset 
    initial 
    begin 
    reset = 1'b1; //把reset初始化为1,使数字电路复位
    #100 reset = 1'b0; //经过100个时间单位后,reset置逻辑0
    end 
②带符号寄存器声明
寄存器也可以声明为带符号(signed)类型的变量,这样寄存器就可以用于带符号的算术运算。
    reg signed[63:0] m;  //64位带符号的值
    integer i;           //32位带符号的值

3.2.4向量
线网和寄存器都可以声明为向量(位宽大于1)
若在声明中没有指定位宽,则默认为标量(1位)
向量通过[hign:low]或[low:high]进行说明,方括号中左边的数总是代表向量的最高有效位
 reg [0:40] virtual_addr;  //向量寄存器,41位宽虚拟地址 其中向量virtual_addr的最高有效位是它的第0位
向量域选择 
      virtual_addr[0:1]  //向量virtual_addr的两个最高位
Verilog HDL还允许指定可变的向量域选择。下面是动态域选择的两个专用操作符
    ①[<starting_bit>+:width]  从起始位开始递增,位宽为width
    ②[<starting_bit>+:width]  从起始位开始递减,位宽为width
/*起始位可以是一个变量,但是位宽必须是一个常量。*/

    reg [255:0] data1;   //datal[255]是最高位
    reg [0:255] data2;   //datal[0]是最高位
    reg [7:0] byte;
    //用变量选择向量的一部分
    byte = datal[31-:8];  //从第31位算起,宽度为8位,相当于datal[31:24]
   byte = datal[24+:8];  //从第24位算起,宽度为8位,相当于datal[31:24]
    byte = data2[31-:8];  //从第31位算起,宽度为8位,相当于datal[24:31]
    byte = data2[24+:8];  //从第24位算起,宽度为8位,相当于datal[24:31]
    //起始位可以是变量,但位宽必须是常数。因此可以通过可变域选择。
    //用循环语句选取一个很长的向量的所有位
    for (j=0;j<=31;j=j+1)
      byte = datal[(j*8)+:8];  //
      datal[(byteNum*8)+:8];   //如果byteNum = 1,共有8位被清零,[15:8]
3.2.5整数、实数和时间寄存器数据类型
①整数integer
整数默认位宽为宿主机的字的位数,与具体实现有关,但最小应为32位。
reg类型的寄存器变量为无符号数,而整数类型的变量则为有符号数

②实数real 
实常量和实数寄存器数据类型使用关键字real来声明,可以用十进制或科学计数法来表示。
实数声明不能带有范围,其默认值为0.若将一个实数赋给一个整数,那么实数将被取整为最接近的整数。
    real delta;  //定义实型变量delta
    initial
    begin
        delta = 4e10;//delta被赋值,用科学计数法表示
         delta = 2.13;//delta被赋值为2.13
    end 
    integer i;     //定义整型变量i
    initial
        i = delta;  // i被赋值为2
        
③时间寄存器time 
 仿真是按照仿真时间来进行的,Verilog使用一个特殊的时间寄存器数据类型来保存仿真时间
 时间变量通过关键字time来声明,其宽度与具体实现有关,最小为64位。
 通过调用系统函数$time可以得到当前的仿真时间。例如:
    time save_sim_time;      //定义时间类型变量save_sim_time
    initial
        save_sim_time = $time; //把当前仿真时间记录下来
        
3.2.6数组
Verilog中允许声明reg,integer,time,real,realtime及其向量类型的数组,可以声明任意维数的数组
线网数组也可用于连接实例的端口,数组中的每个元素都可以作为一个标量或向量形如<数组名>[<下标>]
对于多维数组来讲,用户需要说明其每一维的索引:
    integer count[0:7]; //由8个计数变量组成的数组
    reg bool[31:0]; //由32个1位布尔(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];//声明8位向量的数组
    wire w_array1[7:0][5:0];//声明1位线型变量的二维数组
/*注意,不要将数组和线网或寄存器向量混淆起来。向量是一个单独元件,位宽为n;
数组由多个元件组成,其中的每一个元件的位宽为n或1*/
对数组元素的赋值:
    count[5] = 0;  //把count数组中的第5个整数型单元(32位)复位
    chk_point[100] = 0; //把chk_point数组中的第100个时间型单元(64位)复位
    port_id[3] = 0; //把port_id数组中的第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;  //非法,企图写数组的整个第2行

3.2.7存储器
Verilog中使用寄存器的一维数组来表示存储器,数组的每个元素称为一个元素或一个字(word),
由一个数组索引来指定,每个字的位宽为1位或多位。
如果需要访问存储器中的一个特定的字,则可以通过将字的地址作为数组的下标来完成。
    reg memlbit[0:1023];       //1K的1位存储器memlbit
    reg [7:0] membyte[0:1023]; //1K的字节(8位)存储器membyte
    membyte[511]               //取出membyte中地址511处所存的字节

3.2.8参数
Verilog允许使用关键字parameter在模块内定义常数。
参数代表常数,不能像变量那样赋值,但是每个模块实例的参数值可以在编译阶段被重载。
通过重载使得用户可以对模块实例进行定制
 parameter port_id = 5;
 parameter cache_line_width = 56;
 parameter signed [15:0] WIDTH;
 用户不但可以根据参数来定义模块,还可以通过参数值重定义来改变模块的行为:
 通过模块实例化或使用defparam语句改变参数值。在参数定义时注意避免使用硬编码。
 Verilog中的局部参数使用关键字localparam来定义,其作用等同于参数,区别在于
 它的值不能改变,不能通过参数重载语句(defparam)或通过有序参数列表或命名参数赋值来直接修改。
 例如,状态机的状态编码是不能被修改的,为了避免被意外更改,应当将其定义为局部参数。
 
3.2.9字符串
字符串保存在reg类型的变量中,每个字符占用8位(一个字节)因此寄存器变量的宽度应足够大,以保证容纳全部字符。
若寄存器变量的宽度大于字符串的大小(位)则Verilog使用0来填充左边的空余位;
若寄存器变量的宽度小于字符串的大小(位)则Verilog截去字符串最左边的位
因此声明保存字符串的reg变量时,其位宽应当比字符串的位长稍大。
    reg [8*18:1] string_value;  //声明变量string_value,其宽度为18个字节
    initial
        string_value = "hello verilog world"; //字符串可以存储在变量中
        
\n     换行
\t     tab指标空格
%%     %
\\     \
\"     "
\ooo   1到3个八进制数字字符

3.3系统任务和编译指令
3.3.1系统任务
所有的系统任务都具有$<keyword>的形式。
①显示信息 P26
$display是用于显示变量、字符串或表达式的主要系统任务。
用法:$display(p1,p2,p3,...,pn);
其中,p1,p2,p3,...,pn是双引号括起来的字符串、变量或表达式,$display会自动在字符串的结尾处
插入一个换行符,因此如果参数列表为空,则$display的效果是显示光标移到下一行。

②监视信息
通过系统函数$monitor,Verilog为用户提供了信号值变化进行动态监视的手段。
用法:$monitor(p1,p2,p3,...,pn);
其中,参数p1,p2,p3,...,pn可以是变量、信号名或双引号括起来的字符串。
系统函数$monitor对其参数列表中的变量值或信号值进行不间断的监视,当其中任何一个发生变化的时候,显示所有参数的数值。
$monitor只需要调用一次即可在整个仿真过程中生效。正由于$monitor在整个仿真过程中有效,因此在任意仿真时刻只有一个监视列表有效;
如果用户在源描述中调用多个$monitor,则只有最后一个调用生效,前面的调用会被覆盖。
Verilog还提供两个控制监视的系统任务:$monitoron $monitoroff
仿真开始时仿真器的默认状态是允许监视,调用$monitoroff暂停监视;调用$monitoron允许监视任务的执行。
在$monitor中使用系统函数$time来获取系统仿真时间。
系统任务$stop用于暂停仿真   系统任务$finish用于结束仿真
    //仿真时刻为100单位时暂停仿真,检查结果
    //仿真时刻为1000单位时结束仿真
    initial
    begin
    clock = 0;
    reset = 1;
    #100 $stop;
    #900 $finish;
    end
    
3.3.2编译指令  注意这里的符号不是单引号,而是键盘左上角的撇号
①`define
编译指令`define用于定义Verilog中的文本宏。在编译阶段,当编译器遇到`<宏名>时,使用预定义的文本进行替换,
类似于C语言中的#define结构。在使用预定义的常数或文本宏时,在宏名前加上前缀名"`" ----键盘左上角的撇号
    //规定字长的文本宏
    //在代码中使用`word_size表示
    `define word_size 32
    
    `define s $stop;  //定义别名,可以用`s来代替 $stop
    `define word_reg reg32
  //就可以用`word_reg reg32 来定义一个32位的寄存器变量
②`include
可以在编译期间将一个Verilog源文件包含在另一个Verilog文件中,作用类似C语言中的#include结构。
该指令通常用于将内含全局或公用定义的头文件包含在设计文件中
    //包含header.v文件,在该文件中有主Verilog文件design.v需要的内容
    `include header.v  //译者注,原文错,文件名两边忘加双引号
    ...
    <design.v>文件中的Verilog代码
    ...
    
③`ifdef  `timescale  见第9章

第4章模块和端口
4.1 模块
模块定义以关键字module开始,而模块名、端口列表、端口声明和可选参数声明必须出现在其他部分的前面,
并且endmodule语句必须为模块的最后一句。端口是模块与外部环境交互的通道,只有在模块有端口的情况下
才需要有端口列表和端口声明。
模块内部的5个组成部分是:变量声明、数据流语句、低层模块实例、行为语句块以及任务和函数。
这些部分可以在模块中的任意位置,以任意顺序出现。模块的所有组成部分中,只有module、模块名和endmodule必须出现,
其他部分都是可选的。在一个Verilog源文件中可以定义多个模块,Verilog对模块的排列组合顺序没有要求。
//P32 SR锁存器模块示例


4.2 端口
对于外部环境来讲,模块内部是不可见的,对模块的调用只能通过其端口进行。这种特点带来极大的灵活性:
只要保持接口不变,模块内部的修改并不会影响到外部环境。我们也常常将端口称为 终端(terminal)
4.2.1端口列表
如果模块和外部环境没有交换任何信号,则可以没有端口列表。

4.2.2端口声明 
端口列表中的所有端口必须在模块中进行声明。端口三种类型:
input   输入端口
output  输出端口
inout   输入输出双向端口
所有端口隐含地声明为wire类型,若输出类型的端口需要保存数值,则必须显式地声明为reg数据类型。
不能将input和inout类型的端口声明为reg数据类型。因为reg类型的变量是用于保存数值的,而
输入端口只反映与其相连的外部信号的变化,并不能保存这些信号的值。
在Verilog中,也可以使用ANSI C风格进行端口声明,这种风格避免了端口名在端口列表和端口声明语句的重复。
//例4.5 ANSI C风格的端口声明
    module fulladd4(output reg[3:0] sum,
                    output reg c_out,
                         input [3:0] a,b, //默认类型为wire
                         input c_in);  //默认类型为wire
    ...
    <模块的内容>
    ...
    endmodule
//例4.3 端口声明
    module fulladd4(sum,c_out,a,b,c_in);
    //端口声明开始
    output [3:0] sum,
    output c_out,
    input [3:0] a,b,
    input c_in
    //端口声明结束
    ...
    <模块的内容>
    ...
    endmodule

4.2.3端口连接规则
/*端口声明
端口列表中的所有端口必须在模块中进行声明,verilog中的端口具有以下三种了类型:input、output、和inout。
在verilog中,所有的端口隐含地声明为wire类型
如果输出类型的端口需要保存数值,则必须将其显式的声明为reg数据类型。output reg out;
不能将input和inout类型的端口声明为reg数据类型,因为reg类型的变量是用于保存数值的,而输入端口只反映与其相连的外部信号的变化,并不能保存这些信号的值。
输入端口
从模块内部来讲,输入端口必须为线网数据类型
从模块外部来看,输入端口可以连接到线网或者reg数据类型的变量。
输出端口
从模块内部来讲,输出端口可以是线网或者reg数据类型
从模块外部来看,输出必须连接到线网类型的变量,而不能连接到reg类型的变量。
输入/输出端口
从模块内部来讲,输入/输出端口必须为线网数据类型
从模块外部来看,输入/输出端口也必须连接到线网类型的变量。*/--检索自网络
P35 图
当在一个模块中调用(实例引用)另一个模块时,端口间的连接必须遵守一些规则,
如果违反这些规则,那么Verilog仿真器会报错。
位宽匹配
在对模块的调用(实例引用)时候,Verilog允许端口的内外两个部分具有不同的位宽。
在一般情况下,Verilog仿真器会对此给予警告。
未连接端口
Verilog允许模块实例的端口保持未连接状态。例如,模块的某些输出端口只用于调试,那么
这些端口可以不与外部信号连接。

程序实例。。。

4.2.4端口与外部信号的连接
按顺序连接和按名字连接,但是这两种方法不能混用。
①顺序端口连接
需要连接到模块实例的信号必须与模块声明时目标端口在端口列表中的位置保持一致。
    module top;
    reg [3:0]A,B;
    reg C_IN;
    wire [3:0] SUM;
    wire C_OUT;
    
    fulladd4 fa_ordered(SUM,C_OUT,A,B,C_IN);
    ...
    <测试激励>
    ...
    endmodule
    
    module fulladd4 (sum,c_out,a,b,c_in);
    out[3:0] sum;
    output c_cout;
    input [3:0] a,b;
    input c_in;
    ...
    <模块内容>
    ...
    endmodule
②命名端口连接
大型设计中记住端口顺序容易出错。在这种方法中端口和外部信号按照其名字进行连接,而不是按照位置。
端口连接可以出现在任意顺序出现,只要保证端口和外部信号的正确匹配即可
//调用以fa_byname命名的全加器模块fulladd4,通过端口名与外部信号连接
fulladd4 fa_byname(.c_out(C_OUT), .sum(SUM), .b(B), .c_in(C_IN), .a(A),);
命名端口连接的另一个优点:只要端口名字保持不变,即使模块端口列表中端口的顺序发生变化,
模块实例的端口连接也无需进行调整.

4.3层次命名
设计中的每个标识符都具有唯一的层次名,它使得用户可以在设计中的任何位置访问设计中的每一个标识符。
每一个模块实例、信号或变量都使用一个标识符进行定义;在整个设计层次中,每个标识符都具有唯一的位置。
层次命名允许设计者在整个设计中通过唯一的名字来表示每个标识符。层次名由一连串使用"."分隔的标识符组成。
每个标识符代表一个层次。  如果需要显示层次,用户可以在系统任务$display中使用特殊字符%m。
设计中的顶层模块被称为”根模块“,它不能被其他模块所调用,它是整个设计层次的起点。

第5章 门级建模
5.1.1与门and  或门or
//门级原语是预定义的,可以直接使用且无须声明
门的端口列表中的第一个端口必定是输出端口,其后为输入端口。
当任意一个输入端口的值发生变化时,输出端口的值立即重新计算。Verilog语言中属于与/或门类: 
    and与  nand与非    or或  nor或非    xor异或  xnor同或
所有门实例的输出端口(out)都被连接到OUT,两个输入端口(i1,i2)则被连接到IN1,IN2。
注意,门级原语实例引用的时候,我们可以不指定具体实例的名字,这一点为设计师实例引用几百个门的模块提供方便。
门的实例引用中输入端口数目可以超过两个,这是只需要将输入端口全部排列到端口列表中即可,Verilog会根据
输入端口数目自动选择引用合适的逻辑门。如果输入端口多于两个,则通过重复使用两输入真值表来计算输出的值。
    
    wire OUT,IN1,IN2;
    
    and a1(OUT,IN1,IN2);
    nand na1(OUT,IN1,IN2);
    or or1(OUT,IN1,IN2);
    nor nor1(OUT,IN1,IN2);
    xor xor1(OUT,IN1,IN2);
    xnor nx1(OUT,IN1,IN2);
    
    nand na1_3inp(OUT,IN1,IN2,IN3); //三输入端的与非门
    
    and (OUT,IN1,IN2);//实例引用门时,可以不给实例命名

5.1.2缓冲器buf/非门not
buf/not门具有一个标量输入和多个标量输出,端口列表中的最后一个终端连接至输入端口,其他端口连接至输出端口。
对于具有多个输出端的buf/not门,所有输出端的值都是相同的。
注意,buf/not门可以具有多个输出端口,但只有一个输入端口,这个输入端口必须是实例端口列表的最后一个。

    buf b1_2out(OUT1,OUT2,IN); //输出端多于两个
    not (OUT,IN); //实例引用门时,可以不给实例命名

带控制端的缓冲器/非门 bufif/norif 三态门P43
bufif1 bufif0  notif1 notif0
这四种类型的门只有在控制信号有效的情况下才能传递数据;如果控制信号无效,则输出为高阻抗z
例如当一个信号由多个驱动源所驱动时,我们可以这样设计驱动源:让他们的控制信号的有效时间互相错开,
从而避免一条信号线同时被两个源驱动。这是就需要带控制端的缓冲器/非门来搭建电路。
    notif0 n0(out,in,ctrl);

5.1.3实例数组
许多情况下,我们需要对某种类型的门进行多次调用,这些门实例之间的区别仅仅在于他们分别连接在不同的向量信号位上。
为简化这种类型的门的调用,Verilog允许用户自己来定义实例数组。
    wire [7:0] OUT,IN1,IN2;
    nand n_gate[7:0](OUT,IN1,IN2); //实例门引用
    //上一条语句,相当于8条实例语句
    nand n_gate0(OUT[0],IN1[0],IN2[0]);
    ...
    ...
    nand n_gate7(OUT[7],IN1[7],IN2[7]);
5.1.4 举例
例5.5 多路选择器
设计一个有两位选择信号的四选一选择器

    module mux4_to_1(out,i0,i1,i2,s1,s0);
    //直接取自输入/输出图的端口说明语句
    output out;
    input i0,i1,i2,i3;
    input s1,s0;
    
   wire s1n,s0n;
    wire y0,y1,y2,y3; //内部线网声明
    
    not(s1n,s1);
    not(s0n,s0); //生成s1n和s0n信号
    
    //调用三输入与门
    and(y0,i0,s1n,s0n);
    and(y1,i1,s1n,s0);
    and(y2,i2,s1,s0n);
    and(y3,i3,s1,s0);
    
    //调用四输入或门
    or (out,y0.y1,y2,y3);
    
    endmodule
    
多路选择器激励模块
    module stimulus;
    
    //声明连接到输入端口的变量
    reg IN0,IN1,IN2,IN3;
    reg s1,s0;
    
    wire OUTPUT; //声明输出连线
    //调用多路器
    mux4_to_1 mymux(OUTPUT,IN0,IN1,IN2,IN3,S1,S0);
    //产生输入激励信号
    initial
    begin 
     //设置输入线信号
     IN0 = 1;IN1 = 0;IN2 = 0;IN3 = 0;
     #1 $display("IN0=%b,IN1=%b,IN2=%b,IN3=%b\n",IN0,IN1,IN2,IN3);
     
     //选择IN0
     S1 = 0; S0 = 0;
     #1 $display("S1=%b, S0=%b, OUTPUT=%b\n",S1,S0,OUTPUT);
     
     //选择IN1
     S1 = 0; S0 = 1;
     #1 $display("S1=%b, S0=%b, OUTPUT=%b\n",S1,S0,OUTPUT);
     
     //选择IN2
     S1 = 1; S0 = 0;
     #1 $display("S1=%b, S0=%b, OUTPUT=%b\n",S1,S0,OUTPUT);
     
     //选择IN3
     S1 = 1; S0 = 1;
     #1 $display("S1=%b, S0=%b, OUTPUT=%b\n",S1,S0,OUTPUT);
  end 
  endmodule
  
四位脉冲进位全加器
/*一位全加器 sum =(a 异或 b 异或 cin)
            cout =(a·b)+cin·(a 异或 b) */
定义一位全加器Verilog
    module fulladd(sum,c_out,a,b,c_in);
    output sum,c_out;
    input a,b,c_in;
    
    wire s1,c1,c2; //内部线网
    
    xor(s1,a,b);
    and(c1,a,b);
    
    xor(sum,s1,c_in);
    and(c2,s1,c_in);
    
    xor(c_out,c2,c1)  //调用逻辑门级原语
    
    endmodule

在Verilog中,标识符的作用范围只局限于本模块,从模块外部不可见,除非使用层次名进行访问,
这也意味着不同模块可以使用相同的标识符。此例中一位全加器中的sum是标量,而四位全加器sum是向量。
结构化建模时,调用用户自定义模块时必须指定模块实例的名字,而调用Verilog门级原语时不一定需要指定实例名。
    module fulladd4(sum,c_out,a,b,c_in);
    output[3:0] sum;
    output c_out;
    input[3:0] a,b;
    input c_in;
    
    wire c1,c2,c3; //内部线网
    
    fulladd fa0(sum[0],c1,a[0],b[0],c_in);
    fulladd fa1(sum[1],c2,a[1],b[1],c1);
    fulladd fa2(sum[2],c3,a[2],b[2],c2);
    fulladd fa3(sum[3],cout,a[3],b[3],c3);
    
    endmodule
例5.9 四位脉动进位全加器的激励模块
module stimulus;
reg[3:0] A,B;
reg C_IN;
wire [3:0] SUM;
wire C_OUT;

fulladd4 FA1_4(SUM,C_OUT,A,B,C_IN); //调用四位全加器,把它命名为FA1_4

initial   //设置信号值的监视
begin    
    $monitor($time,"A=%b,B=%b,C_IN=%b,C_OUT=%b,SUM=%b\n",
                    A,B,C_IN,C_OUT,SUM);  
end
 
initial  //激励信号的输入
begin
    A=4'd0; B=4'd0; C_IN=1'd0;
    #5 A=4'd3; B=4'd4;
    #5 A=4'd2; B=4'd5;
    #5 A=4'd9; B=4'd9;
    #5 A=4'd10; B=4'd15;
    #5 A=4'd10; B=4'd5; C_IN=1'b1;
end

endmodule
        
5.2 门延迟
Verilog允许用户通过门延迟来说明逻辑电路中的延迟;此外用户还可以指定端到端的延迟
5.2.1
上升延迟:门的输入发生变化情况下,门输出从0,x,z变化为1所需的时间;
下降延迟:门输出从1,x,z变化为0所需的时间;
关断延迟: 门输出从0,1,x变化为高阻抗z所需的时间;

如果用户只指定一个延迟值,那么对所有类型的延迟都使用这个延迟值;
如果用户指定两个延迟值,则它们分别代表上升/下降延迟,其中两者小的为关断延迟;
如果用户指定三个延迟值,则它们分别代表上升/下降/关断延迟;
如果用户没有指定延迟值,那么默认延迟值为0.
   //以上三种延迟都等于delay_time所表示的延迟时间
    and #(delay_time) a1(out,i1,i2);
    and #(rise_val,fall_val)  a2(out,i1,i2);
    bufif0 #(rise_val,fall_val,turnoff_val) b1(out,in,control);
    
    and #(4,6) a2(out,i1,i2);
    bufif0 #(3,4,5) b1(out,in,control);
    
5.2.2最小/典型/最大延迟
Verilog中用户除了可以指定上面三种类型延迟以外,对每种类型延迟还可以指定其最小值,最大值和典型值。
真实器件延迟总是在最小值和最大值在之间的范围变化。
    //P52表格
使用命令行调用verilog-XL仿真器方法:(假设仿真模块文件为test.v)
>verilog test.v +maxdelays  //启动仿真器,使用最大延迟值进行仿真
>verilog test.v +maxdelays
>verilog test.v +maxdelays
第6章 数据流建模 
6.1 连续赋值语句
连续赋值语句是Verilog 数据流建模的基本语句,用于对线网进行赋值。连续赋值语句必须以assign开始,
    continuous_assign::=assign[drive_strength][delay3] 
                        list_of_net_assignments;
    list_of_net_assignments::=net_assignment{,net_assignment}
    net_assignment::=net_lvalue=expression
注意,上面语法中的驱动强度是可选项,其默认值为strong1和strong0.延迟值也是可选的,用于指定赋值的延迟,类似于门延迟。
//连续赋值语句具有以下特点:
1.连续赋值语句的左值必须是一个标量或向量线网,或者是标量或向量线网的拼接,而不能是向量或向量寄存器。
2.连续赋值语句总是处于激活状态。只要任意一个操作数发生变化,表达式就会被立即重新计算,并且将结果赋给等号左边的线网。
3.操作数可以是标量或向量的线网或寄存器,也可以是函数调用;
4.赋值延迟用于控制对线网赋予新值的时间,根据仿真时间单位进行说明。赋值延迟类似于门延迟,对于描述实际电路时序非常有用。
    //连续赋值语句,out是线网,i1和i2也是线网
    assign out =i1&i2;
    assign addr[15:0] = addr1_bits[15:0]^addr2_bits[15:0];
    assign {c_out,sum[3:0]} =a[3:0] + b[3:0] + c_in;
    
6.1.1隐式连续赋值
除了首先声明然后对其进行连续赋值以外,Verilog还提供另一种对线网赋值的简便方法:
在线网声明的同时对其进行赋值。由于线网只能被声明一次,因此对线网的隐式声明赋值只能有一次。
    //普通的连续赋值
    wire out;
    assign out = in1 & in2;
    
    //使用隐式连续赋值实现与上面两条语句同样的功能
    wire out = in1 & in2;
6.1.2隐式线网声明
如果一个信号名被用在连续赋值语句的左侧,那么Verilog编译器认为该信号是一个隐式声明的线网。如果线网被链接到模块的端口上,
则Verilog编译器认为隐式声明线网的宽度等于模块端口的宽度。
    //连续赋值,out为线网类型
    wire i1,i2;
    assign out = i1 & i2;  //注意,out并未声明为线网,但Verilog仿真器会推断出out是一个隐式声明的线网。
6.2延迟
连续赋值语句中的延迟用于控制任一操作数发生变化到语句左值被赋予新值之间的时间间隔。
6.2.1普通赋值延迟
指定延迟的第一种方法是在连续赋值语句中说明延迟值,延迟值位于关键字assign的后面。上例中如果in1和in2中任意一个发生变化,
那么在计算表达式in1&in2的新值并将新值赋给语句左值之前,会产生10个时间单位的延迟。如果在此10个时间单位期间,即左值获得
新值之前,in1或in2的值再次发生变化,那么在计算表达式的新值时会取in1或in2的当前值。我们称这种性质为惯性延迟。也就是说,
脉冲宽度小于赋值延迟的输入变化不会对输出产生影响。
    assign #10 out = in1 & in2; //连续赋值语句中的延迟

6.2.2隐式连续赋值延迟
使用隐式连续赋值语句来说明对线网的赋值以及赋值延迟。隐式连续赋值等效于声明一个线网并且对其进行连续赋值。
    //隐式连续赋值延迟
    wire #10 out = in1 & in2;
    
    //等效于
    wire out;
    assign #10 out = in1 & in2;
    
6.2.3线网声明延迟
Verilog允许在声明线网的时候指定一个延迟,这样对该线网的任何赋值都会被推迟指定的时间。线网声明同样可以用于门级建模中。
    //线网延迟
    wire #10 out;
    assign out = in1 & in2;
    
    //等效于上面两条语句
    wire out;
    assign #10 out = in1 & in2;
6.3表达式 操作符和操作数
6.3.1表达式
表达式由操作符和操作数构成,其目的是根据操作符的意义计算出一个结果值。
    //表达式的例子,组合了操作数和操作符
    a^b
    addr1[20:17] + addr2[20:17]
    in1 | in2
6.3.2操作数
操作数可以是3.2节中定义的任何数据类型,但是某些语法结构要求使用特定类型的操作数。
操作数可以是常数,整数,实数,线网,寄存器,时间,位选,域选以及存储器和函数调用。
    integer count, final_count;
    final_count = count + 1; //count是整型操作数
    
    real a,b,c;
    c = a - b;  //a,b是实型操作数
    
    reg[15:0] reg1,reg2;
    reg[3:0] reg_out;
    reg_out = reg1[3:0] ^ reg2[3:0];
    
    reg ret_value;
    ret_value = calculate_parity(A,B); //calculate_parity是函数型操作数
    
6.3.3操作符
操作符对操作数进行运算并产生一个结果。P61
    
    //操作符的优先级 P67
    //如果不使用小括号将表达式各部分分隔开,则Verilog将根据操作符间的优先级对表达式进行运算
单目运算           + - ! ~            最高
乘,除,取模         * / %

加减               +  -
移位              <<  >>

关系          < <= > => == !=
等价            ===    !==

缩减           & ~& ^ ^~ |~|
逻辑              &&   ||

条件                 ?:               最低

6.4.1算术操作符
①+和-操作符也可以用作单目操作符,此时他们表示操作数的正负。
在Verilog内部,负数是用其二进制补码来表示的。建议读者使用整数或实数来表示负数,
而避免使用<sss>'<base><nnn>的格式来表示负数,这是因为它们将被转换为无符号的2的补码形式,会产生意想不到的结果。
    //建议使用整型数和实型数
    -10 / 5    //等于-2
    -’d10 / 5  //等于(10的二进制补码)除以5 [(2^32-10)/5]
               //默认的机器字长为32位 这样计算出的结果不符合一般预期,容易出错.
                  
②双目操作符对两个操作数进行算术运算,包括乘 除 加 减 求幂** 取模%
如果操作数的任意一位为x,那么运算结果的全部位为x。


第15章 高级验证技术
功能验证环境通常包含测试向量生成器、输入驱动器、输出接收器、数据检查器、协议检查器和覆盖分析器
高层次验证语言HVL可以高效率创建和维护这些环境。
波形和日志文件是最通用的分析仿真输出的方法。
代码覆盖、翻转覆盖和分支覆盖是三种类型的结构覆盖技术。
15.1.3仿真 
软件仿真  硬件加速  硬件仿真
①软件仿真Software Emulation 
软件仿真器运行,读入Verilog HDL代码并在软件中仿真其行为.

②硬件加速 Hardware Acceleration
在功能验证和拓展验证阶段,硬件加速用于加速现有的仿真,运行冗长的随机事务序列。
仿真被分为两个部分,其一软件仿真器仿真不可综合的Verilog代码,
其二为硬件加速器,仿真所有可综合的代码。
然而价格昂贵并且需要大量的建立时间。另一个缺点是它们通常需要很长的编译时间。
这就意味着只对很长的回归仿真有用(regression)
 
③硬件仿真Hardware Emulation

15.1.4分析
为分析数据值和数据协议的正确性,可以采用许多办法:
①波形观察器 、 





//FPGA的人工智能之路 张瑞
P94  
Quartus Prime工程被定义为最终创建要下载到FPGA的编程图像所需的所有与设计相关的文件和库的集合。
通常,所有工程文件都存储在单个工程文件夹或目录中,但可以引用工程目录以外的其他文件。工程必须具有
指定的顶级设计实体,用于以逻辑实例化或其它设计文件形式将子实体连接在一起。此外,所有工程都必须针对单个设备。
但是,可以在设计过程中的任何位置对顶层实体和目标设备进行更改。所有工程设置都存储在Quartus工程文件(.qsf)的单个文件中。
一旦工程通过编译,编译信息将存储在工程目录的文件夹中,此文件夹名为db,因为此文件夹包含编译数据库信息。

New Project Wizard设置工程名称
第一行 选择工作目录
第二行 工程命名 推荐使用顶层文件名命名
第三行 Top-Level Enitity可以与顶层文件名相同,也可以与顶层文件名不同

如果想使用最新的Intel器件,可以打开Show advanced devices选项

新项目向导最后一页,单击finish将使用所选设置创建该新项目。注意,新项目的设置不是永久的,可以在”设置“对话框中进行更改。

工程导航器 可以使用工程层次结构查看层次结构中每个级别的设备资源的使用情况,更改顶级模块,为增量编译设置设计分区,
以及进行影响整个模块的工程分配。还可以右键单击设计模块,然后使用”定位“子菜单在软件的其他工具找到该模块。你将发现
能够在软件中的其他工具中定位设计元素,这称为交叉探测。

标准版工程文件/文件夹
①(.qpf) 英特尔Quartus Prime工程文件 Project file
②(.qdf) 英特尔Quartus Prime默认文件 Default file
③(.qsf) 英特尔Quartus Prime设置文件 Setup file
④(.sdc) Synosys约束文件            dominate file?
⑤db文件夹 (1)包含已编译设计信息 (2)也可以查看incremental_db,以获取增量编译信息
⑥output_files文件夹(在工程设置中自定义位置/名称)(1)生成的编译报告文件(2)由英特尔Quartus P标准版汇编程序
                    生成的编程文件.
                          
一旦执行了编译过程中的任何步骤,还会找到前面提到的db文件夹,如果使用后面讨论的增量编译功能,还会找到incremental_tb文件夹。
最后output_files文件夹存储用于脱机查看和比特流编程文件的编译信息报告。

4.2.3设计输入
Quartus Prime软件支持多种不同的输入文件格式,可以在同一工程中混合和匹配。
①文本编辑器:VHDL Verilog或system Verilog
②原理图编辑器 Block Diagram或 Schematic File
③系统编辑器   Platform Designer
④状态机编辑器  
⑤内存编辑器  (HEX或MIF)用于创建英特尔标准HEX文件和内存初始化mif文件,以初始化设计中的RAM或ROM
⑥第三方EDA工具 EDIF200  Verilog Quartus Mapping(.vqm)

顶层设计文件可以原理图,HDL,platform designer,DSP Builder,OpenCL或第三方网表文件。
①OpenCL流程中生成 .v或.sv
②第三方EDA工具导入 .edf .edif .v .vlg .lvhd .vhdl .vqm
③标准版软件生成: .bdf(Block file) .bsf(Symbol file) .vhd .v .sv(Text file)     
.v .vhd(State Machine file) .qip .qsys(IP Catalog output)  
.qip .qsys(Platform Designer output)   .qip(DSP Builder output)
顶级文件和所有支持文件可以是这些格式的任何一种。至于在层次结构中的其他设计文件,Quartus Prime软件允许混合使用。
例如,只要例化语法和端口映射正确,就可以在Verilog HDL模块中例化VHDL模块,这提供了任意格式输入的灵活性。















 
夏宇闻第四版  P28
Verilog模块中所有过程块,连续赋值语句,实例引用都是并行的;
它们表示的是一种通过变量名相互连接的关系;
在同一模块中这三者出现的先后秩序没有关系;只有连续赋值语句和实例引用语句可以独立于过程块而存在于模块的功能定义部分.

3.2.1常量
1.数字: 整数 x和z值  负数  下划线
下划线不可以用在位宽和进制处,只能用在具体数字间
2.参数型parameter 
定义一个标识符代表一个常量 符号常量
参数型常数经常用于定义延迟时间和变量宽度
parameter 参数名1=表达式
    parameter msb=7               //定义参数msb为常量7
    parameter delay=(r+f)/2;    //用常数表达式赋值
    
3.2.2变量
网络数据类型表示结构实体间的物理连接。网络类型的变量不能存储值,而且它必须受到驱动器的驱动。
若没有驱动器连接到网络类型的变量上,则该变量就是高阻z
wire型:通常用来表示单个门驱动或连续赋值语句网络型数据
tri型:用来表示多驱动器驱动的网络型数据。
若wire或tri型变量没有定义逻辑强度(logic strength),在多驱动源情况下,逻辑值会发生冲突,从而产生不确定值

wire型数据常用来表示用以assign关键字指定的组合逻辑信号。Verilog程序模块中输入输出信号类型默认定义为wire型
    wire[4:0] c,d;  //定义两个5位的wire型数据

    寄存器型数据类型reg 默认初始值是不定值x
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值