Verilog_赋值语句和块语句

215 阅读4分钟

我正在参加「掘金·启航计划」

写在前面

本篇简要介绍了非阻塞赋值和阻塞赋值,简单描述了块语句,后续会详细描述非阻塞赋值和阻塞赋值,带大家了解这两种赋值语句的妙处。

笔者才疏学浅,本篇如有缺点和疏漏在所难免,恳请广大读者批评指正。

赋值语句

非阻塞赋值方式和阻塞赋值方式的区别常给设计人员带来问题。问题主要是对“always”块内的reg型信号的赋值方式不易把握。非阻塞赋值并不是马上执行,也就是说,“always”块内的下一条语句执行后,信号的值并没有发生改变,而是保持原来的值,“always”块结束后,才进行赋值。阻塞赋值是马上执行的,也就是说执行下一条语句时,信号的值已经发生了改变。尽管阻塞赋值看起来很直观,但是可能引起麻烦。

非阻塞赋值(<=)

  • 在语句块中,上面语句所赋的变量值不能立即就为下面的语句所用。
  • 块语句结束后才能完成这次赋值操作,而所赋的变量值是上一次赋值得到的。
  • 在编写可综合的时序逻辑模块时,这是最常用的赋值方法。

下图为非阻塞赋值举例

  • RTL代码

20221017195619.png

  • RTL视图

20221017195628.png

例子中的“always”块中用了非阻塞赋值方式,定义了两个reg型信号b和c。clk信号的上升沿到来时,b就等于a,c就等于b,这里用到了两个触发器。赋值是在“always”块结束后执行的,c应为原来b的值。这个“always”块实际描述的电路功能如上方RTL视图所示。

注意:非阻塞赋值符“<=”与小于等于符“<=”看起来是一样的,但意义完全不同,小于等于是关系运算符,用于比较大小,而非阻塞赋值符用于赋值操作。

阻塞赋值(=)

  • 赋值语句执行完后,块才结束。
  • 变量的值在赋值语句执行完后立刻就改变的。
  • 在时序逻辑中使用,可能会产生意想不到的结果。

下图为阻塞赋值举例

  • RTL代码

20221017200901.png

  • RTL视图

20221017200910.png

例子中的“always”块中用了阻塞赋值方式。clk信号的上升沿到来时,将发生如下的变化:b马上取a的值,c马上取b的值(即等于a),生成的电路如上方RTL视图所示,图中只用了一个触发器来寄存a的值,又输出给了b和c。

块语句

块语句通常用来将两条或多条语句组合在一起,使其在格式上看更像一条语句。块语句有两种:一种是begin_end语句,通常用来标识顺序执行的语句,用它来标识的块称为顺序块;另一种是fork_join语句,通常用来标识并行执行的语句,用它来标识的块称为并行块。

顺序块

顺序块有以下特点:

  • 块内的语句是按顺序执行的,即只有上面一条语句执行完后下面的语句才能执行。
  • 每条语句的延迟时间是相对于前一条语句的仿真时间而言的。
  • 直到最后一条语句执行完,程序流程控制才跳出该语句块。

顺序块的格式如下:

20221017202440.png

并行块

并行块有以下特点:

  • 块内语句是同时执行的,即程序流程控制一进入到该并行块,块内语句则开始同时并行地执行。
  • 块内每条语句的延迟时间是相对于程序流程控制进入到块内的仿真时间的。
  • 延迟时间是用来给赋值语句提供执行时序的。
  • 当按时间时序排列在最后的语句执行完后或一个disable语句执行时,程序流程控制跳出该程序块。

并行块的格式如下:

20221017203155.png

块名

在Verilog中,可以给每个块取一个名字,只需将名字加在关键词begin或fork后面即可。这样做的原因有以下几点:

  • 可以在块内定义局部变量,即只在块内使用的变量。
  • 可以允许块被其他语句调用,如disable语句。
  • 在Verilog语言里,所有的变量都是静态的,即所有的变量都只有一个唯一的储存地址,因此进入或跳出块并不影响存储在变量内的值。

起始时间和结束时间

在并行块和顺序块中都有一个起始时间和结束时间的概念。对于顺序块,起始时间就是第一条语句开始被执行的时间,结束时间就是最后一条语句执行完的时间。而对于并行块来说,起始时间对于块内所有的语句是相同的,即程序流程控制进入该块的时间,其结束时间是按时间排列在最后的语句执行结束的时间。

当一个块嵌入另一个块时,块的起始时间和结束时间是很重要的。至于跟在块后面的语句只有在该块的结束时间到了才开始执行。也就是说,只有该块完全执行完后,后面的语句才可以执行。

在fork_join块内,各条语句不必按顺序给出,因此在并行块里,各条语句在前还是在后是无关紧要的。