Teach Remote lab lessons

Teach lesson

Digital Logic on the DE1-SoC with VHDL (5/6): Finite state machines

Students build and modify a Moore traffic-light FSM on the DE1-SoC, with an optional Mealy detector extension.

  • Altera DE1-SoC
  • 45 min
  • Undergraduate, introductory
  • English
  • Digital systems & FPGA
Altera DE1-SoC
Altera DE1-SoC

Learning Outcomes

  • Explain what a finite state machine is in terms of states, transitions, and outputs.

  • Explain why the traffic-light controller is a Moore machine, and recognize a Mealy detector as an optional extension.

  • Read and modify a two- or three-block VHDL FSM: state register, next-state logic, output logic.

  • Build and modify a traffic-light controller on the real DE1-SoC.

  • Explain why active-low asynchronous reset matters when a slow-clock FSM must return to a known state.

Student activity preview

Activity Content

Preview only. In a class session, students can fill in responses and submit their work to the teacher.

1

What a finite state machine is

10 min

You have already built circuits that remember: a flip-flop holds one bit, a register holds several, a counter holds its current count. A finite state machine (FSM) is the idea that organizes that memory into *meaningful situations*. At any moment the machine is in exactly one of a small, fixed set of states — for a traffic light, the states might be GREEN, YELLOW, and RED. The number of states is finite (hence the name), and the machine is always in precisely one of them.

An FSM is defined by three things:

- States — the finite set of situations the machine can be in. One special state is the reset state, where the machine starts.
- Transitions — the rules for moving from one state to another. On each clock edge, the machine looks at its current state and its inputs and decides which state to be in next. The transition GREEN -> YELLOW says "if you are in GREEN and the condition holds, go to YELLOW on the next edge."
- Outputs — what the machine drives on its outputs (here, the LEDs) in each situation.

The whole machine advances on the clock: at each edge it (possibly) moves to a new state based on the current state and the current inputs, and it holds that state until the next edge. Between edges it does nothing but wait — exactly the sample-and-hold behavior you saw with flip-flops, now used to step through a sequence of states.

Moore versus Mealy. FSMs come in two flavors that differ only in *where the outputs come from*:

- In a Moore machine, the outputs depend only on the current state. If you know the state, you know the outputs — the inputs do not appear in the output equation. The traffic light is Moore: when the state is RED, the red LED is on, full stop.
- In a Mealy machine, the outputs depend on the current state and the current inputs together. The same state can produce different outputs depending on what the input is doing right now. The sequence detector is Mealy: it asserts "detected" in a particular state *only when the incoming bit is also the right value*.

Moore outputs are steady for as long as you stay in a state. Mealy outputs can change the instant an input changes, even without leaving the state — which makes them quick but also potentially brief and glitchy, a point that will matter on the camera.

The standard VHDL shape of an FSM. Almost every FSM in this course is written as two or three blocks working together:

1. A state register — a clocked VHDL process (if rising_edge(clock) then ...) that holds the current state and, on each edge, copies in the next state. This is the only block that has memory.
2. A next-state process — a combinational VHDL process that, given the current state and inputs, computes which state should come next. It should assign safe defaults before the case, or cover every branch explicitly, so that every path gives the outputs and next_state a value.
3. Output logic — concurrent signal assignments or combinational-process assignments that drive the outputs from the state (Moore) or from state and inputs (Mealy).

You will see both designs in this lesson follow exactly this shape: a clocked state process, a combinational next-state process with defaults, and output logic.

A vending machine remembers how much money has been inserted and dispenses an item when the total reaches the price. Modeling it as a finite state machine, what best plays the role of the state?

A circuit lights an LED whenever it is in state BUSY and the input go is currently 1. Is that output Moore or Mealy? Explain in one sentence.

2

A required note: asynchronous reset in these FSMs

7 min

Before you build anything, one deliberate change from earlier lessons.

In Lesson 3 your flip-flop and register used a synchronous reset: the reset was tested inside the clocked process, like if KEY(0) = '0' then Q <= '0'; inside if rising_edge(CLOCK_50) then. Lesson 4 already used active-low asynchronous reset for slow-clock circuits; this lesson continues that style for FSM state so reset does not wait for the slow clock.

The FSMs in this lesson use an active-low asynchronous reset instead. Look at the state register's sensitivity list:

process(slow_clk, KEY)
begin
    if KEY(0) = '0' then state <= TODO_RESET_STATE;
    elsif rising_edge(slow_clk) then state <= TODO_NEXT_STATE;
    end if;
end process;

Putting KEY in the process sensitivity list and checking if KEY(0) = '0' then before the clock-edge branch means the state process also responds the instant KEY(0) goes low (the moment you press the button), not only on a slow_clk edge. So pressing KEY(0) forces the machine to its known state immediately, without waiting for the (slow) clock.

State the difference clearly:

- Synchronous reset (Lessons 2-3): takes effect only on a clock edge. Clean and predictable, but if the clock is slow you wait for the edge.
- Asynchronous reset (Lessons 5-6): takes effect immediately, the moment the reset signal asserts, regardless of the clock.

Why the change here? These FSMs run from a divided slow_clk whose edges are seconds apart. If reset were synchronous you might press KEY(0) and watch nothing happen for over a second until the next slow edge — and if the machine were stuck, that is exactly when you most want an instant escape. An asynchronous reset gives you that immediate, reliable "force back to the start" on the real board. KEY is active-low, so KEY(0) going low is the moment you press the button and KEY(0) = '0' is true while it is held.

You press KEY(0) when the next slow_clk edge is still about a second away. With the asynchronous reset these FSMs use, when does the machine jump to its reset state? When would a synchronous reset have jumped instead?

The state register is written as process(slow_clk, KEY) with if KEY(0) = '0' then ... elsif rising_edge(slow_clk) then .... What does the asynchronous reset branch (if KEY(0) = '0' then ...) add, and how would the reset behave if you deleted it (leaving only a rising_edge(slow_clk) clocked process)?

3

Exercise 3-A: the traffic light (a Moore FSM)

10 min

Your first FSM is a traffic-light controller. It has three states and steps through them in a fixed cycle, holding each light for a set number of ticks:

GREEN -> YELLOW -> RED -> GREEN -> ...

The wiring is LEDR(0) = green, LEDR(1) = yellow, LEDR(2) = red, and KEY(0) = reset (active-low) which returns the machine to GREEN. The machine is clocked from a divided slow_clk: each rising edge is one state-machine tick. With this divider, use about 0.67 s per visible tick (the first rising edge after reset can arrive sooner).

This is a Moore machine. The output logic at the bottom is

LEDR(0) <= TODO_GREEN_LED;
LEDR(1) <= TODO_YELLOW_LED;
LEDR(2) <= TODO_RED_LED;

Every LED is a function of the state alone — no input appears. Knowing the state tells you exactly which light is on. That is the defining property of a Moore machine.

Two separate registers do two separate jobs. Read the state register carefully, because it contains *two* pieces of memory that are easy to confuse:

- state (a state_t signal) holds which light is currently on — GREEN, YELLOW, or RED. This is the FSM state.
- timer (a unsigned(2 downto 0) signal) is a separate dwell counter that counts down how many ticks remain in the current light. While timer is greater than zero it just decrements (timer <= timer - 1) and the state does not change. Only when timer reaches 0 does the machine advance to next_state and reload timer for the new light.

So the state register holds *which* light is on, and the timer counts *how long* it stays on. They are different registers with different purposes; the timer is what makes each light dwell instead of flashing past in one tick.

The actual dwell times, from the code. When the machine enters each state it reloads timer, and counting from the loaded value down to 0 inclusive gives the number of ticks:

- GREEN loads timer = 3 and counts 3, 2, 1, 0 -> 4 ticks (about 2.7 s).
- YELLOW loads timer = 1 and counts 1, 0 -> 2 ticks (about 1.3 s).
- RED loads timer = 3 -> 4 ticks (about 2.7 s).

One full cycle is therefore 4 + 2 + 4 = 10 ticks, roughly 10 x 0.67 s = 6.7 s end to end.

A three-state Moore finite state machine for a traffic light, drawn as three circles labeled GREEN, YELLOW, and RED connected by arrows in a cycle: GREEN to YELLOW to RED and back to GREEN. Each state is annotated with its dwell time in ticks — GREEN 4 ticks, YELLOW 2 ticks, RED 4 ticks — and each state lists the single LED it lights, showing that the output depends only on the state (Moore). A reset arrow labeled KEY(0) points into the GREEN state, indicating the machine returns to GREEN on reset.

The traffic light as a three-state Moore machine: GREEN -> YELLOW -> RED -> GREEN, with a dwell timer holding each state for a fixed number of ticks (GREEN 4, YELLOW 2, RED 4; one visible tick is about 0.67 s). Each state lights exactly one LED, so the outputs depend on the state alone. KEY(0) resets the machine to GREEN.

Here is the complete entity. The divider produces slow_clk; the state process with asynchronous reset advances the light when the dwell timer expires; a combinational process computes the next light; and the three concurrent LED assignments are the Moore output logic.

-- Student starter: fill the TODO lines before synthesizing.
-- Lesson 5 - Exercise 3-A: traffic light controller (Moore FSM).
-- KEY(0)=reset. LEDR(0)=green, LEDR(1)=yellow, LEDR(2)=red.
-- Dwell times: GREEN = 4 ticks, YELLOW = 2 ticks, RED = 4 ticks.
-- A visible FSM tick is one rising edge of slow_clk, about 0.67 s with this divider.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity blink is
    port (
        CLOCK_50 : in  std_logic;
        KEY      : in  std_logic_vector(3 downto 0);
        LEDR     : out std_logic_vector(9 downto 0)
    );
end entity blink;

architecture rtl of blink is
    type state_t is (GREEN, YELLOW, RED);
    signal state, next_state : state_t := GREEN;
    signal timer : unsigned(2 downto 0) := "011";
    constant DIV : unsigned(24 downto 0) := to_unsigned(16666666, 25);
    signal div_count : unsigned(24 downto 0) := (others => '0');
    signal slow_clk : std_logic := '0';
begin
    process(CLOCK_50)
    begin
        if rising_edge(CLOCK_50) then
            if KEY(0) = '0' then
                div_count <= (others => '0'); slow_clk <= '0';
            elsif div_count = DIV then
                div_count <= (others => '0'); slow_clk <= not slow_clk;
            else
                div_count <= div_count + 1;
            end if;
        end if;
    end process;

    process(state)
    begin
        case state is
            when GREEN  => next_state <= TODO_AFTER_GREEN;
            when YELLOW => next_state <= TODO_AFTER_YELLOW;
            when RED    => next_state <= TODO_AFTER_RED;
        end case;
    end process;

    process(slow_clk, KEY)
    begin
        if KEY(0) = '0' then
            state <= GREEN; timer <= "011";
        elsif rising_edge(slow_clk) then
            if timer = 0 then
                state <= next_state;
                case next_state is
                    when GREEN  => timer <= TODO_GREEN_RELOAD; -- 4 ticks
                    when YELLOW => timer <= TODO_YELLOW_RELOAD; -- 2 ticks
                    when RED    => timer <= TODO_RED_RELOAD; -- 4 ticks
                end case;
            else
                timer <= timer - 1;
            end if;
        end if;
    end process;

    LEDR(0) <= TODO_GREEN_OUTPUT;
    LEDR(1) <= TODO_YELLOW_OUTPUT;
    LEDR(2) <= TODO_RED_OUTPUT;
    LEDR(9 downto 3) <= (others => '0');
end architecture rtl;

The traffic light's outputs are concurrent assignments such as LEDR(0) <= '1' when state = GREEN else '0';. If you wanted to make one of these outputs Mealy instead of Moore, what kind of term would you have to add to its expression that is absent now?

4

Exercise 3-A on the board: observe and modify

12 min

Now build the traffic light on the real DE1-SoC, time each phase, and then change it.

  1. Open the DE1-SoC VHDL lab. The editor shows the file blink.vhd.

  2. Select all of the contents of blink.vhd and replace them with the traffic-light starter above. Fill every TODO line, keep the entity named blink, and keep CLOCK_50 : in std_logic.

  3. Click Synthesize. If a guided tour covers the button, close or skip the tour first. Wait for it to finish (about 1-3 minutes). The most common errors are a missing CLOCK_50 in the port list or a missing semicolon; fix and Synthesize again. Continue only when the final status says the build succeeded with 0 errors.

  4. Click Upload to FPGA and wait for the live board session and camera.

  5. Observe the cycle. Watch LEDR(0) (green), LEDR(1) (yellow), and LEDR(2) (red) cycle. Count how many seconds (or ticks, at about 0.67 s each) each light stays on, and record it. Compare your counts with the code: GREEN should hold for 4 ticks, YELLOW for 2, RED for 4.

  6. Press reset. While the light is on YELLOW or RED, press and release KEY(0). The machine should jump straight back to GREEN immediately. (You are seeing the asynchronous reset.)

Watch the traffic light cycle and time each phase on the camera. Record the duration you observe (in seconds, or in ticks at about 0.67 s per tick) and compare with the expected ticks from the code (GREEN 4, YELLOW 2, RED 4).

Phase (LED lit) Expected ticks (from code) Observed duration (s or ticks)

Upload your evidence. Watch for the YELLOW phase, capture a camera screenshot at a moment when LEDR(1) (yellow) is lit while LEDR(0) (green) and LEDR(2) (red) are off, and attach it below.

Now make one required timing change to the design.

(Modify) Change the dwell times. Edit the timer reload values so the cycle is GREEN = 6 ticks, YELLOW = 4 ticks, RED = 6 ticks. Remember the timer counts from its loaded value down to 0 inclusive, so N ticks means loading N - 1: load "101" (5) for GREEN, "011" (3) for YELLOW, and "101" (5) for RED (in both the case inside the state register and the reset line if a state's reset value changes). Synthesize, Upload, and confirm the new timings on the camera. Then work out the new total cycle length in ticks.

Optional extension: add a flashing-red state. Add a fourth state, FLASH_RED, after RED only if your teacher asks for the extension. To avoid driving LEDR(2) from two places, replace the existing red LED assignment with a single combined driver such as LEDR(2) <= '1' when state = RED else timer(0) when state = FLASH_RED else '0';. Then add FLASH_RED to the VHDL state_t type and next-state case, give it a dwell in the timer case, Synthesize, Upload, and confirm the red LED blinks before the light returns to green.

To make YELLOW last twice as long, which register's value would you change (state or timer), where exactly in the code, and which register would you leave completely untouched?

After changing the dwell reload values, run the traffic light again. Record the modified tick count you observe for each state so your code change is evidenced, not only described.

State Observed modified ticks Reload value used in code

For your modified times (GREEN = 6, YELLOW = 4, RED = 6 ticks), what is the total length of one full cycle in ticks? Show the arithmetic.

Using the code and what you observed, justify in two or three sentences that this is a Moore machine. Identify the output logic and explain why the LEDs are a function of the state only, not of any input.

5

Optional extension: the 1011 sequence detector (a Mealy FSM)

11 min

Your second FSM watches a stream of bits arriving one per clock tick on SW(0) and lights an LED whenever it has just seen the pattern 1011. This is a Mealy machine: it asserts "detected" in a particular state *only when the incoming bit is also correct*, so the output depends on state and input together.

The wiring is SW(0) = the serial input bit, KEY(0) = reset (active-low), LEDR(0) = detected, and LEDR(5 downto 1) = the current state shown one-hot (one LED per state) so you can watch the machine move. The machine is clocked from a divided slow_clk whose rising edge — one tick — happens about every 1.33 s with this divider.

The states. The detector remembers how much of 1011 it has matched so far:

- S0 — nothing matched yet (the reset state).
- S1 — the last bit was 1 (matched 1).
- S2 — matched 10.
- S3 — matched 101.
- S4 — matched the full 1011. From here the machine can still start a new match, which is what makes overlapping detections work (the trailing 1 of one 1011 can be the leading 1 of the next).

On each tick, the next-state block looks at the current state and the bit on SW(0) and moves accordingly. The detect output is asserted inside S3 only when the incoming bit is 1 (because 101 followed by 1 completes 1011) — that is the Mealy output, asserted from state and input.

Why we latch the output — read this carefully. The raw Mealy detect signal is high for only one tick. On a fast clock that pulse would be invisible; even on this slow clock, a single ~1.33 s flash is easy to miss on a remote camera if you blink or the frame lands wrong. So this design does not wire detect straight to the LED. Instead it latches the detection: a separate detected_latch register is set to 1 when detect fires and stays set until you press reset. LEDR(0) shows that latch, so once 1011 is detected the LED comes on and *stays on* until KEY(0). This is a deliberate design choice for remote viewing: a one-tick pulse is unreliable to capture, so we convert the brief event into a steady, latched indication.

A Mealy finite state machine that detects the bit sequence 1011, drawn as five states S0 through S4. S0 is the reset state (nothing matched). Arrows labeled with the input bit show transitions: from S0 a 1 goes to S1 (matched '1') and a 0 stays in S0; from S1 a 1 stays in S1 and a 0 goes to S2 (matched '10'); from S2 a 1 goes to S3 (matched '101') and a 0 returns to S0; from S3 a 1 completes '1011' and the arrow to S4 is marked `1 / detect` (the Mealy output, asserted on state S3 plus input 1), while a 0 from S3 goes back to S2 because the trailing '10' can begin a new match (the overlap rule); from S4 a 1 goes to S1 and a 0 goes to S2.

The 1011 detector as a five-state Mealy machine. States S0..S4 track how much of 1011 has matched. The detect output (the slash on the S3 transition) is asserted from state S3 and input 1 together — a Mealy output. The overlap rule keeps the machine ready for a new match (for example, S3 on a 0 falls back to S2, not all the way to S0). The one-tick detect pulse is latched into LEDR(0) so it stays lit until reset.

Here is the complete entity. Note the two-process shape: a combinational process computes next_state and the Mealy detect, and a clocked process(slow_clk, KEY) holds the state and the latch with asynchronous reset.

-- Student starter: fill the TODO lines before synthesizing.
-- Lesson 5 - Exercise 3-B: 1011 sequence detector (Mealy FSM) with latched detect.
-- SW(0)=serial input bit. KEY(0)=reset. LEDR(0)=detected latch.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity blink is
    port (
        CLOCK_50 : in  std_logic;
        KEY      : in  std_logic_vector(3 downto 0);
        SW       : in  std_logic_vector(9 downto 0);
        LEDR     : out std_logic_vector(9 downto 0)
    );
end entity blink;

architecture rtl of blink is
    type state_t is (S0, S1, S2, S3, S4);
    signal state, next_state : state_t := S0;
    signal detect, detected_latch : std_logic := '0';
    constant DIV : unsigned(26 downto 0) := to_unsigned(33333333, 27);
    signal div_count : unsigned(26 downto 0) := (others => '0');
    signal slow_clk : std_logic := '0';
begin
    process(CLOCK_50)
    begin
        if rising_edge(CLOCK_50) then
            if KEY(0) = '0' then
                div_count <= (others => '0'); slow_clk <= '0';
            elsif div_count = DIV then
                div_count <= (others => '0'); slow_clk <= not slow_clk;
            else
                div_count <= div_count + 1;
            end if;
        end if;
    end process;

    process(state, SW)
    begin
        next_state <= S0;
        detect <= '0';
        case state is
            when S0 => TODO_TRANSITION_FROM_S0;
            when S1 => TODO_TRANSITION_FROM_S1;
            when S2 => TODO_TRANSITION_FROM_S2;
            when S3 => TODO_TRANSITION_FROM_S3_AND_DETECT;
            when S4 => TODO_TRANSITION_FROM_S4;
        end case;
    end process;

    process(slow_clk, KEY)
    begin
        if KEY(0) = '0' then
            state <= S0; detected_latch <= '0';
        elsif rising_edge(slow_clk) then
            state <= next_state;
            if TODO_DETECT_CONDITION then detected_latch <= '1'; end if;
        end if;
    end process;

    LEDR(0) <= detected_latch;
    LEDR(5 downto 1) <= "00001" when state = S0 else
                         "00010" when state = S1 else
                         "00100" when state = S2 else
                         "01000" when state = S3 else
                         "10000";
    LEDR(9 downto 6) <= (others => '0');
end architecture rtl;

Could you predict exactly when detect fires just by watching the state LEDs LEDR(5 downto 1), *without* knowing SW(0)? Use your answer to explain why this is a Mealy output, not a Moore one.

6

Optional extension on the board: feed bits and recover

12 min

Now build the detector and feed it bits by hand. How to feed one bit: set SW(0) to the bit value, then wait one full slow-clock tick (about 1.33 s) so the machine samples it on the next rising edge, then change SW(0) to the next bit and wait again. Watch LEDR(5 downto 1) to follow the state as you go.

  1. In blink.vhd, replace all contents with the 1011 detector starter above. Fill every TODO transition and condition, keep the entity named blink, and keep CLOCK_50 : in std_logic.

  2. Click Synthesize, wait for success, then click Upload to FPGA.

  3. Reset first. Press and release KEY(0). LEDR(0) (detected) should be off and the state LEDs LEDR(5 downto 1) should show S0 (00001).

  4. Feed 1, 0, 1, 1. Set SW(0) = 1, wait ~1.33 s; set SW(0) = 0, wait; set SW(0) = 1, wait; set SW(0) = 1, wait. After the final 1, LEDR(0) should latch on and stay on. Record the state LEDs after each bit.

  5. Reset, then feed 1, 0, 1, 0, 1, 1 (a wrong bit in the middle). Feed each bit the same way. The machine should *not* false-trigger on the bad bit, should recover, and should still detect the 1011 at the end (LEDR(0) latches on). This shows the FSM recovers from a wrong bit instead of getting stuck.

  6. Reset whenever you want to start a fresh stream; reset is the only thing that clears the latched LEDR(0).

Feed the bit stream 1,0,1,1 one bit per slow-clock tick (about 1.33 s each). After each bit, read the state from LEDR(5 downto 1) (one-hot: 00001=S0, 00010=S1, 00100=S2, 01000=S3, 10000=S4) and whether LEDR(0) (detected) has latched on. The detection should latch after the final 1.

Bit fed (SW(0)) LEDR(5 downto 1) observed (one-hot) LEDR(0) detected? (on/off)

Upload your evidence. After feeding 1, 0, 1, 1, LEDR(0) (detected) latches on and stays lit until reset — a steady state you can photograph. Capture a camera screenshot showing LEDR(0) latched on, and attach it below.

Optional extension if time remains: modify the detector to recognize 101 instead of 1011. First draw the new state diagram on paper: you need states for "nothing matched," "matched 1," and "matched 10," and the detect output should fire when, in the "matched 10" state, the next bit is 1. If you implement it, keep the latch and one-hot state display, Synthesize, Upload, and confirm that feeding 1, 0, 1 latches LEDR(0).

Is the raw Mealy detect output registered (stored in a flip-flop) or combinational (computed directly from state and input)? What timing problem can a combinational Mealy output cause, and how does that connect to why we latched LEDR(0)?

Suppose you wired the raw one-tick detect straight to LEDR(0) instead of latching it. On the ~1.33 s slow clock, what would you most likely see on the camera the moment 1011 is detected, and why is that unreliable to capture?

In step 5 you fed 1, 0, 1, 0, 1, 1 and the machine still detected 1011 at the end. Explain briefly how the FSM recovered from the wrong bit instead of getting stuck or false-triggering.

7

What you built

4 min

You built a Moore traffic-light controller on the real DE1-SoC: its outputs depend on the state alone, and it steps GREEN -> YELLOW -> RED on a dwell timer. If you did the optional extension, you also explored a Mealy 1011 sequence detector whose detect output depends on the state and the input together. You used the standard FSM shape — a clocked state register, a combinational next-state block with a default, and output logic — and an active-low asynchronous reset that forces a known state immediately. You also saw how active-low reset brings a slow-clock FSM back to a known state immediately; if you did the optional detector, you saw why a one-tick output is latched so a remote camera can catch it.

Which sequence matches what you did for each exercise in this lesson?

In three or four sentences: explain why the traffic light is a Moore machine (cite the output logic), state the difference between the state register and the timer register, describe the dwell-time change you tested, and name one place where active-low KEY polarity or the asynchronous reset mattered in your steps. If you did the optional detector, add one sentence explaining why it is Mealy or why LEDR(0) was latched.