Verilog三段式FSM,实现十字路口红绿灯

发布于:2025-09-08 ⋅ 阅读:(22) ⋅ 点赞:(0)

运行环境:VCS + verdi

状态说明:

S0 : 初始状态
S1 : 东西方向绿灯亮,南北方向红灯亮;点亮30周期
S2 : 东西方向黄灯亮,南北方向红灯亮;点亮2 周期
S3 : 东西方向红灯亮,南北方向绿灯亮;点亮30周期
S4 : 东西方向红灯亮,南北方向黄灯亮;点亮2 周期

状态转换说明:

S0 → S1

S1:点亮30周期,进入S2,不足30周期维持S1

S2:点亮2周期,进入S3,不足2周期维持S2

S3:点亮30周期,进入S4,不足30周期维持S3

S4:点亮2周期,进入S1,不足2周期维持S4

代码如下:

如果需要控制时间,自行添加分频器,或者修改tb文件。

module traffic_light (
    input   clk,
    input   rst_n,
    output [2:0] light_east_west, light_south_north
);

// 红路灯点亮的时间定义
localparam GREEN  = 30;
localparam YELLOW = 2;

// 亮灯的颜色定义
localparam dark  = 2'b00;
localparam green = 2'b01;
localparam yello = 2'b10;
localparam red   = 2'b11;

// 状态定义
parameter S0 = 5'b0_0001;  // 初始状态
parameter S1 = 5'b0_0010;  // 东西方向绿灯亮,南北方向红灯亮;30周期
parameter S2 = 5'b0_0100;  // 东西方向黄灯亮,南北方向红灯亮;2 周期
parameter S3 = 5'b0_1000;  // 东西方向红灯亮,南北方向绿灯亮;30周期
parameter S4 = 5'b1_0000;  // 东西方向红灯亮,南北方向黄灯亮;2 周期

reg [4:0] status;
reg [4:0] next_status;
reg [7:0] cnt;

// 计数器控制
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) cnt <= 8'b0;
    else if (   (status == S1 && cnt == GREEN-1 ) ||(status == S2 && cnt == YELLOW-1) ||
                (status == S3 && cnt == GREEN-1 ) ||(status == S4 && cnt == YELLOW-1)   )
        cnt <= 8'b0;
    else
        cnt <= cnt + 1'b1;
end

// 状态转换
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        status <= S0;
    else
        status <= next_status;
end

// 状态判断
always @(*) begin
    if(!rst_n) begin
        next_status = S0;
    end
    else begin
        case(status)
            S0 : next_status = S1;
            S1 : next_status = (cnt == GREEN-1)  ? S2 : S1;
            S2 : next_status = (cnt == YELLOW-1) ? S3 : S2;
            S3 : next_status = (cnt == GREEN-1)  ? S4 : S3;
            S4 : next_status = (cnt == YELLOW-1) ? S1 : S4;
            default : next_status = S0;
        endcase
    end
end

reg [2:0] r_light_east_west, r_light_south_north;

// 输出控制
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        r_light_east_west  <= dark;
        r_light_south_north  <= dark;
    end
    else begin
        case(status)
            S0 : begin
                r_light_east_west   <= dark;
                r_light_south_north <= dark;
            end
            S1 : begin
                r_light_east_west   <= green;
                r_light_south_north <= red;
            end
            S2 : begin
                r_light_east_west   <= yello;
                r_light_south_north <= red;
            end
            S3 : begin
                r_light_east_west   <= red;
                r_light_south_north <= green;
            end
            S4 : begin
                r_light_east_west   <= red;
                r_light_south_north <= yello;
            end
            default: begin
                r_light_east_west   <= dark;
                r_light_south_north <= dark;
            end
        endcase
    end
end

assign light_east_west   = r_light_east_west;
assign light_south_north = r_light_south_north;

endmodule

tb文件:

module tb_traffic_light ();
localparam CLK_PERIDO = 20;

reg   clk;
reg   rst_n;
reg [2:0] light_east_west;
reg [2:0] light_south_north;

traffic_light traffic_light_inst(
    .clk    (clk),
    .rst_n  (rst_n),
    .light_east_west    (light_east_west), 
    .light_south_north  (light_south_north)
);

initial begin
    clk = 0;
    forever #(CLK_PERIDO / 2) clk = ~clk;   
end

initial begin
    rst_n = 0;
    repeat(4) @(posedge clk);
    #(CLK_PERIDO /5);
    rst_n = 1;
    repeat(500) @(posedge clk);
    $finish;
end

initial begin
    $dumpfile("traffic_light.fsdb");
    $dumpvars(0);
end

endmodule

时序图:


网站公告

今日签到

点亮在社区的每一天
去签到