traffic_light_fsm

Modified

2025-11-11

Source: Lab3Extra/traffic_light_fsm.sv (modified 2025-11-09 23:33)

// traffic_light_fsm.sv
// Moore FSM for two-way traffic lights with mandatory 5-second all-red intervals.
// Author: <Your Name>
// Course: ECE (HW 3.24)
// -----------------------------------------------------------------------------
// Light encoding: 3'b100=RED, 3'b010=YELLOW, 3'b001=GREEN

module TrafficLightFSM #(
    // ---- Clock setup ----
    parameter int unsigned CLK_HZ   = 50_000_000, // board clock; TB will override
    parameter int unsigned TICK_HZ  = 1,          // 1 tick per second by default

    // ---- Durations in SECONDS ----
    parameter int unsigned T_A_GREEN = 10,
    parameter int unsigned T_A_YEL   = 3,
    parameter int unsigned T_B_GREEN = 10,
    parameter int unsigned T_B_YEL   = 3,
    parameter int unsigned T_ALL_RED = 5
)(
    input  logic clk,
    input  logic rst_n,          // active-low reset (async assert, sync deassert recommended)
    output logic [2:0] LA,       // A-side lights {R,Y,G}
    output logic [2:0] LB        // B-side lights {R,Y,G}
);

    // ---------------------------
    // 1) clock divider to TICK_HZ
    // ---------------------------
    localparam longint DIVISOR = CLK_HZ / TICK_HZ;
    logic [$clog2(DIVISOR)-1:0] div_cnt;
    logic                       tick_1hz;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            div_cnt <= '0;
            tick_1hz <= 1'b0;
        end else begin
            if (div_cnt == DIVISOR-1) begin
                div_cnt  <= '0;
                tick_1hz <= 1'b1;
            end else begin
                div_cnt  <= div_cnt + 1'b1;
                tick_1hz <= 1'b0;
            end
        end
    end

    // ---------------------------
    // 2) FSM states (Moore)
    // ---------------------------
    typedef enum logic [2:0] {
        S_A_G      = 3'd0,   // A: Green,  B: Red
        S_A_Y      = 3'd1,   // A: Yellow, B: Red
        S_ALL_AB   = 3'd2,   // All-Red between A->B (5s)
        S_B_G      = 3'd3,   // B: Green,  A: Red
        S_B_Y      = 3'd4,   // B: Yellow, A: Red
        S_ALL_BA   = 3'd5    // All-Red between B->A (5s)
    } state_t;

    state_t state, state_n;

    // ---------------------------
    // 3) per-state down counter (seconds)
    // ---------------------------
    int unsigned sec_cnt, sec_cnt_n;

    // helper: load value for each state
    function automatic int unsigned state_seconds(state_t s);
        case (s)
            S_A_G    : return T_A_GREEN;
            S_A_Y    : return T_A_YEL;
            S_ALL_AB : return T_ALL_RED;   // exactly 5 seconds per spec
            S_B_G    : return T_B_GREEN;
            S_B_Y    : return T_B_YEL;
            S_ALL_BA : return T_ALL_RED;   // exactly 5 seconds per spec
            default  : return T_ALL_RED;
        endcase
    endfunction

    // ---------------------------
    // 4) next-state logic
    // ---------------------------
    always_comb begin
        state_n   = state;
        sec_cnt_n = sec_cnt;

        if (tick_1hz) begin
            if (sec_cnt == 0) begin
                // time to advance
                unique case (state)
                    S_A_G    : state_n = S_A_Y;
                    S_A_Y    : state_n = S_ALL_AB; // insert ALL-RED before B gets green
                    S_ALL_AB : state_n = S_B_G;
                    S_B_G    : state_n = S_B_Y;
                    S_B_Y    : state_n = S_ALL_BA; // insert ALL-RED before A gets green
                    S_ALL_BA : state_n = S_A_G;
                    default  : state_n = S_A_G;
                endcase
                // load next state's seconds
                sec_cnt_n = state_seconds(state_n) - 1; // we will consume 1s on the next tick edge
            end else begin
                // still waiting in this state
                sec_cnt_n = sec_cnt - 1;
            end
        end
    end

    // ---------------------------
    // 5) state & counter registers
    // ---------------------------
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state   <= S_ALL_BA;                       // start from an all-red, then go to A_G
            sec_cnt <= state_seconds(S_ALL_BA);        // full duration first
        end else begin
            state   <= state_n;
            sec_cnt <= sec_cnt_n;
        end
    end

    // ---------------------------
    // 6) Moore outputs
    // LA/LB are one-hot: {R,Y,G}
    // ---------------------------
    localparam logic [2:0] RED    = 3'b100;
    localparam logic [2:0] YELLOW = 3'b010;
    localparam logic [2:0] GREEN  = 3'b001;

    always_comb begin
        // defaults
        LA = RED;
        LB = RED;

        unique case (state)
            S_A_G   : begin LA = GREEN;  LB = RED;    end
            S_A_Y   : begin LA = YELLOW; LB = RED;    end
            S_ALL_AB: begin LA = RED;    LB = RED;    end
            S_B_G   : begin LA = RED;    LB = GREEN;  end
            S_B_Y   : begin LA = RED;    LB = YELLOW; end
            S_ALL_BA: begin LA = RED;    LB = RED;    end
            default : begin LA = RED;    LB = RED;    end
        endcase
    end

endmodule