05Microblaze最小系统搭建及GPIO测试
软件版本:vitis2021.1(vivado2021.1)
操作系统:WIN10 64bit
硬件平台:适用AMD-XILINX A7/K7/Z7/ZU/KU系列FPGA
登录"米联客"SOC|SOC社区-www.uisrc.com视频课程、答疑解惑!
1 概述
前面我们快速学习了FPGA工程的创建,仿真,调试。FPGA的功能非常强大,还可以在FPGA上做一个CPU.我们这节课给读者演示如何在FPGA上部署一个CPU软核,实现一个最小的SOC。
一般如果你是搞FPGA开发的肯定学习过C语言,学习C语言的时候,我们跑的第一个工程大多都是跑一个Hello World!不同于ZYNQ自带了硬核,FPGA嵌入软核CPU,这个软核是由FPGA内部的逻辑资源实现,虽然性能上比不了ZYNQ的硬件,但在一些控制的领域还是能做到游刃有余。同样的,AMD-XILINX也提供了软核的IP,这就是MicroBlaze。
MicroBlaze™ CPU 是嵌入式、可修改预置 32 位 RISC 微处理器配置系列。利用没有成本、基于 Eclipse 的 AMD-XILINX 软件开发套件,系统设计人员可在没有任何 FPGA 经验的情况下,使用所选的评估套件立即启动 MicroBlaze 处理器的开发。MicroBlaze 处理器符合大量不同应用的需求,这些应用包括工业、医疗、汽车、消费类以及通信市场等。
2 系统框图
一个MicroBlaze最小系统主要由四个部分组成:CPU、内存(BRAM或者DDR等)、FLASH和UART,下一个实验我们会在当前的系统加入MIG DDR控制器。接下来分别介绍这四个部分的搭建,最后使之成为一个完整的系统。
3 基于图形化设计SOC系统
3.1 创建vivado工程
1-1:在打开的VIVADO2021.1软件界面,单击Create Project。
1-2:单击NEXT,在弹出的窗口中输入工程名和选择保存路径,然后单击Next。
1-3:选择RTL Project,单击NEXT
1-4:在弹出的窗口中选择与板卡对应的芯片型号,这里的图片仅作参考
1-5:单击Finish完成工程的创建
3.2 CPU配置
MicroBlaze 是基于AMD-XILINX公司FPGA的一款32/64位软核嵌入式处理器,已针对AMD-XILINX FPGA的实现进行了优化。
MicroBlaze内部有32个32/64位通用寄存器和16个32/64位特殊寄存器。为了提高性能,MicroBlaze还具有指令和数据缓存。所有的指令字长都是32位,有3个操作数和2种寻址模式。指令按功能划分有逻辑运算、算术运算、分支、存储器读/写和特殊指令等。指令执行的流水线是并行流水线,它分为3级流水:取指、译码和执行。MicroBlaze可以响应软件和硬件中断,进行异常处理,通过外加控制逻辑,可以扩展外部中断。
下图显示了MicroBlaze内核的功能框图。
针对AMD-XILINX不同系列的芯片,下表中提供了MicroBlaze内核的最大频率。
MicroBlaze官方提供了三种预设配置: Microcontroller(运行裸机应用程序的简单微控制器)、FreeRTOS(一种具有高速缓存和存储器保护单元接口的实时系统处理器、Application(运行Linux的内存管理单元的应用处理器)。下表显示了这三种配置在官方XC7A200T芯片设备上的性能和利用率
创建一个空的FPGA工程
创建Block Design
BD(block design)命名为system
添加IP
双击MicroBlaze图标,对其进行配置
预设配置:选择current setting。
处理器配置:默认32-bit处理器
Select implemention to optimize(优化设置):
area(面积优先)。如果选了这个,implementation就会优化面积,尤其是减少流水线数量,从5条减少到3条。(推荐:建议在资源比较紧张的架构,如Artix-7,使能这个选项。然而,如果对性能有敏感的要求,就不要选这个选项,因为一些指令需要额外的时钟周期去执行。另外,对于MMU, Branch Target Cache, Instruction Cache Streams, Instruction Cache Victims, Data Cache Victims, ACE是不能进行面积优化的。)
Enable MicroBlaze Debug Module Interface(调试接口)
使能调试功能。用AMD-XILINX Microprocessor Debugger来下载、调试程序。(推荐:除非面积资源奇缺,否则不要禁止这个功能。)
Use Instruction and Data Caches(指令和数据cache,当使用外置DDR的时候使用):
当执行放在LMB之外的程序的时候,可以使用指令缓存来改善性能。指令缓存有如下特点:当使用外部存储时,激活这个选项可以明显地改善性能,即使这个缓存很小。
勾选Use Instruction and Data Caches
Enable Exceptions(异常处理使能)
当使用一个支持异常的操作系统时,需要激活这个选项。或者在一个单独的程序中添加异常回调函数。
Use Memory Management(内存管理)
当使用一个支持虚拟内存保护的操作系统时(如Linux),需要激活。(当你使能面积优化或者堆栈保护功能时,内存管理单元是不可见的,自动禁止)
Enable Discrete Ports:使能软核上的独立端口。
单击NEXT进入下一页配置:
这一页是关于移位器、浮点单元、整形乘法器,整形除法器、模式比较器、其他机器状态寄存器指令、加载/存储和交换指令、额外的流指令、容错功能。
使能桶型移位器(Enable Barrel Shifter):使能软核中的筒形移位器硬件。激活这个参数,就可以使用如下指令(bsrl,bsra,...)使能这个可以提高应用的性能,但是会增大软核的尺寸。如果激活,编译器会自动使用筒形移位器指令。
使能浮点单元(Enable Floating Point Unit):使能一个单精度浮点单元(FPU)。使用FPU可以明显地提高应用的单精度浮点性能,同时也会增大软核的尺寸。
使能整形乘法器(Enable Integer Multiplier):使能一个整形乘法器硬件。若激活,则可以在给MUL32赋值时,使用mul和muli指令。当给MUL64赋值时,使用mulh,mulhu,mulhsu指令。这个参数可以设置为NONE,可以把MUL或者DSP48释放,用作其他用途。这样做对软核的面积影响很小。当使用这个选项,编译器自动使用mul指令。
使能整形除法器(Enable Integer Divider):使能一个整形除法器硬件。若激活,可以使用idiv,iduvu指令。使能这个选项可以提高应用中的除法性能,但是增大了软核的尺寸。当使用这个选项,编译器自动使用idiv指令。
使能额外机器状态寄存器指令(Enable Additional Machine Status Register Instructions):若激活,则可以读写MSR,使用msrset和msrclr命令。可以提高访问MSR的性能。
使能模式比较器(Enable Pattern Comparator):如激活,则可以使用模式匹配指令pcmpbf,pcmpeq,pcmpne。模式匹配字节查找指令(pattern comparator byte find, pcmpbf)返回找到的第一个字节的位置,提高字符串和模式匹配操作的效率。若使能,SDK库会自动使用这个指令。pcmfeq和pcmpne指令根据两个字是否相同,返回1或者0。这些指令会提高setting flags的效率,编译器会自动使用它们。激活这个选项还可以count leading zeros指令,clz。clz指令能提高优先级编码的效率。
使能保留的加载/保存和交换指令(Enable Reserved Load/Store and Swap Instructions):lbur,lhur,lwr,sbr,shr,swr,swapb,和swaph。这些指令能够以相对的字节序来读写数据,交换指令能交换字节或者半个字长。当用little-endian的MicroBlaze访问big-endian的网络时,可以提高效率。
使能额外的流命令(Enable Additional Stream Instructions):当使用AXI4-Stream链接时,提供额外的功能。这包括动态访问指令GETD和PUTD,这两个指令用寄存器来选择接口。(重要:一定要激活流异常功能,才能使用这些指令,而且知道选择一个流链接)
单击Next进入下一页配置:使能cache修改如下
单击Next进入下一页配置:
这一页是对microblaze的调试接口进行配置,选择默认的BASIC配置
单击Next进入下一页配置:
该页面主要用于设置总线接口,设置本地内存总线接口、AXI总线接口、Stream总线接口
单击OK 完成配置
产生使用VIVADO BD的自动化功能
设置如下
自动化连线后
3.3 添加AXI UARTLite IP
我们还要添加一个UART用于串口打印,因为不需要复杂功能,我们只要添加一个基本的UART IP
设置UART核,修改波特率为115200。
通过连线的方式,连接到AXI interconnect,增加AXI interconnect的接口
连接AXI接口到UART的AXI接口
连接中断
连接时钟
连接复位
3.4 CLK_WIZ时钟设置
修改clk_wiz时钟输入,这里注意每个板子的输入可能不一样,有单端,有差分,有25M 50M 100M 200M,请大家核对原理图
取消时钟复位
把时钟输入,和UART端口引出
相同方法,引出时钟
修改时钟名,让时钟名符合使用习惯
3.5 添加AXI-Interconnect IP
添加1个axi-interconnect IP,互联到这两个接口
双击该IP
修改设置如下
3.6 添加MIG IP核心(添加DDR配置)
AMD-XILINX DDR通过MIG控制,添加MIG IP核
双击IP配置
MIG核设置
导入DDR的IO约束,这个文件也是我们从已经做好的工程导出的,直接导入可以提高效率,不同的板子文件名可能不一样注意下。
校对
既然可以导入,那么也可以通过Save Pin Out 导出设置,这里就不再演示
MIG核配置完成后
3.7 完成信号连线设计
连线的过程不再重复描述,根据我们最终的截图,完成IP之间的连线,并且引出MIG DDR接口
因为只用到1个中断,修改concat IP,改为1
3.8 添加AXI QUAD SPI IP核
接下来,添加一个axi_quad_spi核
双击设置如下
增加axi-interconnect
使用相同的方法,完成axi-interconnect IP的连线
3.9 完成基于IP图形化的最小系统设计
完成后的最小系统如下包含了CPU、中断、调试模块、UART、QSPI、DDR:
3.10 BD(block design)层次化功能
我们可以利用BD的层次化功能,把目前的最小系统层次化为1个最小系统,按下ctrl选中所有IP
然后右击,创建层次化
我们可以取一个名字,这里我们输入MinSoc
现在看起很不舒服
继续右击,选中Regenerate Layout
现在看起来非常清爽了,端口只包含了时钟输入、串口、FLASH、DDR
3.11 添加AXI_GPIO
AXI-GPIO的设置如下,输出输出各设置2bit,支持中断,本实验不讲解中断后面再做中断实验
将接口连接到位,初学者可能不知道如何从我们外部连接到Minsoc,我们只需要右键接口,然后右键make Connection随后选择需要连接的接口即可自动完成连线。
连线完成
3.12 地址空间的分配
PS(microblaze cpu 或者ARM 简称PS)通过访问地址空间可以实现寄存器,或者内存的访问。
检查地址空间的分配, BRAM的地址空间已经分配,AXI_INTC IP地址也已经分配,UART和SPI的的地址没有分配。
选中assign ALL
分配完成后
3.13 VIVADO自动校对功能
利用VIVADO自动化校验功能
现在图形化的设计已经完成
3.14自动产生调用BD代码的接口代码
BD图形化设计本质还是FPGA代码,是一种图形化形象编程的方式。
检查自动产生的顶层文件,可以看到顶层文件调用了BD模块
3.15绑定FPGA pin脚
绑定FPGA管脚前文已经有详细的介绍,此处不再重复赘述,xdc文件存放路径为uisrc/04_pin
3.16 编译FPGA工程
4 编译完成后,导出硬件
米联客会新建3个文件路径
到此,完成了SOC FPGA部分的设计工作
5 导出导入BD的tcl脚本方法
最小系统搭建完成之后,在之后的设计中可能会再次使用到这一部分的设计,为了避免重复的设计,浪费开发时间,笔者这里介绍一种高效的工程管理方式——将BD文件导出为tcl脚本。导出tcl脚本之后,在下次设计中,我们就只需要运行tcl脚本即可完成此部分电路的配置,能有效的节省开发时间。
5.1 导出BD为tcl
首先确保Block design处于被打开的状态,在TCL控制台中输入如下命令可将BD文件导出为tcl脚本。
5.2 导入BD tcl
新建一个vivado工程
自定义IP(如果用户用到自定义IP需要设置,没用到不需要设置)路径设置,如果用到必须设置,否则导入tcl会找不到自定义IP.
本实验没有用到自定义IP
导入TCL到新的vivado工程
运行完source指令之后,系统会自动生成我们之前搭建好的BD文件,之后我们就可以使用这个文件来搭建最小系统。
6 搭建Vitis-sdk工程
创建soc_base sdk platform和APP工程。
6.1 创建SDK Platform工程
启动Vitis-Sdk
设置好路径
米联客资料中的路径规范如下图:
soc_prj里面是基于SOC的硬件工程源码
soc_hw里面是xsa格式文件,soc_prj编译会导出system_wrapper.xsa到这个文件
soc_sdk里面是裸机的sdk工程,sdk工程创建依赖soc_hw中的system_wrapper.xsa
单击Create Platform Project 创建基于开发平台的工程
添加之前创建的system_wrapper.xsa文件
6.2 创建hello_world APP工程
自动产生一个helloworld传输输出程序
右击工程编译
6.3 创建axi_gpio_test APP工程
"axi_gpio_test "APP工程代码,我们直接源码,用户直接新建一个空的工程,然后将源码直接复制粘贴即可,我们对于空工程的新建不做过多的讲解。
新建完成后工程如下图所示:
7 程序分析
hello_word APP就是简单通过调用FPGA工程师中的uart-lite IP串口输出一些字符信息,程序代码简单不做分析。
7.1 axi_gpio_test.c测试程序
#include "sys_xintr.h" #include "axi_gpio_intr.h" #include "sleep.h"
extern XGpio Gpio; //定义GPIO实列 extern XIntc Intc; //定义系统中断实列
extern volatile u32 gpio_intr_flag; //中断标志 extern volatile u32 gpio_val; //保存GPIO按键输入读取值
void init_intr_sys(void) {
XInit_Intr_System(&Intc , 0);//初始化系统中断 Gpiopl_init(&Gpio, AXI_GPIO_DEV_ID); //初始化GPIO Gpiopl_Setup_Intr_System(&Intc, &Gpio, GPIO_INTR_ID); //初始化GPIO中断 XSetup_Intr_Exception(&Intc); //设置中断异常 }
int main(void) { init_intr_sys();//初始化系统中断 while(1) { if(gpio_intr_flag) { gpio_intr_flag = 0; XGpio_DiscreteWrite(&Gpio,2,gpio_val); printf("SW=%d int\n\r", gpio_val); } } return XST_SUCCESS; } |
7.2 axi_gpio_intr.c程序
#include "axi_gpio_intr.h"
volatile u32 gpio_intr_flag; volatile u32 gpio_val;
//中断回调函数 void GpioplIntrHandler(void *Callback) { XGpio *GpioPtr = (XGpio *)Callback; u32 IrqStatus;
IrqStatus = XGpio_InterruptGetStatus(GpioPtr); //获取中断状态
if((IrqStatus & 0x01) == 0x01) { gpio_val = XGpio_DiscreteRead(GpioPtr, 1); //GPIO中断需要读按键状态知道哪一个按键产生了中断 gpio_intr_flag = 1; //设置中断标志 }
XGpio_InterruptClear(GpioPtr, IrqStatus); //清除中断
}
//设置中断 void Gpiopl_Setup_Intr_System(XIntc *GicInstancePtr, XGpio *InstancePtr, u16 IntrId) { XIntc_Connect(GicInstancePtr, IntrId, //设置中断回调函数 (Xil_ExceptionHandler)GpioplIntrHandler, (void *)InstancePtr);
XIntc_Enable(GicInstancePtr, IntrId);//使能中断号指定的中断
XGpio_InterruptEnable(InstancePtr, 0x01);//使能中断
XGpio_InterruptGlobalEnable(InstancePtr); //使能全局中断
}
int Gpiopl_init(XGpio *InstancePtr, u32 DeviceId) {
int Status; /* Initialize AXI GPIO */ Status = XGpio_Initialize(InstancePtr, DeviceId); if (Status != XST_SUCCESS) { xil_printf("AXI GPIO config failed!\r\n"); return XST_FAILURE; }
XGpio_SetDataDirection(InstancePtr, 1, 0x3);//设置GPIO通道1用于输入
XGpio_SetDataDirection(InstancePtr, 2, 0x0); //设置GPIO通道2用于输出
XGpio_SetDataDirection (&Gpio, 1, 0x3); //输入设置上拉
XGpio_SetDataDirection (&Gpio, 2, 0x0); //输出设置输出0
return 1; } |
在初始化函数中,设置channel1 的2个IO为输入,channel2 的2个IO为输出
在Gpiopl_Setup_Intr_System(XIntc *GicInstancePtr, XGpio *InstancePtr, u16 IntrId)函数中主要是对,AXI-GPIO的全局中断使能寄存器GIER和IPIER中断使能寄存器进行设置
当GPIO的电平状态发生改变就会触发中断,回调函数中,首先读取AXI-GPIO的状态寄存器,然后判断是否是channel1产生的中断。之后读取GPIO的按键输入状态,设置中断触发变量为1。最后清除中断。
8 实验演示
8.1 硬件准备
8.2 helloworld实验结果
为了观察实现结果,需要打开串口
首选选中Debug,出来Vitis Serial Terminal,串口终端
设置正确的串口号
单击OK 打开终端
选中Design切换界面
右击选中调试
双击System Project Debug
选中SystemDebug_hellowrld_sytem 单击debug
输入结果如下,最小系统运行正常
8.3 axi_gpio_test实验结果
除了通过串口观察按键中断的结果,而且可以通过LED观察。每次按下按键产生一个中断,每次松开按键也会产生一个中断。总之,只要按键的状态发生改变就会产生中断。
9本章小结
本章节我们学会了搭建Microblaze最小系统,并且该系统可以在多种场合重复使用,并且利用我们搭建的最小系统完成一个简单的AXI_GPIO的使用。
本文来米联客(milianke),作者:米联客(milianke),转载请注明原文链接:https://www.cnblogs.com/milianke/p/17693719.html