非阻塞赋值(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存在关联,或者反过来存在关联的话,就说明存在顺序关系。如果没有,则不存在顺序关系,两条赋值语句可以分开综合。
 
综合器把这个事情做了以后,就可以把建模人员解放出来,只关心值的传递这个核心问题即可,
而不用考虑仿真时值如何赋值,综合后电路如何赋值,这样的问题。
 
建模人员保证值传递的逻辑时正确的。仿真器保证仿真的正确性,综合器保证综合出的电路符合模型要求的值传递逻辑。
各司其职,正确的人做正确的事,专业的人做各自专业领域的事,这样可以减少出错。
 
当然,建模人员了解仿真器和综合器的工作原理是有益的。
 
回到这个例子上来,如果建模人员考虑的是变量之间的值如何传递,
而不是仿真时什么时候采样,什么时候赋值,出错的可能性就小多了。
posted @ 2018-09-26 10:05  wjcdx  阅读(511)  评论(0编辑  收藏  举报