Verilog 由于时钟更新当前状态的问题,此FSM机器中的状态变化过快

Verilog 由于时钟更新当前状态的问题,此FSM机器中的状态变化过快,verilog,state-machine,Verilog,State Machine,我正在用verilog实现一个有限状态机,我遇到了一个问题。然而,我知道问题是什么,但我不知道如何解决它 这是我当前的代码: module moore_SM(clk, rstn, btn, z, rstLED, state); //Port Assignments input clk, rstn; input [2:0] btn; output z; output reg rstLED; output reg [5:0] state; //Internal Po

我正在用verilog实现一个有限状态机,我遇到了一个问题。然而,我知道问题是什么,但我不知道如何解决它

这是我当前的代码:

module moore_SM(clk, rstn, btn, z, rstLED, state);

  //Port Assignments
  input clk, rstn;
  input [2:0] btn;
  output  z;
  output reg rstLED;
  output reg [5:0] state;

  //Internal Port Assignments
  reg [1:0] w, x; //NOTE: This is typically the input in FSM, 
                  //but it is internal because there is a conversion from btn to w.
  reg [2:0] y, Y; //Present state and next state respectively
  reg [2:0] pstBtn; 
  reg [31:0] cnt;

  //Define some parameters
      //Input Type (i.e. A, B or C)
      parameter [1:0] A = 2'b00, B = 2'b01, C = 2'b11, DC = 2'b10; //DC => don't care - shouldn't affect FSM

      //State (i.e. S1, S2, S3, S4, S5 or S6)
      parameter [2:0] S1 = 3'b000, S2 = 3'b001, S3 = 3'b010, S4 = 3'b011, S5 = 3'b100, S6 = 3'b101;

  initial begin 
      state = 0;
  end

  //Determine which button is active
  always @(*)
      begin
            case(btn)
                3'b110: begin w = A; end
                3'b101: begin w = B; end
                3'b011: begin w = C; end
            //  default: w = DC;
            endcase
      end

  //Define the next state and output combinational circuits
  always @(w,y)
    begin
        case(y)

            S1: begin
                    state = 6'b000001;
                    if(w == A) begin Y = S2; end
                    else begin Y = S1; end
                 end

            S2: begin
                    state = 6'b000010;
                    if(w == B) begin Y = S3; end
                    else begin if(w == A) begin Y = S2; end
                    else Y = S1; end
                 end

            S3: begin
                    state = 6'b000100;
                    if(w == A) begin Y = S2; end
                    else begin if(w == B) begin Y = S4; end
                    else Y = S1; end 
                 end

            S4: begin
                    state = 6'b001000;
                    if(w == A) begin Y = S5; end
                    else begin Y = S1; end
                 end

            S5: begin
                    state = 6'b010000;
                    if(w == A) begin Y = S2; end
                    else begin if(w == B) begin Y = S3; end
                    else Y = S6; end
                 end

            S6: begin 
                    state = 6'b100000;
                    if(w == A) begin Y = S2; end
                    else begin Y = S1; end
                 end

            //default: Y = 3'b111;

        endcase

      end

    //assign z = (Y == S2);

  //Define the sequential block
  always @(posedge clk)
    begin
        y <= Y;
    end

endmodule
注意:我相信解决方案最终将涉及修改按钮的处理方式。当我释放一个按钮时,这也注册为一个更改。因此,我不得不注释掉默认情况,因为它也会搞乱机器的状态

更新:我编写了上面发布的FSM的简化版本,以尝试调试代码。它尝试检测序列AB

module moore_SM(clk, btn, rstN, state, rstLED);
//Port assignments
input clk;
input rstN;
input [2:0] btn;
output reg rstLED;
output reg [5:0] state;

//Internal Port Assignments
reg [1:0] w;
reg [2:0] y, Y;
reg [2:0] pstBtn;

//Define some parameters
parameter [1:0] A = 2'b00, B = 2'b01, C = 2'b11, DC = 2'b10;
parameter [2:0] S1 = 3'b000, S2 = 3'b001, S3 = 3'b010, S4 = 3'b011, S5 = 3'b100, S6 = 3'b101;

//Initialize some values to prevent Quartus from doing
initial 
    begin
        y = S1;
        Y = y;
        state = 0;
        rstLED = 0;
    end

always @(*)
    begin

        if(btn == pstBtn) begin w = DC; end

        else begin
            case(btn)
                3'b110: w = A;
                3'b101: w = B;
                3'b011: w = C;
                default: w = DC;
            endcase
        end

    end

always @(*)
    begin
        case(y)
            S1: begin
                    state = 6'b000001;
                    if(w == A) begin Y = S2; end
                    else begin Y = S1; end
                 end

            S2: begin
                    state = 6'b000010;
                    if(w == B) begin Y = S3; end
                    if(w == DC) begin Y = S2; end
                    else begin Y = S1; end
                 end

            S3: begin
                    state = 6'b000100;
                    if(w == DC) begin Y = S3; end
                    else begin Y = S1; end
                 end
            default: begin state = 6'b100000; Y = S1; end
        endcase
    end



//Update state and check for reset signal
always @(posedge clk, negedge rstN)
    begin
        pstBtn <= btn;

        if(rstN == 0) begin y <= S1; rstLED <= 1; end
        else begin y <= Y; rstLED <= 0; end
    end

endmodule
模块moore\u SM(时钟、btn、rstN、状态、RST);
//港口分配
输入时钟;
输入rstN;
输入[2:0]btn;
输出寄存器;
输出reg[5:0]状态;
//内部端口分配
reg[1:0]w;
reg[2:0]y,y;
注册[2:0]PSTBN;
//定义一些参数
参数[1:0]A=2'b00,B=2'b01,C=2'b11,DC=2'b10;
参数[2:0]S1=3'b000,S2=3'b001,S3=3'b010,S4=3'b011,S5=3'b100,S6=3'b101;
//初始化一些值以防止Quartus执行此操作
首字母
开始
y=S1;
Y=Y;
状态=0;
RST=0;
结束
始终@(*)
开始
如果(btn==PSTBN)开始w=DC;结束
否则开始
案例(btn)
3'b110:w=A;
3'b101:w=B;
3'b011:w=C;
默认值:w=DC;
尾声
结束
结束
始终@(*)
开始
案例(y)
S1:开始
状态=6'b000001;
如果(w==A)开始Y=S2;结束
否则开始Y=S1;结束
结束
S2:开始
状态=6'b000010;
如果(w==B)开始Y=S3;结束
如果(w==DC)开始Y=S2;结束
否则开始Y=S1;结束
结束
S3:开始
状态=6'b000100;
如果(w==DC)开始Y=S3;结束
否则开始Y=S1;结束
结束
默认值:开始状态=6'b100000;Y=S1;结束
尾声
结束
//更新状态并检查复位信号
始终@(posedge clk、negedge rstN)
开始

pstBtn如果按钮输入来自一个开关而不是测试刺激,它将是异步的,因此应通过2个亚稳定触发器进行输入。要更改每个按钮的一个状态,请按“创建边缘检测”

边缘检测将产生1个时钟周期宽的脉冲,每按一次会产生1次转换

reg [2:0] btn0_meta;
always @(posedge clk or negedge rstN) begin
  if (~rstN) begin
    btn0_meta <= 'b0;
  end
  else begin
    btn0_meta <= {btn0_meta[1:0], btn[0]};
  end
end

reg btn0_posedge;
always @* begin
  btn0_posedge = btn0_meta[1] & ~btn_meta[2]; 
end
reg[2:0]btn0\u meta;
始终@(posedge clk或negedge rstN)开始
如果(~rstN)开始

btn0_元状态应该在时钟边缘改变,而不是组合。在我再次查看代码之后,我相信这就是它正在做的。组合块定义下一个状态,
Y
,然后在时钟边缘更新当前状态。
Y
。不,这本书显示“Y”(为了清楚起见,我们通常称之为nextState或next_state)正在组合变化。但是,nextState值仅在以下时钟边缘上注册:
always@(posedge clk)。。。y我意识到了这一点并改变了我的评论。然而,我相信这就是我的代码所做的。如果我在这里错了,请纠正我。为什么要使用
always@(*)
然后使用
always@(w,y)
合成器将始终显示
always@(*)
的行为。使用
@(*)
是更好的练习。我发现按案例区分变量是一种非常糟糕的做法,有些工具不区分大小写。谢谢,我使用了通用的方法,效果非常好。@Mlagma很高兴我们能提供帮助。对于嘈杂的按钮,您可能还需要查找“去抖动”。基本上检查按钮的变化状态是否持续了一定的时间,这减少了在多次按下按钮时检测到开关触点机械弹跳的可能性。不过,有不少FPGA为您解决了这个问题。是的,我知道我的主板使用了硬件去抖动器,所以这对我来说不太重要。然而,由于去Bouncing使用的概念与我在这里学习的内容类似,所以我一直在阅读一些关于这个主题的文章。谢谢您的建议。@Morgan-边缘检测逻辑假设按下按钮时btn[0]=1,否则为0?只是想澄清一下。@JayK我假设按下的按钮是1。如果是另一种方式,则在比较中,开关的信号是反向的<代码>btn0_negedge=~btn0_meta[1]&btn_meta[2]
reg [2:0] btn0_meta;
always @(posedge clk or negedge rstN) begin
  if (~rstN) begin
    btn0_meta <= 'b0;
  end
  else begin
    btn0_meta <= {btn0_meta[1:0], btn[0]};
  end
end

reg btn0_posedge;
always @* begin
  btn0_posedge = btn0_meta[1] & ~btn_meta[2]; 
end