How to implement linked Finite State Machines

S

Sidney Cadot

Dear all,

Is there a canonical, "idomatically right" way to specify in VHDL a
number of Finite State Machines that depend on each other's states for
their transition behavior?

Below I have given an example (printer.vhdl) that repeatedly prints the
word "DEMO", followed by CR/LF, to RS-232; it basically consists of
three 'nested' FSMs:

a) "baud" FSM: counts clock-ticks of the master clock, to provide a
baud reference for printing bits;

This FSM is implemented as a counter.

b) "bit" FSM walks start, data, and stop bits as required by RS-232;

This FSM is implemented using enumeration of states.

c) "char" FSM walks "D", "E", "M", "O", CR, LF characters.

This FSM is implemented using enumeration of states.

It is clear that (a) triggers a transition in (b), and (b) triggers a
transition in (c). Below, I implemented this using "proceed_baud" and
"proceed_bit", That I have to make sure are only true for 1 master clock
cycle. Is there perhaps a more natural way, that doesn't have this
necessity?

One option would be to view these "proceed" signals as clocks, and have
the dependent FSM trigger a transition on the upflank of such a signal.
However, I have found that the synthesis tool I use (Xilinx XST) does
not properly recognize this as a clock. Furthermore, I am not sure if
such a design would be very robust, since the "proceed" pseudo-clock
would lag a bit after the master clock.

Apart from this, I am not sure whether the initialization as I do it
below (in effect, initializing the FSMs to a state prior to their
desired start-up state, and use the first clock up-flank to get them
there) is the right way to go about start-up; at the very least, I think
it is not very elegant.

Thirdly, I was a bit surprised by XST. I now write my state transition
triggers as follows, e.g.

baud_cs <= baud_ns when rising_edge(CLK) else baud_cs;

Why can't this be written simply as:

baud_cs <= baud_ns when rising_edge(CLK);

.... and have the synthesis tool infer the register? Is this a quirk of
the Xilinx tool, or is there a more fundamental reason at the level of VHDL?

Any other comments on my particular style of VHDL coding would also be
much appreciated. I'm very much a novice in this, so any hints for style
improvements are welcome.


Best regards,

Sidney Cadot



-- printer.vhdl
--
-- Repeatedly prints "DEMO", followed by CR/LF, to RS-232
-- (300 baud, 8N1).
--
-- This code is synthesizable with XST 6.2.0.

library ieee;

use ieee.std_logic_1164.all,
ieee.std_logic_arith.all,
ieee.std_logic_unsigned.all;

entity printer is
port (CLK: in std_logic; RXD: out std_logic);
end entity printer;

architecture arch of printer is

constant CLK_FREQ : positive := 50_000_000; -- 50 MHz
constant RS232_BAUDRATE : positive := 300;

constant RS232_PERIOD : positive := CLK_FREQ/RS232_BAUDRATE;

type BaudState is range 0 to RS232_PERIOD-1;
signal baud_cs: BaudState := 0;
signal baud_ns: BaudState;
signal baud_proceed: boolean;

type BitState is (START, B0, B1, B2, B3, B4, B5, B6, B7, STOP);
signal bit_cs: BitState := B7; -- switch to "STOP" on first upflank
signal bit_ns: BitState;
signal bit_proceed: boolean;

type CharacterState is (D, E, M, O, CR, LF);
signal char_cs: CharacterState := LF; -- switch to "D" on 1st upflank
signal char_ns: CharacterState;

signal out_char: std_logic_vector(7 downto 0);

begin

-- Finite State Machine: baud counter

baud_cs <= baud_ns when rising_edge(CLK) else baud_cs;
baud_ns <= 0 when baud_cs = BaudState'high else baud_cs+1;
baud_proceed <= (baud_cs = 0);

-- Finite State Machine: bit selector

bit_cs <= bit_ns when rising_edge(CLK) and baud_proceed else bit_cs;

with bit_cs select
bit_ns <= B0 when START,
B1 when B0,
B2 when B1,
B3 when B2,
B4 when B3,
B5 when B4,
B6 when B5,
B7 when B6,
STOP when B7,
START when STOP;

with bit_cs select
RXD <= '0' when START,
out_char(0) when B0,
out_char(1) when B1,
out_char(2) when B2,
out_char(3) when B3,
out_char(4) when B4,
out_char(5) when B5,
out_char(6) when B6,
out_char(7) when B7,
'1' when STOP;

bit_proceed <= baud_proceed and (bit_cs = STOP);

-- Finite State Machine: character selector

char_cs <= char_ns when rising_edge(CLK) and bit_proceed else char_cs;

with char_cs select
char_ns <= E when D,
M when E,
O when M,
CR when O,
LF when CR,
D when LF;

with char_cs select
out_char <= x"44" when D,
x"45" when E,
x"4D" when M,
x"4F" when O,
x"0D" when CR,
x"0A" when LF;

end architecture arch;
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top