📜 ⬆️ ⬇️

Implementing a finite state machine in VHDL

State machines play a very important role in the development of FPGA firmware. Everyone has heard of two classic types of automata: the Mile machine gun and the Moore machine gun, which were proposed before the FPGA era. However, the specificity of the construction of the FPGA makes its own adjustments and in the process of working I had a quite definite style of describing the machine.

The literature suggests various implementations of automata. Such an article is on Habré - “Description of digital machines on VHDL” from canny . However, in practical implementation on an FPGA, especially at high frequencies, such an implementation is inconvenient. The following option is proposed.

Component Description:

entity ex_user is port ( reset : in std_logic: -- 1 –  clk : in std_logic; --   a : in std_logic; --   q : out std_logic --   ) end ex_user; 

Description of the type and signals:
')
 type stp_type is ( s0, s1, s2, s3 ); signal stp : stp_type; signal rstp0 : std_logic; signal rstp : std_logic; 

Reset Sync:

 rstp0 <= reset after 1 ns when rising_edge( clk ); rstp <= rstp0 after 1 ns when rising_edge( clk ); 

Actually automatic, in this case - very simple

 pr_stp: process( clk ) begin if( rising_edge( clk ) ) then case( stp ) is when s0 => --    q <= '0' after 1 ns; if( a='1' ) then stp <= s1 after 1 ns; end if; when s1 => --   ,   a=0 if( a='0' ) then stp <= s2 after 1 ns; end if; when s2 => q <= '1' after 1 ns; stp <= s0 after 1 ns; end case; ---    ,     stp --- if( rstp='1' ) then stp <= s0 after 1 ns; --    stp end if; end if; end process; 

Features:


There is nothing revolutionary, all this has long been known. But all together it forms the style of description.

More details about the features of the description. The first is the introduction of a separate type for the state signal. In this case, the type of listing is described. This allows the synthesizer to choose the type of signal. It can be one-hot, it can be a binary counter or a gray counter. The type of implementation can be specified by synthesizer directives. This separates the description from the implementation. If someone does not like it, then it is quite possible to set the type of std_logic_vector and the individual constants s0, s1, etc. Sometimes I make claims that the names s0, s1 are not informative, and it would be better to use constants associated with the meaning of a particular state. So I also tried to do it, but in the development process very often the logic of the state changes, but the name remains, and this only confuses the matter.

Synchronization signal reset - very often the reset is asynchronous with respect to the clock frequency of the machine. In order not to check where it comes from, it is better to always put two triggers. In any case, it will facilitate tracing. A reset only affects the status signal. It also makes tracing easier, especially with a large number of output signals, but it requires that, in the initial state, all output signals are described.

The machine is a synchronous circuit, so all input signals must be synchronous with a clock frequency, here there are no options. It is required to know where the signal comes from. If this signal is generated at a different clock frequency, then it is imperative to put two triggers (as well as for the reset signal).

The fact that the transitions and output signals are described in one process makes it visually easier to develop and visibility. If we make an automaton in two (and even in three processes) as the textbooks advise us, then it is difficult to cover it with a glance. If only because the big machine on one computer screen does not fit.

The most controversial statement is to write after 1 ns when assigning a signal. At one of the forums I was very much criticized for this. They also wrote that as I gained experience, I would get rid of this bad habit. My experience shows that this is a very good habit. The presence of such a record allows you to be sure that the simulation results and the results of work in real equipment will be the same. It's all about the concept of delta delay.
Consider a simple language construct:

 clk2 <= clk1; b <= a when rising_edge( clk1 ); c <= b when rising_edge( clk1 ); d <= b when rising_edge( clk2 ); 

The result of the work is shown in the figure:

image

The diagram visually shows that the signals of the clock frequency clk1 and clk2 are the same, but in fact clk2 is delayed relative to clk1 by the amount of delta delay. The signal c lags behind the signal b by one clock cycle. It is right. But the signal d must coincide with the signal c , but this does not happen. It works before. This is due to the fact that it is latched by the frequency clk2 . When working in the equipment clk2 will coincide with clk1 , it will be the same signal on the global clock line. In real projects, assignments like clk2 <= clk1 are quite common. Of course, you can try all developers to strictly forbid it, but I strongly doubt the result. Instead of assignment, you can make a description of the alias type:

 alias clk2 is clk1; 

In this case, clk2 will just be another name clk1 and the results will be correct. But there is one more thing. Purely visually, on the timing diagram it is not clear when the signal changes relative to the clock frequency.

And this is what happens when after 1 ns is added:

image

Signals c and d are formed correctly. The change in signal b is clearly visible with respect to the clock edge.

Once many years ago, I spent a lot of time searching for the causes of different behaviors in a model and in real hardware. The shift was just one measure. And this reason is the assignment of the clock frequency and the absence of after 1 ns. Since then, I always add a delay and have never regretted it.

And finally, in the example, Moore’s machine gun is shown. The output signals depend only on the state. But the code can always be changed, like this:

 when s1 => --   ,   a=0 if( a='0' ) then stp <= s2 after 1 ns; q <= '1' after 1 ns; end if; 

In this case, the signal q will be formed one clock earlier, i.e. it will depend on the status and input signal. And this is an automatic Miles.

The article Description of digital machines on the VHDL will also provide a description of the description in one process. At first glance, everything is the same.

Description of CA in one process
 PROCESS (clk, reset) BEGIN IF reset = '1' THEN state <= Init; ELSIF (rising_edge(clk)) THEN CASE state IS WHEN Init => IF cnt = '1' THEN state <= R; ELSE state <= Init; END IF; output <= "000"; WHEN R => IF cnt = '1' THEN state <= RG; ELSE state <= R; END IF; output <= "100"; WHEN RG => IF cnt = '1' THEN state <= G; ELSE state <= RG; END IF; output <= "010"; WHEN G => IF cnt = '1' THEN state <= GR; ELSE state <= G; END IF; output <= "001"; WHEN GR => IF cnt = '1' THEN state <= R; ELSE state <= GR; END IF; OUTPUT <= "010"; END CASE; END IF; END PROCESS; 


In this description there is a very serious mistake. During the reset signal, the output signal OUTPUT is not defined, in order to determine it, it is required to make a signal assignment inside the reset:

 IF reset = '1' THEN state <= Init; OUTPUT <= "000"; ELSIF 

In this case, three reset lines are added. With a large number of output signals, this degrades the FPGA trace.

In my case, the reset signal acts only on the state signal, but since it is inside the process after the case, then according to the rules of the language, it takes precedence over the purpose of the state inside the case. A feature of this solution is that the transfer of the output signals to the initial state will occur only on the second clock of the reset signal. However, in the overwhelming majority of cases this is not essential.

In conclusion, I want to note that this style of description has proven itself very well, the machine is well divorced in FPGA. Cascade connections of several automata and connections to other circuits inside the FPGA are easily obtained.

Source: https://habr.com/ru/post/309162/


All Articles