Traffic Light FSM
Digital Design

Traffic Light FSM

April 2026 · VHDL Project

States
4 (S0 → S1 → S2 → S3)
Encoding
2-bit binary ("00" to "11")
Reset
Async active-high → S0
Clock
Rising-edge triggered, 10 ns period

The Problem

Traffic intersections require precise, deterministic control to prevent collisions — both signals can never show Green or Yellow at the same time. The goal of this Discovery Project was to design a digital controller from scratch using VHDL that safely manages a dual-direction traffic light system, cycling through Green → Yellow → Red phases for two roads (A and B) in a guaranteed conflict-free pattern.

This project served as a hands-on introduction to hardware description languages and finite state machine design — core ECE skills that bridge the gap between software logic and actual hardware implementation.

The Process

I started by sketching out the state diagram on paper: four states (S0–S3) arranged in a cycle, where each state defines the exact light for both directions. S0 gives A Green / B Red, S1 gives A Yellow / B Red, S2 flips to A Red / B Green, and S3 gives A Red / B Yellow before looping back.

With the state diagram locked in, I translated it into a VHDL behavioral architecture. The FSM uses a 2-bit state register encoded as a STD_LOGIC_VECTOR, advancing on each rising clock edge. An asynchronous active-high reset forces the machine back to S0. All six output signals (a_red, a_yellow, a_green, b_red, b_green, b_yellow) are derived combinationally from the current state using concurrent signal assignments.

I then wrote a full testbench with a 10 ns clock period, applying reset for 12 ns before releasing it. The simulation runs for 13 full clock cycles, allowing observation of multiple complete rotations through all four states. The testbench separates clock generation and stimulus into independent processes for clean modularity.

Successes & Challenges

The biggest success was getting the FSM to compile, simulate, and produce a correct waveform on the first real attempt. Seeing all eight signals toggle in the exact pattern I designed on paper was a satisfying validation that the state logic was correct.

One early challenge was understanding signal assignment semantics in VHDL — specifically the difference between concurrent assignments (outside a process) and sequential assignments (inside a process). I initially placed the output assignments inside the clocked process, which introduced an unintended one-cycle delay on the outputs. Moving them to concurrent assignments outside the process fixed the timing.

Another roadblock was the reset timing: I initially used a synchronous reset, but the waveform showed the FSM not resetting cleanly when reset was asserted between clock edges. Switching to an asynchronous reset (checking reset before the rising_edge condition) resolved this and matched the intended behavior.

ECE Skills Gained

This project directly developed several core ECE competencies: VHDL hardware description — writing synthesizable RTL using IEEE STD_LOGIC_1164 libraries, entity/architecture pairs, and process blocks. FSM design methodology — translating a state diagram into a case-based state machine with proper encoding. Testbench development — constructing self-checking verification environments with separate clock and stimulus processes.

Beyond the code itself, I gained practical experience reading waveform outputs to debug timing issues, understanding the distinction between combinational and sequential logic, and working with simulation tools to verify that a digital design behaves exactly as intended before it ever touches real hardware.

Final Thoughts

This Discovery Project reinforced my interest in digital design and the hardware side of ECE. Writing VHDL felt fundamentally different from writing software — you're describing physical hardware that runs in parallel, not sequential instructions. That mental shift was one of the most valuable takeaways.

If I were to continue this project, I'd extend it with configurable timing (so each state holds for a realistic duration rather than a single clock cycle), add a pedestrian crossing request input, and target an FPGA board to see it running on real hardware with actual LEDs. The foundation is solid — the FSM structure scales naturally to more complex intersection controllers.

Technologies Used

VHDLFSM DesignTestbenchSimulationIEEE STD_LOGIC_1164Waveform AnalysisConcurrent AssignmentsBehavioral Architecture
Design — design.vhd
1234567891011121314151617181920212223242526272829303132333435363738394041
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity traffic_light is
    Port (
        clk      : in  STD_LOGIC;
        reset    : in  STD_LOGIC;
        a_red    : out STD_LOGIC;
        a_yellow : out STD_LOGIC;
        a_green  : out STD_LOGIC;
        b_red    : out STD_LOGIC;
        b_yellow : out STD_LOGIC;
        b_green  : out STD_LOGIC
    );
end traffic_light;

architecture Behavioral of traffic_light is
    signal state : STD_LOGIC_VECTOR(1 downto 0) := "00";
begin
    process(clk, reset)
    begin
        if reset = '1' then
            state <= "00";
        elsif rising_edge(clk) then
            case state is
                when "00" => state <= "01";
                when "01" => state <= "10";
                when "10" => state <= "11";
                when others => state <= "00";
            end case;
        end if;
    end process;

    a_green  <= '1' when state = "00" else '0';
    a_yellow <= '1' when state = "01" else '0';
    a_red    <= '1' when state = "10" or state = "11" else '0';

    b_red    <= '1' when state = "00" or state = "01" else '0';
    b_green  <= '1' when state = "10" else '0';
    b_yellow <= '1' when state = "11" else '0';
end Behavioral;
Testbench — testbench.vhd
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use STD.env.all;

entity traffic_light_tb is
end traffic_light_tb;

architecture testbench of traffic_light_tb is
    signal clk      : STD_LOGIC := '0';
    signal reset    : STD_LOGIC := '1';
    signal a_red    : STD_LOGIC;
    signal a_yellow : STD_LOGIC;
    signal a_green  : STD_LOGIC;
    signal b_red    : STD_LOGIC;
    signal b_yellow : STD_LOGIC;
    signal b_green  : STD_LOGIC;
begin

    uut: entity work.traffic_light
        port map (
            clk      => clk,
            reset    => reset,
            a_red    => a_red,
            a_yellow => a_yellow,
            a_green  => a_green,
            b_red    => b_red,
            b_yellow => b_yellow,
            b_green  => b_green
        );

    clk_process: process
    begin
        for i in 0 to 12 loop
            clk <= '0';
            wait for 5 ns;
            clk <= '1';
            wait for 5 ns;
        end loop;
        wait;
    end process;

    stim_proc: process
    begin
        wait for 12 ns;
        reset <= '0';
        wait for 100 ns;
        stop;
        wait;
    end process;

end testbench;

State Diagram

S0
"00"
AGreen
BRed
S1
"01"
AYellow
BRed
S2
"10"
ARed
BGreen
S3
"11"
ARed
BYellow
Current: S0A = GreenB = Red

Simulation Waveform

⚡ Simulation Output