Verilog 由于时钟更新当前状态的问题,此FSM机器中的状态变化过快
我正在用verilog实现一个有限状态机,我遇到了一个问题。然而,我知道问题是什么,但我不知道如何解决它 这是我当前的代码: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
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