FPGA状态机设计:流水灯实现、Modelsim仿真、HDLBits练习

news2025/4/13 2:40:30

一、状态机思想

1.概念

状态机(Finite State Machine, FSM)是计算机科学和工程领域中的一种抽象模型,用于描述系统在不同状态之间的转换逻辑。其核心思想是将复杂的行为拆解为有限的状态,并通过事件触发状态间的转移

2.状态机的分类
(1)Moore型状态机
输出仅由当前状态决定。
例如:交通灯的颜色(状态)直接决定输出。

(2)Mealy型状态机
输出由当前状态和输入事件共同决定。
例如:自动售货机根据投币金额(事件)和当前状态计算找零。

二、状态机思想实现LED流水灯

1.用状态机思想重写LED流水灯的FPGA代码

实现功能与我之前文章(FPGA实现LED流水灯(开发板为DE2-115)-CSDN博客)中实现的流水灯效果相同。

代码如下:

module led_state_machine(
    input        clk,      // 50MHz时钟(PIN_Y2)
    input        rst_n,    // 复位信号(PIN_M23,低有效)
    input        key1,     // 启动按键(PIN_M21)
    input        key2,     // 停止按键(PIN_N21)
    output reg [5:0] led   // 高电平有效LED(PIN_E21~G20)
);

// 系统参数定义
parameter CLK_FREQ = 50_000_000;    // 50MHz时钟频率
parameter DEBOUNCE_CYCLES = 1_000_000; // 20ms消抖周期

// 状态机状态定义
localparam 
    S0 = 3'd0,  // LED0亮
    S1 = 3'd1,  // LED1亮
    S2 = 3'd2,  // LED2亮
    S3 = 3'd3,  // LED3亮
    S4 = 3'd4,  // LED4亮
    S5 = 3'd5;  // LED5亮

reg [2:0] current_state, next_state;
reg [25:0] counter;
reg run_enable;

// 按键同步与消抖逻辑
reg [1:0] key1_sync, key2_sync;
reg key1_stable, key2_stable;
reg [19:0] key1_cnt, key2_cnt;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        key1_sync <= 2'b11;
        key2_sync <= 2'b11;
        key1_stable <= 1'b1;
        key2_stable <= 1'b1;
        key1_cnt <= 0;
        key2_cnt <= 0;
    end else begin
        // 按键同步链
        key1_sync <= {key1_sync[0], key1};
        key2_sync <= {key2_sync[0], key2};
        
        // KEY1消抖逻辑
        if (key1_sync[1] != key1_stable) begin
            key1_cnt <= (key1_cnt == DEBOUNCE_CYCLES) ? 0 : key1_cnt + 1;
            if (key1_cnt == DEBOUNCE_CYCLES) 
                key1_stable <= key1_sync[1];
        end else begin
            key1_cnt <= 0;
        end
        
        // KEY2消抖逻辑
        if (key2_sync[1] != key2_stable) begin
            key2_cnt <= (key2_cnt == DEBOUNCE_CYCLES) ? 0 : key2_cnt + 1;
            if (key2_cnt == DEBOUNCE_CYCLES) 
                key2_stable <= key2_sync[1];
        end else begin
            key2_cnt <= 0;
        end
    end
end

// 运行控制逻辑
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        run_enable <= 1'b0;
    end else begin
        case ({key2_stable, key1_stable})
            2'b10: run_enable <= 1'b1;  // KEY1按下启动
            2'b01: run_enable <= 1'b0;  // KEY2按下停止
            default: ;                   // 保持状态
        endcase
    end
end

// 状态转移控制
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        current_state <= S0;
        counter <= 0;
    end else begin
        // 1秒定时器
        if (run_enable) begin
            counter <= (counter == CLK_FREQ-1) ? 0 : counter + 1;
            // 状态转移条件
            if (counter == CLK_FREQ-1)
                current_state <= next_state;
        end
    end
end

// 状态转移逻辑
always @(*) begin
    case (current_state)
        S0: next_state = S1;
        S1: next_state = S2;
        S2: next_state = S3;
        S3: next_state = S4;
        S4: next_state = S5;
        S5: next_state = S0;
        default: next_state = S0;
    endcase
end

// 输出逻辑
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        led <= 6'b000001;
    end else begin
        case (current_state)
            S0: led <= 6'b000001;
            S1: led <= 6'b000010;
            S2: led <= 6'b000100;
            S3: led <= 6'b001000;
            S4: led <= 6'b010000;
            S5: led <= 6'b100000;
            default: led <= 6'b000001;
        endcase
    end
end

endmodule

具体过程看之前的文章 ,此处不再过多赘述。

在DE2-115板子上的效果如下:

状态机思想LED流水灯

2.写出仿真测试代码并用Modelsim进行仿真分析 

仿真测试代码

`timescale 1ns/1ps

module tb_led_state_machine();

reg clk;
reg rst_n;
reg key1;
reg key2;
wire [5:0] led;

// 实例化被测模块,调整参数以加速仿真
led_state_machine #(
    .CLK_FREQ(10),          // 原CLK_FREQ=50_000_000改为10
    .DEBOUNCE_CYCLES(10)    // 原DEBOUNCE_CYCLES=1_000_000改为10
) uut (
    .clk(clk),
    .rst_n(rst_n),
    .key1(key1),
    .key2(key2),
    .led(led)
);

// 生成50MHz时钟(周期20ns)
always #10 clk = ~clk;

initial begin
    // 初始化信号
    clk = 0;
    rst_n = 0;      // 复位有效
    key1 = 1;       // 按键未按下(高电平)
    key2 = 1;

    // 释放复位信号
    #100;
    rst_n = 1;
    #20; // 等待稳定

    // 验证复位后初始状态
    if (led !== 6'b000001)
        $display("错误:复位后初始状态应为S0,LED=000001,实际值为%b", led);
    else
        $display("测试1:复位成功,LED=000001");

    // 测试启动按键(key1)
    #20;
    key1 = 0;       // 按下key1
    #200;           // 等待消抖完成(200ns)
    key1 = 1;       // 释放key1

    // 验证状态转换(每个状态持续200ns)
    #200; // S1
    if (led === 6'b000010)
        $display("测试2:成功转换到S1,LED=000010");
    else
        $display("错误:应为S1,LED=%b", led);

    #200; // S2
    if (led === 6'b000100)
        $display("测试3:成功转换到S2,LED=000100");
    else
        $display("错误:应为S2,LED=%b", led);

    #200; // S3
    if (led === 6'b001000)
        $display("测试4:成功转换到S3,LED=001000");
    else
        $display("错误:应为S3,LED=%b", led);

    #200; // S4
    if (led === 6'b010000)
        $display("测试5:成功转换到S4,LED=010000");
    else
        $display("错误:应为S4,LED=%b", led);

    #200; // S5
    if (led === 6'b100000)
        $display("测试6:成功转换到S5,LED=100000");
    else
        $display("错误:应为S5,LED=%b", led);

    #200; // S0
    if (led === 6'b000001)
        $display("测试7:成功返回S0,LED=000001");
    else
        $display("错误:应为S0,LED=%b", led);

    // 测试停止按键(key2)
    key2 = 0;       // 按下key2
    #200;
    key2 = 1;

    #400; // 等待两个周期,状态应保持不变
    if (led === 6'b000001)
        $display("测试8:成功停止,保持S0");
    else
        $display("错误:停止后状态异常,LED=%b", led);

    // 再次启动
    key1 = 0;
    #200;
    key1 = 1;

    #200; // 应进入S1
    if (led === 6'b000010)
        $display("测试9:重新启动成功,进入S1");
    else
        $display("错误:重启失败,LED=%b", led);

    $display("所有测试完成");
    $finish;
end

endmodule

三、CPLD和FPGA芯片的主要技术区别以及适用场合(借助deepseek)

1.CPLD和FPGA芯片对比​​

​特性​​CPLD​​FPGA​
​核心架构​基于乘积项(Product-Term)结构,逻辑单元通过全局互连连接基于查找表(LUT)结构,逻辑单元通过细粒度互连网络连接
​逻辑资源规模​较小(通常为数千至数万门)较大(数万门至数百万门)
​存储技术​基于非易失性存储器(EEPROM/Flash)基于易失性存储器(SRAM),需外挂配置芯片
​时序确定性​固定延迟(信号路径可预测)可变延迟(依赖布线路径)
​功耗特性​静态功耗低,适合低功耗场景静态功耗较高,动态功耗依赖逻辑复杂度
​启动时间​瞬时启动(配置信息内置)需要从外部加载配置(毫秒级延迟)
​灵活性​适合固定逻辑和简单状态机支持复杂算法、并行计算和动态重构

2. CPLD和FPGA芯片适用场合

(1)CPLD适用场景​​
​​低复杂度控制逻辑​​
        工业控制系统的接口管理(如PLC信号转换)
        通信协议转换(UART转SPI/I2C)
        简单的状态机实现(如电梯控制逻辑)
​​确定性时序要求​​
        实时控制系统(电机驱动时序控制)
        高速信号译码(如键盘扫描电路)
​​低功耗与高可靠性​​
        便携式设备的电源管理
        汽车电子中的传感器接口电路
​​(2)FPGA适用场景​​
​​高复杂度数据处理​​
        通信系统(5G基带处理、光纤通信编解码)
        图像处理(实时视频压缩、医学影像增强)
        人工智能加速(CNN推理、边缘计算)
​​动态可重构需求​​
        软件定义无线电(SDR)的硬件重配置
        数据中心网络加速(动态负载均衡)
​​高性能计算​​
        金融高频交易算法加速
        科学仿真(流体力学、分子动力学)

四、hdlbits网站中组合逻辑(combinational logic)题

4.1 2选1多路复用器(MUX)

1位宽 2选1多路复用器(MUX)是数字电路中最基础的逻辑单元之一,其核心功能是 根据选择信号(sel)从两个输入(a和b)中选择一个输出。它的作用可以类比为一个“数据开关”

问题描述:

解决方案:

module top_module(
    input a,
    input b,
    input sel,
    output out
);
    assign out = sel ? b : a;  // 三元运算符实现
endmodule

说明: sel ?b:a;是条件运算符,等价于 if - else 的逻辑,这是最简洁的组合逻辑实现方式。

用于测试用例的时序图:Selab 之间进行选择

4.2 256:1 多路复用器

是2对1多路复用器的升级版

问题描述:

代码 :

module top_module (
    input [255:0] in,
    input [7:0] sel,
    output out
);
    assign out = in[sel];  // 直接使用 sel 作为索引
endmodule

4.3 半加器设计

真值表:

absumcout
0000
0110
1010
1101

 问题描述:

代码:

module top_module (
    input a, b,
    output sum, cout
);
    assign sum = a ^ b;   // 异或:本位和
    assign cout = a & b;  // 与:进位
endmodule

4.4 全加器设计

全加器将两个1位二进制数和一个进位输入相加,产生和(sum)和进位输出(carry out)。

问题描述:  

代码: 

module top_module( 
    input a, b, cin,
    output cout, sum );
    
    // 全加器实现
    assign sum = a ^ b ^ cin;               // 和的计算
    assign cout = (a & b) | (a & cin) | (b & cin);  // 进位输出的计算
    
endmodule

结果:

4.5 4位BCD加法器

这个加法器可以处理两个4位BCD数字(每个数字0-9,共16位输入)和一个进位输入,产生4位BCD和和进位输出。

问题描述:

代码:

module top_module( 
    input [15:0] a, b,
    input cin,
    output cout,
    output [15:0] sum );
    
    // 定义中间进位信号
    wire c1, c2, c3;
    
    // 实例化4个bcd_fadd模块,形成波纹进位链
    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

说明(工作原理):

·最低位BCD加法器(add0)接收cin作为进位输入

·每个加法器的进位输出连接到下一个高位加法器的进位输入

·最高位BCD加法器(add3)的进位输出作为整个模块的cout

·每个加法器产生的4位和组合成最终的16位sum输出

测试用例的时序图:

4.6 100位二进制加法器

使用波纹进位(Ripple Carry)结构,该加法器将两个100位二进制数和一个进位输入相加,产生100位和和一个进位输出。

问题描述:

代码:

module top_module( 
    input [99:0] a, b,
    input cin,
    output cout,
    output [99:0] sum );
    
    // 定义中间进位信号
    wire [100:0] carry;
    
    // 初始进位设为输入进位
    assign carry[0] = cin;
    
    // 生成100个全加器实例
    genvar i;
    generate
        for (i = 0; i < 100; i = i + 1) begin : adder_chain
            // 每个全加器实例
            full_adder fa (
                .a(a[i]),
                .b(b[i]),
                .cin(carry[i]),
                .cout(carry[i+1]),
                .sum(sum[i])
            );
        end
    endgenerate
    
    // 最终进位输出
    assign cout = carry[100];
    
endmodule

// 全加器模块定义
module full_adder(
    input a, b, cin,
    output cout, sum
);
    assign sum = a ^ b ^ cin;
    assign cout = (a & b) | (a & cin) | (b & cin);
endmodule

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2333512.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024年第十五届蓝桥杯CC++大学A组--成绩统计

2024年第十五届蓝桥杯C&C大学A组--成绩统计 题目&#xff1a; 动态规划&#xff0c; 对于该题&#xff0c;考虑动态规划解法&#xff0c;先取前k个人的成绩计算其方差&#xff0c;并将成绩记录在数组中&#xff0c;记录当前均值&#xff0c;设小蓝已检查前i-1个人的成绩&…

Kotlin 学习-集合

/*** kotlin 集合* List:是一个有序列表&#xff0c;可通过索引&#xff08;下标&#xff09;访问元素。元素可以在list中出现多次、元素可重复* Set:是元素唯一的集合。一般来说 set中的元素顺序并不重要、无序集合* Map:&#xff08;字典&#xff09;是一组键值对。键是唯一的…

自动驾驶的未来:多模态感知融合技术最新进展

作为自动驾驶领域的专业人士&#xff0c;我很高兴与大家分享关于多模态感知融合技术的前沿研究和实践经验。在迅速发展的自动驾驶领域&#xff0c;多模态感知融合已成为提升系统性能的关键技术。本文将深入探讨基于摄像头和激光雷达的多模态感知融合技术&#xff0c;重点关注最…

亮相2025全球分布式云大会,火山引擎边缘云落地AI新场景

4 月 9 日&#xff0c;2025 全球分布式云大会暨 AI 基础设施大会在深圳成功举办&#xff0c;火山引擎边缘云产品解决方案高级总监沈建发出席并以《智启边缘&#xff0c;畅想未来&#xff1a;边缘计算新场景落地与 Al 趋势新畅想》为主题&#xff0c;分享了边缘计算在 AI 技术趋…

无损分区管理,硬盘管理的“瑞士军刀”!

打工人们你们好&#xff01;这里是摸鱼 特供版~ 今天给大家带来一款简单易用、功能强大的无损分区软件——分区助手技术员版&#xff0c;让你的硬盘管理变得轻松又高效&#xff01; 推荐指数&#xff1a;★★★★★ 软件简介 分区助手技术员版是一款功能强大的硬盘分区工具&…

VS Code下开发FPGA——FPGA开发体验提升__下

上一篇&#xff1a;IntelliJ IDEA下开发FPGA-CSDN博客 Type&#xff1a;Quartus 一、安装插件 在应用商店先安装Digtal IDE插件 安装后&#xff0c;把其他相关的Verilog插件禁用&#xff0c;避免可能的冲突。重启后&#xff0c;可能会弹出下面提示 这是插件默认要求的工具链&a…

ffmpeg播放音视频流程

文章目录 &#x1f3ac; FFmpeg 解码播放流程概览&#xff08;以音视频文件为例&#xff09;1️⃣ 创建结构体2️⃣ 打开音视频文件3️⃣ 查找解码器并打开解码器4️⃣ 循环读取数据包&#xff08;Packet&#xff09;5️⃣ 解码成帧&#xff08;Frame&#xff09;6️⃣ 播放 / …

SpringCloud微服务: 分布式架构实战

# SpringCloud微服务: 分布式架构实战 第一章&#xff1a;理解SpringCloud微服务架构 什么是SpringCloud微服务架构&#xff1f; 在当今互联网应用开发中&#xff0c;微服务架构已经成为业界的主流趋势。SpringCloud是一个基于Spring Boot的快速开发微服务架构的工具&#xff0…

AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年4月11日第49弹

从今天开始&#xff0c;咱们还是暂时基于旧的模型进行预测&#xff0c;好了&#xff0c;废话不多说&#xff0c;按照老办法&#xff0c;重点8-9码定位&#xff0c;配合三胆下1或下2&#xff0c;杀1-2个和尾&#xff0c;再杀6-8个和值&#xff0c;可以做到100-300注左右。 (1)定…

【models】Transformer 之 各种 Attention 原理和实现

Transformer 之 各种 Attention 原理和实现 本文将介绍Transformer 中常见的Attention的原理和实现&#xff0c;其中包括&#xff1a; Self Attention、Spatial Attention、Temporal Attention、Cross Attention、Grouped Attention、Tensor Product Attention、FlashAttentio…

老硬件也能运行的Win11 IoT LTSC (OEM)物联网版

#记录工作 Windows 11 IoT Enterprise LTSC 2024 属于物联网相关的版本。 Windows 11 IoT Enterprise 是为物联网设备和场景设计的操作系统版本。它通常针对特定的工业控制、智能设备等物联网应用进行了优化和定制&#xff0c;以满足这些领域对稳定性、安全性和长期支持的需求…

Git开发

目录 Linux下Git安装Git基本指令分支管理远程仓库与本地仓库标签管理多人协作同一分支下不同分支下 企业级开发模型 -- git flow 模型 在现实中&#xff0c;当我们完成一个文档的初稿后&#xff0c;后面可能还需要对初稿进行反复修改&#xff0c;从而形成不同版本的文档。显然&…

verilog有符号数的乘法

无符号整数的乘法 1、单周期乘法器&#xff08; 无符号整数 &#xff09; 对于低速要求的乘法器&#xff0c;可以简单的使用 * 实现。 module Mult(input wire [7:0] multiplicand ,input wire [7:0] multipliter ,output wire [7:0] product);as…

DevDocs:抓取并整理技术文档的MCP服务

GitHub&#xff1a;https://github.com/cyberagiinc/DevDocs 更多AI开源软件&#xff1a;发现分享好用的AI工具、AI开源软件、AI模型、AI变现 - 小众AI DevDocs 是一个完全免费的开源工具&#xff0c;由 CyberAGI 团队开发&#xff0c;托管在 GitHub 上。它专为程序员和软件开发…

第十四届蓝桥杯大赛软件赛国赛Python大学B组题解

文章目录 弹珠堆放划分偶串交易账本背包问题翻转最大阶梯最长回文前后缀贸易航线困局 弹珠堆放 递推式 a i a i − 1 i a_ia_{i-1}i ai​ai−1​i&#xff0c; n 20230610 n20230610 n20230610非常小&#xff0c;直接模拟 答案等于 494 494 494 划分 因为总和为 1 e 6 1e6…

折叠屏手机:技术进步了,柔性OLED面板测试技术需求跟上了吗?

全球智能手机市场陷入创新焦虑&#xff0c;折叠屏手机被寄予厚望&#xff0c;2023 年出货量同比增长 62%。但在供应链技术狂欢背后&#xff0c;存在诸多问题。消费端数据显示&#xff0c;用户使用频率低&#xff0c;定价策略反常。产业链重构虽让部分企业获利&#xff0c;却推高…

30天学Java第九天——线程

并行与并发的区别 并行是多核 CPU 上的多任务处理&#xff0c;多个任务在同一时间真正的同时执行并发是单核 CPU 上的多任务处理&#xff0c;多个任务在同一时间段内交替执行&#xff0c;通过时间片轮转实现交替执行&#xff0c;用于解决 IO 密集型任务的瓶颈 线程的创建方式…

自行搭建一个Git仓库托管平台

1.安装Git sudo apt install git 2.Git本地仓库创建&#xff08;自己选择一个文件夹&#xff09; git init 这里我在 /home/test 下面初始化了代码仓库 1. 首先在仓库中新建一个txt文件&#xff0c;并输入一些内容 2. 将文件添加到仓库 git add test.txt 执行之后没有任何输…

LeeCode 409.最长回文串

给定一个包含大写字母和小写字母的字符串 s &#xff0c;返回 通过这些字母构造成的 最长的 回文串 的长度。 在构造过程中&#xff0c;请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串。 示例 1: 输入:s "abccccdd" 输出:7 解释: 我们可以构造的…

【前端分享】JavaScript异步编程详解!

JavaScript 的异步编程是其核心特性之一&#xff0c;主要用于处理非阻塞操作&#xff08;如网络请求、文件读写、定时任务等&#xff09;。由于 JavaScript 是单线程的&#xff0c;异步机制可以避免代码阻塞&#xff0c;提高性能和用户体验。以下是 JavaScript 异步编程的核心概…