减少运算符无法正常工作

问题描述 投票:0回答:2

我有一个FSM设计,使用计数器在特定状态内计数并保持在那里直到表达式&counteryields 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

越野区用评论表示。为什么不工作?

谢谢!

verilog counter system-verilog hdl fsm
2个回答
3
投票

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

always @(State, COL)

always @(*)

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

其次,不应在组合逻辑中使用非阻塞赋值(<=)。使用阻塞赋值(=)替换第二个“always”块中的所有内容。


2
投票

正如Convergent指出的那样。 always @(State, COL)应该是always @*always @(*)的同义词)或SystemVerilog的always_comb,阻塞任务(=)应该用在组合块中;不是非阻塞(<=)。

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

推断锁存器是指当reg没有为always块中的所有可能路径分配值时。就其本身而言,锁存器本身并不是一件坏事,数据需要设置和保持时间,并且启用引脚无故障。通常,没有必要并被许多人视为设计错误。 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时执行。参见IEEE Std 1800-2012§9.2.2.2.2always_combalways @*相比较。使用Verilog,您可以使用assign语句。

© www.soinside.com 2019 - 2024. All rights reserved.