【正点原子FPGA连载】第十七章双核AMP实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html
第十七章双核AMP实验
在前面的例程中,实现的都是基于单核CPU的实验,在很多应用场景中,往往只需要使用其中的一个CPU即可实现相应的功能。然而对于复杂的设计,如多任务处理、并行控制等,单个CPU将难以胜任。MPSOC芯片内部集成了两个(或四个)独立的Cortex-A53内核处理器,即两个(或四个)CPU,可以很好的应对各种复杂的设计。本章我们来学习下基于双核AMP的实验。
本章包括以下几个部分:
1717.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中断初始化
34 cpu0_intr_init(&Intc);
35 while(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 }
54 return 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 //初始化中断控制器
61 XScuGic_Config *intc_cfg_ptr;
62 intc_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 {
79 xil_printf("This is CPU0,Soft Interrupt from CPU1\r\n");
80 xil_printf("\r\n");
81 rec_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 #define LED_IP_BASEADDR XPAR_BREATH_LED_IP_0_S0_AXI_BASEADDR //LED IP基地址
18 #define LED_IP_REG0 BREATH_LED_IP_S0_AXI_SLV_REG0_OFFSET //LED IP寄存器地址0
19 #define LED_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 {
33 int freq_step = 0;
34
35 //CPU1中断初始化
36 cpu1_intr_init(&Intc);
37 //打开呼吸灯
38 BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, LED_IP_REG0, 1);
39 while(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 }
58 return 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 //初始化中断控制器
65 XScuGic_Config *intc_cfg_ptr;
66 intc_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 {
83 xil_printf("This is CUP1,Soft Interrupt from CPU0\r\n") ;
84 soft_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文件
相关文章:

【正点原子FPGA连载】第十七章双核AMP实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第十七章双核AMP…...

内存管理框架---页(一)
文章目录物理内存的模型非一致内存访问--NUMA一致内存访问模型--UMA内存管理架构页页框管理页描述符页描述符字段flags字段详解gfp_mask 标志获得页alloc_pages__get_free_pages获得填充为0的页释放页kmallocvmalloc参考资料你用心写的每一篇文章,可能会带别人和自己…...

华为OD机试真题Python实现【流水线】真题+解题思路+代码(20222023)
流水线 题目 一个工厂有m条流水线 来并行完成n个独立的作业 该工厂设置了一个调度系统 在安排作业时,总是优先执行处理时间最短的作业 现给定流水线个数m 需要完成的作业数n 每个作业的处理时间分别为 t1,t2...tn 请你编程计算处理完所有作业的耗时为多少 当n > m时 首先…...

「JVM 编译优化」Graal 编译器
文章目录1. 历史背景2. 构建编译调试环境3. JVMCI 编译器接口4. 代码中间表示5. 代码优化与生成1. 历史背景 Graal 编译器在 JDK 9 以 Jaotc 提前编译工具的形式首次加入到官方的 JDK 中,JDK 10 开始提供替换(得益于 HotSpot 编译器接口,Jav…...

蓝牙标签操作指南
一、APP安装指南 1.APP权限问题 电子标签APP安装之后,会提示一些权限的申请,点击允许。否则某些会影响APP的正常运行。安装后,搜索不到蓝牙标签,可以关闭App,重新打开。 2.手机功能 运行APP时候,需要打开…...

嵌入式 Linux Shell编程
目录 1、shell脚本 2、执行shell脚本 3、shell脚本编写 3.1 shell变量 3.2 标准变量或环境变量 3.4 变量赋值有五种格式 3.5 运算符和表达式 关系运算符 布尔运算符 3.6 Test命令用法 1、判断表达式 2、判断字符串 3.判断整数 4、判断文件 3.7 数组 1、数组定义…...

Web前端学习:一
编辑器的基础使用 编辑器推荐使用: HBuilderx(免费中文)(建议使用) Sublime(免费英文) Sublime中文设置方法,下载语言插件: 1、进入Sublime后,ShiftCtrlP…...

SpringBoot集成Redis实现分布式会话
在单体应用的时代,Session 会话直接保存在服务器中,实现非常简单,但是随着微服务的流行,现代应用架构基本都是分布式架构,请求随机的分配到后端的多个应用中,此时session就需要共享,而存储在red…...

2023年关于身份安全的4 个预测
如果您身处技术领域,就会知道现在是时候盘点过去的一年,展望未来 365 天将影响业务、创新以及我们工作方式的因素的季节。这不是一门精确的科学,我们也不总是对的。但是推测很有趣,当我们看到其中一些趋势成为现实时会更有趣。本文…...

Linux期末考试应急
Linux期末考试应急 虚拟机添加硬盘、分区、格式化、挂载、卸载 fdisk -l#查看系统现有分区fdisk <指定磁盘>#指定磁盘分区sudo mkfs.ext3 <指定分区>#格式化磁盘###挂载磁盘1.新建一个目录sudo mkdir /mnt/test2.将指定分区挂载到对应目录sudo mount /dev/sdb10 /…...

mars3d对geojson图层分属性设置样式
开发中可能会遇到如下需求,在全省的数据中按某个属性⾼亮展示某市区。此时就需要使⽤分属性样式的api了。⽂档如下。GeoJsonLayer - Mars3D API文档属性是根据⽮量数据的属性进⾏匹配。可以通过 layer.graphics[0]?.attr ⽅式获取。 指导有哪些属性之后先设置…...

三、锁相关知识
文章目录锁的分类可重入锁、不可重入锁乐观锁、悲观锁公平锁、非公平锁互斥锁、共享锁深入synchronized类锁、对象锁synchronized的优化synchronized实现原理synchronized的锁升级重量锁底层ObjectMonitor深入ReentrantLockReentrantLock和synchronized的区别AQS概述加锁流程源…...

C语言数据类型
C 数据类型 在 C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。 C 中的类型可分为以下几种: 1 基本类型: 它们是算术类型,…...

华为OD机试真题Python实现【水仙花数】真题+解题思路+代码(20222023)
水仙花数 题目 所谓的水仙花数是指一个n位的正整数其各位数字的n次方的和等于该数本身, 例如153 = 1^3 + 5^3 + 3^3,153是一个三位数 🔥🔥🔥🔥🔥👉👉👉👉👉👉 华为OD机试(Python)真题目录汇总 输入 第一行输入一个整数N, 表示 N 位的正整数 N 在3…...

【华为OD机试模拟题】用 C++ 实现 - 非严格递增连续数字序列(2023.Q1)
最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…...

RN面试题
RN面试题1.React Native相对于原生的ios和Android有哪些优势?1.性能媲美原生APP 2.使用JavaScript编码,只要学习这一种语言 3.绝大部分代码安卓和IOS都能共用 4.组件式开发,代码重用性很高 5.跟编写网页一般,修改代码后即可自动刷…...

【数据存储】浮点型在内存中的存储
目录 一、存储现象 二、IEEE标准规范 1.存储 2.读取 三、举例验证 1.存储 2.读取 浮点型存储的标准是IEEE(电气电子工程师学会)754制定的。 一、存储现象 浮点数由于其有小数点的特殊性,有很多浮点数是不能精确存储的,如&#…...

Servlet笔记(8):异常处理
1、错误页面配置 web.xml <!-- servlet 定义 --> <servlet><servlet-name>ErrorHandler</servlet-name><servlet-class>ErrorHandler</servlet-class> </servlet> <!-- servlet 映射 --> <servlet-mapping><servle…...

stm32f407探索者开发板(二十一)——窗口看门狗
文章目录一、窗口看门狗概述1.1 看门狗框图1.2 窗口看门狗工作过程总结1.3 超时时间1.4 为什么需要窗口看门狗1.5 其他注意事项二、常用寄存器和库函数2.1 控制寄存器WWDG_ CR2.2 配置寄存器WWDG_ CFR2.3 状态寄存器WWDG_SR三、手写窗口看门狗3.1 配置过程3.2 初始化窗口看门狗…...

C++ 模板
1. 泛型编程实现一个通用的交换函数,使用函数重载虽然可以实现,但是有以 下几个不好的地方:1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数2. 代码的可维…...

C++中的友元及运算符重载
友元 意义 程序中,有些私有属性也想让类外特殊的一些函数或者类进行访问,就要用到友元技术 关键字 friend 友元的三种实现 全局函数做友元 class Room{friend void test(Person &p);//friend class test;public:string phone_number;private:string…...

五、运行时数据区内部结构、JVM中的线程
内存是非常重要的系统资源,是硬盘和cpu的中间仓库及桥梁,承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程种内存申请、分配‘、管理的策略,保证了JVM的高效稳定运行,不同的JVM对于内存的划分方式和管理机制…...
Codeforces Round #848 (Div. 2)A-C
传送门 目录 A. Flip Flop Sum 代码: B. The Forbidden Permutation 代码: C. Flexible String 代码: A. Flip Flop Sum 题意:给你一个长度为n的数组(数组元素只为1或者-1),你要且只能进行…...

机器学习笔记之近似推断(一)从深度学习角度认识推断
机器学习笔记之近似推断——从深度学习角度认识推断引言推断——基本介绍精确推断难的原因虽然能够表示,但计算代价太大无法直接表示引言 本节是一篇关于推断总结的博客,侧重点在于深度学习模型中的推断任务。 推断——基本介绍 推断(Inference\text{…...

指针的进阶
一、字符指针 int main() {char ch w;char* pc &ch;//pc就是字符指针//const char *p "abcdef";//这里其实是把字符串"abcdef"的首地址放入了指针p中//*p w;//这是错误的无法修改值(可以看到这里绿色波浪线警告)char arr[] …...

一元二次方程方程的类
1 问题设计一个一元二次方程的类,其中包括能够反映一元二次方程的属性与操作行为,然后再设计一个测试类,检测类的使用情况。2 方法使用package语句将方程的属性即计算跟的方法封装在一个有包名的类中,包名为tom.jiafei,…...

Ask林曦|来回答,30个你关心的日常问题(二)
在林曦老师的线上书法直播课上,上课前后的聊天时间里,时常有同学向林曦老师提问,这些问题涵盖了日常生活的诸多方面,从身体的保养,到快乐的法门,皆是大家感兴趣的,也都共同关切的。 暄桐教室…...

哪款电容笔适合开学季?电容笔和Apple Pencil的区别
其实,市场上一般的电容笔和Apple Pencil的最大差别,就在于Apple Pencil与普通电容笔两者的重量和压感。然而,由于苹果电容笔价格过高,目前电容笔的市场份额逐渐转向平替电容笔,平替电容笔其性能也逐渐得到改善。下面&a…...

Qt之Qprocess
QProcess 可用于完成启动外部程序,并与之交互通信。 一、启动外部程序的两种方式 1)一体式:void QProcess::start(const QString & program,const QStringList &arguments,OpenMode mode ReadWrite) 外部程序启动后&…...

为什么不愿意专升本 学历有什么用
专升本包括两种形式普通专升本和成人专升本。普通专升本毕业是全日制学历,考试仅有一次,错过不能补考所以考生不愿意选择,成人专升本毕业是非全日制学历,学历被国家承认,和普通高校毕业证有相同的使用效力。为何考生不…...