微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > FPGA,CPLD和ASIC > 一个不小心导致的隐秘的组合逻辑环

一个不小心导致的隐秘的组合逻辑环

时间:10-02 整理:3721RD 点击:
最近在写代码的时候发现一个很隐秘的组合逻辑环,一个不小心就会出现,应该是自己代码习惯的问题。代码见下方。刚好如下的两个模块fsm和fack形成了一个组合逻辑环“ack_i -> nstate -> req_o -> ack_i",用modelsim仿真一下代码会直接报错,用quartusII编译综合可以通过,但是会有警告表明产生了组合逻辑环。
仔细分析,产生这种情况的原因是,两个模块中都存在”input -> output 的组合逻辑“,如果这两个电路刚好匹配,就会形成组合逻辑环。但是如果是两个规模较大的模块,发现这种比较隐蔽的组合逻辑环感觉应该不是那么简单。

问题来了:是不是说每个模块里面最好将输入和输出用至少一级寄存器隔开比较安全?因为你不仔细分析不会知道其他人的代码是怎么写的,如果一旦产生了组合逻辑环,再次修改代码可能会导致比较复杂的改动(当然这里举例比较简单)。所以考虑安全性,最好采用这种方式。

请问,我的分析是不是正确的?上面讲的代码方式”每个模块里面最好将输入和输出用至少一级寄存器隔开比较安全“是不是应该采取?还是说”出现问题后再修改设计切断组合逻辑环“?谢谢!
代码如下:"fsm.v":

  1. `timescale 1ns/1ns

  2. module fsm(
  3.          input         clk
  4.         ,input         rst_n
  5.         ,output req_o
  6.         ,input         ack_i
  7.         ,output led_o
  8. );
  9.        
  10.         localparam         F_IDLE         = 2'd0,
  11.                                                         F_S0                =        2'd1,
  12.                                                         F_S1                =        2'd2;
  13.        
  14.         reg        [1:0] cstate;
  15.         reg        [1:0]        nstate;
  16.        
  17.         always @(posedge clk,negedge rst_n) begin
  18.                 if(!rst_n)
  19.                         cstate <= F_IDLE;
  20.                 else
  21.                         cstate <= nstate;
  22.         end
  23.        
  24.         always @( * ) begin
  25.                 case(cstate)
  26.                         F_IDLE:
  27.                                 nstate = F_S0;
  28.                                
  29.                         F_S0:
  30.                                 if(ack_i)
  31.                                         nstate = F_S1;
  32.                                 else
  33.                                         nstate = F_S0;
  34.                        
  35.                         F_S1:
  36.                                 nstate = F_IDLE;
  37.                        
  38.                         default:
  39.                                 nstate = F_IDLE;
  40.                        
  41.                 endcase
  42.         end
  43.        
  44.         //assign req_o = (cstate == F_S0);  //ok;
  45.         assign req_o = (cstate == F_S0) && (nstate == F_S0);  //error;
  46.        
  47.        
  48.         assign led_o = (cstate == F_S1);
  49.        
  50.        
  51. endmodule

复制代码



"fack.v":

  1. `timescale 1ns/1ns

  2. module fack(
  3.          input         req_i
  4.         ,output ack_o
  5. );

  6.         assign ack_o = req_i;
  7.        
  8. endmodule

复制代码



"top.v":

  1. `timescale 1ns/1ns

  2. module top(
  3.          input         clk
  4.         ,input         rst_n
  5.         ,output led_o
  6. );

  7.         fsm                                fsm(
  8.                  .clk                        (clk        )
  9.                 ,.rst_n                (rst_n)
  10.                 ,.req_o                (req        )
  11.                 ,.ack_i                (ack        )
  12.                 ,.led_o                (led_o)
  13.         );
  14.        
  15.         fack                        fack(
  16.                  .req_i                (req        )
  17.                 ,.ack_o                (ack        )
  18.         );
  19.        

  20. endmodule

复制代码

怎么没人遇到么,,,,,



    根据需要来插入适当的寄存器,关键还是设计的时候要把电路结构考虑清楚。



   嗯,谢谢了,设计时考虑好电路结构才是最重要的。

模块划分和模块间接口定义,需要在写代码前完成的。一般在输入和输出各加打一拍,对时序收敛是有帮助的,因为有可能在另外的逻辑里,输出前已经做了很长的组合逻辑,你的逻辑你又有一些组合逻辑,可能会影响时序。当然,能不能加打拍,也要看设计,有些情况,两个模块间的交互要当前或下一周期完成。  总的来说,多个模块间的交互一定要先沟通设计好后,再写代码实现

好像一般规范要求是尽量做到每个模块组合逻辑入,时序逻辑出,目的是为了减小组合逻辑路径长度,也可以避免组合逻辑环路。
这种组合逻辑环路,一般的check工具都会报warning的,很容易检查出来

我公司的代码要求要求赋值规定如下:
always中只允许使用非堵塞性赋值<=,assign只有一种赋值符号=。
所以不允许在always里的使用组合逻辑。所有我也很少仿真像你发的那种例子。
你的状态机避免使用组合逻辑而使用时序逻辑,不能实现你的要求吗?我感觉这样好理解些

这个逻辑环的产生并不是你的代码风格引发的,而是你对你需要的功能并不清晰。
从你的代码来看fsm中的req_o就是ack_i同一条线,岂能ack_i作为因,req_o作为果呢。即便按照你正确的代码来运行,也不会有任何的波形出现的。
就好像你使用一根水管一头放在水桶里,然后你拿着另一头在外面绕了一下又放回了水桶里,会有水流出现吗?
理清楚你需要的时序关系再想如何写代码。

Combinational loop can be easily found by lint tool such as lec, spyglass, etc.



    nLint更好用

你这个出现组合逻辑环很显然啊。
不清楚你单独写个fack.v是什么目的,整个模块就是一个信号的assign,综合后肯定被优化成一根线,这么写有什么意义。fsm.v的output打到这个模块,等于是改个名字再回到fsm.v,显然的组合逻辑环
感觉还是小编一开始没有整体分划好模块和功能定义把,不能写到哪算哪



   谢谢你的回复,这个我只是举一个简单的例子,当组合逻辑环由两个复杂模块甚至更多个复杂模块共同形成时,产生组合逻辑环就比较难发现,我想表达的只是这个意思。当然,事先进行设计的划分非常重要。



   是的,仿真或者综合都会产生warning或者error,但是最好在写代码之前就发现,等到所有的代码都集成好,可能由于时序原因,改动比较复杂。


  谢谢你的回复,我有两个疑问:
  1. 比如像mux这种组合逻辑,如果不用always 和case产生,用assign怎么设计?  2.“你的状态机避免使用组合逻辑而使用时序逻辑,不能实现你的要求吗?我感觉这样好理解些”是什么意思呢?这种三段式状态机不是最常用呢么?



   恩,最后一句话是关键。



   恩,谢谢。



   我只听过没用过lint这个工具,o(╯□╰)o

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top