减少运算符无法正常工作

时间:2015-11-10 01:15:11

标签: verilog counter system-verilog hdl fsm

我有一个FSM设计,它使用计数器在特定状态内计数并保持在那里,直到表达式&counter产生TRUE,但是当它完成时(得到1111 ... 111 - 检查通过模拟器)&counter永远不会进入HIGH并像无限循环一样停留在那个状态。如何克服这个问题?我将提供我的错误部分代码。我是verilog的新手,所以希望这是一个容易捕捉的问题。

reg [2:0] State, NextState;
reg [20:0] counter;
reg EN;

//State register 
always @(posedge Clk) begin 
    if(Rst) begin
        counter <= 0;
        State <= S0;
    end
    else begin
        State <= NextState;
        counter <= EN ? counter + 1 : 0;
    end
end

//Next State Logic
always @(State, COL)
    case(State)
        S0: begin 
            NextState <= S1;
            EN <= 0;
        end
        S1: begin 
            if(ROW) begin
                if(COL) begin 
                    if(&counter[18:0]) begin // <--- THIS EXP. NEVER GETS 1
                        PrevCOL <= COL;
                        NextState <= S3;
                        EN <= 0;
                    end
                    else begin
                        EN <= 1;
                        NextState <= S1;
                    end
                end
                else
                    NextState <= S2;
            end
            else
                NextState <= S0;
        end
        S2:  NextState <= S1;
        S3: begin
            if(PrevCOL == COL) begin
                if(&counter) begin // <-------- THIS EXPRESSION NEVER GETS 1
                    NextState <= S4;
                    EN <= 0;
                    case(ROW)
                        8: 
                            case(COL)
                                8: ID <= I0;
                                4: ID <= I1;
                                2: ID <= I2;
                                1: ID <= I3;
                            endcase
                        4:
                            case(COL)
                                8: ID <= I4;
                                4: ID <= I5;
                                2: ID <= I6;
                                1: ID <= I7;
                            endcase
                        2:
                            case(COL)
                                8: ID <= I8;
                                4: ID <= I9;
                                2: ID <= I10;
                                1: ID <= I11;
                            endcase
                        1:
                            case(COL)
                                8: ID <= I12;
                                4: ID <= I13;
                                2: ID <= I14;
                                1: ID <= I15;
                            endcase
                    endcase
                end
                else begin
                    NextState <= S3;
                    EN <= 1;
                end
            end
            else
                NextState <= S0;
        end
        S4: NextState <= S0;
    endcase

错误区域用注释表示。为什么它不起作用?

谢谢!

2 个答案:

答案 0 :(得分:3)

对于组合逻辑,请避免自己编写灵敏度列表。你应该用这个代替:

always @(State, COL)

使用

always @(*)

您也可以使用always_comb。基本上,当计数器递增时,第二个always块不会执行。它仅在State或COL信号发生变化时执行。

其次,您不应在组合逻辑中使用非阻塞分配(&lt; =)。使用阻塞赋值(=)替换第二个“始终”块中的所有内容。

答案 1 :(得分:2)

正如Convergent指出的那样。 always @(State, COL)应为always @*(与always @(*)同义)或SystemVerilog的always_comb,并且阻止分配(=)应在组合块中使用;不是非阻塞的(<=)。

另一个问题是ENPrevCOLID中的推断复数锁存器。当我说锁存器时,我指的是电平敏感(有源高/低)锁存器。当我说翻牌时,我指的是边缘触发的触发器。

推断锁存器是指未为reg分配始终块内所有可能路径的值的情况。就其本身而言,锁存器本身并不是一件坏事,数据需要设置和保持时间,并且启用引脚无故障。通常,没有必要并被许多人视为设计错误。 FPGA通常具有更多的触发器和逻辑门然后锁存器(锁存器使得形式意图逻辑门的时序变得更差,然后是专用锁存器)。如果使用锁存器,最好保持它尽可能简单,将其放在与解码逻辑分开的始终块中,并使用非阻塞分配。 Verilog只涉及锁存器; lint和LEC(逻辑等效检查)将针对可能的设计错误发出警告。 SystemVerilog允许显式锁存;链接和LEC不会发出警告。以下示例。总之,仅在需要时使用锁存器并保持简单。

// Verilog clean implicit latch
always @* begin
  if (lat_en)    // requirement: 'lat_en' must be glitch free
    lat <= data; // requirement: 'data' must respects setup/hold time
end
// SystemVerilog explicit latch
always_latch begin
  if (lat_en)    // requirement: 'lat_en' must be glitch free
    lat <= data; // requirement: 'data' must respects setup/hold time
end

推断的复杂锁存器比简单的锁存器更危险。复数锁存器的使能引脚或数据基于其他组合逻辑或锁存器。很难满足时机和经常出现故障。这意味着您的代码可以运行一段时间,然后意外地获取错误的数据。它们很难调试作为时序窗口,当应用合成和SDF注释时,可能会发生故障,并且可以根据其他变量再次更改FPGA或芯片。根据事件调度,在0次RLT模拟中甚至可能发生故障。总而言之,仅在需要时使用锁存器并保持简单。

谈到RTL编码风格,我通常遵循Cliff Cummings的2-always阻塞解决方案的建议(一个用于下一个状态组合逻辑,另一个用于同步分配):

在我的个人资料中还有其他链接指向有用的Verilog / SystemVerilog引用。

我建议将ENPrevCOLID放入适当的触发器中,并在组合块中计算出被尊重的next_*分配。

//Synchronous Logic Assignments
always @(posedge Clk) begin // SV: use 'always_ff' instead of 'always'
  if(Rst) begin
    counter <= 0;
    State <= S0;
    EN <= 0;
    PrevCOL <= 0;
    ID <= 0;
  end
  else begin
    State <= NextState;
    counter <= next_EN ? counter + 1 : 0;
    EN <= next_EN;
    PrevCOL <= next_PrefCOL;
    ID <= next_ID;
  end
end

//Next State Logic Calculations
always @* begin // SV: use 'always_comb' instead of 'always @*'
  // Default value assignments
  next_EN = EN;
  next_PrevCOL = PrevCOL;
  next_ID = ID;

  // Update value logic
  case(State)
    // Update NextState and next_* values in here
  endcase
end

always @*的FYI,其中RHS只是常量,然后可能不会在模拟中执行。这个条件没有定义LRM,模拟器的某些创建者决定执行时间0执行,其他人没有。它会正确合成。 SystemVerilog的always_comb更好,因为它将在时间0执行。与always_comb 相比,请参阅IEEE Std 1800-2012§9.2.2.2.2 always @*。使用Verilog,您可以使用assign语句。