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的高级功能,探索其与物联网技术的结合点,并积极参与实际项目和竞赛,以积累更多经验,为未来的职业发展打下坚实基础。

相关推荐
霖001 小时前
FPGA中级项目4——DDS实现
大数据·经验分享·嵌入式硬件·学习·fpga开发·fpga
宫瑾5 小时前
逻辑派G1 6层高速板学习
学习·fpga开发
Terasic友晶科技6 小时前
07-SDRAM控制器的设计——Sdram_Control.v代码解析
fpga开发·tsp·sdram·de1-soc·de2i-150·de2-115·c5g
逾越TAO6 小时前
AXI总线之FPGA应用
fpga开发
绿算技术6 小时前
技术点提升效率详解
科技·缓存·ai·fpga开发
szxinmai主板定制专家1 天前
轨道交通CPU+FPGA控制器,支持codesys/vxWorks/翼辉等实时系统
arm开发·人工智能·fpga开发·架构
发光的沙子1 天前
FPGA----完美解决Windows下[XSIM 43-3409][XSIM 43-3915]错误
fpga开发
2201_755183711 天前
【FPGA】——实现六位流水灯
fpga开发
君临天下.鑫2 天前
基于 Verilog 的数字电路设计与仿真:乘数器与多路复用器实践
fpga开发·课程设计·个人开发