在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 1177|回复: 6

[原创] 孩子都能学会的FPGA:第二课——第一个FPGA工程(用计数器实现流水灯)

[复制链接]
发表于 2023-11-16 13:43:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
(原创声明:该文是作者的原创,面向对象是FPGA入门者,后续会有进阶的高级教程。宗旨是让每个想做FPGA的人轻松入门,作者不光让大家知其然,还要让大家知其所以然!每个工程作者都搭建了全自动化的仿真环境,只需要双击top_tb.bat文件就可以完成整个的仿真(前提是安装了modelsim),降低了初学者的门槛。如果需要整个工程请加微信15092150280索要即可,不收任何费用,但是仅供参考,不建议大家获得资料后从事一些商业活动!)
软件编程人员入门的第一个程序一般都是在屏幕上打印hello,world!这就像是一个里程碑,标志着大家要进入软件的编程世界!而FPGA开发人员的第一个里程碑是实现流水灯,当把编译好的下载文件烧写到板卡,看着流水灯不停的闪烁,就是在欢迎您进入FPGA的编程世界!
流水灯看似简单,实则包含硬件运行的最基本的哲理。板卡上电后流水灯会一直闪烁,就如时间日复一日,年复一年,永不停止,如果用英语的一个词来描述,那就是always!对FPGA来说,就是要上电后一直运行下去,这就是所谓的FPGA之道!所以FPGA的工程,不管大小,都是由一个个always块组合而成的!
对于第一个FPGA工程,要求很简单,系统时钟是100MHz,低电平复位,假设有4个led灯(都是高电平点亮,低电平熄灭),按照1秒的频率进行流水闪烁(第1秒1号灯亮,2-4号灯灭;第2秒2号灯亮,1,3,4号灯灭;第3秒3号灯亮,1,2,4号灯灭;第4秒4号灯灭,1-3号灯灭,然后以此反复就可以达到流水灯的效果)。
要实现流水灯,首先要实现计数器。计数器是FPGA开发中用的最多的模块,就如乐高中的那个最基本的积木。工程的要求是按照1秒的频率进行流水闪烁,那就要实现1秒的计数器,每当计数到1秒4个灯就流水一次。本工程的系统时钟是100MHz,那计数一秒就是计数100_000_000次,而FPGA计数一般是从0开始,那计数99_999_999次就是一秒。
分析差不多了,那就说一下计数器的设计要点,首先是计数器的开始条件,本工程流水灯一直运行,那计数器的开始条件就是系统上电,就是上电稳定后计数器要一直运行的;其次是计数器的清零条件(停止条件),本工程有两个清零条件,一个是系统复位(就是复位信号拉低),一个是计数时间到1秒,就是计数器加到了99_999_999;还有就是计数器累加(减)条件,本工程只要有系统时钟,复位信号没有拉低,计数器就会一直累加下去。
还有一点要特别注意,就是当计数器的清零条件和累加条件同时满足的情况下,谁的优先级高?我们一般把清零当成特殊处理,累加当成正常处理。 不管是硬件编程还是软件编程编程,一般的共识是先处理特殊,再处理正常,所以清零的优先级比累加的优先级高
其实可以具体分析一下,比如在计数到99_999_999后,下一个时钟周期清零和累加的条件同时满足,这时候如果清零,计数器就可以从0重新开始累加,功能正常;这时候如果累加,那计数值就会变成100_000_000,第二个清零条件计数到99_999_999就会失效,计数器就会一直累加下去,从而功能异常。
下面是计数器的FPGA编程,首先定义了系统时钟SYS_CLK是100_000_000,然后定义了一个32位的计数器one_second_cnt,用于1秒时间的计数;还定义了一位线网信号one_second_done,用于指示1秒时间到。
always块中主要实现了计数器one_second_cnt的清零和递增,if的优先级最高,else if次之,else最低。就是复位清零的优先级最高,计数到1秒清零的条件次之,递增的优先级最低。初学者常常把计数到1秒和递增的优先级弄反,结果计数器一直递增下去,造成功能错误!
1.png
根据多年的FPGA开发经验,给出如下的建议:不管是计数器模块还是其它的模块,建议按照优先级把所有的特殊处理列举完,剩下else就是正常的处理!
我们看一下计数器的modelsim仿真结果(由于计数99_999_999次仿真时间较长,仿真时改为了计数9次)。如下所示,在复位rst_n信号拉低的情况下,计数器是在初始的清零状态;当计数值one_second_cnt达到9后执行的是清理操作,在其它情况下都是进行的累计操作。
2.png
我们可以把计数值改成64,重新仿真,展开计数器one_second_cnt展开看一下,就会发现一个很有趣的现象,one_second_cnt[0]是时钟clk的2分频,one_second_cnt[1]是时钟clk的4分频,one_second_cnt[2]是时钟clk的8分频,one_second_cnt[3]是时钟clk的16分频,one_second_cnt[4]是时钟clk的32分频。。。。。。就是说如果clk是128MHz,one_second_cnt[0]是64MHz,one_second_cnt[1]是32MHz,one_second_cnt[2]就是16MHz,one_second_cnt[3]是8MHz。。。。。。这些分频的信号在后续的开发中会大发神威,我们后续再说,所以计数器是不是很神奇呀!
3.png
计数器设计完毕了,下面就该流水灯登场了。基本的工作都完成了,那流水灯实现就很简单了,流水灯的模块设计如下。流水灯就是要流水闪亮起来,在复位rst_n拉低的情况下给流水灯一个默认值,就是1号灯两,2-3号等灭,即流水灯的默认值是0001。每当计数时间到,即线网信号one_second_done拉高的时候,流水灯的值左移一位,高位移出来的值补到最低位,这样就形成了一个封闭的圆圈,最终实现了流水灯!
4.png
我们看一下流水灯的modelsim仿真图,在rst_n的时候leds是0001,然后one_second_done拉高后就是在0001->0010->0100->1000->0001这样循环流水
5.png
目前的工程里面有两个always模块了,那这两个模块是怎么运行的?如下所示,计数器模块在上面,流水灯模块在下面,那是不是一般的顺序执行,先执行计数器,再执行流水灯?不是的,我们把这两个模块换一下位置,功能还是一样的。所以大家要记住:一个工程里面的所有always块是同时执行的,没有任何的先后顺序!就像小学常用一边一边来造句,比如我一边走路,一边唱歌,走路和唱歌是同时发生的,没有先后顺序!

6.png
我们也可以把上面两个always合成一个always,功能也是一样的,但是不建议这样写,因为多个信号交织在一起,加大了设计的难度,同时也增加了后续的维护成本。比如后续要修改一个信号变化的条件,按照下面的编程方式可能会引起另外一个信号的异常变化。
7.png


发表于 2023-11-17 14:10:06 | 显示全部楼层
两个alway语句合在一起的code : leds 有问题, 最后一个else begin end 里,不应该有leds
 楼主| 发表于 2023-11-17 17:11:36 | 显示全部楼层


bhd740 发表于 2023-11-17 14:10
两个alway语句合在一起的code : leds 有问题, 最后一个else begin end 里,不应该有leds ...


谢谢指出错误,当时想写leds <=leds,复制过去忘记改了
发表于 2023-11-22 07:08:10 | 显示全部楼层
发表于 2024-6-25 13:21:54 | 显示全部楼层
感谢分享
发表于 2024-8-2 11:39:11 | 显示全部楼层
感谢分享
发表于 2024-8-22 17:50:15 | 显示全部楼层
学习
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /2 下一条


小黑屋| 手机版| 关于我们| 联系我们| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-12-27 03:25 , Processed in 0.021570 second(s), 8 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表