当前课程知识点:IC设计与方法 > 3、Verilog语法 > g)阻塞与非阻塞赋值 > Video
刚才我们讲完了always进程里
所允许使用的条件(if)和case语句
下面我们再来看一下这里面
所允许使用的阻塞赋值语句
和非阻塞赋值语句
阻塞和非阻塞的概念
是硬件描述语言所特有的概念
和传统的计算机上的C语言等等
那种纯粹在计算机上执行的概念
是完全不一样的
是新提出来的概念
要理解阻塞和非阻塞语句的的执行效果
也要从两个角度去考虑
一个是当我们写完的硬件描述语言的代码
在计算机上运行的时候
因为这些代码都会
首先在计算机上仿真模拟
它实际上(代表)的电路能实现的功能
是什么样的功能
这个过程称之为仿真
在仿真的时候阻塞和非阻塞有什么区别
有什么差异
第二个就是阻塞和非阻塞
语句所描述的代码
变成真正的电路的时候
他们俩有什么差异
所以我们理解阻塞和非阻塞的时候
一定要从这两个角度考虑
我们来看一下幻灯片
硬件描述语言的赋值语句
不管是阻塞也好 非阻塞也好
我们看它的细节
其实是分两个过程的
首先是赋值语句右边可能有一个
很复杂的表达式
要计算出这个表达式的值
这是第一步
计算出表达式值
第二步是把计算出来的表达式的值
再赋给左边的信号或者变量
所以任何操作、任何运算都有两步
第一个是计算
第二个是赋值
对于阻塞赋值语句而言
也就是说计算和赋值
是要等计算赋值过程全部做完以后
程序或者代码才会执行下一条语句
换句话说
如果有连续三条语句的话
一定要等到第一条语句的计算赋值完成以后
才执行第二条语句的计算赋值
第二条语句执行完以后才执行第三条语句
这个概念跟C语言里的赋值语句是完全一样的
这就是阻塞赋值语句
非阻塞赋值语句是什么意思
也就是说
它把计算跟赋值分开了
如果有连续的多条语句的话
计算过程可以并行的做
但是赋值的话
计算过程是有一个先后的过程的
但是赋值的话
这是在第一条语句的赋值语句
赋值过程还没有执行的时候
第二条语句后续句就开始执行了
所以换句话来说
这个非阻塞赋值语句
它的效果相当于这几条语句是并行执行的
从时间上来说
它们应该是同时开始执行
这是阻塞和非阻塞的定义
这个定义是一个纯语法角度的定义
我们再看一下从工程或者说从实用角度来说
是什么意义呢
或者说有什么要求
第一个在工程里面
通常我们说在一个always块
同一个always块或者说initial块里
或者同一段程序里不允许
或者说不应该混用这两种赋值语句
我到目前为止还没有见过
在任何一个工程代码里面
出现过这两种混用的
虽然在语法上不禁止混用这些语句
但是在工程里
我们要求这两个是不能混用的
第二个 从实现效果来说
通常我们规定:组合逻辑电路设计的时候
用阻塞赋值语句
时序逻辑电路,例如D触发器这样的
电路设计的时候,用非阻塞赋值语句
这是工程的要求
后面两个要求
我们结合到后面的具体的例子
再来说到底怎么做
现在
因为有些概念还没有提出来
可能反而不方便大家对这些概念的理解
所以我们现在先不解释了
我们先来看一下阻塞赋值语句
阻塞赋值语句的符号是等于号
具体的例子我们可以看
这就是一个阻塞赋值语句的例子
刚才已经说了
阻塞赋值语句很像C语言
在上一句执行完之前不会执行下一句
比如说下面这个例子有三句话
一定是等到第一句执行完了
才执行第二句话
它的用处是用来设计组合逻辑电路
一般来说不允许在时序逻辑电路里面
使用阻塞赋值语句
这是工程上的要求
并不是语法上要求
我们来看一下
下面这段代码
这是一段组合逻辑电路
这段电路的功能是什么
对照这三行代码
我们来看一下
首先是把a与上b赋给信号x
然后再把x和b或一下
最后是把a又赋给x
这是阻塞赋值语句
我们说过它跟C语言的顺序执行的概念
是完全相同的
所以我们可以直观地理解
第一句话执行完了以后
x的值是什么
x等于a与上b
或者写成a乘b也行
第一句话执行完了以后再执行第二句话
在第二句话开始执行的时候
这个x的值已经变成了a与上b 对吧
所以x或b就变成了a与上b 再加上b
这个逻辑值
大家应该都能够根据逻辑优化的定理得出
它的值就是b
所以这句话执行完了以后
y的值就是b了
然后再执行第三句话
第三句话的话把a又赋给了x
所以x值是a
所以这样执行完了以后
最终结果是什么
最终结果就是:y的值是b
x的值是a 是吧
这就是顺序执行完了以后最终的结果
我们再仔细看一下这段代码
按照我们前面对组合逻辑电路的定义
对于一个组合逻辑电路而言
它的输入信号是a和b
我们只要写成a或b就可以了
为什么还要“或x”呢
这是硬件描述语言的要求
在这块电路里
x是电路的一个内部信号
它是a与b输出产生的计算结果
产生的一个内部信号
但这个内部信号
又被送回到另一个电路的输入端
作为它的输入信号
也就是说这个组合逻辑电路
实际上它的真正的输入信号还包含了x
从某个角度来说
还包含了x
所以我们在这个地方要
把x也放到敏感表里面
从另一个角度
如果我们要严格地定义敏感表
always后面的敏感表
我们应该这么定义
所有在赋值语句右边出现的信号
所以在赋值语句等号右边出现的信号
我们都要放到敏感表里
如果有if和case语句的话
if和case语句里面的条件信号
也都需要放到敏感表里
这样才是一个完整的
没有错误的组合逻辑电路的描述
我们下面再来看一下非阻塞赋值语句
非阻塞赋值语句的(赋值)符号是小于等于这个符号
具体形式就像这么一个形式
前面我们已经说了非阻塞赋值语句
它把计算赋值号右边的表达式的计算过程
跟赋值过程是分开的
这是第一个特点
第二个特点
赋值语句在执行的时候
相当于这三句话是在并行执行的
相当于是在并行执行
那这是什么意思呢
在以下面这段代码为例
我们来看一下
相当于是赋值语句在执行的时候
always块在执行的时候
这三句话同时在执行
在执行的时候
先把赋值语句右边的
这3个表达式全部计算出来以后
然后再同时分配这3个信号
换句话说
赋值语句在执行的时候
这个always块在执行的时候
到第一句话的时候
把ab的值算出来了
这时候就是a与上b
然后等到执行赋值操作的时候
赋值操作是要稍微等一会才执行的
执行赋值操作的时候
这个a与b计算值就赋给了x
执行第二句话的时候
是把x和b的值去做或运算
这时候x的值是什么
x值到底是什么
是a与上b
还是什么别的值
我们现在还不知道 对吧
还不知道
所以这是一个悬念或者说疑念
我们先放在这
然后再执行第三句话
第三句话是把a的值赋给x
第三句话是把a的值赋给x
我们先写在这
x值就等于a
那从前到后我们看
对x赋了两次值
对y赋了一次值 是吧
对y的这个赋值
我们先不管
先看对x的两次赋值
因为我们对x赋了两次值
一次是赋了a与b
一次是赋了a
在实际操作的时候
是以后一次赋值为有效(操作)
前一次就会忽略掉了
所以最终x的值是a
然后我们回过头来再看这句话
因为x值是a
x最终值是a
所以这些语句执行的结果是y等于a加上b
y等于x(的值)是a
而不是a与上b
所以最终y的值是a加上b
这样的结果
最终的结果就是说
y是a加b x是a
分析的结果和过程好像有一点点诡异
对吧
当然
实际的真实的分析并不像
我刚才这样有点猜测性的分析
实际的真实分析(过程)是一个更复杂的过程
要用到一个基于delta延时的概念
去进行特别精细的分析
才能得到刚才所说的这样的结果
总结一下
如果用非阻塞赋值语句
在这样的组合逻辑电路设计
这种方式去进行描述
你得到的电路结果的值是很难去预测出来的(结果不直观)
或者说很难预期的
这是最终实际的结果
从工程来说
我们就要求在组合逻辑电路设计的时候
大家用阻塞赋值语句
时序逻辑电路设计的时候用非阻塞赋值语句
刚才我们是用仿真的概念
来解释阻塞赋值语句和非阻塞赋值语句
它们之间的差异
下面我们再从最终它们实现的
电路的角度去看一下
非阻塞赋值语句和阻塞赋值语句
它们俩的差异
我们看一下幻灯片
上面是我们刚才列出来的阻塞赋值语句
下面是我们刚才列的非阻塞赋值语句
我们说任何一条语句都对应一个电路模块
都会映射出一个电路模块
实际上的电路
就是这个样子
对于第一条语句
x等于a与上b会映射出一个与门
这个门画的是一个方框
不是特别好看
x的值是a与上b,(所以)映射出一个门
第二个的话是y又是把a与上b以后
在或上一个b
也就是把a与b的这个结果再去或上一个
再去或上一个b
或上一个b 得到输出信号y
这是直接映射出来的结果
第三句话又把a赋给x
所以的话最终x这根线又是从a这里进来
a点进来
所以这是上面这三条语句所得到的电路
在这里面
x这个信号
大家可以把它想象成
是一个临时使用的中间信号
或者说一个临时变量
在这个阶段是一个临时变量
在前两句话的时候
它起的作用相当于一个临时变量
在最后一句话的时候
x(才是)一个真的输出信号
这是直接映射出来的电路
长的样子
下面这种非阻塞赋值的方式
第一句话 a与上b
我们就没法直接按照这种意思理解了
因为这种映射的话
应该说这种执行方式
这种描述方式并不是我们所期望的这种方式
或者说我们所允许的方式
所以映射出来的电路
有时候是很难理解的
在这个电路里
y的值是a或上b
然后x的值会直接从输入端引过去
这是我们根据分析所得到的电路
最右边的这两张图实际上
是软件所产生出来的电路
跟我们分析的结果是一致的
总结一下
或者说小结一下
非阻塞赋值语句
我们一般来说不允许用于组合逻辑电路设计
当你的代码描写的不太好的时候
或者说描述的不太准确的时候
综合出来的电路
或者说实现的结果是难以预测的
我们说非阻塞赋值语句
通常是用来设计时序逻辑电路的
我们再来看一下刚才这样两段代码
如果变成时序逻辑电路的话会是什么样子
时序逻辑电路概念我们还没有讲
我们这大概先看一下
大致先看一下
右边的这个always @
是clock的上升沿,表示在时钟信号的上升沿
表示这段代码对应的
是一些D触发器所构成的电路
这个D触发器的时钟信号是clock信号
在这段代码里
分别把a赋给x
然后x赋给y,y赋给w
每一条赋值语句都会映射出一个D触发器
比如说第一句话
映射出一个D触发器
表示这个D触发器的输入信号是a
输出信号是x
第二句话又映射出一个D触发器
表示它的输入信号是x
d触发器的输出信号是y
第三句话
同样又是一个D触发器
所以下面这种方式会映射出3个D触发器
他们是一个级联的过程
级联的关系
下面这句话
是我们所不建议或者是所不允许的方式
在这种方式里
最终只产生一个D触发器
它的输出信号是w
在这里边x和这个y都变成了一个中间信号
从某个角度来说就变成了中间信号
或者说被优化掉了
被忽略掉了
所以跟刚才的结论一样
非阻塞赋值语句
我们是用于时序逻辑电路设计的
如果在时序逻辑电路的设计里面
用了阻塞赋值语句
最终电路出来的电路形式
有可能是你所难以理解的一种
不可预测的一种电路形式
我们再来看一个实际的例子
用阻塞赋值语句去实现组合逻辑电路
这个描述方式
在我们前面举例的时候已经讲过
前面
我们只是看了代码
没有看具体的电路
在这个电路里面
我们看一下实际产生出来电路是什么样子
大家
要跟着我的思路看一下
电路的分析过程
module表示我们有这么一个电路
它的名字叫test
里面有个输出信号叫dout
dout信号是reg类型
那我们就知道
reg类型信号应该是always块的输出
然后我们(检查),不错
确实是有一个always块的输出信号叫dout
反过头来再去检查的话
dout信号在定义的时候
也应该定义成是reg类型
然后再去看这个always块
我们一看敏感表就知道了
因为是对信号的值判断
所以这块描述的应该是组合逻辑电路
对吧
描述的是组合逻辑电路
然后有一个if语句
表示这里面描述的
应该是一个2选1的多路选择器
根据b的情况
决定把a送给dout
或者是c送给dout
所以这个always块
所映射出来的电路就是右边这个电路
是一个2选1的多路选择器
他的选择信号是b
根据b的值是1的话
把a送给dout
如果是0的话
把c送给dout
我们可以看一下
在这样的语句块里面
要求把if语句的两个分支
既然是组合逻辑电路设计
我们就要把if语句的两个分支都写全了
并且在两个分支里面
都要对dout信号进行赋值
这样
才是组合逻辑电路的描述
我们再关注一下always块的敏感表
按照我们的要求
如果是组合逻辑电路
必须把敏感表里的信号
电路的输入信号
以这条语句为例
电路的输入信号就是赋值语句的右边的
所有信号都要写进去
abc都要写进去
这样
代码的描述才是正确的
如果我们漏掉了一个信号
比如说abc的c漏掉了
那会是什么情况
我们说对电路的理解要从两个层次考虑 对吧
一个是从仿真的角度考虑
一个是从综合的角度考虑
如果漏写了一个信号
这段代码没有违反任何Verilog语言
没有违反Verilog的任何规则
所以这段代码是一段合法的代码
仿真的结果没有任何问题
仿真也可以进行下去
仿真的结果也可能是对的
但是把这段代码送到综合工具
也就是说去产生具体电路的时候
综合工具的检查会更严格
它会检查
输入信号c为什么没有写到敏感表里面
然后综合工具就会给出警告
在相应的窗口里面会给出一个警告
告诉大家这个c没有被放进(敏感表)
这是一个"warning"
不是错误
然后让设计人员去仔细的检查
是不是哪些地方发生了遗漏
这就是我一开头对大家提出的要求
我们在做设计的时候
作为设计人员得特别认真仔细
任何一个遗漏都不能够忽视
在这里面工具给出一个warning
那你就要去检查warning问题
到底是怎么回事
在大的设计里面有可能
软件会给你报告出几百个上千个warning
那我们就得耐心的一个一个去检查warning
如果是一个大的工程项目
通常我们还(需要)填一个文件,填一个表
去写出这个warning为什么可以被忽略
这个warning为什么
可以不用关注就可以往下去走(流程)
将来项目实施下去以后
如果成功了
肯定是没有什么问题
如果错误了 找到原因了
追查到这个表里面
在这个地方warning你没有去仔细检查的话
这个责任就认定是你的问题了
具体到我刚才那个一开头
我说的例子
我们的150万的损失就有人来扛了
在实际使用always语句
或者说实际使用always块
来设计组合逻辑电路的时候
我们通常要避免出现latch
或者说代码设计的不好的时候
有可能会把组合逻辑电路
变成一个时序逻辑电路
什么时候才会出现这样情况?
我们来看一下这个例子
第一个例子
这是一个always块里面用了一个case语句
想设计
作者的预期是要设计一个多路选择器
是一个多位的多分支的多路选择器
那我们看一下
输入信号有什么
输入信号有0 1 2(这些分支)
后面
没有3或者4、5、6、7等等这样的分支 对吧
按照这样的描述
我们前面说了对于case语句
如果有一个分支没有写的话
表示在其他情况下
也就是说D是3的时候
这个值是3的时候
bcd这个值是3的时候
输出信号out要保持原来的值不变
怎么才能保持原来的值不变?
就意味着电路最终
在综合出实际电路的时候
必须增加一些存储单元
也可能是D触发器
也可能是锁存器
因为这是一个电平型的电路
所以产生出一个锁存器
让电路具备了一定的保持功能
才能够跟这段代码的功能一一对应上
不管怎么说
这跟我们作者所预期的方式不同了
预计要实现的电路的性质变了
原来我们预期实现的是一个组合逻辑电路
现在变成了一个时序逻辑电路
所以这段代码就是错的
从工程来说
一个设计人员设计完源代码以后
另一个人检查的时候
通常也是按照这种方式检查
一发现错误
一发现这个地方少一个default
那就不用再去仔细分析了
把这个代码打回去重新修改
因为这段代码肯定是有错的
具体错成了什么样子
就没有意义去关心(分析)了
因为一定要改成正确的才行
下面这段代码 同样
你看这个always @后面是若干个信号的或
所以我们知道作者预期的是
设计人员预期的是要设计一个组合逻辑电路
下面我们再仔细检查if
用if语句设计一个类似2选1的多路选择器
是吧
if的else分支没有写
意味着什么呢?
在条件不满足的时候
out信号要保持原来的值不变
电路就变成这个样子
除了有一个或门之外,产生a或者b之外
out信号还要有一个锁存器
来保存在其他时候的值的不变
所以电路的性质就发生了变化
从组合逻辑电路变成了时序逻辑电路
所以我们说怎么才能避免呢?
避免这种情况
也就是说
我们的case语句的这种默认分支
if-else分支一定要写上
碰到条件判断语句的时候
一定要把所有的分支也都要选上
这是第一种常见的情况
第二种容易出错的情况
对于复杂一点的电路而言
我们在一个case语句里边的某一个分支里面
通常不会只对一个信号进行赋值
换句话说
电路不会只产生一个输出信号
在这个例子里
电路产生了两个输出信号
case分支也都写全了
一共4个分支
bcd的话一共有4个分支
但是在第三个分支里
只对out1进行了赋值
没有对out2赋值
这表示什么意思呢?
也就是说
在4种情况下
out1都有明确的值
但是out2在第三种情况下
它的值要保持原来的值不变
out2要保持原来的值不变
所以最终在out2的电路产生逻辑里面
会存在一个触发器
导致了这个电路的性质发生了变化
所以我们说这段代码也是有错误的
怎么才能避免发生类似刚才这样的错误
我们说
第一种方法就是在case语句里
我们一定要写default分支
保证你无论如何
不管在什么情况下
对你要产生的信号
一定都会有一个固定的值
第二种写法是在case语句之前
在真正进入到case语句之前
先对要产生的输出信号赋一个值
有点像C语言里面对信号(变量)要赋初值
但是注意
通常写的时候是在紧挨着case语句这个地方
对case语句要产生的这些输出信号
在case语句之前
先都赋上值
这个值相当于是在case语句里边
写了default分支
这是两种写法
各个公司或者个工程项目的要求不太一样
我们当然推荐是写这个default写法
当然有些项目里面出于别的原因考虑
或者说仿真和综合的一致性等等各方面考虑
会推荐第二种写法
下面我们稍微小结一下
我们现在已经讲完了
Verilog语言设计的大多数内容
包括怎么定义一个模块
怎么定义信号的端口以及信号的属性
以及怎么设计组合逻辑电路
用两种手段
一个是连续赋值语句
一个是always块设计组合逻辑电路
在always块里
我们又可以用条件判断语句、case语句
以及两种赋值语句
描述电路的内部行为
当然在组合逻辑电路设计的时候
我们只允许用阻塞赋值语句
非阻塞赋值语句
我们要留到时序逻辑电路设计里才用
-软件下载说明
-a) 集成电路的应用及市场
-a) 集成电路的应用及市场--作业
-b)集成电路的制造过程
-b)集成电路的制造过程--作业
-c)从CPU的发展看IC的进展
-c)从CPU的发展看IC的进展--作业
-d)从行业的发展看IC的进展
--Video
-d)从行业的发展看IC的进展--作业
-e)从ISSCC看IC的发展方向
--讲课视频
-e)从ISSCC看IC的发展方向--作业
-a)数字系统的实现方法 (ASSP/FPGA/ASIC的对比)
--讲课视频
-a)数字系统的实现方法 (ASSP/FPGA/ASIC的对比)
-b)组合逻辑电路
--Video
-2、数字集成电路设计方法--b)组合逻辑电路
-c)时序逻辑电路(1)
-d)时序逻辑电路(2)
-2、数字集成电路设计方法--d)时序逻辑电路(2)
-a)Verilog的历史和学习要点
--讲课视频
-b)端口、信号及数据类型
--讲课视频
-b)端口、信号及数据类型--作业
-c)逻辑电平及数据操作
--讲课视频
-3、Verilog语法--c)逻辑电平及数据操作
-d)Assign 语句
-e)Assign 举例
-f)Always
-f)Always--作业
-g)阻塞与非阻塞赋值
--Video
-3、Verilog语法--g)阻塞与非阻塞赋值
-h)D触发器的描述
--Video
-i)时序电路的设计
--Video
-i)时序电路的设计--作业
-j) 面向测试的Verilog语法(1)
-k) 面向测试的Verilog语法(2)
-k) 面向测试的Verilog语法(2)--作业
-a)电路设计实例1
--Video
-b)电路设计实例2
--讲课视频
-b)电路设计实例2--作业
-c)电路设计实例3
--讲课视频
-Modelsim仿真
-a)综合及相关基本概念
--Video
-a)综合及相关基本概念--作业
-b)综合及优化
--Video
-c)门级仿真
--门级仿真
-d)Quartus综合及分析(1)
--讲课视频
-e)Quartus综合及分析(2)
--讲课视频
-e)Quartus综合及分析(2)--作业
-f)Quartus综合及分析(3)
--Video
-g)Quartus综合及分析(4)
--Video
-g)Quartus综合及分析(4)--作业