状态机实现LED流水灯
本次实验,我们将利用状态机的思想来进行Verilog编程实现一个LED流水灯,并通过Modelsim来进行模拟仿真,再到DE2-115开发板上进行验证。 首先进行主要代码的编写。
module led (
input sys_clk,
input sys_rst_n,
output reg [7:0] led
);
// 状态定义(8个状态)
parameter S0 = 3'd0,
S1 = 3'd1,
S2 = 3'd2,
S3 = 3'd3,
S4 = 3'd4,
S5 = 3'd5,
S6 = 3'd6,
S7 = 3'd7;
parameter MAX_COUNT = 25_000_000; // 0.5秒@50MHz
reg [2:0] current_state; // 当前状态寄存器
reg [2:0] next_state; // 下一状态寄存器
reg [25:0] counter; // 26位定时计数器
// 状态寄存器
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
current_state <= S0; // 异步复位
else
current_state <= next_state; // 正常状态转移
end
// 计数器逻辑
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
counter <= 0; // 复位清零
else if (current_state != next_state)
counter <= 0; // 状态切换时清零
else
counter <= counter + 1;
end
// 状态转移逻辑
always @(*) begin
case (current_state)
S0: next_state = (counter == MAX_COUNT-1) ? S1 : S0;
S1: next_state = (counter == MAX_COUNT-1) ? S2 : S1;
S2: next_state = (counter == MAX_COUNT-1) ? S3 : S2;
S3: next_state = (counter == MAX_COUNT-1) ? S4 : S3;
S4: next_state = (counter == MAX_COUNT-1) ? S5 : S4;
S5: next_state = (counter == MAX_COUNT-1) ? S6 : S5;
S6: next_state = (counter == MAX_COUNT-1) ? S7 : S6;
S7: next_state = (counter == MAX_COUNT-1) ? S0 : S7;
default: next_state = S0;
endcase
end
// 输出逻辑(循环右移模式)
always @(*) begin
case (current_state)
S0: led = 8'b00000001;
S1: led = 8'b00000010;
S2: led = 8'b00000100;
S3: led = 8'b00001000;
S4: led = 8'b00010000;
S5: led = 8'b00100000;
S6: led = 8'b01000000;
S7: led = 8'b10000000;
default: led = 8'b00000001;
endcase
end
endmodule
然后为了能在Modelsim中进行模拟仿真,我们还需要编写一个测试模块代码。
`timescale 1ns/1ps
module tb_led();
reg sys_clk;
reg sys_rst_n;
wire [7:0] led;
// 实例化被测试模块(缩小计数器值便于仿真)
led_fsm_8bit #(.MAX_COUNT(3)) uut (
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.led(led)
);
// 生成50MHz时钟
initial begin
sys_clk = 0;
forever #10 sys_clk = ~sys_clk; // 20ns周期=50MHz
end
// 测试流程控制
initial begin
// 初始化
sys_rst_n = 0;
#20; // 等待一个时钟上升沿
sys_rst_n = 1;
// 运行2000ns(观察完整状态周期)
#2000;
$finish;
end
// 监控输出
initial begin
$monitor("Time = %tns | State = %d | LED = %08b",
$time, uut.current_state, led);
end
endmodule
编写完测试代码并成功编译后,就可以准备进行模拟仿真了,首先对仿真文件进行绑定,选择Setting-->EDA Tool Settings-->Simulation。



然后就可以进行模拟仿真了。


仿真完成后,进行DE2-115开发板的实物验证。首先对管脚进行配置。



把程序烧录到开发板,就可以看到效果。

CPLD和FPGA
对比维度 | CPLD | FPGA |
---|---|---|
核心架构 | 基于乘积项(Product-Term)和宏单元(Macrocell),结构简单,逻辑资源有限 | 基于查找表(LUT)和寄存器,逻辑单元(LE)灵活组合,资源丰富 |
存储技术 | 采用EEPROM或Flash工艺,非易失性,无需外部配置芯片 | 基于SRAM工艺,掉电丢失配置数据,需外部存储器 |
资源规模 | 逻辑单元较少(几十至几百宏单元),适合小规模逻辑设计 | 逻辑单元可达数百万级,支持大规模复杂设计 |
时序特性 | 连续式布线,延迟均匀且可预测 | 分段式布线,延迟不可预测 |
功耗 | 静态功耗较高,适合低复杂度场景 | 动态功耗优化更好,适合高性能计算 |
编程灵活性 | 编程次数有限(约1万次),逻辑固化后不可重构 | 支持无限次动态重构,灵活适配不同算法 |
启动时间 | 上电即用,无需配置时间 | 需从外部加载配置数据,存在启动延迟 |
CPLD的典型应用场景:简单逻辑控制比如状态机、地址译码、总线控制等组合逻辑密集型任务;某些接口的转换,比如电平转换(TTL与LVDS)、I/O扩展、协议适配(SPI转UART);胶合逻辑,这在复杂系统中作为"粘合剂",可以连接不同功能模块(DSP与存储器间的控制逻辑);低功耗需求场景比如工业控制、仪器仪表中的简单逻辑处理。
FPGA的典型应用场景:复杂时序逻辑比如如高速数据处理(通信协议处理、雷达信号处理)、实时控制(自动驾驶传感器融合);并行计算加速:数字信号处理(DSP)、AI推理、图像处理(ISP算法加速);动态重构系统,常用于需要硬件功能随需求变化的场景(软件定义无线电);高性能计算如数据中心加速、加密解密、科学仿真等对算力要求高的领域。
从设计复杂度上看,CPLD适用于门数小于1万的设计,FPGA更适合大规模设计(>10万门)。从时序要求上看,CPLD延迟可预测,适合实时性强的控制逻辑;FPGA虽延迟不可预测,但通过时序约束优化可实现高频运行(如500MHz以上)。从功耗与成本上看,CPLD成本低但功耗较高,FPGA能效比更优但需要额外配置芯片。从技术融合趋势上看,现代CPLD(如Altera MAX系列)逐渐采用FPGA的LUT架构,界限模糊,但核心差异仍存在。
hdlbitsFPGA组合逻辑练习
D触发器

D锁存器

Two gates

计数器1-12

简单电路A

总结
本次实验通过状态机设计方法,成功实现了LED流水灯的Verilog编程,并在Modelsim中进行了仿真验证,最终在DE2-115开发板上进行了实物验证。此外,通过对CPLD和FPGA的对比分析,进一步加深了对这两种器件的理解,为今后的设计提供了参考。