你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

学习FPGA过程中的一些心得体会

[复制链接]
gaosmile 发布时间:2020-9-22 16:13
前一段时间在和导师进行沟通交流之后,我开始了学习FPGA的路程,往后的推送也大多数会和FPGA挂钩,给大家推送一些我在学习FPGA过程中的一些心得体会。
首先我开始的便是最基础的FPGA流水灯程序,我学习的FPGA编程语言是verilog语言,verilog被称为硬件电路描述语言,和c语言不同的是verilog就是在编写一个实际的硬件电路。
本期介绍的是使用AC620开发板上的4个LED状态每500ms翻转一次。AC620开发板上的晶振时钟频率为50MHz,换算为时钟周期就是20ns。
下图便是我使用的AC620 FPGA。
微信图片_20200922161048.jpg


01
编写电路介绍
微信图片_20200922161052.png

上图即为本期需要用verilog语言编写出来的计时电路,该电路由加法器、二选一选择器、D触发器和比较器组成。首先加法器的其中一个输入端始终是1,也就是每一个时钟周期都加1,而另一端则由D触发器的输出端提供。电路最初运行的时候,D触发器的输出端为0,即在加法器处为0+1,加法器的输出端为1,然后输入到二选一选择器输入端,因为是第一轮运行选择器的SEL端口也为0(SEL为0时输出A端口信号,为1时输出B端口信号),则选择器的输出端输出A端口的信号1,此信号传至D触发器的输入端,一个时钟周期后D触发器的输出端也变为1,D触发器的输出端信号1又传至加法器的输入端,如此循环自加直到D触发器的输出端达到比较器上的值之后,比较器输出端输出1,这个1又传至选择器的SEL端口,致使选择器输出B端口信号,实现D触发器清零。D触发器的时钟源连接一个50M的时钟脉冲,复位端单独连接一个引脚低电平触发复位清零。

(备注:比较器上的25'd24_999_999,此处定义的是一个25位的寄存器,在verilog中d表示十进制,b表示二进制,h表示十六进制,这个24_999_999表示的是十进制中的数字25000000,在此处是为了判断脉冲个数的计数是否达到我们要的值,因为50M的晶振为时钟周期为20ns,我们要实现500ms翻转一次,500ms=500_000_000ns,每一个脉冲20ns,则需计数500000000/20=25000000次,而比较器上写24_999_999是因为25000000是[0~24999999])

微信图片_20200922161055.png
此为AC620 FPGA上的四路LED电路图,四路LED的一端均有上拉电阻拉至高电平,即一般情况下全灭,控制时只需将拉高的一端给一个低电平,led即亮。

02
verilog编写该硬件电路

FPGA的编程软件为Quartus II,首先创建.v文件,然后我们要对需要的端口进行定义,即时钟信号、复位信号和led。

微信图片_20200922161058.png
然后需要对这些端口的类型进行定义,比如哪些是输入端口哪些是输出端口。
微信图片_20200922161101.png
这个时钟信号需要连接到FPGA上的晶振时钟所以为外部输入信号,复位信号也由FPGA上的按键控制也为输入信号,我们最终是要控制LED的状态,所以LED的信号为输出信号。
接下来是对时序电路进行编写,如下图所示。
微信图片_20200922161104.png
在时序电路中,我们需要一个寄存器来对脉冲计数,所以需要定义一个能在时序逻辑中自加的寄存器reg型的变量cnt,cnt前面的[24:0]表示这个寄存器是25位的(如前面解释的24_999_999,二进制寄存器的每一位只能是0和1),然后always@编写捕获时钟的上升沿和复位信号的下降沿,这是时序逻辑的写法,只有时钟的上升沿和复位信号的下降沿能触发,接下来便判断是哪个信号触发,if(!Rst_n)如果是复位信号触发则将计数脉冲寄存器cnt清零。else if(cnt == 25'd24_999_999)如果不是复位信号触发但是脉冲计数达到了我们要的标准,则也将脉冲计数寄存器清零。如果既不是复位信号触发脉冲计数寄存器也没有忙,则计数脉冲寄存器则实行加法器进行自加。
在这里面需要注意的是赋值方式,比如“cnt <= 25'd0”这是在always@里面需要用到非阻塞赋值方式,所以定义的端口类型必须为reg,像output reg [3:0]led这里就必须声明reg型,如若不声明reg直接编写output [3:0]led,则系统会自动将led识别为wire型,导致编译出错。
下面则介绍一下led的变换逻辑。
微信图片_20200922161108.png
相同的这一部分也是写在always@时序中,if(!Rst_n)当复位信号为零,则四个led灯全给高电平,led灯全灭。else if(cnt == 25'd24_999_999)当计数满后则led的电平翻转一次(由4'b1111翻转为4'b0000,因为b表示二进制,二进制里面不是1就是0),即由1高电平翻转为0低电平,led灯亮。cnt每计数一次脉冲则是20ns,计数25000000(0~24_999_999)次,也就是25000000*20ns=500'000'000ns=500ms。即实现每500ms翻转一次LED。
下面介绍两种流水灯写法。
微信图片_20200922161111.png
这是使用位操作进行的流水灯,需要使用一个4位寄存器led_r进行中间代换,因为是在always@时序逻辑中编写,所以led_r定义为reg型,首先给led_r为4'b0001,然后对这个1进行向左依次移位,就好比4个灯,假设1为亮,则向左依次逐个点亮(移位0001—>移位一次0010—>移位两次0100—>移位三次1000),此时就需要注意了当移位到1000时便不能再进行左移,此时就需要判断一下begin if(led_r == 4'b1000)则将led_r <= 4'b0001,如果不是led_r==1000,则else继续左移 led_r <= led_r << 1,这里的“<<”便是左移符号。
最后将左移的结果赋值给led,也就是led的管脚,此处也需要注意的便是led必须定义为wire型,因为此处在assign中进行阻塞赋值而不是在always@中。所以在定义处改为output [3:0]led,这里等效为output wire[3:0]led。

接下来还有一种骚操作。
微信图片_20200922161114.png
注意此处就需要将led的类型变回来了,因为是在always@中进行赋值,所以需要定义为reg型。
if(cnt == 25'd24_999_999)计数满led <={led[2:0],led[3]}则将led[2:0]三位向前移,将led[3]移到最后面去。基本流程我做了一个表,如下所示。
微信图片_20200922161117.png

03
modelsim仿真
进行modelsim仿真需要重新建立一个.v文件。编写仿真的时钟程序和复位程序。
微信图片_20200922161121.png
首先开头的是设置仿真波形的周期单位为ns,第二行的相当于c语言中的宏定义,将clock_period宏定义为20。然后定义端口和端口类型,下面led_flash led_flash0是引用的上一个.v文件里的程序,flash0只是在这的一个标签。
再下面便是编写时钟,首先给一个高电平clk50M=0;然后always每10ns翻转一次,便形成一个周期为20ns的方波。接下来的复位程序便是,待500ms后也就是cnt计数25000000后复位信号置零。
接下来看一下仿真波形的现象。
微信图片_20200922161124.png
最上面为clk50M的方波,当cnt从0计数到24999999时,四路led的电平全部被拉低,即led全亮。

1 收藏 评论0 发布时间:2020-9-22 16:13

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版