当前位置: 首页 > news >正文

FPGA-流水灯

Quartus中使用Verilog实现

根据之前所学内容,打开Quartus 软件,新建FPGA项目文件,建立好空项目过后,选择Verilog HDL File,因为我们要使用Verilog代码实现仿真。

详细操作可参考往期博客:

FPGA 实验报告:四位全加器与三八译码器仿真实现_quartus 四位二进制全加器-CSDN博客

然后输入以下代码:

代码展示

module led_flow #(parameter TIME_1S = 50_000_000)(  

    input               sys_clk     ,

    input               sys_rst_n   ,

    output  reg [5:0]   led         

);

    reg     [25:0]      cnt     ;  

    wire                add_cnt ;

    wire                end_cnt ;

    reg     [2:0]       cnt1;

    wire                add_cnt1;

    wire                end_cnt1;

    // 时间基准计数器模块

    always @(posedge sys_clk or negedge sys_rst_n)begin

        if(!sys_rst_n) begin

            cnt <= 26'b0;  // 修改为26位清零

        end

        else if(add_cnt) begin

            if(end_cnt) begin

                cnt <= 26'b0;  // 26位清零

            end

            else begin

                cnt <= cnt+1'b1;

            end

        end

        else begin

            cnt <= cnt;

        end

    end

    // LED状态计数器模块

    always @(posedge sys_clk or negedge sys_rst_n) begin

        if(!sys_rst_n)begin

            cnt1 <= 3'b0;

        end

        else if(add_cnt1) begin

            if(end_cnt1)begin

                cnt1 <= 3'b0;

            end

            else begin

                cnt1 <= cnt1 + 1'b1;

            end

        end

    end

    // LED控制逻辑模块

    always @(posedge sys_clk or negedge sys_rst_n)begin

        if(!sys_rst_n)begin

            led <= 6'b0;  // 6位LED清零

        end

        else begin

            case (cnt1)

                3'b000 : led <= 6'b000001;  // 6位LED的点亮顺序

                3'b001 : led <= 6'b000010;

                3'b010 : led <= 6'b000100;

                3'b011 : led <= 6'b001000;

                3'b100 : led <= 6'b010000;

                3'b101 : led <= 6'b100000;

                3'b110 : led <= 6'b000001;  // 循环回到第一个LED

                default: led <= led;

            endcase

        end

    end

    // 信号赋值模块

    assign add_cnt = 1'b1;

    assign end_cnt = add_cnt && cnt == TIME_1S - 1;  // 1秒的时间基准

    assign add_cnt1 = (cnt == TIME_1S-1);  

    assign end_cnt1 = add_cnt1 && cnt1 == 3'b110;  // 6个LED的状态计数器最大值

endmodule

模块讲解

在上述代码中共有四个显著的模块,分别为

时间基准计数器模块

这部分实现了一个计数器,用于产生1秒的时间基准。在代码中定义了一个26位的寄存器cnt,用于存储计数值。当系统复位信号sys_rst_n为低电平时,计数器cnt被清零。

在每个时钟上升沿,如果add_cnt信号有效(始终为高电平),计数器cnt会增加。当计数器cnt达到TIME_1S - 1(即49,999,999)时,end_cnt信号有效,表示1秒的时间基准已到,计数器会清零并重新开始计数。

LED状态计数器模块:

这部分实现了一个计数器,用于控制6个LED的点亮顺序。在代码中定义了一个3位的寄存器cnt1,用于存储LED的状态计数值。

当系统复位信号sys_rst_n为低电平时,计数器cnt1被清零。

在每个时钟上升沿,如果add_cnt1信号有效(当时间基准计数器达到预设值时),计数器cnt1会增加。当计数器cnt1达到6(即3'b110)时,end_cnt1信号有效,表示LED状态计数器已到最大值,计数器会清零并重新开始计数。

LED控制逻辑模块:

这部分实现了控制6个LED灯的点亮顺序。当系统复位信号sys_rst_n为低电平时,所有LED被设置为熄灭状态。

根据LED状态计数器cnt1的值,控制6个LED灯的点亮顺序。使用case语句实现不同状态下的LED点亮模式。

当cnt1为3'b000时,点亮第一个LED(led[0])。

当cnt1为3'b001时,点亮第二个LED(led[1])。

依此类推,直到cnt1为3'b101时,点亮第六个LED(led[5])。

当cnt1为3'b110时,循环回到第一个LED,实现跑马灯效果。

信号赋值模块:

这部分实现了对控制信号进行赋值,用于协调计数器和LED控制逻辑的工作。

dd_cnt:始终为高电平(1'b1),表示时间基准计数器cnt始终在计数。

end_cnt:当时间基准计数器cnt达到TIME_1S - 1(即49,999,999)时,end_cnt信号有效,表示1秒的时间基准已到。

add_cnt1:当时间基准计数器cnt达到TIME_1S - 1时,add_cnt1信号有效,表示需要增加LED状态计数器cnt1的值。

end_cnt1:当LED状态计数器cnt1达到6(即3'b110)时,end_cnt1信号有效,表示LED状态计数器已到最大值,需要清零。

在这里要注意:module led_flow #(......)中,led_flow要和顶层模块名称相同,不然编译会出错。

运行与烧录

完成代码的copy过后,点击保存:

然后选择file文件,右键led.v将其设置为顶层文件;

点击Assignments',选择目录下的Pin Planner进行引脚分配;

随后进行烧录,详细烧录教程可以参考CSDN博客:

Nios实验入门——用Verilog编程方式完成LED流水灯显示并使用串口输出“Hello Nios-II”字符到笔记本电脑_verilog led灯-CSDN博客

运行效果截图:

VScode分模块化实现

vscode下载与汉化

首先下载VScode:code.visualstudio.com

在官网链接下载并成功安装过后,打开软件,根据操作,选择汉化扩展:

扩展下载

随后再下载两个扩展,实现FPGA的Verilog代码;

代码示例

随后根据我们的代码,用模块化设计实现,在这里分为top顶层模块(比如LedBlink.v)、分频模块(fenpin.v)、显示模块(display.v);

1、顶层模块 LedBlink.v

module LedBlink (

    input sys_clk,      // 系统时钟

    input sys_rst_n,    // 系统复位信号,低电平有效

    output reg [5:0] led // 6个LED输出

);

    // 分频模块信号

    wire clk_1s; // 1秒时钟信号

    // 实例化分频模块

    fenpin #(.TIME_1S(50_000_000)) u_fenpin (

        .sys_clk(sys_clk),

        .sys_rst_n(sys_rst_n),

        .clk_out(clk_1s)

    );

    // 实例化显示模块

    display u_display (

        .sys_clk(sys_clk),

        .sys_rst_n(sys_rst_n),

        .clk_1s(clk_1s),

        .led(led)

    );

endmodule

2、分频模块 fenpin.v

module fenpin #(

    parameter TIME_1S = 50_000_000 // 1秒的时间基准,假设系统时钟为50MHz

)(

    input sys_clk,      // 系统时钟

    input sys_rst_n,    // 系统复位信号,低电平有效

    output reg clk_out  // 分频后的1秒时钟信号

);

    reg [25:0] cnt; // 计数器,用于产生1秒的时间基准

    always @(posedge sys_clk or negedge sys_rst_n) begin

        if (!sys_rst_n) begin

            cnt <= 26'b0; // 复位时计数器清零

            clk_out <= 1'b0; // 分频时钟信号初始化

        end

        else begin

            if (cnt == TIME_1S - 1) begin

                cnt <= 26'b0; // 计数器达到预设值时清零

                clk_out <= ~clk_out; // 翻转分频时钟信号

            end

            else begin

                cnt <= cnt + 1'b1; // 计数器加1

            end

        end

    end

endmodule

3、显示模块 display.v

module display (

    input sys_clk,      // 系统时钟

    input sys_rst_n,    // 系统复位信号,低电平有效

    input clk_1s,       // 1秒时钟信号

    output reg [5:0] led // 6个LED输出

);

    reg [2:0] cnt1; // LED状态计数器

    always @(posedge sys_clk or negedge sys_rst_n) begin

        if (!sys_rst_n) begin

            cnt1 <= 3'b0; // 复位时计数器清零

            led <= 6'b0; // LED初始化为熄灭状态

        end

        else if (clk_1s) begin // 在1秒时钟信号的上升沿更新LED状态

            case (cnt1)

                3'b000: led <= 6'b000001; // 点亮第一个LED

                3'b001: led <= 6'b000010; // 点亮第二个LED

                3'b010: led <= 6'b000100; // 点亮第三个LED

                3'b011: led <= 6'b001000; // 点亮第四个LED

                3'b100: led <= 6'b010000; // 点亮第五个LED

                3'b101: led <= 6'b100000; // 点亮第六个LED

                3'b110: begin

                    led <= 6'b000001; // 循环回到第一个LED

                    cnt1 <= 3'b000; // 计数器清零

                end

                default: led <= led; // 默认情况保持当前状态

            endcase

            if (cnt1 != 3'b110) begin

                cnt1 <= cnt1 + 1'b1; // 计数器加1

            end

        end

    end

endmodule

  分模块化设计能降低复杂度,让每个模块功能明确且简单,同时提高代码可读性和维护性,便于团队协作和后期功能扩展。它还能增强代码复用性,减少重复开发,通过清晰的模块接口和职责划分,使系统更灵活、更易调试和升级,从而提升整体设计质量和开发效率。

实验结果

fpga流水灯视频

总结与思考

  本次实验让我对FPGA开发有了更深入的理解和实践,不仅掌握了Verilog HDL编程的基本技巧,还学会了如何运用模块化设计思想将复杂系统分解为功能明确的模块,从而降低开发难度并提高代码的可维护性和复用性。在实验中遇到的问题,如顶层模块名称不匹配、信号连接错误以及时钟分频不准确等,通过仔细检查和逻辑分析得以解决,这个过程显著提升了我的问题解决能力。展望未来,我将继续深入学习FPGA的高级功能,探索其与物联网技术的结合点,并积极参与实际项目和竞赛,以积累更多经验,为未来的职业发展打下坚实基础。

相关文章:

  • vulhub/joker 靶机----练习攻略
  • 基于Java(Springboot+Gradle+Mybatis+templeaf 框架)+Mysql构建的(Web)校园二手平台系统
  • on-policy对比off-policy
  • 微服务的网关配置
  • 厨卫行业供应链产销协同前中后大平台现状需求分析报告+P120(120页PPT)(文末有下载方式)
  • Java面试黄金宝典2
  • LeetCode BFS解决FloodFill算法
  • 无需刷机、root,畅享原生安卓的丝滑体验。
  • 智能提示语链分析平台技术解析
  • 动态库、静态库、导入库
  • 人事档案管理系统基于Spring BootSSM
  • 268.数组美丽值求和
  • 【C++】函数next_permutation
  • 生成式AI红队测试:如何有效评估大语言模型
  • 基于FPGA频率、幅度、相位可调的任意函数发生器(DDS)实现
  • zabbix统计闲置资产
  • HTML课后实践
  • 代码随想录 Day 45 | 【第九章 动态规划part 08】121. 买卖股票的最佳时机、122.买卖股票的最佳时机II、123.买卖股票的最佳时机III
  • SPI驱动(九) -- SPI_Master驱动程序
  • Netty基础—8.Netty实现私有协议栈一
  • 新修订的《餐饮业促进和经营管理办法》公布,商务部解读
  • 一种声音·阿甘本|即将到来的中世纪;“新”与“旧”……
  • 定制基因编辑疗法治愈罕见遗传病患儿
  • 圆桌丨新能源车超充技术元年,专家呼吁重视电网承载能力可能面临的结构性挑战
  • 沪指跌0.68%报3380.82点,创指跌1.92%:券商、军工跌幅靠前
  • 机构发布“2025中国高职院校排名”