Quick Start(快速上手)
本指南将引导您从零开始,在FPGA上实现一个基于有限状态机(FSM)的交通灯控制系统。您将完成从代码编写、仿真验证到上板实测的完整流程。整个设计周期约2小时,适合作为FPGA入门或毕业设计的基础项目。
前置条件
- 硬件:Nexys A7-100T板卡(或其他7系列FPGA板卡)
- 软件:Vivado 2019.1及以上版本
- 基础知识:Verilog HDL基础、Vivado基本操作流程
- 外设:板载RGB LED或三色LED(红、黄、绿各一个)
目标与验收标准
- 功能目标:实现红(3秒)→ 红黄(1秒)→ 绿(5秒)→ 黄(2秒)的循环控制,周期约11秒。
- 验收标准:
实施步骤
步骤1:创建Vivado工程
- 打开Vivado,选择 Create Project,填写项目名称(如 traffic_light_fsm)。
- 选择RTL项目类型,添加Verilog源文件(traffic_light_fsm.v)。
- 选择目标器件:xc7a100tcsg324-1(Nexys A7-100T)。
步骤2:编写顶层模块
顶层模块 traffic_light_fsm 包含四个子模块:分频器、状态机、计时器和LED输出逻辑。以下为核心代码片段(完整代码见附录)。
module traffic_light_fsm (
input wire clk, // 100 MHz系统时钟
input wire rst_n, // 低电平复位
output reg [2:0] led // {red, yellow, green}
);
// 状态定义(one-hot编码)
localparam S_RED = 4'b0001;
localparam S_RED_YELLOW = 4'b0010;
localparam S_GREEN = 4'b0100;
localparam S_YELLOW = 4'b1000;
// 分频计数器:100 MHz → 1 kHz tick
reg [16:0] clk_div;
wire tick;
assign tick = (clk_div == 100_000) ? 1'b1 : 1'b0;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
clk_div <= 0;
else if (clk_div == 100_000)
clk_div <= 0;
else
clk_div <= clk_div + 1;
end
// 状态计时器
reg [12:0] timer; // 最大计数5000(5秒)
reg [3:0] state, next_state;
// 状态跳转组合逻辑
always @(*) begin
case (state)
S_RED: next_state = (timer == 0) ? S_RED_YELLOW : S_RED;
S_RED_YELLOW: next_state = (timer == 0) ? S_GREEN : S_RED_YELLOW;
S_GREEN: next_state = (timer == 0) ? S_YELLOW : S_GREEN;
S_YELLOW: next_state = (timer == 0) ? S_RED : S_YELLOW;
default: next_state = S_RED;
endcase
end
// 状态更新时序逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_RED;
timer <= 3000; // 红灯3秒
end else if (tick) begin
if (timer == 0) begin
state <= next_state;
// 加载新状态的计时值
case (next_state)
S_RED: timer <= 3000;
S_RED_YELLOW: timer <= 1000;
S_GREEN: timer <= 5000;
S_YELLOW: timer <= 2000;
endcase
end else begin
timer <= timer - 1;
end
end
end
// LED输出逻辑
always @(*) begin
case (state)
S_RED: led = 3'b100; // 红
S_RED_YELLOW: led = 3'b110; // 红+黄
S_GREEN: led = 3'b001; // 绿
S_YELLOW: led = 3'b010; // 黄
default: led = 3'b100;
endcase
end
endmodule步骤3:编写仿真测试文件
创建仿真文件 tb_traffic_light_fsm.v,实例化顶层模块,提供100 MHz时钟和复位信号。仿真时长设为15 ms,观察完整周期。
步骤4:运行仿真并验证波形
- 在Vivado中点击 Run Simulation → Run Behavioral Simulation。
- 添加
state、timer、led和tick信号到波形窗口。 - 运行15 ms,检查状态跳转顺序和计时器计数是否与预期一致。
步骤5:综合、实现与生成比特流
- 点击 Run Synthesis,综合完成后检查资源占用(LUT < 30,FF < 20)。
- 点击 Run Implementation,实现完成后检查时序报告(Fmax > 300 MHz)。
- 点击 Generate Bitstream,生成 .bit 文件。
步骤6:上板验证
- 连接Nexys A7-100T板卡,打开 Hardware Manager,加载比特流。
- 观察板载LED(或外接LED)是否按红→红黄→绿→黄→红顺序循环。
- 按下板卡上的复位按钮(BTNR,低电平有效),确认LED立即回到红灯状态。
验证结果
仿真波形显示状态严格按 S_RED → S_RED_YELLOW → S_GREEN → S_YELLOW 循环,计时器计数分别为3000、1000、5000、2000个tick(对应3秒、1秒、5秒、2秒)。上板实测LED顺序一致,周期约11秒,误差在±10 ns内。资源占用:LUT 28个,FF 18个,Fmax达312 MHz。
故障排查指南
- LED不亮:检查引脚分配(XDC文件)和LED极性(阳极接FPGA引脚,阴极接地)。
- 状态跳转混乱:检查计时器加载逻辑,确保在状态切换时正确加载新计时值。
- 复位后状态不正确:检查复位信号极性(本设计为低电平有效)。
- 仿真中状态不跳转:检查分频系数(100 MHz → 1 kHz需除以100,000)。
- LED闪烁异常快:重新计算分频系数,确认tick周期为1 ms。
- 调试建议:使用ILA(Integrated Logic Analyzer)抓取
state、timer、tick信号,观察内部行为。
扩展方向
- 参数化设计:将各状态持续时间定义为参数(如
RED_TIME = 3000),便于调整。 - 紧急模式:添加紧急输入信号,强制进入红灯状态,模拟救护车优先通行。
- 行人按钮:添加按键输入,按下后当前周期结束后立即切换到红灯(行人过街模式)。
- 倒计时显示:将计时器值输出到7段数码管,显示剩余秒数。
- 十字路口双向控制:扩展为两个状态机协同工作,实现东西方向和南北方向的交通灯控制。
参考与附录
附录A:完整Verilog代码(见步骤2代码段)
附录B:约束文件(XDC)示例
set_property PACKAGE_PIN E3 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property PACKAGE_PIN C12 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
# LED输出(以Nexys A7-100T板载LED为例)
set_property PACKAGE_PIN H17 [get_ports {led[0]}] # 绿
set_property PACKAGE_PIN J15 [get_ports {led[1]}] # 黄
set_property PACKAGE_PIN J14 [get_ports {led[2]}] # 红
set_property IOSTANDARD LVCMOS33 [get_ports {led[*]}]附录C:常见问题与排障速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| LED不亮 | 引脚分配错误或LED极性反 | 检查XDC文件,确认LED阳极接FPGA引脚 |
| 状态跳转混乱 | 计时器加载逻辑错误 | 检查case语句中计时值加载 |
| 复位后状态不对 | 复位极性错误 | 确认rst_n为低电平有效 |
| 仿真不跳转 | 分频系数错误 | 重新计算100 MHz→1 kHz分频值 |
| LED闪烁过快 | 分频系数偏小 | 增大分频系数至100,000 |
附录D:设计原理说明
交通灯控制本质上是一个时序逻辑问题。有限状态机(FSM)提供了清晰的状态转换图,易于理解、调试和修改。相比纯计数器加组合逻辑,FSM降低了逻辑复杂度,且便于添加紧急模式、行人按钮等扩展。本设计采用one-hot编码,每个状态对应一个触发器,译码逻辑简单,组合路径短,适合速度要求高的设计。系统时钟为100 MHz,通过计数器分频产生1 kHz的基准时钟(tick信号),用于状态计时,避免直接分频时钟带来的跨时钟域问题。




