《ATK-DFPGL22G之FPGA开发指南_V1.0》第十七章双核AMP实验
本帖最后由 正点原子 于 2023-4-19 09:47 编辑1)实验平台:正点原子 DFZU2EG_4EV MPSoC开发板
2)购买链接:https://item.taobao.com/item.htm?&id=692368045899
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-340252-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子FPGA交流群:994244016
第十七章双核AMP实验
在前面的例程中,实现的都是基于单核CPU的实验,在很多应用场景中,往往只需要使用其中的一个CPU即可实现相应的功能。然而对于复杂的设计,如多任务处理、并行控制等,单个CPU将难以胜任。MPSOC芯片内部集成了两个(或四个)独立的Cortex-A53内核处理器,即两个(或四个)CPU,可以很好的应对各种复杂的设计。本章我们来学习下基于双核AMP的实验。
本章包括以下几个部分:
17.1简介
17.2实验任务
17.3硬件设计
17.4软件设计
17.5下载验证
17.1简介
多核处理器从多核的结构上是否一致,分为两种基本架构:同构多核架构和异构多核架构。同构多核处理器是指系统中的处理器在结构上是相同的;而异构处理器是指系统中的处理器在结构上是不同的,这些处理器可以是通用处理器,也可以是解决某些特定应用的专用硬核。同构多核架构相比于异构多核架构,在硬件和软件设计上较为简单,通用性较高。但在某些特定应用场合下,如异构多核架构专用的硬件加速硬核,异构多核架构的性能会更高。
Xilinx的MPSOC融合了这两种架构,MPSOC芯片包含多个独立的Cortex-A53处理器,这些处理器核在结构上是相同的,同时又包括了可编程的逻辑单元(PL),使得MPSOC整体系统成为了一个异构多核系统,同时具有较高的通用性和性能。
从软件的角度看,多核处理器的运行模式有AMP(非对称多处理)、SMP(对称多处理)和BMP(受约束多处理)三种运行模式。
AMP运行模式指多个内核相对独立的运行不同的任务,每个内核相互隔离,可以运行不同的操作系统(OS)或裸机应用程序。
SMP运行模式指多个处理器运行一个操作系统,这个操作系统同等的管理多个内核,如PC电脑。
BMP运行模式与SMP类似,但开发者可以指定将某个任务仅在某个指定内核上执行。
一般来说,SMP为较高级的应用提供统一的OS平台,开发者在OS之上构建应用时,无需考虑两个内核之间的资源共享和进程间通信。除此之外,对SMP而言存在性能开销,这会对实时性要求较高的应用,其性能造成较大影响。如PC机电脑的多核处理器一般运行在SMP模式,实现的功能较为复杂,但对实时性的要求不高。
而AMP的运行模式基本没有开销问题,在运行裸机应用程序时,甚至完全没有开销,比较适合实时性要求较高的应用,但需要精心定制的软件设计来实现处理器资源共享和处理器间通信。如电力控制保护设备通常需要与人机接口实现复杂的通信和高实时性的计算能力,一般采用AMP运行模式,一个处理器运行Linux操作系统,另一个处理器运行裸机应用程序,从而兼顾了电力系统控制设备需要的复杂功能和实时性。
AMP和SMP运行模式的框图如图 17.1.1所示。
图 17.1.1 AMP与SMP运行模式的框图
AMP运行模式给开发者提供了一个与传统单核CPU系统相类似的运行环境,使得开发者已有的经验和知识可以继续加以利用;同时,也为程序的移植提供了很大的便利性。
ZYNQ MPSOC分为CG、EG、EV三个系列,其中CG系列是双核Cortex-A53处理器,EG和EV系列是四核Cortex-A53处理器,这些处理器在AMP的机制下,可以运行各自独立的操作系统或者裸机应用程序。本次试验采用的是双核AMP的运行模式,两个CPU分别运行不同的裸机应用程序。
MPSOC中的Cortex-A53处理器,都具有各自私有的资源,同时也有一些共享的资源。私有资源有L1指令缓存、L1数据缓存、私有定时器等。共享的资源有L2 Cache、DDR存储器、OCM(On Chip Memory)和其它共享外设等。在AMP运行模式下,这两个处理器彼此隔离、分别运行。但在访问共享资源或者外设时,要注意避免同时访问,否则会出现冲突,导致程序在运行时出现问题。
我们可以通过类似互斥的方式来实现裸机情况下简单的双核使用,在MPSOC系统中,可以利用软件产生中断(SGI,Software Generated Interrupts)来避免访问的冲突。MPSOC系统中的每个CPU都能用SGI来中断自己、另一个CPU或同时中断两个CPU。向软件产生的中断寄存器(ICDSGIR)写入中断编号并指向目标CPU,就产生了一个SGI。每个CPU各自有一组SGI寄存器,可以产生16个软件产生中断中的一个或多个。图 17.1.2列出了SGI的中断ID号,范围是0~15。
图 17.1.2 软件中断
SGI可以触发CPU产生中断,但是如果想要传递数据的话,需要利用共享的内存实现数据的交互。共享内存指CPU0和CPU1在内存中约定一块地址及长度已知的内存区域,两个CPU通过读写共享内存中的数据来实现互相通信,而利用CPU产生的软件中断可以避免对共享内存的同时访问。
MPSOC中的OCM包含一个256KB的RAM,可以被多个CPU进行读写。OCM分为64个Blocks,每个block大小为4KB,存储空间较小,而外置的DDR4存储器一般存储空间较大。当两个CPU需要进行大量数据交互的时候,可以使用DDR存储器作为共享内存;而当交互的数据较少时,既可以使用OCM作为共享内存,也可以使用DDR4存储器作为共享内存。值得一提的是,当交互的数据量较少时,OCM作为共享内存有着独特的优势,与DDR内存相比,OCM提供了非常高的性能和来自多个处理器的低延迟访问。
需要注意的是,两个CPU仍然都是在DDR4存储器中运行,只不过使用OCM作为两个CPU通信的共享内存,而不是用DDR4存储器。值得一提的是,无论是OCM还是DDR4存储器作为共享内存,在访问之前,需要禁止存储空间的Cache(缓存)功能,这样CPU能够及时读到该地址内存中变化的数据,以避免两个CPU访问共享内存的一致性问题。通过Xil_SetTlbAttributes(INTPTR Addr, u32 attrib)函数禁用Cache属性,第一个参数为共享内存的基地址,第二个参数为设置内存的参数,包括是否禁用Cache等。
Xilinx官方提供了两个双核AMP的应用笔记,“XAPP1078” 是基于一个核心运行Linux操作系统,另一个核心运行裸机程序的应用笔记,“XAPP1079”是基于两个核心在各自独立的环境中运行裸机应用程序的应用笔记。本章实验所实现的功能是两个核心各自运行独立的裸机应用程序,因此XAPP1079文档是非常值得一看的,里面详细的介绍了ZYNQ芯片中两个CPU的启动顺序和方式、裸机VITIS环境中的配置等,该文档位于资料盘(A盘)\8_MPSOC&FPGA参考资料\Xilinx\Application Note\XAPP1079文件夹下,我们可以用来参考。
17.2实验任务
本章的实验任务是通过MPSOC开发板完成双核AMP的实验。CPU0接收串口的数据,并写入OCM中,然后利用软件产生中断触发CPU1;CPU1接收到中断后,根据从OCM中读出的数据控制呼吸灯的频率,并在控制结束后触发CPU0中断,实现了双核CPU通信的功能。
17.3硬件设计
由实验任务可知,我们只使用四个处理器中的两个,可以画出本次实验的系统框图,如下图所示:
图 17.3.1 系统框图
本次试验的两个CPU都会使用到串口,并且CPU0会对OCM进行写操作,CPU1对OCM进行读操作,这就要求共享外设和内存不能同时访问,以免产生冲突。我们可以利用软件产生中断(SGI)的方式来规避冲突。在初始状态下,CPU0先使用串口,在接收到用户数据后,将数据写入OCM中,并产生中断来触发CPU1中断,此时CPU0不在访问串口和OCM;CPU1触发中断后,开始访问串口和OCM,先从OCM中读出数据,通过串口来输出信息,并产生中断触发CPU0中断,之后不再访问串口和OCM;CPU0接收到CPU1的中断后,此时可以重新通过串口来接收用户输入的数据,从而避免了共享外设和内存的同时访问。
首先创建一个Vivado工程。打开《Hello World实验》工程,另存为 “dual_core_amp”工程。由于本次试验需要将程序固化在SD卡和Quad SPI Flash中(注意Bank0和Bank2的IO电平需要改成LVCMOS 1.8V),所以需要添加SD卡控制器和Quad SPI Flash控制器(图 17.3.1中未画出),如下图所示:
图17.3.1 添加Flash和SD卡控制器
接下来将《Hello World实验》中关闭的PS和PL交互信号再重新打开,注意勾选Fabric Reset Enable后,还要将其数量设置为。
然后将《自定义IP核 呼吸灯实验》中IP核所在的ip_repo文件夹拷贝到本实验的工程目录下,并将ip核添加到工程的IP库中,添加方法参考自定义IP核实验。最后在Diagram窗口中将breath_led核添加到设计中,如下图所示:
图17.3.2 添加breath_led_ip核
将led引脚引出,然后点击Run Connection Automation自动连线,右键Regenerate Layout重新布局。最终MPSOC系统硬件搭建的框图如下图所示:
图17.3.3 MPSOC系统硬件框图
选择“Validate Design”验证设计,验证无误后,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。接下来在Source窗口中右键点击Design Source设计文件“design_1.bd”,然后执行“Generate Output Products”。
为led_0分配引脚,生成bitstream文件。具体步骤可参考《自定义IP核-呼吸灯实验》step5。
导出Hardware硬件平台文件到vitis文件夹,注意勾选include bitstream。
启动Vitis软件。
17.4软件设计
在硬件设计的最后,我们打开了Vitis开发环境。
首先创建CPU0的应用工程,在菜单栏中选择File->New->Application Project,新建一个Vitis应用工程。工程名命名为cpu0_uart,然后点击“Next”,如下图所示:
图 17.4.1 创建CPU0应用工程
然后添加应用平台文件,添加完成后,接下来依次点击“Next>”,直到弹出选择模板界面,选择 “Empty Application”模版,然后点击“Finish”按钮完成应用工程的创建。
由于CPU0和CPU1共享DDR内存空间,需要分别为CPU0和CPU1分配DDR内存。在分配内存空间时,没有什么特殊的要求,只需要将一部分分配给CPU0,其余部分给CPU1即可。如将前半部分内存分配给CPU0,后半部分内存分配给CPU1;或者前1/4内存分配给CPU0,剩余的3/4内存分配给CPU1。MPSOC开发板PS端板载4片DDR4芯片,每片大小为512MB。本次实验只使用一片DDR,所以为CPU0和CPU1分别分配256MB。
双击lscript.ld(位于cpu0_uart→src下),设置CPU0的访问空间,如下图所示。
图 17.4.2 设置CPU0的DDR访问空间
注意上图中红框标注的地方左侧为基地址,右侧为存储空间大小,以字节为单位,存储空间大小设置为0x10000000(十进制268’435’456),即256MByte(268435456/1024/1024)。修改完成后,按下“Ctrl”+“S”保存。另外上图中的psu_ocm_ram_0_MEM_0为OCM共享内存的基地址和存储空间大小。
然后设置CPU0的板级支持包,在设置页面勾选xilffs即FATFS库,详细步骤参考《SD卡读写TXT文本实验》中软件设计部分。
在创建CPU1应用工程之前,需要新建基于CPU1的板级支持包(BSP)。
双击platform.spr,打开design_1_wrapper设置界面,如下图所示:
图17.4.3 添加CPU1板级支持包
在弹出的页面中,Name一栏输入新建的CPU1板级支持包名称“standalone_on_cortexa53_1”,处理器选择(Processor)选择psu_cortexa53_1,其它选项保持默认,然后点击“OK”如下图所示:
图17.4.4 设置CPU1的板级支持包
CPU1板级支持包创建完成后,同样对其设置,勾选xilffs,使其支持FATFS文件系统。
接下来编写CPU0的用户代码。在cpu0_uart/src目录上右键,选择New->File,在弹出的对话框File name一栏中我们输入文件名“cpu0_uart.c”,然后点击“Finish”。
我们在新建的cpu0_uart.c文件中输入本次实验的代码。代码的主体部分如下所示:
1#include "xparameters.h"
2#include "xscugic.h"
3#include "xil_printf.h"
4#include "xil_exception.h"
5#include "xil_mmu.h"
6#include "stdio.h"
7
8//宏定义
9#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断ID
10 #define SHARE_BASE 0xfffc0000 //共享OCM首地址
11 #define CPU1_COPY_ADDR 0xfffffff0 //存放CPU1应用起始地址的地址
12 #define CPU1_START_ADDR 0x10000000 //CPU1应用起始地址
13
14 #define CPU1_ID XSCUGIC_SPI_CPU1_MASK //CPU1 ID
15 #define SOFT_INTR_ID_TO_CPU0 0 //软件中断号 0 ,范围:0~15
16 #define SOFT_INTR_ID_TO_CPU1 1 //软件中断号 1 ,范围:0~15
17
18 //"SEV"指令唤醒CPU1并跳转至相应的程序
19 #define sev() __asm__("sev") //C语言内嵌汇编写法 send event指令
20
21 //函数声明
22 void cpu0_intr_init(XScuGic *intc_ptr);
23 void soft_intr_handler(void *CallbackRef);
24
25 //全局变量
26 XScuGic Intc; //中断控制器驱动程序实例
27 int rec_freq_flag = 0; //接收到呼吸灯频率设置的标志
28 int freq_gear; //频率档位
29
30 //CPU0 main函数
31 int main()
32 {
33//CPU0中断初始化
34cpu0_intr_init(&Intc);
35while(1){
36 if(rec_freq_flag == 0){
37 xil_printf("This is CPU0,Please input the numbers 1~5 to change "
38 "breath led frequency\r\n");
39 scanf("%d",&freq_gear);
40 if(freq_gear >= 1 && freq_gear <=5){
41 xil_printf("You input number is %d\r\n",freq_gear);
42 //向共享的地址中写入输入的数据
43 Xil_Out8(SHARE_BASE,freq_gear);
44 //给CPU1触发中断
45 XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_TO_CPU1,CPU1_ID);
46 rec_freq_flag = 1;
47 }
48 else{
49 xil_printf("Error,The number range is 1~5\r\n");
50 xil_printf("\r\n");
51 }
52 }
53}
54return 0 ;
55 }
程序的第10行定义的参数SHARE_BASE表示OCM共享内存的开始地址,这个地址在lscript.ld可以查找到,即psu_ocm_ram0_MEM0的地址(0xFFFC_0000~0xFFFF_FFFF),OCM存储空间大小为256KB。程序中第12行定义的参数CPU1_START_ADDR表示CPU1应用程序的起始地址。
在主函数中,通过cpu0_intr_init()函数对CPU0的中断进行初始化,以响应CPU1的中断。
然后是一个while(1)的无限循环。首先判断rec_freq_flag(接收到呼吸灯频率设置的标志)的值是否为0,如果等于0,此时打印字符来提示用户输入数据,输入值的范围为1~5,输入的值越大,呼吸灯的呼吸频率就越高。当用户输入正确的值之后,打印用户输入的值,并通过Xil_Out8()函数将数值写入OCM存储器中。然后通过XScuGic_SoftwareIntr()函数向CPU1触发中断。其中函数第二参数为软件中断号,第三个参数为需要中断的CPU1 ID。XSCUGIC_SPI_CPU0_MASK表示CPU0的中断ID,XSCUGIC_SPI_CPU1_MASK表示CPU1的中断ID,该参数在xscugic_hw.h中有定义。
触发CPU1中断后,将rec_freq_flag(接收到呼吸灯频率设置的标志)设置为1,while循环停止接收用户输入的数据。
如果用户输入的值不是1~5之间的数字,则返回错误,用户重新输入数字。
我们在程序中,除了main()函数之外,另外还编写了两个函数: cpu0_intr_init()和soft_intr_handler()。CPU0中断初始化和中断处理函数的代码如下:
57 //CPU0中断初始化
58 void cpu0_intr_init(XScuGic *intc_ptr)
59 {
60//初始化中断控制器
61XScuGic_Config *intc_cfg_ptr;
62intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID);
63 XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr,
64 intc_cfg_ptr->CpuBaseAddress);
65 //设置并打开中断异常处理功能
66 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
67 (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr);
68 Xil_ExceptionEnable();
69
70 XScuGic_Connect(intc_ptr, SOFT_INTR_ID_TO_CPU0,
71 (Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr);
72
73 XScuGic_Enable(intc_ptr, SOFT_INTR_ID_TO_CPU0); //CPU0软件中断
74 }
75
76 //软件中断函数
77 void soft_intr_handler(void *CallbackRef)
78 {
79xil_printf("This is CPU0,Soft Interrupt from CPU1\r\n");
80xil_printf("\r\n");
81rec_freq_flag = 0;
82 }
cpu0_intr_init()函数用户对CPU0的中断进行初始化。程序将中断函数设置为soft_intr_handler(),软件中断号的范围是0~15之间,这里设置为0。
而soft_intr_handler()函数为CPU1触发CPU0中断的软件中断函数,当CPU0进入中断后,打印字符并将rec_freq_flag设置为0,以重新接收用户输入的呼吸灯频率。
程序设计完成后,按快捷键Ctrl+S保存main.c文件,右键点击应用工程名cpu0_uart选择Build Project对工程进行编译。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。
接下来创建CPU1的应用工程。在菜单栏中选择File->New->Application Project,新建一个Vitis应用工程。工程名命名为cpu1_led,然后点击“Next”,平台选择已经添加的“design_1_wrapper”,点击Next,如下图所示:
图17.4.5 创建CPU1应用工程
在弹出的Domain页面中,可以看到已经默认选择了CPU1,这里我们使用默认设置,然后点击“Next”,如下图所示:
图17.4.6 选择CPU1
最后选择“Empty Application”,点击“Finish”按钮完成Vitis应用工程的创建。
接下来双击lscript.ld(位于cpu1_led→src下),设置CPU1的访问空间。由于CPU0使用了地址从0x0到0xff_fffff,也就是256MB的DDR存储空间,所以CPU1使用的DDR地址从0x100_00000开始,大小也设置成256MB,如下图所示。
图17.4.7 设置CPU1的DDR访问空间
修改完成后,按下“Ctrl”+“S”保存。需要注意的是,两个CPU设置的DDR地址不能重合,否则程序在运行时会出现异常。
接下来编写CPU1的用户代码。在cpu1_led/src目录上右键,选择New-> File,在弹出的对话框File name一栏中我们输入文件名“cpu1_led.c”,然后点击“Finish”。
我们在新建的cpu1_led.c文件中输入本次实验的代码。代码的主体部分如下所示:
1#include "xparameters.h"
2#include "xscugic.h"
3#include "xil_printf.h"
4#include "xil_exception.h"
5#include "xil_mmu.h"
6#include "stdio.h"
7#include "breath_led_ip.h"
8
9//宏定义
10 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断ID
11 #define SHARE_BASE 0xfffc0000 //共享OCM首地址
12
13 #define CPU0_ID XSCUGIC_SPI_CPU0_MASK //CPU0 ID
14 #define SOFT_INTR_ID_TO_CPU0 0 //软件中断号 0 ,范围:0~15
15 #define SOFT_INTR_ID_TO_CPU1 1 //软件中断号 1 ,范围:0~15
16
17 #defineLED_IP_BASEADDR XPAR_BREATH_LED_IP_0_S0_AXI_BASEADDR //LED IP基地址
18 #defineLED_IP_REG0 BREATH_LED_IP_S0_AXI_SLV_REG0_OFFSET //LED IP寄存器地址0
19 #defineLED_IP_REG1 BREATH_LED_IP_S0_AXI_SLV_REG1_OFFSET //LED IP寄存器地址1
20
21 //函数声明
22 void cpu1_intr_init(XScuGic *intc_ptr);
23 void soft_intr_handler(void *CallbackRef);
24
25 //全局变量
26 XScuGic Intc; //中断控制器驱动程序实例
27 int soft_intr_flag = 0; //软件中断的标志
28 int freq_gear; //频率档位
29
30 //CPU1 main函数
31 int main()
32 {
33int freq_step = 0;
34
35//CPU1中断初始化
36cpu1_intr_init(&Intc);
37//打开呼吸灯
38BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, LED_IP_REG0, 1);
39while(1){
40 if(soft_intr_flag){
41 freq_gear = Xil_In8(SHARE_BASE); //从共享OCM中读出数据
42 xil_printf("CUP1 Received data is %d\r\n",freq_gear) ;
43 switch(freq_gear){
44 case 1 : freq_step = 20;break;
45 case 2 : freq_step = 50;break;
46 case 3 : freq_step = 100;break;
47 case 4 : freq_step = 200;break;
48 case 5 : freq_step = 500;break;
49 default : freq_step = 50;break;
50 }
51 //设置呼吸灯频率,最高位为1,设置有效
52 BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG1,(0x80000000|freq_step));
53 //给给CPU0触发中断
54 XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_TO_CPU0,CPU0_ID);
55 soft_intr_flag = 0;
56 }
57}
58return 0 ;
59 }
在主函数中,同样先通过cpu1_intr_init()函数用户对CPU1的中断进行初始化。中断初始化完成后,通过LED_IP_mWriteReg()函数打开呼吸灯,此时呼吸灯的频率是以默认的频率进行渐变的。
接下来同样是一个无限循环while(1),实现了根据从OCM读取到的值,来控制呼吸灯的频率。当接收到CPU0的软件中断后(soft_intr_flag的值为1),从OCM读取数据的函数为Xil_In8(),根据读取到的值,通过LED_IP_mWriteReg()函数设置输出呼吸灯的频率。这里将呼吸灯的频率共分为5档,用户输入的值越大,呼吸灯的频率步长越大,呼吸灯的呼吸频率就越大。
最后通过XScuGic_SoftwareIntr()函数给CPU0触发中断,并设置soft_intr_flag(软件中断的标志)为0。
我们在程序中,除了main()函数之外,另外还编写了两个函数:cpu1_intr_init()和soft_intr_handler()。代码如下所示:
61 //CPU1中断初始化
62 void cpu1_intr_init(XScuGic *intc_ptr)
63 {
64//初始化中断控制器
65XScuGic_Config *intc_cfg_ptr;
66intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID);
67 XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr,
68 intc_cfg_ptr->CpuBaseAddress);
69 //设置并打开中断异常处理功能
70 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
71 (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr);
72 Xil_ExceptionEnable();
73
74 XScuGic_Connect(intc_ptr, SOFT_INTR_ID_TO_CPU1,
75 (Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr);
76
77 XScuGic_Enable(intc_ptr, SOFT_INTR_ID_TO_CPU1); //CPU1软件中断
78 }
79
80 //软件中断函数
81 void soft_intr_handler(void *CallbackRef)
82 {
83xil_printf("This is CUP1,Soft Interrupt from CPU0\r\n") ;
84soft_intr_flag = 1;
85 }
cpu1_intr_init()函数对CPU1的中断进行初始化。程序将中断函数设置为soft_intr_handler(),软件中断号的范围是0~15之间,这里设置为0。
soft_intr_handler()函数为CPU0触发CPU1中断的软件中断函数,当CPU1进入中断后,打印字符并将soft_intr_flag的值设置为1,从而在main()函数中设置呼吸灯的频率。
程序设计完成后,按快捷键Ctrl+S保存main.c文件,右键点击应用工程名cpu1_led选择Build Project对工程进行编译。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。
17.5下载验证
首先我们将下载器与MPSOC开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用USB连接线将USB UART接口与电脑连接,用于串口通信。最后连接开发板的电源,给开发板上电。
在Vitis软件的下方的Vitis Serial Terminal窗口中点击右上角的加号连接串口。如果没有该窗口,可以在菜单栏中依次点击Windows->Show view,然后在弹出的窗口中搜索“terminal”,选择第三个Vitis Serial Terminal,然后点击Open打开。
右击应用程序cpu0_uart,选择Run As→Run Configurations,在弹出的界面中双击Single Application Debug选项,该选项下面便出现一个名为“Debugger_cpu0_uart-Default”的子选项,同时界面中右侧可以对下载选项进行设置。
在Application标签页中勾选cpu1_led,如下图所示:
图 17.5.1 “Application”设置
将标签页切换至Target Setup,可以看到“Reset entire system”和“Program FPGA”两个选项已经默认勾选,如下图所示:
图 17.5.2 “Target Setup”设置
设置完成后,点击“Run”按钮开始运行。此时MPSOC开发板上的PL_LED1灯开始呈现呼吸灯的效果,如图 17.5.3所示:
图 17.5.3 呼吸灯实验现象
与此同时,在Vitis Serial Terminal窗口中可以看到CPU0打印的字符。此时我们可以在Vitis Serial Terminal窗口中输入“5”,然后点击“Send”发送,来改变LED灯的呼吸频率,如下图所示:
图 17.5.4 设置呼吸灯的频率
从Vitis Serial Terminal窗口中可以看到CPU1接收到了CPU0的数据,数值为5,如下图所示:
图 17.5.5 Vitis Serial Terminal窗口打印实验结果
与此同时,观察MPSOC开发板上的PL_LED1灯,其呼吸频率变快,说明双核AMP实验在MPSOC开发板上验证成功。
注意:如果Vitis Serial Terminal窗口打印信息错误或者未打印信息,请检查cpu0_uart和cpu1_led应用程序的DDR基地址和存储空间大小的设置是否正确。
在实验的最后,我们再来向大家介绍双核AMP实验的程序固化方法。右键点击应用工程名cpu0_uart,选择“Create Boot Image”,如下图所示:
图17.5.6 创建启动镜像
在弹出的创建镜像设置界面中,下面的“Boot image partitions”一栏中有三个文件,分别是fsbl.elf、design_1_wrapper.bit、cpu0_uart.elf,如下图所示:
图17.5.7 启动镜像设置界面
这时创建启动镜像还缺少cpu1_led.elf文件,需要我们手动添加。点击上图中的Add添加,在弹出窗口中File path一栏指定cpu1_led.elf的路径,并在Destination CPU一栏中选择“A53 1”,然后点击“OK”,如下图所示:
图17.5.8 添加elf文件
回到图17.5.7所示界面中,可以发现cpu0_uart.elf文件下面多了一个cpu1_led.elf文件。注意这四个文件的顺序。
然后点击“Create Image”创建镜像文件。在弹出的是否覆盖BIF文件提示窗口中,点击“OK”确认。
最终生成的boot.bin文件如下图所示。其路径为/cpu0_uart/_idle/bootimage。
图17.5.9 BOOT.bin文件
页:
[1]