一、引言
1、MicroBlaze简介。
用于做嵌入式处理操作的软核,来加速系统设计。与传统独立CPU相比,软核嵌入式处理器同样有较高的处理能力,并且在可构建多核系统、可定制取舍等方面均优于传统CPU。下图是传统的系统架构与SOPC架构,即较新的可编程片上系统。
MicroBlaze 是 Xilinx 公司提供的一款 32/64 位软核嵌入式处理器,是一款高度灵活可配置的易用型处理器, 它能够利用 FPGA 内部通用资源和相关 IP 核,实现可编程片上系统(SOPC)的设计。该处理器采用的是32 位 RISC(Reduced Insrtction System Computer)优化结构和 Harvard 总线结构,广泛适用于 Spartan、 Virtex 和 Artix 等系列的 FPGA。
MicroBlaze 软核嵌入式处理器是高度可定制的 IP 核,支持 70 多个配置选项,有 32 个 32 位通用寄存器以及 2 个 32 位特殊寄存器(PC 指针寄存器和 MSR 状态标志寄存器)。另外 MicroBlaze 软核处理器还配有指令和数据缓存、 浮点单元、内存管理单元和许多其他选项,从而大大提高其运算性能。 MicroBlaze 软核嵌入式处理器的所有指令字长都是 32 位,具有 3 个操作数和两种寻址模式, 指令按功能可划分为:逻辑运算、算术运算、分支、存储器读/写和特殊指令等等。指令以并行流水线的方式执行,其流水线可分为取指、译码和执行。
下图为 MicroBlaze 的框图,展示了固定的硬件特性模块和可配置选项,如指令和数据缓存。
MicroBlaze 处理器的外部接口定义如下:
- DPLB(Data Processor Local Bus):数据接口,处理器本地总线。
- DOPB(Data On-chip Peripheral Bus):数据接口,片上外设总线,该接口实现 CPU 与片内外设的数据交换。
- DLMB(Data Local Memory Bus):数据接口,本地存储器总线, 该总线为 CPU 与本地块存储器间的数据交换通道。
- IPLB(Instruction Processor Local Bus): 指令接口,处理器本地总线。
- IOPB(Instruction On-Chip Peripheral Bus): 指令接口,片上外设总线, CPU 通过此总线读取外部存储器的程序代码。
- ILMB(Instruction Local Memory Bus): 指令接口,本地存储器总线, 该总线与内部块存储器相连,提供高速指令的提取。
- MFSL0…15 (Fast Simple Link FSL master interface): FSL 主设备数据接口,提供点对点的通信通道。
- SFSL0…15 (Fast Simple Link FSL slave interface): FSL 从接口,提供点对点的通信通道。
- IXCL(Instruction side Xilinx Cache Link interface): 指令侧高速缓存链接接口。
- DXCL(Data side Xilinx Cache Link interface):数据侧高速缓存链接接口。
MicroBlaze 处理器通常有三种预设配置,如下图所示:
- Microcontroller:运行裸机应用程序的简单微控制器。
- Real-Time:具有高速缓存和与运行嵌入式实时操作系统的实时处理器。
- Application:最后是带有运行 Linux 的内存管理单元的应用处理器。
MicroBlaze可以在所有 Xilinx FPGA 中用作独立处理器,也可以在 Zynq SoC 系统中用作协处理器。
2、MicroBlaze系列计划。
MicroBlaze系列属于FPGA软核部分的学习,主要还是CPU相关的学习,将其列为了FPGA_ASIC部分。首先,会做一部分实验,包括串口、LED、按键中断、定时器中断等。接着,使用Xilinx提供的一些AXI模板,来设计一些能挂接MicroBlaze的IP。最后,MicroBlaze应该有些有趣的项目了,考虑用它来做一些较为大型的设计。开发板使用达芬奇Pro。
二、HelloWorld实验
1、基本流程。
step1 至 step4 为硬件设计部分,在 Vivado 软件中实现。step5 为软件设计部分,在 Vitis 软件中实现。step6 为功能的验证。
2、嵌入式最小系统。
以 MicroBlaze 为核心、 LocalMemory(片上存储) 为内存,加上传输信息使用的 UART 串口就构成了嵌入式最小系统。当程序比较简单时, Local Memory 可以作为程序的运行空间以及存储空间,空间大小可以根据需要设置最小 8KB 到最大 128KB;当程序比较复杂的时候,我们也可以使用片上搭载的外部存储器(如 DDR3)作为程序的运行空间以及存储空间。
AXI Interconnect IP 核用于将一个(或多个) AXI 存储器映射的主器件与一个(或多个) 存储器映射互联。互联实际上是一个开关,它管理并指挥所连接的 AXI 接口之间的通信。
3、工程搭建。
1°添加MicroBlaze。
2°对IP进行配置。
Predefined Configurations:用于配置模板。点击 Select Configuration 右侧的目录框我们能够看到多个模板选项。本次试验不需要用到模板,因此保持默认选项“Current settings”就可以。
Select Processor Implementation:用于选择 32 位或 64 位处理器。 64 位处理器将两个 32 位通用寄存器扩展为一个 64 位寄存器,提供处理 64 位数据的附加指令,并且可以使用最多 64 位地址寻址和最多 4 个 EB 指令和数据。 一般情况使用 32 位处理器就可以了。
Select implementation optimization:用于使能面积优化功能,打开其右侧的目录,我们可以看到有PERFORMANCE、 AREA 和 FREQUENCY 三个选项表示三种优化方式,其中 AREA(区域)表示三级优化,速度最慢占用资源最少; FREQUENCY(频率)表示八级优化,速度最快占用资源最多;PERFORMANCE(性能)是五级优化,速度和资源占用量在 AREA 和 FREQUENCY 两者之间,本实验我们选择PERFORMANCE。
Enable MicroBlaze Debug Module Interface:使能调试功能,一般情况下我们都开启该功能,只有在资源十分紧张的情况下才会禁止此功能
Use Instruction and Data Caches: 使用指令和数据缓存。当使用外部存储时,激活这个选项可以明显地改善性能,由于本次实验我们使用的是本地存储,该选项对实验没影响,因此不选择。
Enable Exceptions:异常功能的使能。
Enable Discrete Ports:使能软核上的独立端口。
配置完成后点击“Next”进入第二页 General 页面, General 页面能够选择单元的选择和优化。 直接保持默认就可以了,点击“Next”进入 Debug 页面
Debug 页面是进行断点设置和查看点的数量。 这里我们同样保持默认设置。
Buses页面能进行总线设置。Local Memory Bus Interfaces是本地内存总线接口(LMB),我们将两项都选中; AXI and ACE Interfaces 是 AXI 和 ACE 接口, 下拉选择 AXI 接口, 勾选 Enable Peripheral AXI Data interface 用来控制外围模块(AXI 数据接口是与外围接口数据交互的总线, 之后所有模块都挂在这个总线上); Stream Interfaces 和 Other Interfaces 分别用于开启 Stream 流接口和一些其它接口,本实验中我们直接保持默认配置就好了,配置完成后点击“OK”完成 MicroBlaze 的配置。
3°点击Run Block Automation进行自动连线。
在弹出的界面选择“Local Memory”为 64KB,其余选项默认,点击“OK”。
4°自动连接后,生成四个新模块。
clk_wiz_1 模块提供系统时钟, rst_clk_wiz_1_100M 是系统复位模块,用以给系统各个模块提供复位信号, mdm_1 是 MicroBlaze 调试模块, microblaze_0_local_memory 模块是片上存储模块。
5°时钟配置。
将输入时钟设为50MHz,在 Source 目录将信号类型改为单端信号。切换到 Output Clocks 页面设置输出时钟,输出时钟设为 100MHz,在 Reset Type 栏选择“Acitive Low”设置为低电压复位,点击“OK”。
6°时钟模块Make External改名。
更改一些外部连接的名称后,再将“ext_rest_in”与“sys_rst_n”进行连接。
7°添加AXI UART的IP。
点击左上方“Run Bonnection Autmation”在弹出的页面中,选中所有信号,点击“OK”进行自动连线。
可以生成下面新的互联模块。uart模块和MicroBlaze之间的数据可以相互传输。
验证一下,没啥问题。后面就是常规的generator,生成wrapper,添加下约束并综合实现等。
具体的管脚约束文件如下所示:
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN E14 IOSTANDARD LVCMOS33} [get_ports UART_rxd]
set_property -dict {PACKAGE_PIN D17 IOSTANDARD LVCMOS33} [get_ports UART_txd]
最终,得到xsa的平台文件,用于vitis新建工程用。
新建一个vitis_prj文件夹,用来做开发vitis软件的环境。
8°Launch Vitis并建立工程。
1)tools——>Launch Vitis。
2)选择之前新建的vitis_prj文件夹,作为工作空间。
3)新建一个application project。
4)添加自己刚刚生成的硬件平台。
5)CPU默认microblaze_0,OS选择standalone,编程语言选C语言。模板选择HelloWorld。
9°点击展开硬件平台 Platfrom 工程“design_1_wrapper” , 双击 platform.spr 即可看到 Platform 对应生成的 BSP 工程(Board Support Package 板级支持包), 在这里可以对 BSP 进行配置, 里面包含了用于应用程序开发的驱动信息。
10°源文件代码。
非常简单。可以看到 init_platform 函数的作用是使能 caches 和初始化 uart; cleanup_platform 函数的作用是取消使能 caches。 对于软件部分我们不需要做修改,直接使用官方的实例就可以了。如果想查看函数的定义,可以按住 Ctrl 键不放,用鼠标点击相应的函数,就会跳转到其定义的地方(超级有用)。
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
int main()
{
init_platform();
print("Hello World\n\r");
cleanup_platform();
return 0;
}
11°编译工程。
接下来编译工程, 选中 APP 工程“HelloWorld_system” ,右键“Build Project” 或点击图中“锤子” 按键,进行工程编译。工程编译结束后, 成功生成 elf 文件, 在 Vitis 软件右下 Console 信息栏中会打印相关信息。
12°Terminal 窗口打开。
在 Vitis 软件的下方,找到 Terminal 窗口。如果界面中没有找到该窗口,或者操作过程中把该窗口给关闭了,则可以通过在菜单栏中依次点击“Window->Show view->Terminal 文件夹->Terminal”, 最后点击“Open”,接口成功添加 Terminal 窗口。
下面开发的板子,每次按下复位都会输出一个Hello World,测试成功。
三、AXI_GPIO实验
AXI GPIO IP 核为 AXI 接口提供了一个通用的输入/输出接口。 AXI GPIO 是一个软核(Soft IP),是由用户通过配置芯片的逻辑资源来实现的一个功能模块。。
AXI GPIO 可以配置成单通道或者双通道,每个通道的位宽可以单独设置。另外通过打开或者关闭三态缓冲器,AXI GPIO 的端口还可以被动态地配置成输入或者输出接口。
模块的左侧实现了一个 32 位的 AXI4-Lite 从接口,用于主机访问 AXI GPIO 内部各通道的寄存器。 当右侧接口输入的信号发生变化时,模块还能向主机产生中断信号。不过只有在配置 IP 核时选择“使能中断”,才会启用模块的中断控制功能。
1、系统框图。
由系统框图可以看出, AXI GPIO 和 AXI UART 都通过 AXI Interconnect 模块与 MicroBlaze 互联,Microblaze 处理器输出 LED灯的控制信号,通过AXI Interconnect互联模块传输到AXI GPIO 模块, AXI GPIO 模块根据 AXI4-Lite 协议将 LED 灯控制信号解析出来,输出到 FPGA 的 LED 引脚,从而控制 LED 灯。
2、UART实现另存为新工程。
选择一个新的路径,另存为一个新的工程。
下图为生成的一个新的工程。至此,在原工程的基础上成功创建了一个新的工程而没有破坏原来的工程,也避免了重新创建工程或复制工程后修改的麻烦。
3、打开Block Design,新建一个AXI GPIO。
自动连接后,得到下面的工程。
管脚文件如下所示。
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN E14 IOSTANDARD LVCMOS33} [get_ports UART_rxd]
set_property -dict {PACKAGE_PIN D17 IOSTANDARD LVCMOS33} [get_ports UART_txd]
set_property -dict {PACKAGE_PIN V9 IOSTANDARD LVCMOS15} [get_ports {gpio_tri_io[0]}]
set_property -dict {PACKAGE_PIN Y8 IOSTANDARD LVCMOS15} [get_ports {gpio_tri_io[1]}]
set_property -dict {PACKAGE_PIN Y7 IOSTANDARD LVCMOS15} [get_ports {gpio_tri_io[2]}]
set_property -dict {PACKAGE_PIN W7 IOSTANDARD LVCMOS15} [get_ports {gpio_tri_io[3]}]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
4、综合实现,得到xsa文件,新建工程。
打开platform.spr 文件,找到点击板级支持包“Board_Support_Package”,点击展开“Peripheral Drivers” , 右侧有相关文档和示例。点击“Documentation” 会在浏览器窗口打开 GPIO_API 文档, 会展示关于 GPIO 的详细信息, 想了解 GPIO 的,可以仔细浏览其中的信息。
路径:D:/Xilinx/2019.2/Vitis/2019.2/data/embeddedsw/XilinxProcessorIPLib/drivers/gpio_v4_5/doc/html/api/index.html。
这里,点击“Import Examples” ,会弹出下图的导入示例界面, 关于 GPIO 有四个示例。
点击OK后,可以发现在左边的工程中,添加了新的样例工程。“xparameters.h”定义了一些默认的参数,“xil_printf.h”是打印函数用来控制打印信息,“xgpio.h” 文件包含 Xilinx 通用 I/O(Xgpio)设备驱动程序的软件 API 定义。我们本次实验的软件程序部分就可以根据官方示例“xgpio_example.c”进行设计。
5、在src文件夹下,新建一个file文件。进行新的开发。
新建一个工程。
#include "xparameters.h"
#include "xgpio.h"
#include "xil_printf.h"
#include "sleep.h"
#define LED_ID XPAR_GPIO_0_DEVICE_ID //led器件ID
#define LED_CHANNEL 1 //LED通道
XGpio Gpio; //GPIO实例
int main(void){
int i=0;
xil_printf("GPIO LED TEST\n\r");
//初始化GPIO
XGpio_Initialize(&Gpio, LED_ID);
//为指定的信道设置方向 ,0 输出 ,1输入
XGpio_SetDataDirection(&Gpio, LED_CHANNEL, 0);
while (1) {
//向指定通道写入数据,LED每0.5秒流转一次
XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 0x01 << i);
//循环计数,表示第几个灯亮
if(i == 3)
i = 0;
else
i = i + 1;
//延时0.5秒
usleep(500000);
}
return 0;
}
多调用了一个“sleep.h”的头文件,该头文件包含 ARM CortexA53、A9、 R5、 Microblaze 处理器特定的延迟 API。调用该文件中的相关函数可以实现延迟处理。宏定义了 LED_ID,使其为 XPAR_GPIO_0_DEVICE_ID。 如果在 Vitis 软件中,按住 Ctrl 键不放,将鼠标移动到 XPAR_GPIO_0_DEVICE_ID 上,当鼠标变成手指状时,单击鼠标左键,会自动跳转到 xparameters.h 文件中,该文件定义了各个外设的基地址、器件 ID、中断等,我们这里重新宏定义 XPAR_GPIO_0_DEVICE_ID 是为了以后方便修改。定义了 LED 的通道,这里我们指定通道 1。
6、实验测试。
实验结果,四个led循环流水。
四、一些注意
1、uart实验关键:microblaze基本的配置,复习vitis工程的建立。
2、gpio实验关键:vivado工程另存为方式,vitis工程中导入模块模板。