• 非阻塞赋值(Non-blocking Assignment)是个伪需求(2)


     
    13hope:
    个人理解,Verilog本身只是“建模”语言。具体到阻塞/非阻塞,只规定了两种赋值语句的行为。所以无论怎么写,仿真器和综合器都不会报错。但是存在两个问题,所描述的行为是否有物理电路与之对应;电路行为在仿真阶段和综合后是否一致。 像是电平敏感always快内使用多个多个非阻塞赋值就没有意义,仿真结果不可信
     
    wjcdx:
    > 个人理解,Verilog本身只是“建模”语言。
     
    Verilog是个大杂烩(中性,非贬义),包含了支持验证、综合、测试的语法结构,比如:initial, repeat, forever, task等。
     
    > 具体到阻塞/非阻塞,只规定了两种赋值语句的行为。所以无论怎么写,仿真器和综合器都不会报错。但是存在两个问题,所描述的行为是否有物理电路与之对应;电路行为在仿真阶段和综合后是否一致。
     
    感谢提醒,可能就是与综合后电路一致的原因。Verilog发展历经了好多个版本,有可能是历史原因。
     
    不过,最终要求的不是你建模的电路和综合后的电路一模一样,否则不是要写RTL代码,而是要画版图了。最终的要求是综合后的电路符合RTL描述的模型,行为一致。
     
    RTL是一个抽象的建模层次,而不是物理的建模层次,无法与综合后的电路完全对应:比如+-x÷这些operators都不是使用电路描述的,但综合后却有电路,怎么对应呢?
     
    还有综合器要对RTL的代码做优化,有一些电路不必要的会被优化掉,效率不高的会被优化成更高效的方式。
     
    再有,很多时候使用的是工具提供的库里面的模块,这个是黑盒的,也不清楚里面的电路。
     
    今天发现一个2001版本的新特性,可以佐证我的观点:
     
    从这个里面可以看到,register不是物理的寄存器(DFF)。而是一个变量数据类型(variable data type).
    但是又遗留了明显针对物理寄存器的非阻塞赋值,做的不彻底。NBA增加了学习的难度,进而在使用中也会产生问题。
     
    建模方法(Modeling Methodology)应该尽量简单,把这部分工作放在综合器里面去做更合适。能写综合器的人,都是对电路特性更了解的人,出错的概率更小。
     
    RTL层建模需要的只是对变量赋值(variable assignment),甚至连 连续赋值 和 过程赋值 都不需要区分。这样的区分有助于理解,却不是必须。
     
    > 像是电平敏感always快内使用多个多个非阻塞赋值就没有意义,仿真结果不可信
     
    这个链接里有很多NBA在一个always块内:
    https://github.com/alexforencich/verilog-ethernet/blob/master/rtl/arp_64.v
    cache_query_request_ip_reg <= cache_query_request_ip_next;
    outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next;
    outgoing_arp_oper_reg <= outgoing_arp_oper_next;
    outgoing_arp_tha_reg <= outgoing_arp_tha_next;
    outgoing_arp_tpa_reg <= outgoing_arp_tpa_next; 
    wjcdx:
    > 像是电平敏感always快内使用多个多个非阻塞赋值就没有意义,仿真结果不可信
    昨天没看清楚,给的例子是边沿敏感。你说的是电平敏感。
     
    其实,“电平敏感”这个词不准确,准确的说法是“电平变化敏感”:
    always @(a, b, c)
    begin
      if (a) begin
      end
    end
     
    这个并不是只在a为1时执行,在a为0时也执行。所以不是对a的高电平敏感,而是对a的电平变化敏感。
     
    always在Verilog里面没有区分,在SystemVerilog里面区分的就比较细。上面的always在SystemVerilog里面应该是always_comb, 而不是always_seq,综合出来是组合电路,里面使用的是wire,而不是reg.
     
    对wire使用非阻塞赋值是不正确的。这也是我为什么说在RTL建模时把NBA这个概念拿掉的原因:理解时容易混淆,使用时容易出错。
     
    13hope:
    很细致的回复,基本都赞同。只是最后一点,GitHub上的代码是always @(posedge clk)内部,用了多个<=赋值当然没问题,综合后就是一堆DFF。
     
    至于我说的“没意义”,比如下面代码,a “电平变化敏感” (双沿),首先,想描述的一定是组合逻辑。其次,语义上非阻塞赋值是在当前仿真step之后同时给temp和b赋值,也就是说a值改变触发该always,其第二条在对temp采样时,temp还未更新,所以b始终等于a上一次“变化前”的值。这个“行为”没有任何组合电路与之对应(组合电路没有记忆功能)。
    always @(a) begin
      temp <= a;
      b <= temp;
    end 
     
    至于综合后,b确实是一根wire直接连到a,temp可能被优化掉,但仿真结果与综合不一致。
    Verilog非常容易写出仿真与综合行为不一致的电路,所以有很多guideline帮助构建可综合电路。
     
     
    wjcdx:
    如果不存在NBA,可能就不会存在这样的问题了。
    如果不存在NBA,则continuous和procedural assignment都只是简单的使用等号“=”来表示。
     
    1.
    always @(a) begin
      temp = a;
      b = temp;
    end
     
    这样的写法意思是,如果a变化,则把a的值赋值给temp, 然后在把temp的值赋值给b。
    这是简单的组合逻辑,可以化简为:
    assign b = a;
     
     
    2. 如果想让b每次都取a上一次的值,则可以写成:
    always @(a) begin
      b = temp;
      temp = a;
    end
     
    仿真和综合针对这个写法,不清楚是否一致?
     
    仿真器想必不会出错,综合器如何决定这两条赋值语句能否调换顺序呢?要做数据流分析。
    如果两条赋值语句各自的left hand side与对方的right hand side存在关联,或者反过来存在关联的话,就说明存在顺序关系。如果没有,则不存在顺序关系,两条赋值语句可以分开综合。
     
    综合器把这个事情做了以后,就可以把建模人员解放出来,只关心值的传递这个核心问题即可,
    而不用考虑仿真时值如何赋值,综合后电路如何赋值,这样的问题。
     
    建模人员保证值传递的逻辑时正确的。仿真器保证仿真的正确性,综合器保证综合出的电路符合模型要求的值传递逻辑。
    各司其职,正确的人做正确的事,专业的人做各自专业领域的事,这样可以减少出错。
     
    当然,建模人员了解仿真器和综合器的工作原理是有益的。
     
    回到这个例子上来,如果建模人员考虑的是变量之间的值如何传递,
    而不是仿真时什么时候采样,什么时候赋值,出错的可能性就小多了。
  • 相关阅读:
    Linux中/etc目录下passwd和shadow文件
    Linux基本命令
    Linux目录结构说明与基本操作
    如何用虚拟机VMware Workstation安装CentOs-7
    VPP源码分析及流程图
    VPP环境搭建及配置
    【转】智能指针的交叉引用问题及解决方法
    二叉树的前 中 后 层序遍历
    排序算法
    C和C++的一些区别
  • 原文地址:https://www.cnblogs.com/wjcdx/p/9705266.html
Copyright © 2020-2023  润新知