目录
[(1)CPLD 的典型应用场景](#(1)CPLD 的典型应用场景)
[(2)FPGA 的典型应用场景](#(2)FPGA 的典型应用场景)
[3.1More logic gates-Gates](#3.1More logic gates-Gates)
[3.2 9-to-1 multiplexer](#3.2 9-to-1 multiplexer)
[3.3 Gates and vectors](#3.3 Gates and vectors)
[3.4Full adder](#3.4Full adder)
[3.5 Signed addition overflow](#3.5 Signed addition overflow)
一、状态机思想实现FPGA点亮LED流水灯
1.1状态机思想
状态机是一种用于描述系统行为的数学模型,它将系统抽象为:
有限的状态:系统在某一时刻只能处于其中一个状态。
转移条件:状态之间的切换规则(通常由事件或条件触发)。
动作:在进入、离开或保持状态时执行的操作。
下面是状态机和普通顺序逻辑的对比表:
对比项 | 状态机 | 普通顺序逻辑 |
---|---|---|
复杂度管理 | 适合多条件分支的复杂逻辑 | 适合线性流程 |
可读性 | 状态转移图直观易维护 | 嵌套if-else难以扩展 |
灵活性 | 动态调整状态和转移条件 | 修改可能影响整体逻辑 |
硬件实现 | 明确对应寄存器+组合逻辑 | 可能产生冗余电路 |
(1)状态机类型
Moore型状态机
输出仅与当前状态有关,与输入事件无关。例:交通灯控制器(红灯、绿灯、黄灯的切换逻辑固定)。
公式:输出 = f(当前状态)
Mealy型状态机
输出与当前状态和输入事件都关。例:自动售货机(投币金额不同,输出找零行为不同)。
公式:输出 = f(当前状态, 输入事件)
(2)状态机设计步骤
明确系统需求:确定所有可能的状态和触发条件。
绘制状态转移图:图形化表示状态、事件和转移关系。
状态编码 :用二进制值表示每个状态(如 S0=00
, S1=01
)。
实现逻辑:通过代码(Verilog/VHDL/C)描述状态转移和输出。
验证:仿真测试覆盖所有状态路径,硬件验证实际行为。
1.2实现LED流水灯
(1)在VScode中进行代码编辑
新建工程项目文件,并在该文件夹中添加下面的文件目录

在rtl目录下创建LS_LED.v文件并编写代码

module LS_LED(
input clk,
input rst_n,
input pause_sw,
output reg [5:0] led
);
parameter T = 50_000_000; // 计数器最大值,用于生成 1 秒定时
reg [2:0] cstate; // 现态
reg [2:0] nstate; // 次态
// 状态划分
localparam state_led0 = 0; // LED0 亮
localparam state_led1 = 1; // LED1 亮
localparam state_led2 = 2; // LED2 亮
localparam state_led3 = 3; // LED3 亮
localparam state_led4 = 4; // LED4 亮
localparam state_led5 = 5; // LED5 亮
reg [25:0] cnt = 0; // 计时器赋初值为 0
// 计数器模块
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
cnt <= 0; // 按下复位键,清零
else if (cnt == T - 1) // 计时器达到最大值,清零重新计数
cnt <= 0;
else if (!pause_sw) // 如果未暂停,计数器继续计数
cnt <= cnt + 1;
end
// 第一段:现态跟随次态,时序逻辑,非阻塞赋值
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
cstate <= state_led0; // 复位键被按下,当前状态设置为 LED0 亮
else if (!pause_sw) // 如果未暂停,更新状态
cstate <= nstate;
end
//第二段:组合逻辑,阻塞赋值
always @(*) begin
if (!rst_n)
nstate = state_led0; // 复位时回到初始状态
else
case (cstate)
state_led0: begin
if (cnt == T - 1) // 该状态持续时间为 1 秒,1 秒后跳转到下一个状态
nstate = state_led1;
nstate = state_led0;
end
state_led1: begin
if (cnt == T - 1)
nstate = state_led2;
else
nstate = state_led1;
end
state_led2: begin
if (cnt == T - 1)
nstate = state_led3;
else
nstate = state_led2;
end
state_led3: begin
if (cnt == T - 1)
nstate = state_led4;
else
nstate = state_led3;
end
state_led4: begin
if (cnt == T - 1)
nstate = state_led5;
else
nstate = state_led4;
end
state_led5: begin
if (cnt == T - 1)
nstate = state_led0;
else
nstate = state_led5;
end
default: nstate = state_led0; // 默认状态
endcase
end
//第三段:跟随状态输出
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
led <= 6'b000001; // 复位时点亮第一个 LED
else
case (cstate)
state_led0: led <= 6'b000001;
state_led1: led <= 6'b000010;
state_led2: led <= 6'b000100;
state_led3: led <= 6'b001000;
state_led4: led <= 6'b010000;
state_led5: led <= 6'b100000;
default: led <= 6'b000001; // 默认状态
endcase
end
endmodule
在tb文件目录项新建LS_LED_tb.v仿真测试文件,并编写代码
`timescale 1ns / 1ps
module LS_LED_tb;
//输入信号
reg clk;
reg rst_n;
reg pause_sw;
//输出信号
wire [5:0] led; //6个LED输出
//实例化被测模块
LS_LED uut(
.clk(clk),
.rst_n(rst_n),
.pause_sw(pause_sw),
.led(led)
);
//生成时钟信号
initial begin
// 初始化信号
rst_n = 0; // 初始复位
pause_sw = 0; // 初始不暂停
#20; // 等待 20 ns
// 释放复位信号
rst_n = 1;
#200; // 等待一段时间观察初始状态
// 测试正常流水灯效果
pause_sw = 0; // 不暂停
#2_000_000; // 运行 2 秒,观察 LED 变化
// 测试暂停功能
pause_sw = 1; // 暂停
#1_000_000; // 停留 1 秒,观察 LED 是否保持不变
pause_sw = 0; // 恢复
#2_000_000; // 再次运行 2 秒,观察 LED 变化
// 测试复位功能
rst_n = 0; // 触发复位
#20; // 等待复位完成
rst_n = 1; // 释放复位
#200; // 观察复位后的初始状态
//仿真结束
$stop;
end
endmodule
(2)在QUartus中创建项目

添加代码文件

添加对应芯片

将LS_LED.v设置为top文件

进行编译

(3)仿真测试
若有同学是第一次进行Modesim仿真,还需要进行一些配置步骤,具体可以看我前面的博客:【FPGA】分秒计数器设计-CSDN博客
这里我们直接进行tb文件的添加,在工具栏处的Assignments选择Settings,然后进行下图的操作
**注:**Test bench name不能有.v

进行RTL Simulation仿真

跳转到Modelsim后就可以观察到波形

(4)DE2-115烧录实现
管脚配置

配置完成后,我们进行代码烧录:
点击Programmer,点击Hardware Setup

选择USB

添加.sof文件,若已经存在则无需再次添加

start开始烧录

二、CPLD和FPGA芯片的对比
2.1主要技术区别
对比项 | CPLD (复杂可编程逻辑器件) | FPGA (现场可编程门阵列) |
---|---|---|
基本结构 | 基于 乘积项(PAL/GAL结构),逻辑块通过全局互连连接 | 基于 查找表(LUT)+ 寄存器,逻辑单元为可配置逻辑块(CLB) |
逻辑容量 | 较小(通常几千到几万门) | 较大(几万门到数百万门) |
时序特性 | 固定延迟,适合同步逻辑 | 延迟可变,需时序约束优化 |
存储资源 | 较少(依赖外部存储器) | 丰富(内置Block RAM、分布式RAM) |
配置方式 | 非易失性(EEPROM/Flash,上电即用) | 易失性(需外部配置芯片加载SRAM,上电需配置) |
功耗 | 静态功耗低,动态功耗中等 | 静态功耗较高,动态功耗依赖设计复杂度 |
灵活性 | 适合简单、确定性逻辑 | 适合复杂、并行化设计 |
开发工具 | 工具简单(如Quartus Prime的CPLD模块) | 工具复杂(需综合、布局布线、时序分析,如Vivado) |
2.2适用场合对比
(1)CPLD 的典型应用场景
简单控制逻辑
-
地址译码、总线接口(如PCI局部总线控制)。
-
替代传统74系列逻辑芯片(如多路复用器、编码器)。
实时性要求高的系统
- 电机控制、电源管理(因固定延迟,响应快)。
上电即用场景
- 汽车电子(点火控制)、工业设备(非易失性配置,无需外部ROM)。
胶合逻辑(Glue Logic)
- 连接不同接口标准的芯片(如UART转SPI)。
(2)FPGA 的典型应用场景
高性能并行处理
-
数字信号处理(DSP)、图像处理(如摄像头实时滤波)。
-
深度学习加速(利用并行计算单元)。
可重构计算
- 通信协议栈(如5G基带处理)、软件定义无线电(SDR)。
原型验证与ASIC开发
- 芯片设计前期功能验证(如RTL仿真后的硬件验证)。
复杂时序逻辑
- 高速接口(DDR控制器、PCIe PHY)、视频编解码(H.264/H.265)。
三、HDLbits组合逻辑题目训练
下面是HDLbits地址,可以通过点击【组合逻辑】进入该模块的学习:

3.1More logic gates-Gates
题目

该题目要求设计一个 多功能逻辑门电路模块 ,能够同时计算两个1位输入信号 a
和 b
的7种基本逻辑运算结果。具体功能如下:
-
输入信号:
a
:1位输入b
:1位输入 -
输出信号:
out_and
:a
ANDb
(逻辑与)out_or
:a
ORb
(逻辑或)out_xor
:a
XORb
(逻辑异或)out_nand
:a
NANDb
(逻辑与非)out_nor
:a
NORb
(逻辑或非)out_xnor
:a
XNORb
(逻辑同或)out_anoth
:a
AND (NOTb
)(a
与b
的非)
代码实现
module top_module(
input a, b,
output out_and,
output out_or,
output out_xor,
output out_nand,
output out_nor,
output out_xnor,
output out_anotb
);
// AND 逻辑与
assign out_and = a & b;
// OR 逻辑或
assign out_or = a | b;
// XOR 逻辑异或
assign out_xor = a ^ b;
// NAND 逻辑与非
assign out_nand = ~(a & b);
// NOR 逻辑或非
assign out_nor = ~(a | b);
// XNOR 逻辑同或
assign out_xnor = ~(a ^ b);
// A AND NOT B
assign out_anotb = a & (~b);
endmodule
结果

3.2 9-to-1 multiplexer
题目

该题目要求设计一个 16位宽度的9选1数据选择器(Multiplexer) ,根据4位选择信号 sel
的值,从9个16位输入数据(a
至 i
)中选择一个输出到 out
。具体要求如下:
-
输入信号:
9个16位数据输入:
a[15:0]
,b[15:0]
, ...,i[15:0]
。4位选择信号:
sel[3:0]
(可表示范围0-15
,但仅使用0-8
)。 -
输出信号:
16位输出:
out[15:0]
,其值由sel
决定。
代码实现
module top_module(
input [15:0] a, b, c, d, e, f, g, h, i, // 16位输入信号a-i
input [3:0] sel, // 4位选择信号
output [15:0] out // 16位输出信号
);
always @(*) begin
case(sel)
4'd0: out = a; // sel=0时选择输入a
4'd1: out = b; // sel=1时选择输入b
4'd2: out = c; // sel=2时选择输入c
4'd3: out = d; // sel=3时选择输入d
4'd4: out = e; // sel=4时选择输入e
4'd5: out = f; // sel=5时选择输入f
4'd6: out = g; // sel=6时选择输入g
4'd7: out = h; // sel=7时选择输入h
4'd8: out = i; // sel=8时选择输入i
default: out = {16{1'b1}}; // sel=9-15时输出全1(16位均为1)
endcase
end
endmodule
结果

3.3 Gates and vectors
题目

题目要求设计一个 Verilog 模块,对 4 位输入 in[3:0]
进行以下三种邻位关系检测:
-
out_both[2:0]
(3 位输出):检测当前位与其左侧高位 是否均为1
。例:
out_both[2] = in[2] & in[3]
注:
in[3]
无左侧高位,故不输出out_both[3]
。 -
out_any[3:1]
(3 位输出):检测当前位与其右侧低位 是否存在至少一个1
。例:
out_any[2] = in[2] | in[1]
注:
in[0]
无右侧低位,故不输出out_any[0]
。 -
out_different[3:0]
(4 位输出):检测当前位与其左侧高位 是否不同(循环左邻,即in[3]
的左邻是in[0]
)。例:
out_different[2] = in[2] ^ in[3]
特殊:
out_different[3] = in[3] ^ in[0]
(循环检测)。
代码实现
module top_module (
input [3:0] in,
output [2:0] out_both,
output [3:1] out_any,
output [3:0] out_different
);
// out_both: 当前位与左侧高位是否均为1(不包含in[3]的左侧)
assign out_both = in[3:1] & in[2:0]; // 直接按位与
// out_any: 当前位或右侧低位是否为1(不包含in[0]的右侧)
assign out_any = in[3:1] | in[2:0]; // 直接按位或
// out_different: 当前位与左侧高位是否不同(循环左邻)
assign out_different = in ^ {in[0], in[3:1]}; // 通过循环左移后异或
endmodule
结果

3.4Full adder
题目

该题需要设计一个 1位全加器 的 Verilog 模块,实现以下功能:
输入"
a:1 位加数
b:1 位加数
cin:来自低位的进位输入
输出:
sum:a + b + cin 的本位和(1 位)
cout:向高位的进位输出(1 位)
代码实现
module top_module(
input a, // 输入位a
input b, // 输入位b
input cin, // 进位输入
output cout, // 进位输出
output sum // 和输出
);
// 全加器逻辑实现
assign sum = a ^ b ^ cin; // 计算本位和(异或运算)
assign cout = (a & b) | (a & cin) | (b & cin); // 计算进位(或运算)
endmodule
结果

3.5 Signed addition overflow
题目

该题目需要设计一个 Verilog 模块,实现以下功能:
-
输入 :两个 8 位二进制补码形式的有符号数
a[7:0]
和b[7:0]
。 -
输出:
s[7:0]:a 和 b 的加法结果(8 位二进制补码形式)。
overflow:1 位标志位,当加法结果发生有符号溢出时置 1,否则置 0。
代码实现
module top_module (
input [7:0] a, // 8位有符号数输入a(补码表示)
input [7:0] b, // 8位有符号数输入b(补码表示)
output [7:0] s, // 8位和输出
output overflow // 溢出标志(1表示溢出)
);
// 计算和
assign s = a + b;
// 检测有符号溢出
assign overflow =
(~a[7] & ~b[7] & s[7]) | // 正+正=负(上溢)
(a[7] & b[7] & ~s[7]); // 负+负=正(下溢)
endmodule
结果
