Why use a state machine
- Efficient sequence control model;
- It is easy to optimize the design using off-the-shelf EDA tools;
- The performance of state machine is stable, and it is easy to form synchronous sequential logic module with good performance, which is beneficial to solve the competition and risk phenomenon in large-scale logic circuit design. FSM gives designers more solutions to eliminate circuit burrs and enhance system stability than other solutions.
- High-speed performance, in high-speed communication and high-speed control, the state machine has great advantages; A state machine functions more like a CPU in terms of sequence control;
- High reliability. The state machine is composed of pure hardware circuits and does not rely on the execution of software instructions, so it does not have many inherent defects in the process of CPU running software. Various fault-tolerant techniques can be used in state machine design. When a state machine enters an illegal state, it takes a very short time to pick it out and enter the normal state. It usually takes only 2-3 clocks, i.e. tens of nanoseconds, and does not cause great harm to the system.
- Mealy state machine: Output is determined by the current state and input.
- Moore state machine: The output is determined only by the current state.
Why use a three-stage state machine
- FSM, like other designs, is best designed using a synchronous timing approach to improve design stability and eliminate burrs.
- After the realization of the state machine, generally speaking, the state transition part is synchronous timing circuit, and the judgment of the state transition condition is combinatorial logic. The reason two-part coding is more reasonable than one-part coding is that the synchronization timing and composition logic are implemented separately in separate ALWAYS blocks. This is not only easy to read, understand, and maintain, but more importantly, it helps the integrator to optimize the code, the user to add appropriate timing constraints, and the layout cabler to implement the design. One-paragraph FSM description is not conducive to timing constraints, function changes, debugging, etc., and cannot well represent Miller FSM output, which is easy to write Latches, resulting in logical function errors.
- In the general two-paragraph description, in order to describe the output of the current state, many designers are used to implement the current output by combinatorial logic. This combinatorial logic still has the potential for burrs and is not conducive to constraints, integrators and layout cablers for high-performance designs. So if the design allows the insertion of an extra clock beat, it is required to register the output of the state machine as much as possible.
- If parenthesis is not allowed to register a beat, it can be solved by the three-paragraph description method. Compared with two sections, the key lies in that the output of the current state is judged according to the input conditions in the previous state according to the rule of state transition, so the register output can be realized without inserting extra clock beats.
- An FSM description that contains n always blocks cannot be described as an N-section description method. Syntactically, you can split a ALWAYS module into multiple ALWAYS modules, or vice versa, combine multiple ALWAYS modules into a single ALWAYS module. The n-section FSM description method emphasizes a modeling idea, which is by no means a simple always statement block number.
Three state machine models
Relationships between various modeling approaches
One-paragraph and three-paragraph
- Combining the combinatorial logic in the three-step state machine is the same as one-step modeling. Therefore, the biggest difference between these two modeling methods is that when using one-step modeling of FSM register output, it is necessary to comprehensively consider which sub-states the state will enter under what state transition conditions. Then the output of each substate is described separately under the case branch of each state, which is not consistent with the habit of thinking. When describing the FSM output in three-stage modeling, you only need to specify the case-sensitive table as a sub-state register and directly describe the state output in the case branch of each sub-state without considering the condition of state transition.
Two-stage and three-stage
- From a code point of view, the first two sections of the three-step modeling are exactly the same as the two-step modeling, with only one more section of register FSM output. In general, it is recommended to use register outputs to improve output timing conditions and to avoid burrs in composite circuits.
- However, circuit design is not static, and in some cases, two-stage structure is more advantageous than three-stage structure. Compared with this state machine modeling diagram, the two-part combination logic (state transition condition combination logic and output combination logic) is separated by the state register. In the three-stage structure, on the path from the input to the output of the register state, the combination logic of the two parts (the combination logic of the state transition condition and the combination logic of the output) can be seen as a whole from the time sequence. Therefore, the combination logic of this path is more complicated, and the timing sequence of this path is relatively tight.
- Two-end modeling uses state registers to segment combinatorial logic, while three-stage modeling moves registers to the end of combinatorial logic. If the combination logic before the register is too complex, it is bound to become the critical path of the whole design, so it is not suitable to use three-stage modeling, but two-stage modeling. The solution to the burr of the two-stage modeling combinational logic is to insert additional registers in the post-FSM level, adjust the timing, and complete the function.
State machine design skills
coding
Binary and grey-code use the fewest triggers and more combinative logic, one-hot and vice versa. Gray-code is used for CPLD combination logic resources. FPGA more trigger resources, using one-HOT.
Binary (sequential) encoding gray code disadvantages
Using sequential encoding, transition states “00” and “11” may occur during state transition from “01” to “10”. This is because in the state transition process of the intermediate signal, the high and low turnover time of the state register may be inconsistent. The high turnover speed is fast, and the transition state “00” will be generated, and the transition state “00” will be generated on the contrary. If gray code is used, the transition state caused by delay can be largely eliminated because there is only one bit difference between two adjacent states. However, if there are multiple transition paths from one state to the next, there is no guarantee that there will be only one bit change in the jump. Therefore, the unique thermal code is generally used.
Of course, gray code also has two characteristics: asynchronous output, low power devices
If the output of the state machine or any logic of the state machine operation is asynchronous, it is usually best to use gray codes. Because asynchronous circuits do not protect against race conditions and burrs, different paths between two digits in the state register can cause unpredictable behavior related to layout configurations and parasitic parameters. Gray codes only undergo one unit inversion for each state transition, thus eliminating the race condition in asynchronous logic. In addition, FPGA is best to use unique thermal codes.
Initialization status of the FSM node
A complete state machine (robustness) should have both an initial state and a default state. When the chip is powered on or reset, the state machine should automatically reset all the judgment conditions and enter the initialization state. It should be noted that most FPgas have Globe Set/Reset (GSR) signal. When THE FPGA is powered on, the GSR signal is raised and all the registers, RAM and other units are Reset/Set. At this time, the logic configured in the FPGA does not take effect, so it cannot be guaranteed to enter the initialization state correctly. Therefore, using GSR to try to enter the initialization state of FPGA, often produce all kinds of unnecessary trouble. The general method is to use asynchronous reset signal, of course, can also use synchronous reset, but pay attention to the design of synchronous reset logic. Another way to solve this problem is to set the default initial state code to all zero, so that when the GSR is reset, the state machine automatically enters the initial state.
Default FSM status
A complete state machine should also contain a default state to ensure that the logic does not fall into an “infinite loop” when transition conditions are not met or the state changes abruptly. This is an important requirement for state machine robustness, known as “self-recovery.” The if… Else statements use full-fledged conditional statements. Case statements use default to establish the default state. You can add an additional default state, which automatically changes to IDLE and restarts the state machine.
FSM output
The Mealy state machine for FSM is described in two paragraphs. The output logic can be “? Statement description, or use case statement to determine the transfer condition and input signal. If the output conditions are complex and some outputs are shared by multiple states, you are advised to use Task/endTask to encapsulate the outputs for module reuse.
State machine Example
A one-paragraph state machine example
module state1 ( nrst,clk,
i1,i2,
o1,o2,
err
);
input nrst,clk;
input i1,i2;
output o1,o2,err;
reg o1,o2,err;
reg [2:0] NS; //NextState
parameter [2:0] //one hot with zero idle
IDLE = 3'b000,
S1 = 3'b001,
S2 = 3'b010,
ERROR = 3'b100;
//1 always block to describe state transition, state output, state input condition
always @ (posedge clk or negedge nrst)
if(! nrst)begin
NS <= IDLE;
{o1,o2,err} <= 3'b000;
end
else
begin
NS <= 3'bx;
{o1,o2,err} <= 3'b000;
case (NS)
IDLE: begin
if (~i1) begin{o1,o2,err}<=3'b000; NS <= IDLE;end
if (i1 && i2) begin{o1,o2,err}<=3'b100; NS <= S1;end
if (i1 && ~i2) begin{o1,o2,err}<=3'b111; NS <= ERROR;end
end
S1: begin
if (~i2) begin{o1,o2,err}<=3'b100; NS <= S1;end
if (i2 && i1) begin{o1,o2,err}<=3'b010; NS <= S2;end
if (i2 && (~i1)) begin{o1,o2,err}<=3'b111; NS <= ERROR;end
end
S2: begin
if (i2) begin{o1,o2,err}<=3'b010; NS <= S2;end
if (~i2 && i1) begin{o1,o2,err}<=3'b000; NS <= IDLE;end
if (~i2 && (~i1))begin{o1,o2,err}<=3'b111; NS <= ERROR;end
end
ERROR: begin
if (i1) begin{o1,o2,err}<=3'b111; NS <= ERROR;end
if (~i1) begin{o1,o2,err}<=3'b000; NS <= IDLE;end
end
endcase
end
endmodule
Copy the code
Example of a two-stage state machine
module state2 ( nrst,clk,
i1,i2,
o1,o2,
err
);
input nrst,clk;
input i1,i2;
output o1,o2,err;
reg o1,o2,err;
reg [2:0] NS,CS;
parameter [2:0] //one hot with zero idle
IDLE = 3'b000,
S1 = 3'b001,
S2 = 3'b010,
ERROR = 3'b100;
//sequential state transition
always @ (posedge clk or negedge nrst)
if(! nrst) CS <= IDLE;else
CS <=NS;
//combinational condition judgment
always @ (nrst or CS or i1 or i2)
begin
NS = 3'bx;
ERROR_out;
case (CS)
IDLE: begin
IDLE_out;
if (~i1) NS = IDLE;
if (i1 && i2) NS = S1;
if (i1 && ~i2) NS = ERROR;
end
S1: begin
S1_out;
if (~i2) NS = S1;
if (i2 && i1) NS = S2;
if (i2 && (~i1)) NS = ERROR;
end
S2: begin
S2_out;
if (i2) NS = S2;
if (~i2 && i1) NS = IDLE;
if (~i2 && (~i1)) NS = ERROR;
end
ERROR: begin
ERROR_out;
if (i1) NS = ERROR;
if (~i1) NS = IDLE;
end
endcase
end
//output task
task IDLE_out;
{o1,o2,err} = 3'b000;
endtask
task S1_out;
{o1,o2,err} = 3'b100;
endtask
task S2_out;
{o1,o2,err} = 3'b010;
endtask
task ERROR_out;
{o1,o2,err} = 3'b111;
endtask
endmodule
Copy the code
Example of a three-section state machine
The output state of the three-stage state machine can be either combinational logic or sequential logic, and sequential logic is generally used to avoid circuit burrs. The output synchronization process is usually case, so here’s the example from the book, case
module state3 ( nrst,clk,
i1,i2,
o1,o2,
err
);
input nrst,clk;
input i1,i2;
output o1,o2,err;
reg o1,o2,err;
reg [2:0] NS,CS;
parameter [2:0] //one hot with zero idle
IDLE = 3'b000,
S1 = 3'b001,
S2 = 3'b010,
ERROR = 3'b100;
//1st always block, sequential state transition
always @ (posedge clk or negedge nrst)
if(! nrst) CS <= IDLE;else
CS <=NS;
//2nd always block, combinational condition judgment
always @ (nrst or CS or i1 or i2)
begin
NS = 3'bx;
case (CS)
IDLE: begin
if (~i1) NS = IDLE;
if (i1 && i2) NS = S1;
if (i1 && ~i2) NS = ERROR;
end
S1: begin
if (~i2) NS = S1;
if (i2 && i1) NS = S2;
if (i2 && (~i1)) NS = ERROR;
end
S2: begin
if (i2) NS = S2;
if (~i2 && i1) NS = IDLE;
if (~i2 && (~i1)) NS = ERROR;
end
ERROR: begin
if (i1) NS = ERROR;
if (~i1) NS = IDLE;
end
endcase
end
//3rd always block, the sequential FSM output
always @ (posedge clk or negedge nrst)
if(! nrst) {o1,o2,err} <=3'b000;
else
begin
{o1,o2,err} <= 3'b000;
case (NS)
IDLE: {o1,o2,err}<=3'b000;
S1: {o1,o2,err}<=3'b100;
S2: {o1,o2,err}<=3'b010;
ERROR: {o1,o2,err}<=3'b111;
endcase
end
endmodule
Copy the code
Adi-ad7980 sample code
Two always blocks, three ways of thinking
// -----------------------------------------------------------------------------
// KEYWORDS : AD7980
// -----------------------------------------------------------------------------
// PURPOSE : Driver for the AD7980 16-Bit, 1 MSPS PulSAR ADC in MSOP/QFN
// -----------------------------------------------------------------------------
// REUSE ISSUES
// Reset Strategy : Active low reset signal
// Clock Domains : 2 clocks - the system clock that drives the internal logic
// : and a clock for ADC conversions
// Critical Timing : N/A
// Test Features : N/A
// Asynchronous I/F : N/A
// Instantiations : N/A
// Synthesizable (y/n) : Y
// Target Device : AD7980
// Other : The driver is intended to be used for AD7980 ADCs configured
// : in /CS MODE, 3-WIRE, WITHOUT BUSY INDICATOR
// -----------------------------------------------------------------------------
`timescale 1ns/1ns //Use a timescale that is best for simulation.
//----------- Module Declaration -----------------------------------------------
module AD7980
//----------- Ports Declarations -----------------------------------------------
(
//clock and reset signals
input fpga_clk_i, //system clock
input adc_clk_i, //clock to be applied to ADC to read the conversions results
input reset_n_i, //active low reset signal
//IP control and data interface
output [15:0] data_o, //data read from the ADC
output reg data_rd_ready_o, //when set to high the data read from the ADC is available on the data_o bus
//ADC control and data interface
input adc_sdo, //ADC SDO signal
input adc_sdi, //ADC SDI signal (not used in 3-WIRE mode)
output adc_sclk_o, //ADC serial clock
output adc_cnv_o //ADC CNV signal
);
//----------- Registers Declarations -------------------------------------------
reg [ 3:0] adc_state; //current state for the ADC control state machine
reg [ 3:0] adc_next_state; //next state for the ADC control state machine
reg [ 3:0] adc_state_m1; //current state for the ADC control state machine in the ADC clock domain
reg [ 6:0] adc_tcycle_cnt; //counts the number of FPGA clock cycles to determine when an ADC cycle is complete
reg [ 6:0] adc_tcnv_cnt; //counts the number of FPGA clock cycles to determine when an ADC conversion is complete
reg [ 4:0] sclk_clk_cnt; //counts the number of clocks applied to the ADC to read the conversion result
reg adc_clk_en; //gating signal for the clock sent to the ADC
reg adc_cnv_s; //internal signal used to hold the state of the ADC CNV signal
reg [15:0] adc_data_s; //interal register used to store the data read from the ADC
//----------- Wires Declarations -----------------------------------------------
wire adc_sclk_s; //internal signal for the clock sent to the ADC
//----------- Local Parameters -------------------------------------------------
//ADC states
parameter ADC_IDLE_STATE = 4'b0001;
parameter ADC_START_CNV_STATE = 4'b0010;
parameter ADC_END_CNV_STATE = 4'b0100;
parameter ADC_READ_CNV_RESULT = 4'b1000;
//ADC timing
parameter real FPGA_CLOCK_FREQ = 100000000; //FPGA clock frequency [Hz]
parameter real ADC_CYCLE_TIME = 0000001000.; //minimum time between two ADC conversions (Tcyc) [s]
parameter real ADC_CONV_TIME = 0000000670.; //conversion time (Tcnvh) [s]
parameter [6:0] ADC_CYCLE_CNT = FPGA_CLOCK_FREQ * ADC_CYCLE_TIME - 1;
parameter [6:0] ADC_CNV_CNT = FPGA_CLOCK_FREQ * ADC_CONV_TIME;
//ADC serial clock periods
parameter ADC_SCLK_PERIODS = 5'd15; //number of clocks to be sent to the ADC to read the conversion result
//----------- Assign/Always Blocks ---------------------------------------------
assign adc_cnv_o = adc_cnv_s;
assign adc_sclk_s = adc_clk_i & adc_clk_en;
assign adc_sclk_o = adc_sclk_s;
assign data_o = adc_data_s;
//update the ADC timing counters
always@ (posedge fpga_clk_i)
begin
if(reset_n_i == 1'b0)
begin
adc_tcycle_cnt <= 0;
adc_tcnv_cnt <= ADC_CNV_CNT;
end
else
begin
if(adc_tcycle_cnt ! =0)
begin
adc_tcycle_cnt <= adc_tcycle_cnt - 1;
end
else if(adc_state == ADC_IDLE_STATE)
begin
adc_tcycle_cnt <= ADC_CYCLE_CNT;
end
if(adc_state == ADC_START_CNV_STATE)
begin
adc_tcnv_cnt <= adc_tcnv_cnt - 1;
end
else
begin
adc_tcnv_cnt <= ADC_CNV_CNT;
end
end
end
//read data from the ADC
always@ (negedge adc_clk_i)
begin
if(adc_clk_en == 1'b1)
begin
adc_data_s <= {adc_data_s[14:0], adc_sdo};
sclk_clk_cnt <= sclk_clk_cnt - 1;
end
else
begin
sclk_clk_cnt <= ADC_SCLK_PERIODS;
end
end
//determine when the ADC clock is valid to be sent to the ADC
always@ (negedge adc_clk_i)
beginadc_state_m1 <= adc_state; adc_clk_en <= ((adc_state_m1 == ADC_END_CNV_STATE) || (adc_state_m1 == ADC_READ_CNV_RESULT) && (sclk_clk_cnt ! =0))?1'b1 : 1'b0;
end
//update the ADC current state and the control signals
always@ (posedge fpga_clk_i)
begin
if(reset_n_i == 1'b0)
begin
adc_state <= ADC_IDLE_STATE;
end
else
begin
adc_state <= adc_next_state;
case (adc_state)
ADC_IDLE_STATE:
begin
adc_cnv_s <= 1'b0;
data_rd_ready_o <= 1'b0;
end
ADC_START_CNV_STATE:
begin
adc_cnv_s <= 1'b1;
data_rd_ready_o <= 1'b1;
end
ADC_END_CNV_STATE:
begin
adc_cnv_s <= 1'b0;
data_rd_ready_o <= 1'b0;
end
ADC_READ_CNV_RESULT:
begin
adc_cnv_s <= 1'b0;
data_rd_ready_o <= 1'b0;
end
endcase
end
end
//update the ADC next state
always @(adc_state, adc_tcycle_cnt, adc_tcnv_cnt, sclk_clk_cnt)
begin
adc_next_state <= adc_state;
case (adc_state)
ADC_IDLE_STATE:
begin
if(adc_tcycle_cnt == 0)
begin
adc_next_state <= ADC_START_CNV_STATE;
end
end
ADC_START_CNV_STATE:
begin
if(adc_tcnv_cnt == 0)
begin
adc_next_state <= ADC_END_CNV_STATE;
end
end
ADC_END_CNV_STATE:
begin
adc_next_state <= ADC_READ_CNV_RESULT;
end
ADC_READ_CNV_RESULT:
begin
if(sclk_clk_cnt == 1)
begin
adc_next_state <= ADC_IDLE_STATE;
end
end
default:
begin
adc_next_state <= ADC_IDLE_STATE;
end
endcase
end
endmodule
Copy the code
Another way of writing a state machine
module simple_fsm(
input wire sys_clk , // System clock 50MHz
input wire sys_rst_n , // Global reset
input wire pi_money , // Coin can be: no coin (0), 1 yuan (1)
outputreg po_cola Po_cola = 1, Po_Cola = 0, po_Cola = 0
);
// Only three states, use unique heat code
parameter IDLE =3'b001;
parameter ONE =3'b010;
parameter TWO =3'b100;
reg[2:0] state;
// The first paragraph of the state machine describes the current state. How does the input jump to the next state
always@ (posedge sys_clk ornegedge sys_rst_n)
if(sys_rst_n ==1'b0)
state <= IDLE; // In any case, just press reset to return to the initial state
else
case(state)
IDLE : if(pi_money ==1'b1)// Determine the input
state <= ONE;
else
state <= IDLE;
ONE : if(pi_money ==1'b1)
state <= TWO;
else
state <= ONE;
TWO : if(pi_money ==1'b1)
state <= IDLE;
else
state <= TWO;
default: state <= IDLE; // If the state machine jumps out of the encoded state, it returns to its original state
endcase
// The second paragraph describes the current state state and how input pi_money affects po_COLA output
always@ (posedge sys_clk ornegedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_cola <=1'b0;
else if((state == TWO)&&(pi_money ==1'b1))
po_cola <=1'b1;
else
po_cola <=1'b0;
endmodule
Copy the code
Public account – Darwin’s explanation
The most critical part of the state machine, whether the synthesizer can synthesize RTL code into a state machine, depends on how the code is implemented. You see that the code uses a two-step state machine, but it feels strange. The difference between the state machine and the other data is that it uses a new way of writing it. Many people have seen state machine code written in one-step, two-step, three-step (one-step refers to the use of sequential logic in a state machine to describe both the state transition and the data output; In two-stage state machine, sequential logic is used to describe state transition in the first stage and combinatorial logic is used to describe data output in the second stage. In three-stage state machine, sequential logic is used to describe state transition in the first stage, combinational logic is used to judge state transition conditions and describe state transition rules in the second stage, and state output is described in the third stage state machine, which can be output by combinational circuit or sequential circuit). This one before, 2-phase, three-step are classic old writing, also some old engineer is still used to writing, the old method was established according to the theory of state machine model after the abstract design, the implementation of the code to write code, in strict accordance with the fixed format or comprehensive device will not be able to identify you write the code as a state machine, Because early development tools recognized only fixed state machine formats, code integrators that were not written in standard formats could not be synthesized to look like state machines. This often increases the difficulty of design. Many people need to understand the theoretical model when learning, and they can design a good state machine only after repeated learning and understanding for a long time. Therefore, we need to improve.
Wang Jinming, digital System Design and Verilog HDL
There are three objects in the state machine design
- Current State (CS)
- The Next State, the Next State (NS)
- Output Logic (OL)
The way a state machine is described
- 3 process description :CS NS OL is described by an ALWAYS process block
- Dual-process description 1:CS+NS OL
- Dual-process description 2:CS NS+OL
- Single process description :CS+NS+OL
Other additions to the book
Synchronous or asynchronous reset
- The synchronous reset signal resets the finite state machine when the edge of the clock jump arrives, and assigns the reset value to the output signal and returns the finite state machine to its initial state. Add an if statement at the beginning of the state transition that evaluates the synchronous reset signal. This way, if the value of the output signal is not specified, the output signal value remains unchanged. This situation requires additional registers to hold the original value, thus increasing resource consumption, so the value of the output signal should be specified in the if statement.
- If you only need to reset during power-on and system errors, the asynchronous reset mode is better than the synchronous one. Cause: Synchronous reset occupies more extra resources, while asynchronous reset can eliminate the possibility of introducing additional registers. And Verilog language with asynchronous reset signal is simple to describe, only need to introduce asynchronous reset signal in the process of describing the status register.