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)beginif(!sys_rst_n) begincnt <= 26'b0; // 修改为26位清零endelse if(add_cnt) beginif(end_cnt) begincnt <= 26'b0; // 26位清零endelse begincnt <= cnt+1'b1;endendelse begincnt <= cnt;endend// LED状态计数器模块always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)begincnt1 <= 3'b0;endelse if(add_cnt1) beginif(end_cnt1)begincnt1 <= 3'b0;endelse begincnt1 <= cnt1 + 1'b1;endendend// LED控制逻辑模块always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginled <= 6'b0; // 6位LED清零endelse begincase (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; // 循环回到第一个LEDdefault: led <= led;endcaseendend// 信号赋值模块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) beginif (!sys_rst_n) begincnt <= 26'b0; // 复位时计数器清零clk_out <= 1'b0; // 分频时钟信号初始化endelse beginif (cnt == TIME_1S - 1) begincnt <= 26'b0; // 计数器达到预设值时清零clk_out <= ~clk_out; // 翻转分频时钟信号endelse begincnt <= cnt + 1'b1; // 计数器加1endendendendmodule
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) beginif (!sys_rst_n) begincnt1 <= 3'b0; // 复位时计数器清零led <= 6'b0; // LED初始化为熄灭状态endelse if (clk_1s) begin // 在1秒时钟信号的上升沿更新LED状态case (cnt1)3'b000: led <= 6'b000001; // 点亮第一个LED3'b001: led <= 6'b000010; // 点亮第二个LED3'b010: led <= 6'b000100; // 点亮第三个LED3'b011: led <= 6'b001000; // 点亮第四个LED3'b100: led <= 6'b010000; // 点亮第五个LED3'b101: led <= 6'b100000; // 点亮第六个LED3'b110: beginled <= 6'b000001; // 循环回到第一个LEDcnt1 <= 3'b000; // 计数器清零enddefault: led <= led; // 默认情况保持当前状态endcaseif (cnt1 != 3'b110) begincnt1 <= cnt1 + 1'b1; // 计数器加1endendendendmodule
分模块化设计能降低复杂度,让每个模块功能明确且简单,同时提高代码可读性和维护性,便于团队协作和后期功能扩展。它还能增强代码复用性,减少重复开发,通过清晰的模块接口和职责划分,使系统更灵活、更易调试和升级,从而提升整体设计质量和开发效率。
实验结果
fpga流水灯视频
总结与思考
本次实验让我对FPGA开发有了更深入的理解和实践,不仅掌握了Verilog HDL编程的基本技巧,还学会了如何运用模块化设计思想将复杂系统分解为功能明确的模块,从而降低开发难度并提高代码的可维护性和复用性。在实验中遇到的问题,如顶层模块名称不匹配、信号连接错误以及时钟分频不准确等,通过仔细检查和逻辑分析得以解决,这个过程显著提升了我的问题解决能力。展望未来,我将继续深入学习FPGA的高级功能,探索其与物联网技术的结合点,并积极参与实际项目和竞赛,以积累更多经验,为未来的职业发展打下坚实基础。