So I am pretty new to Verilog and I am trying to write a simple FSM.
Input a
is a push button and by pushing it starts the machine. But every other next state, after initially pressing a
, causes to go back to the start/initial state.
input clk, reset;
input a,b,c;
reg [1:0] state;
reg [1:0] nextstate;
parameter = START=2b'0,
STATE1=2b'1,
STATE2=2b'11;
always @(posedge clk)
if(reset) state <= START;
else state<=nextstate;
always @(state, a)
case (state)
START: begin if(a) nextstate<=STATE1; else nextstate<=START; end
STATE1: begin if(a) nextstate<=START; else if(b) nextstate<=STATE2; else nextstate<=STATE1; end
STATE2: begin if(a) nextstate<=START; else if(c) nextstate<=STATE1; else nextstate<=STATE2; end
endcase
Keeping your finger on a
now means my state is alternating between STATE1
and START
every positive edge of clk
.
How do I fix this?
A simple solution is to track the "edge" of the button, rather than the current state. For example:
reg aPressed, aPrev;
always @(posedge clk)
aPrev<=a;
always@(*)
if (a&&!aPrev) // a is high now, but was low last cycle
aPressed = 1;
else
aPressed = 0;
In this code, aPressed
is asserted only when a
was low last cycle, and is high now (i.e. it was just pressed). If you replace a
with aPressed
in your next state logic, the user will be required to release a
for a least one cycle before aPressed
will trigger again.
always @(*)
case (state)
START: begin
if(aPressed) nextstate=STATE1;
else nextstate=START;
end
STATE1: begin
if(aPressed) nextstate=START;
else if(b) nextstate=STATE2;
else nextstate=STATE1;
end
STATE2: begin
if(aPressed) nextstate=START;
else if(c) nextstate=STATE1;
else nextstate=STATE2;
end
endcase
Side note: it's good practice to use always@(*)
for combinational logic, rather than specifying the sensitivity list (always@(state,a)
)