本文目录
【Arty-A7填坑笔记】02:Microblaze软核与HDL点灯对比
0.前言
在上一期中我们大概了解了Arty-A7板卡的各方面信息并准备好了开发环境,那么现在我们就来尝试一下喜闻乐见的点灯吧。
0.1.为什么要使用Microblaze
对于Arty-A7板卡上搭载的XC7A35T这一纯FPGA(没有嵌入式硬核)来说,最直白的点灯方式就是用硬件描述语言(这里使用VerilogHDL)设计一个计数器对板上晶振输入的信号进行分频,得到肉眼可分辨频率的信号后输出,驱动LED灯闪烁。
而另一方面,考虑到Arty-A7作为专为Microblaze软核开发设计的板卡,我也想先学习设计这样一个片上的最小系统,然后使用硬件资源为软核生成GPIO外设,再像使用传统单片机一样编写C语言代码点亮LED。
上述第二种方法肯定是牛刀杀鸡了,不过这也是使用软核控制逻辑外设,协调完成复杂任务的开端。试想设计一个ADC的数据采集系统,使用逻辑资源构建高速的ADC接口及并行数据FIFO,控制采集逻辑,再将这一套系统打包成一个用户IP,抽象成为软核的一个外设,而软核则负责ADC较为复杂的串行初始化流程,以及最终数据的传递以及人机交互接口。
Microblaze的使用免除了FPGA+MCU的硬件连接以及交互调试过程,使得设计流程更加简单流畅。同时在Vivado设计工具中,可将用户IP核打包成带有AXI总线的IP核,Microblaze软核就可通过类似内存映射或DMA的方式进行操作,配合Vivado生成的驱动代码还是比较方便的。
说的有点远,那么这次我们就同时尝试一下使用硬件逻辑点灯以及Microblaze软核点灯。
0.2.资源链接
本次Microblaze最小系统的配置主要参考了正点原子编写的《达芬奇之Microblaze开发指南》。该教程可以在这里找到:
http://www.alientek.com/
同时也参考了Digilent提供的例程:Arty – Getting Started with Microblaze,这个例程更适合Arty板卡使用,不过我们这次并没有使用板上的DDR3(没有添加例程中的MIG)
https://reference.digilentinc.com/learn/programmable-logic/tutorials/arty-getting-started-with-microblaze/start?redirect=1
1.设计过程
1.1.Microblaze最小系统搭建
首先建立Vivado工程,在器件选择界面切换到板卡选择,选择Arty-A7-35T板卡:
建立文件后进入主界面,点击左侧Flow Navigatot 栏的Generate Block Design 来新创建一张框图,我们将用图形化的方式进行顶层设计,组织各个模块。在这里我把新建的Block Design 命名为system
。
首先添加时钟部分。找到Board选项卡,将System Clock 拖入图纸:Board选项卡是在我们通过板卡建立工程才有的,其中包含一些针对板卡配置好的模块,包括引脚约束也做好了。直接使用这些模块比较方便,不过也会带来一些坑,所以还需要一些手动的修改。
这里的时钟模块应配置为输入单端100MHz时钟(板载100MHz单端有源晶振),输出单路100MHz时钟(作为系统时钟),这些其实是默认的,需要手动修改的一处是将时钟复位设置为低电平有效:
接下来点击图纸工具栏的“+”按键,输入“microblaze”添加Microblaze处理器,之后点击Run Block Automatio 来配置一下Microblaze的相关组件:
Microblaze的本地RAM设置为64kB(完全够用了),这部分RAM将使用FPGA的内部Block RAM 生成。时钟选择到Clock Wizard 模块产生的100MHz时钟,其他保持默认:
点击Run Connectio Automatio ,在弹出的窗口中全选信号,将自动生成连接:
点击图纸工具栏类似刷信符号的按键,将自动整理框图:
添加一个AXI总线GPIO模块,用与Microblaze点灯:
双击刚添加进来的AXI GPIO IP进行设置,设置为全部输出,位宽3(驱动RGB LED)。每个AXI GPIO IP有两个通道可用,这里只启用了GPIO1:
拖入USB UART 模块,修改波特率为115200
(不改也可以):
这样,包含GPIO和UART这两个外设的Microblaze最小系统就搭建好了。
1.2.插入RTL代码
接下来,再用Verilog写一个计数器,对100MHz时钟分频后驱动LED。由于我们之前建立的Block Design 就相当于顶层文件,所以可以将RTL代码直接作为一个模块插入使用。
先切换到Sources选项卡,右键添加源文件:
点击创建文件,这里我命名为led_flow.v
,注意verilog文件名要和文件内模块名相同:
开始编写RTL代码,由于输入时钟为100MHz,使用二进制计数器至少要27位才能分频至1s以上,使用计算器可以方便得到计数器位宽:
完整代码如下,这里仅简单对计数器进行了累加操作,并把高四位取出来驱动LED,实现二进制计数器的闪动效果,并没有做严格的1s计数:
module led_flow(
input clk_100mega,
input rst_n,
output [3:0] led_output
);
reg [26:0] cnt;
always @(posedge clk_100mega) begin
if (!rst_n) begin
cnt <= 'd0;
end else begin
cnt <= cnt + 1'b1;
end
end
assign led_output = cnt[26:23];
endmodule
完成后保存,在图纸空白处右键选择“Add Module... ”,在弹出的窗口中选择刚才的.v文件,即可将HDL代码插入图纸:
分别连接sys_clk
与clk_100mega
、reset
与rst_n
,并右键led_output
端口引出向外接口:
RTL代码点灯部分完成。
1.3.完成框图,综合布线
此处我发现了一点问题,就是我们手动加入的GPIO模块偷偷变成板卡工程分配的按键模块了,这也算是板卡工程带来的坑吧。这里可以双击模块,如图改为自定义(Custom),就可以对IP进行修改了,改为之前GPIO配置中的全输出,位宽3:
不要忘了删掉dip_switches_4bit
这个外接端口,仿照上文自己引出端口然后改名为gpio_rgb0
。如果直接修改原来的dip_switches_4bit
端口,可能带来位宽冲突:
最后完成的系统图纸如下:
右键图纸文件system.bd
,点击Generate Output Product... 生成输出文件(比较耗时),完成后再次右键system.bd
,点击Generate HDL Wrapper... 生成打包整个图纸的顶层RTL文件(.v)(很快):
最后,不要忘了在生成的system_wrapper.v
文件上右键,点击Set as Top 将其设置为顶层文件,因为我们之前编写的led_flow.v
文件可能被当成顶层文件了,这里要纠正:
接下来依次点击左侧Flow Navigatot 栏的Run Synthesis 和Run Implementation 进行综合与布线,不出意外应该不会有error
,之后会顺利进入Implemented Design 中的引脚分配界面:
其中,打勾的选项表示已完成约束,这是板卡工程按照板上外设分配好的。我们自己添加的两组接口则要单独约束。在Arty-A7的用户手册上可以找到我们使用的几个LED灯所在的管脚:
完成的约束信息如下,注意IO电平一定要都设置为LVCMOS33
:
之后按Ctrl+s
,将引脚约束保存为.xdc文件。
最后,点击左侧Flow Navigatot 栏的Generate Bitstream 生成配置FPGA所需的比特流(较慢)。全部完成后,可右键Block Design 点击关闭,在Project Manager 界面的Project Summary 选项卡可看见工程消耗的硬件资源:
此时在Vivado就可以下载一下比特流看看效果。点击左侧Flow Navigatot 栏的Open Hardware Manager 按下图点击器件编程,选择刚刚生成的system_wrapper.bit
文件(在xxx.runs/impl_1/
处)进行下载。
完成后,效果如下:
可见只有RTL控制的点灯效果,这是因为Microblaze的点灯代码还没有编写。若此时拔插开发板电源或按下板上的PROG按键,则FPGA会重新配置,之前所见效果消失,因为我们并没有将比特流固化进入配置FPGA的非易失性存储器。
1.4.软件设计
先为Microblaze嵌入式开发准备一个工作区,这里我直接仿照Vivado文件目录建立:
在Vivado顶部依次点击File -> Export -> Export Hardware ,勾选Include bitstream 并选择刚才建立的工作区文件夹,导出硬件配置文件。
在Vivado顶部点击File菜单中的Launch SDK ,选择到此工作区,打开SDK,其开启后会自动读取工作区目录下的system_wrapper.hdf
文件并生成system_wrapper_hw_platform_0
工程。同时,我们需要建立自己的应用工程,即点灯工程。在SDK软件中部依次点击File -> New -> Application Project建立,命名(我命名为led_flow
),并选择Hello world 模板。
打开新建立的led_flow
工程下的helloworld.c
,可见已经写好了从串口输出“Hello World”的代码。稍加修改为循环输出,以便观察:
保存并编译无误后,准备下载测试。右键点击led_flow
工程,点击Run AS -> Run Configurations :
双击GDB选项以新建一个运行/调试方式,点击后在右侧完成配置,勾选Reset及Program:
点击Run后即会将硬件配置及软件代码一同下载进入FPGA,应该可以观察到效果了。点击Window -> Show View -> Other... ,找到Terminal双击打开,配置为Serial,选到Arty-A7所在的端口,波特率设置为之前配置的115200,可以看到输出的信息了:
接下来开始使用Xilinx自带的函数驱动GPIO等外设。根据我们配置的Microblaze最小系统,SDK会在xparameter.h
文件中包含地址等相关信息,在使用时需包含此头文件:
抽象定义一下我们使用到的外设,使用Alt+/
可打开代码提示:
最后完整的代码如下,实现RGBLED依次点亮的效果:
仿照上文的运行步骤下载后,可看到RTL及Microblaze控制下的两种流水灯效果:
最后一步,把我们的硬件比特流以及软件代码一起固化到配置FPGA的FLASH中。在SDK软件顶部工具栏先点击Xilinx -> Program FPGA,在弹出的窗口中,确定Bitstream
项中为system_wrapper.bit
,即FPGA的硬件配置比特流,然后在microblaze_0
后填入led_flow
工程编译后产生的led_flow.elf
文件,点击Program。
此举将把硬件比特流以及软件代码一起下载进FPGA,并把这二者合并生成一个download.bit
文件(在system_wrapper_hw_platform_0
下)。我们下一步就要将这个比特文件固化进Flash:
在SDK软件顶部工具栏点击Xilinx -> Program Flash,在弹出的窗口中,确定Image File
项中为上一步生成的download.bit
,Offset
项填写0x0
,因为FPGA会从0x0
地址处启动,Flash Typr
选择mt25ql128-x1_x2_x4
,可以兼容板上的Flash,点击Program开始固化Flash(稍慢):
固化完成后,拔插开发板电源或按下板上的PROG按键即可重新配置FPGA,就能看到我们设计的效果了,如下面GIF所示。可见配置所需的时间还是有些长的:
2.总结
本次,RTL计数器模块与Microblaze软核相对独立地分别实现了点灯的效果。在下一篇中,将尝试搭建带有AXI接口的RTL模块,将其与Microblaze软核联系起来,换句话说,为Microblaze软核定制RTL外设。