目录
一、状态机思想实现FPGA点亮LED流水灯
1.1状态机思想
(1)状态机类型
Moore型状态机
Mealy型状态机
(2)状态机设计步骤
1.2实现LED流水灯
(1)在VScode中进行代码编辑
(2)在QUartus中创建项目
(3)仿真测试
(4)DE2-115烧录实现
二、CPLD和FPGA芯片的对比
2.1主要技术区别
2.2适用场合对比
(1)CPLD 的典型应用场景
(2)FPGA 的典型应用场景
三、HDLbits组合逻辑题目训练
3.1More logic gates-Gates
3.2 9-to-1 multiplexer
3.3 Gates and vectors
3.4Full adder
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) beginif (!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) beginif (!rst_n)cstate <= state_led0; // 复位键被按下,当前状态设置为 LED0 亮else if (!pause_sw) // 如果未暂停,更新状态cstate <= nstate;
end//第二段:组合逻辑,阻塞赋值
always @(*) beginif (!rst_n)nstate = state_led0; // 复位时回到初始状态elsecase (cstate)state_led0: beginif (cnt == T - 1) // 该状态持续时间为 1 秒,1 秒后跳转到下一个状态nstate = state_led1;nstate = state_led0;endstate_led1: beginif (cnt == T - 1)nstate = state_led2;elsenstate = state_led1;endstate_led2: beginif (cnt == T - 1)nstate = state_led3;elsenstate = state_led2;endstate_led3: beginif (cnt == T - 1)nstate = state_led4;elsenstate = state_led3;endstate_led4: beginif (cnt == T - 1)nstate = state_led5;elsenstate = state_led4;endstate_led5: beginif (cnt == T - 1)nstate = state_led0;elsenstate = state_led5;enddefault: nstate = state_led0; // 默认状态endcase
end//第三段:跟随状态输出
always @(posedge clk or negedge rst_n) beginif (!rst_n)led <= 6'b000001; // 复位时点亮第一个 LEDelsecase (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
endendmodule
在tb文件目录项新建LS_LED_tb.v仿真测试文件,并编写代码
`timescale 1ns / 1psmodule 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;endendmodule
(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地址,可以通过点击【组合逻辑】进入该模块的学习:
Step one - 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 Bassign 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-iinput [3:0] sel, // 4位选择信号output [15:0] out // 16位输出信号
);always @(*) begincase(sel)4'd0: out = a; // sel=0时选择输入a4'd1: out = b; // sel=1时选择输入b4'd2: out = c; // sel=2时选择输入c4'd3: out = d; // sel=3时选择输入d4'd4: out = e; // sel=4时选择输入e4'd5: out = f; // sel=5时选择输入f4'd6: out = g; // sel=6时选择输入g4'd7: out = h; // sel=7时选择输入h4'd8: out = i; // sel=8时选择输入idefault: out = {16{1'b1}}; // sel=9-15时输出全1(16位均为1)endcase
endendmodule
结果
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, // 输入位ainput 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
结果