verilog数字系统设计教程(夏闻宇)|第一章-第六章

早听闻此书大名,虽然有些verilog的编程经验,今天来系统的学习下verilog,完善知识体系,提升编程能力。本文章只当自己做笔记使用,记录自己没掌握的重难点,和大家一起学习。

目录

第一章 

第二章 verilog语法

第三章 模块的结构、数据类型、变量和基本运算符号

1.常量

2.变量

3.算术运算符

第四章 运算符、赋值语句和结构说明语句

1.等式运算符

2.拼接运算符

3.缩减运算符

4.赋值语句

5.块语句

第五章 条件语句、循环语句、块语句与生成语句

1.条件语句(if_ else 语句)

2.case语句

3.循环语句

4.生成语句

第六章 结构语句、系统任务、函数语句和显示系统任务

1.结构语句

2.任务与函数task和function

3.系统任务


第一章 

Verilog 语言以及它的扩展SystemVerilog是设计可重用的IP,即软核、固核、硬核和验证用虚拟核所必须的语言。

第二章 verilog语法

verilog支持三种方式来实现逻辑功能:

1.行为级的逻辑功能描述 2.布尔表达式

3.还可以调用fpga内部基本逻辑单元,如下,第一列表示调用的基本逻辑单元非,与,与,或;第二列表示输入到输出的延时时间,以单位时间为基准;第三列式调用结构,用例化的方式,第一个都是表示输出。verilog支持这种搭建电路的形式来描述功能。

EDA一般会将第一种行为语言通过2自动转换为3这种形式,这就是综合的过程。

verilog支持对已完成模块的再次引用,引用格式如下:

由模块mytri定义的实例部件tri_ inst。在实例部件tri_ inst 中,带“.”表示被引用模块的端口,名称必须与被引用模块mytri的端口定义一致,小括号中表示在本模块中与之连接的线路。

verilog对模块的测试

相当于在被测试模块的外部再搭建模块,然后通过实例化被测试模块的方式进行验证。

如下是测试模块t对模块muxtwo模块的测试实例:

首先定义测试模块,并根据测试模块定义连线,这里reg和wire与被测试模块中的数据类型不同原因是因为我们在测试模块中一般需要对输入信号进行改变,而输出测试信号只起到连线的作用。但在被测试模块中,一般是输出信号要被改变。

第三章 模块的结构、数据类型、变量和基本运算符号


程序组成部分:端口定义,IO口说明(数据类型和位数等),内部信号声明,功能定义(assign 布尔或always)。

重点:在verilog中,各个语法块是并行发生的(如每个assign和always块之间),而always内部是顺序发生的。

1.常量

parameter可以定义一个标识符来指定常量,通常用于延时时间和变量宽度,方便调用时修改。

通过参数传递修改调用模块中的参数变量

在多层次模块中,参数需要跨层修改时,需要用defparam。如下图在顶层模块annote修改下层模块B中的值,使用.

2.变量

wire网络数据类型表示结构实体(例如门)之间的物理连接。网络类型的变量不能储存值,而且它必须受到驱动器(例如门或连续赋值语句,assign)的驱动。如果没有驱动器连接到网络类型的变量上,则该变量默认就是高阻的,即其值为z。常用的网络数据类型包括wire型和tri型。

reg型数据常用来表示“always"模块内的指定信号,常代表触发器。通常,在设计中要由“always"模块通过使用行为描述语句来表达逻辑关系。在“always"模块内被赋值的每一个信号都必须定义成reg型。

reg型数据的默认初始值是不定值。reg型数据可以赋正值,也可以赋负值。但当一个reg型数据是一个表达式中的操作数时,它的值被当作是无符号值,即正值。
 

Verilog HDL通过对reg型变量建立数组来对存储器建模,可以描述RAM型存储器、ROM存储器和reg文件。数组中的每一个单元通过一个数组索引进行寻址。在Verilog语言中没有多维数组存在memory型数据是通过扩展reg型数据的地址范围来生成的。其格式如下:

reg [n-1:0] (每一个存储单元的大小)存储器名[m-1:0](有多少个存储单元);

表示m个n位寄存器
 

3.算术运算符

除法运算取整数,取模运算取余数,符号位采用第一个操作数的符号位

第四章 运算符、赋值语句和结构说明语句

1.等式运算符

这4个运算符都是双目运算符,它要求有两个操作数。“==”和“! =”又称为逻辑等式运算符,其结果由两个操作数的值决定。由于操作数中某些位可能是不定值x和高阻值z,结果可能为不定值x。而“===”和“!=="运算符则不同,它在对操作数进行比较时对某些位的不定值x和高阻值z也进行比较,两个操作数必须完全一致,其结果才是1,否则为0。“===”和“! ==”运算符常用于case表达式的判别,所以又称为“case等式运算符”。这4个等式运算符的优先级别是相同的。

2.拼接运算符

位拼接还可以用嵌套的方式来表达。见下例:

3.缩减运算符

位运算是对操作数的相应位进行与、或、非运算,操作数是几位数,其运算结果也是几位数。而缩减运算则不同,缩减运算是对单个操作数进行或与、非递推运算,最后的运算结果是1位的二进制数。缩减运算的具体运算过程是这样的:第一步先将操作数的第1位与第2位进行或.与、非运算;第二步将运算结果与第3位进行或、与、非运算,依次类推,直至最后1位。例如:

4.赋值语句

阻塞赋值(如b=a;)

在赋值语句执行结束后立即执行,这在时序逻辑中可能会产生意想不到的结果。

我们通常需要再每个上升或下降沿的时候再进行赋值,这是为了保证时序

所以时序逻辑常采用:非阻塞(Non_ Blocking) 赋值方式(如b<= a;)

在语句块中,上面语句所赋的变量值不能立即就为下面的语句所用;块结束后才能完成这次赋值操作,而所赋的变量值是上一次赋值得到的;

5.块语句

块语句有两种:一种是begin_end语句,通常用来标识顺序执行的语句,用它来标识的块称为顺序块;另一种是fork_ join 语句,通常用来标识并行执行的语句,用它来标识的块称为并行块。

块名
在VerilgHDL语言中,可以给每个块取一个名字,只需将名字加在关键词begin或fork后面即可。之后可以被其他块调用,块名就提供了一个在任何仿真时刻确认变量值的方法。

起始时间和结束时间

对于顺序块,起始时间就是第一条语句开始被执行的时间,结束时间就是最后一条语句执行完的时间。而对于并行块来说,起始时间对于块内所有的语句是相同的,即程序流程控制进人该块的时间,其结束时间是按时间排序在最后的语句执行结束的时间。
当一个块嵌人另一个块时,块的起始时间和结束时间是很重要的。至于跟在块后面的语句只有在该块的结束时间到了才开始执行。也就是说,只有该块完全执行完后,后面的语句才可以执行。
 

第五章 条件语句、循环语句、块语句与生成语句

1.条件语句(if_ else 语句)

条件语句只能在过程快中即initial和always引导的过程中使用

统对表达式的值进行判断,若为0,x,z,按“假”处理;若为1,按“真”处理,执行指定的语句。

2.case语句

允许分支表达式中是xz

对于那些分支表达式中存在不定值x和高阻值z时,case语句提供了处理这种情况的手段。下面的两个例子介绍了处理分支表达式中某位的值值为x、z位的case语句。

casez和casex可用来处理比较过程中的不必考虑的情况(don'tcarecondition)。

其中casez语句用来处理不考虑高阻值z的比较过程,casex语句则将高阻值z和不定值都视为不必关心的情况。所谓不必关心的情况,即在表达式进行比较时,不将该位的状态考虑在内。这样,在case语句表达式进行比较时,就可以灵活地设置对信号的某些位进行比较。见下面的两个例子:

要避免锁存器的产生

在对信号设计时一定要将情况考虑完全,即要考虑没有给定情况下的值,一般是初始值。

如果用到if语句,最好写上else 项;如果用case语句,最好写上default项。遵循上面两条原则,就可以避免发生这种错误,使设计者更加明确设计目标,同时也增强了Verilog程序的可读性。

3.循环语句

(1) forever语句:连续的执行语句。

forever循环语句常用于产生周期性的波形,用来作为仿真测试信号。它与always语句不同之处在于不能独立写在程序中,而必须写在initial块中。

(2) repeat语句:连续执行一条语句n次
(3) while语句:执行一条语句直到某个条件不满足。如果一开始条件即不满足(为假),则语句一次也不能被执行。
(4)for语句:通过以下3个步骤来决定语句的循环执行。和c语言里一样
①先给控制循环次数的变量赋初值。
②判定控制循环的表达式的值,如为假,则跳出循环语句;如为真,则执行指定的语句后,
转到第③步。
③执行一条赋值语句来修正控制循环变量次数的变量值,然后返回第②步。

语句块

分为顺序快和并行块,并行块为我们提供了并行执行语句的机制。不过在使用并行块时需要注意,如果两条语句在同一时刻对同一个变量产生影响,那么将会引起隐含的竞争,这种情况是需要避免的。从仿真的角度来讲,并行块中的所有语句是一起执行的,但是实际上运行仿真程序的CPU在任一时刻只能执行一条语句,而且不同的仿真器按照不同的顺序执行。因此无法正确的处理竞争是目前所使用的仿真器的一个缺陷,这一缺陷并不是并行块所引起的。

语句块可以互相嵌套,顺序和并行也可以;

可以为语句块进行命名,块内部的声明变量为静态本地变量:

关键字disable 语句块 提供了一种中止命名块执行的方法。 disable可以用来从循环中退出、处理错误条件以及根据控制信号来控制某些代码段是否被执行。对块语句的禁用导致紧接在块后面的那条语句被执行。对于C程序员来说,这一点非常类似于使用break退出循环。两者的区别在于break只能退出当前所在的循环,而使用disable则可以禁用设计中任意一个命名块

4.生成语句

生成语句可以在仿真开始前的详细设计阶段动态地生成Verilog 代码,这促进了参数化建模。当需要对矢量的多个位进行重复操作、模块实例的重复引用或根据参数的定义确定是否包括某一段代码的时候,使用生成语句是非常方便的。生成语句有3种类型是:循环生成语句、条件生成语句和case生成语句。
生成语句能够控制变量的声明、任务或函数的调用,还能对实例引用进行全面的控制。编写代码时必须在模块中说明生成的实例范围,关键字generate-endgenerate用来指定该范围。

eg1 对两条总线(位数为N)的变量按位与

可以看到,在生成语句中我们可以根据模块的参数位数进行行为的循环操作,生成变量用genvar关键字。生成变量只能用在生成块之中;在确立后的仿真代码中,生成变量是不存在的。

eg2说明如何用条件生成语句实现参数化乘法器。如果参数a0_ width 或al_ width小于8(生成实例的条件)则调用(实例引用)超前进位乘法器;否则调用(实例引用)树形乘法器。
 

即根据输入信号的位数实例不同乘法器

case生成块类似:

可以看到,生成块的本质是可以以模块参数为条件进行多次的实例化引用

第六章 结构语句、系统任务、函数语句和显示系统任务

1.结构语句

主要是initial和always语句,initial只执行一次,always反复执行。

initial常用于赋初值和产生激励信号

initial语句在仿真开始时对各变量进行初始化,注意这个初始化过程不需要任何仿真时间,即在0ns时间内,便可以完成初始化工作。

always

关键字wait可以表示等待条件为真:

2.任务与函数task和function

任务和函数都相当于一个子程序,用来定义经常重复使用的代码。他们的区别是,任务可以有任意多的输入输出变量,在内部也可以使用延时,可以调用其他的任务和函数,而函数只有一个返回值并且至少一个输入信号,在函数内部不能使用延迟事件,可以调用其他函数但是不能调用事件。此外,任务还可以定义自己的仿真时间而函数只能与主模块共用。

函数的目的是把返回值传递到一个表达式而任务可以当做一个事件。

例如,定义一任务或函数对一个16 位的字进行操作让高字节与低字节互换,把它变为另一个字(假定这个任务或函数名为: (switch_ bytes)。

任务返回的新字是通过输出端口的变量,因此16位字字节互换任务的调用源码是

switch_ bytes(old_ word , new_ word) ;

任务switch_ bytes 把输人old_ word 字的高、低字节互换放人new_ word 端口输出。

而函数返回的新字是通过函数本身的返回值。因此16位字字节互换函数的调用源码是:

new_ word = switch_ bytes(old_ word) ;

上述例子中在always语句块中对任务light进行启动

函数可以使用C风格进行描述:

注意函数名后是分号,且函数名就是返回值的变量名

函数可以被自己递归,但是要声明时使用关键字automatic
Verilog中的函数是不能够进行递归调用的。设计模块中若某函数在两个不同的地方被同时并发调用,由于这两个调用同时对同一块地址空间进行操作,那么计算结果将是不确定的。若在函数声明时使用了关键字automatic,那么该函数将成为自动的或可递归的,即仿真器为每一次函数调用动态地分配新的地址空间,每一个函数调用对各自的地址空间进行操作。

其中integer是整型变量的数据类型

我们可以调用函数来根据存储器深度计算地址总线的长度

对于带符号的函数名声明如下:

可以看到基本格式为 function 函数变量类型 位宽 函数名 (输入信号类型 输入信号名)

3.系统任务

1.$display和$write打印任务

这两个函数和系统任务的作用是用来输出信息,即将参数p2到pn按参数pl给定的格式输出。参数p1通常称为“格式控制”,参数p2至pn通常称为“输出表列”。这两个任务的作用基本相同。$display自动地在输出后进行换行,$ write'则不是这样。如果想在一行里输出多个信息,可以使用$write.在$display和$write中,其输出格式控制是用双引号括起来的字符串,它包括以下两种信息:

2.打开文件

文件可以用系统任务$ fopen打开。
用法: $ fopen(“< <文件名>”);
用法:<文件句柄> = $ fopen(“<文件名> >”);

3.

下面将只考虑$fdisplay和$fmonitor任务。
用法:
$ fdisplay(<文件描述符>, p1, p2,.*,pn);
$ fmonitor(< <文件描述符>, p1, p2,..,pn);
p1, p2,.., pn可以是变量、信号名或者带引号的字符串。文件描述符是一个多通道描述符,它可以是一个文件句柄或者多个文件句柄按位的组合。Verilog会把输出写到与文件描述符中值为1的位相关联的所有文件中。下面将使用[例6.20]中定义的文件描述符来解释$fdisplay和$fmonitor任务的使用。

4.关闭文件

用法: $ fclose(<文件描述符> );
文件一旦被关闭就不能再写人。多通道描述符中的相应位被设置为0,下一次$ fopen的.调用可以重用这一位。

5.选通显示

选通显示(Strobing)由关键字为$strobe的系统任务完成。这个任务与$display任务除了一.点小差异外,其他非常相似。如果许多其他语句与$ display任务在同一个时间单位执行,那么这些语句与$display任务的执行顺序是不确定的。如果使用$strobe,该语句总是在同时刻的其他赋值语句执行完成之后才执行。因此,$ strobe提供了一-种同步机制,它可以确保所有在同一时钟沿赋值的其他语句在执行完毕之后才显示数据

  • 41
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值