How to implement linked Finite State Machines

Discussion in 'VHDL' started by Sidney Cadot, Apr 18, 2004.

  1. Sidney Cadot

    Sidney Cadot Guest

    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

    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,

    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);


    -- 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;
    Sidney Cadot, Apr 18, 2004
    1. Advertisements

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. deejayfred
    Oct 2, 2003
  2. SomeDude
    Aug 14, 2006
  3. Martin Maurer
    Andrew Hall
    Jun 14, 2004
  4. Inderkal
    Dec 9, 2004
  5. Roberto Nunnari
    Thomas Weidenfeller
    Feb 4, 2004
  6. Henrik Vallgren
    Henrik Vallgren
    Jun 15, 2004
  7. Rui Maciel
    Christopher Layne
    Feb 16, 2007
  8. Aardalath
    Jan 27, 2009