【FPGA】状态机思想实现LED流水灯&HDLbits组合逻辑题训练

目录

一、状态机思想实现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 的典型应用场景](#(1)CPLD 的典型应用场景)

[(2)FPGA 的典型应用场景](#(2)FPGA 的典型应用场景)

三、HDLbits组合逻辑题目训练

[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地址,可以通过点击【组合逻辑】进入该模块的学习:

Step one - HDLBits

3.1More logic gates-Gates

题目

该题目要求设计一个 多功能逻辑门电路模块 ,能够同时计算两个1位输入信号 ab 的7种基本逻辑运算结果。具体功能如下:

  1. 输入信号

    a:1位输入

    b:1位输入

  2. 输出信号

    out_anda AND b(逻辑与)

    out_ora OR b(逻辑或)

    out_xora XOR b(逻辑异或)

    out_nanda NAND b(逻辑与非)

    out_nora NOR b(逻辑或非)

    out_xnora XNOR b(逻辑同或)

    out_anotha AND (NOT b)(ab 的非)

代码实现

复制代码
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位输入数据(ai)中选择一个输出到 out。具体要求如下:

  1. 输入信号

    9个16位数据输入:a[15:0], b[15:0], ..., i[15:0]

    4位选择信号:sel[3:0](可表示范围 0-15,但仅使用 0-8)。

  2. 输出信号

    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] 进行以下三种邻位关系检测:

  1. out_both[2:0] (3 位输出):检测当前位与其左侧高位 是否均为 1

    例:out_both[2] = in[2] & in[3]

    注:in[3] 无左侧高位,故不输出 out_both[3]

  2. out_any[3:1] (3 位输出):检测当前位与其右侧低位 是否存在至少一个 1

    例:out_any[2] = in[2] | in[1]

    注:in[0] 无右侧低位,故不输出 out_any[0]

  3. 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 模块,实现以下功能:

  1. 输入 :两个 8 位二进制补码形式的有符号数 a[7:0]b[7:0]

  2. 输出

    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

结果

相关推荐
1560820721919 小时前
在vivado中,国产CH347芯片实现USB转JTAG的操作
fpga开发
数字芯片实验室1 天前
IP验证最终回归到时序级建模
网络·网络协议·tcp/ip·fpga开发
雨洛lhw1 天前
三模冗余资源量对比
fpga开发·三模冗余技术
XINVRY-FPGA1 天前
XC7VX690T-2FFG1761I Xilinx AMD FPGA Virtex-7
arm开发·嵌入式硬件·fpga开发·硬件工程·fpga
FPGA_无线通信1 天前
FPGA 组合逻辑和时序逻辑
fpga开发
Js_cold1 天前
Xilinx FPGA温度等级及选型建议
fpga开发·fpga·vivado·xilinx
从此不归路1 天前
FPGA 结构与 CAD 设计(第5章)上
fpga开发
洋洋Young1 天前
【Xilinx FPGA】7 Series Clocking 设计
fpga开发·xilinx fpga
156082072191 天前
FPGA下AD采集时钟相位的调整
fpga开发
从此不归路1 天前
FPGA 结构与 CAD 设计(第5章)下
fpga开发