目录
[三、 CPLD芯片介绍及与FPGA芯片进行对比](#三、 CPLD芯片介绍及与FPGA芯片进行对比)
[(一) CPLD芯片简介](#(一) CPLD芯片简介)
[1. 结构与逻辑容量](#1. 结构与逻辑容量)
[2. 配置技术与易失性](#2. 配置技术与易失性)
[3. 时序特性与时钟管理](#3. 时序特性与时钟管理)
[4. 功耗与成本](#4. 功耗与成本)
[5. 适用场景](#5. 适用场景)
一、状态机简单介绍
(一)状态机的核心要素
-
状态(State)
系统在任意时刻只能处于其中一个预设的状态。例如,交通灯控制器可能包含"红灯""绿灯""黄灯"三种状态。每个状态代表系统的一种特定行为模式。
-
转移条件(Transition Condition)
状态之间的切换由事件或条件触发。例如,当倒计时结束时,交通灯从"绿灯"转移到"黄灯"。转移条件可以是外部输入(如按键信号)或内部逻辑(如计数器溢出)。
-
动作(Action)
在进入、离开或保持某一状态时执行的操作。例如,进入"绿灯"状态时启动倒计时,离开时关闭当前灯。
(二)状态机的类型
1、Moore型状态机
输出仅依赖于当前状态,与输入无关。例如,交通灯的亮灯逻辑完全由当前状态决定,与外部输入(如车辆检测信号)无关。其特点是输出稳定,但灵活性较低。

特点:
- 输出仅取决于当前状态
- 时序逻辑输出,稳定性更好
- 典型应用:模式识别、定时控制
2、Mealy型状态机
输出由当前状态和输入共同决定。例如,自动售货机在投币后根据金额和当前状态决定是否出货或找零。这种类型能更灵活地响应输入,但输出可能因输入变化而产生瞬时波动。

特点:
- 输出由当前状态和输入共同决定
- 组合逻辑输出,响应更快
- 典型应用:通信协议、实时控制

(三)状态机的设计步骤
1、需求分析
明确系统的所有可能状态、触发条件和预期动作。例如,流水灯系统需要定义四个状态(每个LED亮起),并确定切换的时间间隔。
2、绘制状态转移图
用图形化工具(如UML状态图)表示状态、转移条件和动作。例如,用箭头连接S0→S1→S2→S3→S0,标注转移条件为计时器溢出。
3、状态编码
为每个状态分配唯一的二进制编码。例如,S0=00、S1=01、S2=10、S3=11。编码需考虑资源优化和可扩展性。
4、代码实现
使用硬件描述语言(如Verilog)描述状态转移和输出逻辑。常见的实现方式分为以下三种:
- 一段式:将所有逻辑(状态转移、输出)放在单个时序块中。代码紧凑但可维护性差,适合简单逻辑。
- 二段式:用时序逻辑处理状态转移,组合逻辑处理输出和条件判断。可读性高,但输出可能存在毛刺。
- 三段式:独立时序块处理状态转移、组合逻辑判断条件、另一时序块生成输出。资源消耗稍高,但输出稳定且易于维护。
5、验证与调试
通过仿真覆盖所有状态路径,确保逻辑正确;硬件验证时需检查实际时序和物理信号。
(四)状态机的应用要点
-
复杂度管理
状态机通过模块化设计,将复杂逻辑分解为独立状态,降低系统整体复杂度。例如,通信协议解析器可划分为"空闲""接收头""接收数据""校验"等状态。
-
可读性与维护性
状态转移图直观展示逻辑流程,便于团队协作和后期修改。相比之下,嵌套的 if-else 语句在扩展时易引入错误。
-
硬件实现优化
明确的状态编码和分离的组合/时序逻辑,有助于综合工具生成高效电路。避免冗余逻辑(如未使用的状态)可节省FPGA资源。
-
抗干扰设计
添加"默认状态"处理未定义状态,防止系统锁死;关键路径添加时序约束,确保信号稳定。
二、实现LED流水灯及其仿真
(一)流水灯代码
module LS_LED(
input clk,
input rst_n,
input pause_sw,
output reg [5:0] led
);
// 仿真时使用较小的计数值,实际部署时改为50_000_000
`ifdef SIMULATION
parameter T = 50; // 仿真时缩短计数周期
`else
parameter T = 50_000_000; // 实际硬件运行时1秒定时
`endif
// 使用独热码(one-hot)编码状态,提高可读性
typedef enum logic [5:0] {
STATE_LED0 = 6'b000001,
STATE_LED1 = 6'b000010,
STATE_LED2 = 6'b000100,
STATE_LED3 = 6'b001000,
STATE_LED4 = 6'b010000,
STATE_LED5 = 6'b100000
} state_t;
state_t cstate, nstate; // 使用枚举类型定义状态
reg [25:0] cnt; // 26位计数器
// 三段式状态机实现
// 第一段:状态寄存器更新
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cstate <= STATE_LED0;
cnt <= 0;
end
else begin
if (!pause_sw) begin
if (cnt == T - 1) begin
cnt <= 0;
cstate <= nstate;
end
else begin
cnt <= cnt + 1;
end
end
// 暂停时保持当前状态和计数值
end
end
// 第二段:次态逻辑
always @(*) begin
case (cstate)
STATE_LED0: nstate = (cnt == T - 1) ? STATE_LED1 : STATE_LED0;
STATE_LED1: nstate = (cnt == T - 1) ? STATE_LED2 : STATE_LED1;
STATE_LED2: nstate = (cnt == T - 1) ? STATE_LED3 : STATE_LED2;
STATE_LED3: nstate = (cnt == T - 1) ? STATE_LED4 : STATE_LED3;
STATE_LED4: nstate = (cnt == T - 1) ? STATE_LED5 : STATE_LED4;
STATE_LED5: nstate = (cnt == T - 1) ? STATE_LED0 : STATE_LED5;
default: nstate = STATE_LED0;
endcase
end
// 第三段:输出逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
led <= STATE_LED0;
end
else begin
led <= cstate; // 状态直接映射到LED输出
end
end
endmodule
测试代码:
`timescale 1ns / 1ps
module LS_LED_tb;
// 输入信号
reg clk;
reg rst_n;
reg pause_sw;
// 输出信号
wire [5:0] led;
// 生成50MHz时钟
initial begin
clk = 0;
forever #10 clk = ~clk; // 50MHz时钟周期20ns
end
// 实例化被测模块
LS_LED uut(
.clk(clk),
.rst_n(rst_n),
.pause_sw(pause_sw),
.led(led)
);
// 测试流程
initial begin
// 初始化
rst_n = 0;
pause_sw = 1; // 初始暂停
#100;
// 测试1: 复位释放
rst_n = 1;
#100;
// 测试2: 正常流水灯效果
pause_sw = 0;
#5000; // 观察多个状态转换
// 测试3: 暂停功能
pause_sw = 1;
#1000;
// 测试4: 恢复运行
pause_sw = 0;
#3000;
// 测试5: 复位功能
rst_n = 0;
#50;
rst_n = 1;
#100;
$display("Simulation complete");
$finish;
end
// 波形记录
initial begin
$dumpfile("ls_led.vcd");
$dumpvars(0, LS_LED_tb);
end
endmodule
(二)modesim仿真
- 生成.vt文件
点击Processing→Start→Start test bench template writer,便生成了对应的.vt文件。

- 修改.vt文件内容为测试代码
在quartus界面中打开,随后将测试代码写入其中并保存

- 然后对仿真文件设置点击Assignments→Settings,再点击下面的Simulation按照如图设置

- 然后浏览刚才的.vt文件,最后点击Add添加达到如下图效果,再点击OK即可。

- 随后回到初始界面点击下图按钮进行仿真得到流水灯的仿真结果

(三)结果展示
1、modesim仿真结果

2、硬件结果
FPGA流水灯0.0
三、 CPLD芯片介绍及与FPGA芯片进行对比
(一) CPLD芯片简介
CPLD(复杂可编程逻辑器件)是基于EEPROM/Flash的非易失性半定制数字电路,集成可编程逻辑块、互连矩阵和I/O单元,具备确定时序、低功耗、上电即用特性。较FPGA规模小(数百至万门级)但延迟固定且无需外置配置芯片,专用于接口协议转换(如UART/SPI)、状态机控制及信号处理,是嵌入式系统胶合逻辑核心,开发通过HDL设计、仿真至JTAG烧录完成,代表型号包括Xilinx CoolRunner和Altera MAX系列。
(二)CPLD和FPGA芯片主要技术区别
1. 结构与逻辑容量
-
CPLD
基于乘积项(与或阵列)结构,内部逻辑单元通过固定互连资源连接,逻辑容量较小(通常为千门级)。其核心模块包括可编程逻辑模块(PLM)和寄存器阵列(PRA),适合实现简单的组合逻辑和时序逻辑,如状态机、接口控制等。
-
FPGA
采用查找表(LUT)和可编程互连结构,逻辑单元(CLB)与片上存储(Block RAM)、DSP模块等高度灵活组合,逻辑容量可达百万门级。FPGA的资源动态分配能力使其能够实现复杂算法(如FFT、卷积运算)和高并行任务。
2. 配置技术与易失性
-
CPLD
使用非易失存储器(如EEPROM或Flash)存储配置信息,上电后自动加载,无需外部配置芯片。配置速度快,但无法实时修改设计。
-
FPGA
依赖SRAM存储配置数据,断电后信息丢失,需外接配置芯片(如Flash)。支持动态重配置(部分型号),允许运行时更新逻辑功能,灵活性更高。
3. 时序特性与时钟管理
-
CPLD
时序延迟固定,时钟管理简单,适用于对确定性延时要求高的场景(如实时控制)。
-
FPGA
时序路径依赖布局布线结果,需通过约束文件优化关键路径。提供锁相环(PLL)、时钟分频器等复杂时钟管理模块,适合高频、高精度时序设计(如高速通信协议)。
4. 功耗与成本
-
CPLD
静态功耗低,整体功耗较小,适合电池供电或低功耗场景。成本较低,适合中小规模设计。
-
FPGA
逻辑资源丰富导致静态功耗较高,动态功耗随逻辑复杂度增加而显著上升。成本高于CPLD,但性能与灵活性优势明显。
5. 适用场景
-
CPLD
控制密集型应用 :电源管理、接口转换、简单协议实现(如UART、SPI)。
特点:上电即运行、低延迟、设计周期短。 -
FPGA
计算密集型应用 :数字信号处理(DSP)、图像处理、高速通信(如PCIe、以太网)。
特点:并行计算能力强、支持复杂算法、可重构性高。
四、HDLbitsFPGA组合逻辑题目训练
(一)创建一个半加器
半加器实现两个二进制数相加,输出和与进位,无进位输入。

题目介绍:

答案代码:
module top_module(
input a, b,
output cout, sum );
endmodule
关键点解析:
1、功能 :实现两个1位二进制数(a
和b
)的加法运算。
2、逻辑规则:
- 和(sum):由a^b(异或)计算,结果为两数相加的本位值。
- 进位(cout):由a&b(与)计算,当两数均为1时进位为1。
系统生成仿真图:

(二)创建一个全加器
全加器将三个一位二进制数相加,输出和及进位。

题目介绍:

答案代码:
module top_module(
input a, b, cin,
output cout, sum );
assign sum = a ^ b ^ cin; // 全加器求和逻辑
assign cout = (a & b) | (a & cin) | (b & cin); // 进位生成逻辑
endmodule
关键点解析
1、功能:实现三个1位二进制数(a、b和进位Cin)的加法运算。
2、逻辑规则:
- 和(sum):由a ^ b ^ Cin 计算,结果为三数相加的本位值(奇偶校验逻辑)。
- 进位(cout):若任意两个输入为1(a & b、a & Cin或b & Cin),则产生进位。
系统生成仿真图:
(三)创建一个或门运算
或门电路实现逻辑或操作,当任一输入为高电平时输出高电平,全低则输出低电平。

题目介绍:

答案代码:
module top_module (
input in1,
input in2,
output out);
assign out = in1 & in2; // 实现与门逻辑,输出为输入信号的逻辑与
endmodule
关键点解析
1、功能:实现两个1位二进制输入(in1和in2)的逻辑与操作。
2、逻辑规则:
- 输出 out 仅在 in1 和 in2 同时为
1
时输出1
,否则输出0
。
系统生成仿真图:

(四)创建一个4位BCD码加法器
四位BCD码加法器用于两个十进制数的相加,通过两个4位二进制加法器实现。若相加结果超过9或产生进位,则加6校正并进位,确保结果符合BCD编码规则,适用于精确十进制运算。
题目介绍:

答案代码:
module top_module (
input [15:0] a, b,
input cin,
output cout,
output [15:0] sum );
wire c1, c2, c3; // 中间进位信号
// 实例化四个BCD加法器,级联进位
bcd_fadd add0 ( .a(a[3:0]), .b(b[3:0]), .cin(cin), .cout(c1), .sum(sum[3:0]) );
bcd_fadd add1 ( .a(a[7:4]), .b(b[7:4]), .cin(c1), .cout(c2), .sum(sum[7:4]) );
bcd_fadd add2 ( .a(a[11:8]), .b(b[11:8]), .cin(c2), .cout(c3), .sum(sum[11:8]) );
bcd_fadd add3 ( .a(a[15:12]), .b(b[15:12]), .cin(c3), .cout(cout), .sum(sum[15:12]));
endmodule
关键点解析
1、输入与输出:
- **a[15:0] 和 b[15:0]:**16位BCD输入,每4位代表一个 十进制数字(如 a[3:0] 表示个位,a[7:4]表示十位,依此类推)。
- **sum[15:0]:**16位BCD输出,格式与输入相同,每个4位段为对应十进制位的和。
2、级联结构:
-
通过中间进位信号c1、c2、c3 将4个bcd_fadd模块级联,依次计算个位→十位→百位→千位。
-
进位传递:低位模块的进位输出(如c1)作为高位模块的进位输入(如add1 的 cin)。
3、BCD加法规则:
-
每个bcd_fadd 模块需实现BCD加法逻辑:
-
对4位输入进行二进制加法。
-
调整进位:若结果超过 9(即1001),则需 加6校正(0110)并产生进位。
-
公式:
temp_sum = a + b + cin; // 二进制加法结果
sum = (temp_sum > 9) ? temp_sum + 6 : temp_sum; // 加6校正
cout = (temp_sum > 9) ? 1 : 0; // 进位生成
-
-
例如:5+7 = 12,二进制和为 1100(12),但BCD需要表示为 0010(2)并进位1,因此实际操作为 1100
+
0110= 1
0010,取低4位 0010,进位为1。
4、模块实例化
bcd_fadd 模块接口:
module bcd_fadd (
input [3:0] a, b, // 4位BCD输入
input cin, // 进位输入
output cout, // 进位输出
output [3:0] sum // 4位BCD和
);
系统生成仿真图:

(五)创建一个2对1多路复用器
2对1多路复用器根据选择信号(sel)从两个输入(a、b)中选择一个输出。sel=0时输出a,sel=1时输出b。
题目介绍:

答案代码:
module top_module(
input a, b, sel,
output out );
assign out = sel ? b : a; // sel=1时选b,否则选a
endmodule
关键点解析
1、功能:通过选择信号 sel 从两个输入(a和b)中选择一个作为输出。
-
当 sel = 1 时,输出 out = b。
-
当 sel = 1 时,输出 out = 1。
2、逻辑规则:
-
使用条件运算符?
:
实现选择逻辑,等效于以下逻辑表达式:out = (sel & b) | (~sel & a)
系统生成仿真图:

(六)创建一个2对1总线多路复用器
2对1总线多路复用器通过控制信号选择两路N位输入中的一路输出,每比特位均含独立2选1单元,实现并行数据切换,常用于总线系统、数据路由及多通道控制等场景。
题目介绍:

答案代码:
module top_module(
input [99:0] a, b,
input sel,
output [99:0] out );
assign out = sel ? b : a; // sel=1时选择100位宽的b,否则选a
endmodule
关键点解析:
1、功能 :通过单比特选择信号 sel
,从两个100位宽的输入数据 a
和 b
中选择一个作为输出 out
。
-
当
sel = 1
时,out
完全等于b
的100位。 -
当
sel = 0
时,out
完全等于a
的100位。
2、逻辑规则:
-
并行选择:每个比特位独立选择,所有100位同时完成选择操作。
-
等效电路 :由100个独立的2选1多路选择器组成,每个选择器由
sel
信号控制。
系统生成仿真图:

五、总结
在本次FPGA入门学习中,我深刻体会到硬件设计与软件编程的思维差异。通过状态机的三段式设计(状态转移、次态逻辑、输出分离),我不仅掌握了Moore/Mealy型状态机的核心区别,更在实践中感受到模块化设计对复杂逻辑的简化作用------例如LED流水灯项目中,通过独热码编码和计时器控制,直观实现了状态切换与暂停功能,但仿真阶段因计数器位宽设置错误导致时序异常,调试后才理解硬件设计中"精确时序约束"的重要性。
此外,对比CPLD与FPGA时,我意识到芯片选型需紧密结合需求:CPLD的确定性延迟适合实时控制,而FPGA的并行计算能力更适配算法加速。在HDLbits练习中,BCD加法器的"加6校正"逻辑让我初次体会到硬件设计中的"规则映射"思维------并非所有运算都能直接移植软件逻辑,需结合硬件特性调整(如进位链优化)。
未来,我计划深入时序分析与高速接口协议实现,同时加强仿真覆盖率训练,以更系统性地提升FPGA开发能力。