【Arty-A7填坑笔记】02:Microblaze软核与HDL点灯对比

【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板卡:

65lhq0.md.png

建立文件后进入主界面,点击左侧Flow Navigatot 栏的Generate Block Design 来新创建一张框图,我们将用图形化的方式进行顶层设计,组织各个模块。在这里我把新建的Block Design 命名为system

首先添加时钟部分。找到Board选项卡,将System Clock 拖入图纸:Board选项卡是在我们通过板卡建立工程才有的,其中包含一些针对板卡配置好的模块,包括引脚约束也做好了。直接使用这些模块比较方便,不过也会带来一些坑,所以还需要一些手动的修改。

这里的时钟模块应配置为输入单端100MHz时钟(板载100MHz单端有源晶振),输出单路100MHz时钟(作为系统时钟),这些其实是默认的,需要手动修改的一处是将时钟复位设置为低电平有效:

65lIaT.md.png

接下来点击图纸工具栏的“+”按键,输入“microblaze”添加Microblaze处理器,之后点击Run Block Automatio 来配置一下Microblaze的相关组件:

65lfrq.md.png

Microblaze的本地RAM设置为64kB(完全够用了),这部分RAM将使用FPGA的内部Block RAM 生成。时钟选择到Clock Wizard 模块产生的100MHz时钟,其他保持默认:

65l5ZV.md.png

点击Run Connectio Automatio ,在弹出的窗口中全选信号,将自动生成连接:

65lWMn.md.png

点击图纸工具栏类似刷信符号的按键,将自动整理框图:

65l7iF.md.png

添加一个AXI总线GPIO模块,用与Microblaze点灯:

65loIU.md.png

双击刚添加进来的AXI GPIO IP进行设置,设置为全部输出,位宽3(驱动RGB LED)。每个AXI GPIO IP有两个通道可用,这里只启用了GPIO1

65lHG4.md.png

拖入USB UART 模块,修改波特率为115200(不改也可以):

65lbRJ.md.png

这样,包含GPIO和UART这两个外设的Microblaze最小系统就搭建好了。

1.2.插入RTL代码

接下来,再用Verilog写一个计数器,对100MHz时钟分频后驱动LED。由于我们之前建立的Block Design 就相当于顶层文件,所以可以将RTL代码直接作为一个模块插入使用。

先切换到Sources选项卡,右键添加源文件:

65lqz9.md.png

点击创建文件,这里我命名为led_flow.v,注意verilog文件名要和文件内模块名相同:

65lOMR.md.png

开始编写RTL代码,由于输入时钟为100MHz,使用二进制计数器至少要27位才能分频至1s以上,使用计算器可以方便得到计数器位宽:

65lXs1.md.png

完整代码如下,这里仅简单对计数器进行了累加操作,并把高四位取出来驱动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代码插入图纸:

65lxZ6.md.png

分别连接sys_clkclk_100megaresetrst_n,并右键led_output端口引出向外接口:

65ljqx.md.png

RTL代码点灯部分完成。

1.3.完成框图,综合布线

此处我发现了一点问题,就是我们手动加入的GPIO模块偷偷变成板卡工程分配的按键模块了,这也算是板卡工程带来的坑吧。这里可以双击模块,如图改为自定义(Custom),就可以对IP进行修改了,改为之前GPIO配置中的全输出,位宽3:

65lzdK.md.png

不要忘了删掉dip_switches_4bit这个外接端口,仿照上文自己引出端口然后改名为gpio_rgb0。如果直接修改原来的dip_switches_4bit端口,可能带来位宽冲突:

651SIO.md.png

最后完成的系统图纸如下:

651CJe.md.png

右键图纸文件system.bd,点击Generate Output Product... 生成输出文件(比较耗时),完成后再次右键system.bd,点击Generate HDL Wrapper... 生成打包整个图纸的顶层RTL文件(.v)(很快):

6519iD.md.png

最后,不要忘了在生成的system_wrapper.v文件上右键,点击Set as Top 将其设置为顶层文件,因为我们之前编写的led_flow.v文件可能被当成顶层文件了,这里要纠正:

651PRH.png

接下来依次点击左侧Flow Navigatot 栏的Run SynthesisRun Implementation 进行综合与布线,不出意外应该不会有error,之后会顺利进入Implemented Design 中的引脚分配界面:

651izd.md.png

其中,打勾的选项表示已完成约束,这是板卡工程按照板上外设分配好的。我们自己添加的两组接口则要单独约束。在Arty-A7的用户手册上可以找到我们使用的几个LED灯所在的管脚:

651ELt.md.png

完成的约束信息如下,注意IO电平一定要设置为LVCMOS33

651edf.md.png

之后按Ctrl+s,将引脚约束保存为.xdc文件。

最后,点击左侧Flow Navigatot 栏的Generate Bitstream 生成配置FPGA所需的比特流(较慢)。全部完成后,可右键Block Design 点击关闭,在Project Manager 界面的Project Summary 选项卡可看见工程消耗的硬件资源:

651AsI.png

此时在Vivado就可以下载一下比特流看看效果。点击左侧Flow Navigatot 栏的Open Hardware Manager 按下图点击器件编程,选择刚刚生成的system_wrapper.bit文件(在xxx.runs/impl_1/处)进行下载。

651ZeP.md.png

完成后,效果如下:

651jpQ.gif

可见只有RTL控制的点灯效果,这是因为Microblaze的点灯代码还没有编写。若此时拔插开发板电源或按下板上的PROG按键,则FPGA会重新配置,之前所见效果消失,因为我们并没有将比特流固化进入配置FPGA的非易失性存储器。

1.4.软件设计

先为Microblaze嵌入式开发准备一个工作区,这里我直接仿照Vivado文件目录建立:

651kQA.png

在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 模板。

651mo8.md.png

打开新建立的led_flow工程下的helloworld.c,可见已经写好了从串口输出“Hello World”的代码。稍加修改为循环输出,以便观察:

651uFS.md.png

保存并编译无误后,准备下载测试。右键点击led_flow工程,点击Run AS -> Run Configurations :

651MWQ.md.png

双击GDB选项以新建一个运行/调试方式,点击后在右侧完成配置,勾选ResetProgram

651Qzj.md.png

点击Run后即会将硬件配置及软件代码一同下载进入FPGA,应该可以观察到效果了。点击Window -> Show View -> Other... ,找到Terminal双击打开,配置为Serial,选到Arty-A7所在的端口,波特率设置为之前配置的115200,可以看到输出的信息了:

651KJg.md.png

6511Qs.md.png

接下来开始使用Xilinx自带的函数驱动GPIO等外设。根据我们配置的Microblaze最小系统,SDK会在xparameter.h文件中包含地址等相关信息,在使用时需包含此头文件:

6513yn.md.png

抽象定义一下我们使用到的外设,使用Alt+/可打开代码提示:

651Wfe.md.png

最后完整的代码如下,实现RGBLED依次点亮的效果:

651Tmt.md.png

仿照上文的运行步骤下载后,可看到RTL及Microblaze控制下的两种流水灯效果:

651vlj.gif

最后一步,把我们的硬件比特流以及软件代码一起固化到配置FPGA的FLASH中。在SDK软件顶部工具栏先点击Xilinx -> Program FPGA,在弹出的窗口中,确定Bitstream项中为system_wrapper.bit,即FPGA的硬件配置比特流,然后在microblaze_0后填入led_flow工程编译后产生的led_flow.elf文件,点击Program

651IOI.png

此举将把硬件比特流以及软件代码一起下载进FPGA,并把这二者合并生成一个download.bit文件(在system_wrapper_hw_platform_0下)。我们下一步就要将这个比特文件固化进Flash:

65156A.png

在SDK软件顶部工具栏点击Xilinx -> Program Flash,在弹出的窗口中,确定Image File 项中为上一步生成的download.bitOffset项填写0x0,因为FPGA会从0x0地址处启动,Flash Typr 选择mt25ql128-x1_x2_x4,可以兼容板上的Flash,点击Program开始固化Flash(稍慢):

6514ld.md.png

固化完成后,拔插开发板电源或按下板上的PROG按键即可重新配置FPGA,就能看到我们设计的效果了,如下面GIF所示。可见配置所需的时间还是有些长的:

651zXn.gif

2.总结

本次,RTL计数器模块与Microblaze软核相对独立地分别实现了点灯的效果。在下一篇中,将尝试搭建带有AXI接口的RTL模块,将其与Microblaze软核联系起来,换句话说,为Microblaze软核定制RTL外设。

发表回复