发 帖  
原厂入驻New
[资料] 【正点原子FPGA连载】第十三章双核AMP实验-领航者 ZYNQ 之嵌入式开发指南
2020-9-4 11:07:03  44 正点原子FPGA
分享
1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/FPGA/zdyz_linhanz.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900
5)关注正点原子公众号,获取最新资料


第十三章双核AMP实验
在前面的例程中,实现的都是基于单核CPU的实验,在很多应用场景中,往往只需要使用其中的一个CPU即可实现相应的功能。然而对于复杂的设计,如多任务处理、并行控制等,单个CPU将难以胜任。ZYNQ 7000 SOC芯片内部集成了两个独立的Cortex-A9内核处理器,即两个CPU,可以很好的应对各种复杂的设计。本章我们来学习下基于双核AMP的实验。
本章包括以下几个部分:
1313.1简介
13.2实验任务
13.3硬件设计
13.4软件设计
13.5下载验证
13.1简介
多核处理器从多核的结构上是否一致,分为两种基本架构:同构多核架构和异构多核架构。同构多核处理器是指系统中的处理器在结构上是相同的;而异构处理器是指系统中的处理器在结构上是不同的,这些处理器可以是通用处理器,也可以是解决某些特定应用的专用硬核。同构多核架构相比于异构多核架构,在硬件和软件设计上较为简单,通用性较高。但在某些特定应用场合下,如异构多核架构专用的硬件加速硬核,异构多核架构的性能会更高。
xilinx的ZYNQ SOC融合了这两种架构,ZYNQ SOC芯片包含两个独立的Cortex-A9处理器,这两个处理器核在结构上是相同的,同时又包括了可编程的逻辑单元(PL),使得ZYNQ整体系统成为了一个异构多核系统,同时具有较高的通用性和性能。
从软件的角度看,多核处理器的运行模式有AMP(非对称多处理)、SMP(对称多处理)和BMP(受约束多处理)三种运行模式。
AMP运行模式指多个内核相对独立的运行不同的任务,每个内核相互隔离,可以运行不同的操作系统(OS)或裸机应用程序。
SMP运行模式指多个处理器运行一个操作系统,这个操作系统同等的管理多个内核,如PC电脑。
BMP运行模式与SMP类似,但开发者可以指定将某个任务仅在某个指定内核上执行。
一般来说,SMP为较高级的应用提供统一的OS平台,开发者在OS之上构建应用时,无需考虑两个内核之间的资源共享和进程间通信。除此之外,对SMP而言存在性能开销,这会对实时性要求较高的应用,其性能造成较大影响。如PC机电脑的多核处理器一般运行在SMP模式,实现的功能较为复杂,但对实时性的要求不高。
而AMP的运行模式基本没有开销问题,在运行裸机应用程序时,甚至完全没有开销,比较适合实时性要求较高的应用,但需要精心定制的软件设计来实现处理器资源共享和处理器间通信。如电力控制保护设备通常需要与人机接口实现复杂的通信和高实时性的计算能力,一般采用AMP运行模式,一个处理器运行Linux操作系统,另一个处理器运行裸机应用程序,从而兼顾了电力系统控制设备需要的复杂功能和实时性。
AMP和SMP运行模式的框图如图 13.1.1所示。



图 13.1.1 AMP与SMP运行模式的框图

AMP运行模式给开发者提供了一个与传统单核CPU系统相类似的运行环境,使得开发者已有的经验和知识可以继续加以利用;同时,也为程序的移植提供了很大的便利性。本次试验采用的是双核AMP的运行模式,两个CPU分别运行不同的裸机应用程序。
ZYNQ SOC提供的两个Cortex-A9处理器,都具有各自私有的资源,同时也有一些共享的资源。私有资源有L1指令缓存、L1数据缓存、私有定时器等。共享的资源有L2 Cache、DDR存储器、外设和OCM(On Chip Memory)等。在AMP运行模式下,这两个处理器彼此隔离、分别运行。但在访问共享资源或者外设时,要注意避免冲突,否则程序在运行时可能会出现问题。
我们可以通过类似互斥的方式来实现裸机情况下简单的双核使用,在ZYNQ系统中,可以利用软件产生中断(SGI,Software Generated Interrupts)来避免访问的冲突。ZYNQ系统中的每个CPU都能用SGI来中断自己、另一个CPU或同时中断两个CPU。向软件产生的中断寄存器(ICDSGIR)写入中断编号并指向目标CPU,就产生了一个SGI。每个CPU各自有一组SGI寄存器,可以产生16个软件产生中断中的一个或多个。图 13.1.2列出了SGI的中断ID号,范围是0~15。



图 13.1.2 软件中断

SGI可以触发CPU产生中断,但是如果想要传递数据的话,需要利用共享的内存实现数据的交互。共享内存指CPU0和CPU1在内存中约定一块地址及长度已知的内存区域,两个CPU通过读写共享内存中的数据来实现互相通信,而利用CPU产生的软件中断可以避免对共享内存的同时访问。
ZYNQ中的OCM共有256KB,分为4个64KB,而外置的ddr3存储器一般存储空间较大。当两个CPU需要进行大量数据交互的时候,可以使用DDR3存储器作为共享内存;而当交互的数据较少时,既可以使用OCM作为共享内存,也可以使用DDR3存储器作为共享内存。值得一提的是,当交互的数据量较少时,OCM作为共享内存有着独特的优势,与DDR内存相比,OCM提供了非常高的性能和来自两个处理器的低延迟访问。
需要注意的是,无论是OCM还是DDR3存储器作为共享内存,在访问之前,需要禁止存储空间的Cache(缓存)功能,这样CPU能够及时读到该地址内存中变化的数据,以避免两个CPU访问共享内存的一致性问题。通过Xil_SetTlbAttributes(INTPTR Addr, u32 attrib)函数禁用Cache属性,第一个参数为共享内存的基地址,第二个参数为设置内存的参数,包括是否禁用Cache等。
Xilinx官方提供了两个双核AMP的应用文档,“XAPP1078” 是基于两个Linux操作系统的双核AMP应用文档,“XAPP1079”是基于两个裸机应用程序的双核AMP应用文档,感兴趣的朋友可以参考下。
13.2实验任务
本章的实验任务是通过领航者ZYNQ开发板完成双核AMP的实验。CPU0接收串口的数据,并写入OCM中,然后利用软件产生中断触发CPU1;CPU1接收到中断后,根据从OCM中读出的数据控制呼吸灯的频率,并在控制结束后触发CPU0中断,实现了双核CPU通信的功能。
13.3硬件设计
根据实验任务我们可以画出本次实验的系统框图,如下图所示:



图 13.3.1 系统框图

本次试验的两个CPU都会使用到串口,并且CPU0会对OCM进行写操作,CPU1对OCM进行读操作,这就要求共享外设和内存不能同时访问,以免产生冲突。我们可以利用软件产生中断(SGI)的方式来规避冲突。在初始状态下,CPU0先使用串口,在接收到用户数据后,将数据写入OCM中,并产生中断来触发CPU1中断,此时CPU0不在访问串口和OCM;CPU1触发中断后,开始访问串口和OCM,先从OCM中读出数据,通过串口来输出信息,并产生中断触发CPU0中断,之后不再访问串口和OCM;CPU0接收到CPU1的中断后,此时可以重新通过串口来接收用户输入的数据,从而避免了共享外设和内存的同时访问。
首先创建一个Vivado工程,工程名为“dual_core_amp”。本次试验的硬件环境搭建和“自定义IP核-呼吸灯试验”基本相同,只不过为了将程序固化在SD卡或者Quad SPI Flash中(注意BANK1的IO电平需要改成LVCMOS 1.8V),本次试验在ZYNQ处理系统中,添加了SD卡控制器和Quad SPI Flash控制器(图 5.3.1的框图中未画出)。最终ZYNQ系统硬件搭建的框图如下图所示:



图 13.3.2 ZYNQ系统硬件框图

双核AMP的实验在硬件环境搭建上和单核CPU的使用是一致的,仅在软件设计上存在差异。
13.4软件设计
在硬件设计的最后,我们打开了SDK开发环境。
首先创建CPU0的应用工程,在菜单栏中选择File->New->Application Project,新建一个SDK应用工程。工程名命名为cpu0_uart,处理器选择ps7_cortexa9_0,如下图所示:



图 13.4.1 创建CPU0应用工程

点击“NEXT”,然后选择“Empty Application”,点击“Finish”按钮完成SDK应用工程的创建。
接下来双击lscript.ld(位于cpu0_uart→src下),设置CPU0的访问空间。由于ZYNQ-7020核心板和ZYNQ-7010核心板的DDR3存储空间不同,因此这里设置有所区别。如果大家使用的是ZYNQ-7020核心板,DDR3的存储空间为1GByte,这里将CPU0的访问空间大小设置成约为DDR3存储空间的一半,即存储空间大小为0x1FF00000(Byte),如下图所示。



图 13.4.2 ZYNQ-7020核心板设置CPU0的DDR3访问空间

如果大家使用的是ZYNQ-7010核心板,DDR3的存储空间为512MByte,这里将CPU0的访问空间大小设置成约为DDR3存储空间的一半,即存储空间大小为0xFF00000(Byte),如下图所示。



图 13.4.3 ZYNQ-7010核心板设置CPU0的DDR3访问空间

注意图中红框标注的地方左侧为基地址,右侧为存储空间大小,修改完成后,按下“Ctrl”+“S”保存。另外图 13.4.2和图 13.4.3中的ps7_ram_0和ps_ram_1为OCM共享内存的基地址。
接下来创建CPU1的应用工程。在菜单栏中选择File->New->Application Project,新建一个SDK应用工程。工程名命名为cpu1_LED,处理器选择ps7_cortexa9_1,如下图所示:



图 13.4.4 创建CPU1应用工程

点击“NEXT”,然后选择“Empty Application”,点击“Finish”按钮完成SDK应用工程的创建。
接下来双击lscript.ld(位于cpu1_led→src下),设置CPU1的访问空间。由于ZYNQ-7020核心板和ZYNQ-7010核心板的DDR3存储空间不同,因此这里设置同样有所区别。如果大家使用的是ZYNQ-7020核心板,这里将地址设置为DDR3存储器的另一半寻址空间,即存储空间大小为0x1FFFFFFF(Byte),如图 13.4.5所示,即基地址为0x02000000,存储空间大小为0x1FFFFFFF。



图 13.4.5 ZYNQ-7020核心板设置CPU1的DDR3访问空间

如果大家使用的是ZYNQ-7010核心板,这里同样将地址设置为DDR3存储器的另一半寻址空间,即存储空间大小为0xFFFFFFF(Byte),如图 13.4.6所示,即基地址为0x01000000,存储空间大小为0xFFFFFFF。



图 13.4.6 ZYNQ-7010核心板设置CPU1的DDR3访问空间

修改完成后,按下“Ctrl”+“S”保存。需要注意的是,两个CPU设置的DDR3的地址不能重合,否则程序在运行时会出现异常。
接下来还需要对CPU1的板级支持包做额外的设置,才能使双核同时工作。右击cpu1_led_bsp,选择Board Support Package Settings。



图 13.4.7 设置CPU1板级支持包

在弹出的界面中,点击ps7_cortexa9_1,在extra_compiler_flags一栏的Value下,补充“-DUSE_AMP=1”,设置完成后点击“OK”按钮,如下图所示:



图 13.4.8 设置双核启动

接下来编写CPU0的用户代码。在cpu0_uart/src目录上右键,选择New->Source File,在弹出的对话框中Source file一栏我们输入文件名“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 0xffff0000 //共享OCM首地址
  • 11 #define CPU1_START_ADDR 0xfffffff0 //CPU1开始地址
  • 12 #define CPU1_START_MEM 0x10000000 //CPU1程序开始地址
  • 13
  • 14 #define SOFT_INTR_ID_TO_CPU0 0 //软件中断号 0 ,范围:0~15
  • 15 #define SOFT_INTR_ID_TO_CPU1 1 //软件中断号 1 ,范围:0~15
  • 16 #define CPU1_ID 2 //CPU1 ID,0bxxxxxx1x指向CPU1
  • 17 //"SEV"指令唤醒CPU1并跳转至相应的程序
  • 18 #define sev() __asm__("sev")
  • 19
  • 20 //函数声明
  • 21 void start_cpu1();
  • 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 //S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
  • 34 Xil_SetTlbAttributes(SHARE_BASE,0x14de2); //禁用OCM的Cache属性
  • 35
  • 36 //启动CPU1
  • 37 start_cpu1();
  • 38 //CPU0中断初始化
  • 39 cpu0_intr_init(&Intc);
  • 40 while(1){
  • 41 if(rec_freq_flag == 0){
  • 42 xil_printf("This is CPU0,Please input the numbers 1~5 to change "
  • 43 "breath led frequency\r\n");
  • 44 scanf("%d",&freq_gear);
  • 45 if(freq_gear >= 1 && freq_gear <=5){
  • 46 xil_printf("You input number is %d\r\n",freq_gear);
  • 47 //向共享的地址中写入输入的数据
  • 48 Xil_Out8(SHARE_BASE,freq_gear);
  • 49 //给CPU1触发中断
  • 50 XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_TO_CPU1,CPU1_ID);
  • 51 rec_freq_flag = 1;
  • 52 }
  • 53 else{
  • 54 xil_printf("Error,The number range is 1~5\r\n");
  • 55 xil_printf("\r\n");
  • 56 }
  • 57 }
  • 58 }
  • 59 return 0 ;
  • 60 }

在主函数中,首先通过Xil_SetTlbAttributes()函数禁用Cache缓存,以维护两个CPU访问OCM一致性的问题。其中输入参数SHARE_BASE为共享内存的基地址,这个基地址在lscript.ld可以查找到,即ps7_ram0的基地址或者ps_ram_1的基地址,本次实验使用ps7_ram_1作为两个CPU通信的共享内存。
然后通过start_cpu1()函数启动CPU1,需要注意的是,如果只是通过JTAG模式下载程序的话,是可以不编写这个启动函数,这个函数仅在固化程序的时候起作用。
接下来通过cpu0_intr_init()函数对CPU0的中断进行初始化,以响应CPU1的中断。
最后是一个while(1)的无限循环。首先判断rec_freq_flag(接收到呼吸灯频率设置的标志)的值是否为0,如果等于0,此时打印字符来提示用户输入数据,输入值的范围为1~5,输入的值越大,呼吸灯的呼吸频率就越高。当用户输入正确的值之后,打印用户输入的值,并通过Xil_Out8()函数将数值写入OCM存储器中。然后通过XScuGic_SoftwareIntr()函数向CPU0触发中断。其中函数第二参数为软件中断号,第三个参数为CPU1的ID。由UG585文档可以查询到,0bxxxxxxx1 指向CPU0,0bxxxxxx1x指向CPU1,程序中将CPU1的ID设置为2。
触发CPU1中断后,将rec_freq_flag(接收到呼吸灯频率设置的标志)设置为1,while循环停止接收用户输入的数据。
如果用户输入的值不是1~5之间的数字,则返回错误,用户重新输入数字。
我们在程序中,除了main()函数之外,另外还编写了三个函数:start_cpu1()、cpu0_intr_init()和soft_intr_handler()。代码如下所示:

  • 62 //启动CPU1,用于固化程序
  • 63 void start_cpu1()
  • 64 {
  • 65 //向 CPU1_START_ADDR(0Xffffffff0)地址写入 CPU1 的访问内存基地址
  • 66 Xil_Out32(CPU1_START_ADDR, CPU1_START_MEM);
  • 67 dmb(); //等待内存写入完成
  • 68 sev(); //通过"SEV"指令唤醒CPU1并跳转至相应的程序
  • 69 }
  • 70
  • 71 //CPU0中断初始化
  • 72 void cpu0_intr_init(XScuGic *intc_ptr)
  • 73 {
  • 74 //初始化中断控制器
  • 75 XScuGic_Config *intc_cfg_ptr;
  • 76 intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID);
  • 77 XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr,
  • 78 intc_cfg_ptr->CpuBaseAddress);
  • 79 //设置并打开中断异常处理功能
  • 80 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
  • 81 (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr);
  • 82 Xil_ExceptionEnable();
  • 83
  • 84 XScuGic_Connect(intc_ptr, SOFT_INTR_ID_TO_CPU0,
  • 85 (Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr);
  • 86
  • 87 XScuGic_Enable(intc_ptr, SOFT_INTR_ID_TO_CPU0); //CPU0软件中断
  • 88 }
  • 89
  • 90 //软件中断函数
  • 91 void soft_intr_handler(void *CallbackRef)
  • 92 {
  • 93 xil_printf("This is CPU0,Soft Interrupt from CPU1\r\n");
  • 94 xil_printf("\r\n");
  • 95 rec_freq_flag = 0;
  • 96 }

代码中第接下来通过start_cpu1()函数启动CPU1,启动函数的代码如代码中第62至第69行代码所示。需要注意的是,如果只是通过JTAG模式下载程序的话,可以不编写这个启动函数,这个函数仅在固化程序的时候起作用。启动CPU1的方法在UG585文档中有着详细的介绍,即通过Xil_Out32()函数向CPU1的开始地址写入CPU1的DDR3内存地址,等待内存写入完成,然后通过“SEV”指令唤醒CPU1并跳转至相应的程序,UG585文档中启动CPU1的描述如下图所示:




图 13.4.9 启动CPU1
cpu0_intr_init()函数用户对CPU0的中断进行初始化。程序将中断函数设置为soft_intr_handler(),软件中断号的范围是0~15之间,这里设置为0。
而soft_intr_handler()函数为CPU1触发CPU0中断的软件中断函数,当CPU0进入中断后,打印字符并将rec_freq_flag设置为0,以重新接收用户输入的呼吸灯频率。
程序设计完成后,按快捷键Ctrl+S保存main.c文件,工具会自动进行编译。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。
接下来编写CPU1的用户代码。在cpu1_led/src目录上右键,选择New->Source File,在弹出的对话框中Source file一栏我们输入文件名“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 0xffff0000 //共享OCM首地址
  • 12 #define SOFT_INTR_ID_TO_CPU0 0 //软件中断号 0 ,范围:0~15
  • 13 #define SOFT_INTR_ID_TO_CPU1 1 //软件中断号 1 ,范围:0~15
  • 14 #define CPU0_ID 1 //CPU1 ID,0bxxxxxxx1指向CPU0
  • 15
  • 16 #define LED_IP_BASEADDR XPAR_BREATH_LED_IP_0_S0_AXI_BASEADDR //LED IP基地址
  • 17 #define LED_IP_REG0 BREATH_LED_IP_S0_AXI_SLV_REG0_OFFSET //LED IP寄存器地址0
  • 18 #define LED_IP_REG1 BREATH_LED_IP_S0_AXI_SLV_REG1_OFFSET //LED IP寄存器地址1
  • 19
  • 20 //函数声明
  • 21 void cpu1_intr_init(XScuGic *intc_ptr);
  • 22 void soft_intr_handler(void *CallbackRef);
  • 23
  • 24 //全局变量
  • 25 XScuGic Intc; //中断控制器驱动程序实例
  • 26 int soft_intr_flag = 0; //软件中断的标志
  • 27 int freq_gear; //频率档位
  • 28
  • 29 //CPU1 main函数
  • 30 int main()
  • 31 {
  • 32 int freq_step = 0;
  • 33 //S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
  • 34 Xil_SetTlbAttributes(SHARE_BASE,0x14de2); //禁用OCM的Cache属性
  • 35
  • 36 //CPU1中断初始化
  • 37 cpu1_intr_init(&Intc);
  • 38 //打开呼吸灯
  • 39 BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, LED_IP_REG0, 1);
  • 40 while(1){
  • 41 if(soft_intr_flag){
  • 42 freq_gear = Xil_In8(SHARE_BASE); //从共享OCM中读出数据
  • 43 xil_printf("CUP1 Received data is %d\r\n",freq_gear) ;
  • 44 switch(freq_gear){
  • 45 case 1 : freq_step = 20;break;
  • 46 case 2 : freq_step = 50;break;
  • 47 case 3 : freq_step = 100;break;
  • 48 case 4 : freq_step = 200;break;
  • 49 case 5 : freq_step = 500;break;
  • 50 default : freq_step = 50;break;
  • 51 }
  • 52 //设置呼吸灯频率,最高位为1,设置有效
  • 53 BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG1,(0x80000000|freq_step));
  • 54 //给给CPU0触发中断
  • 55 XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_TO_CPU0,CPU0_ID);
  • 56 soft_intr_flag = 0;
  • 57 }
  • 58 }
  • 59 return 0 ;
  • 60 }

在主函数中,同样是先通过Xil_SetTlbAttributes()函数禁用Cache缓存,以维护两个CPU访问OCM一致性的问题。然后通过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()。代码如下所示:

  • 62 //CPU1中断初始化
  • 63 void cpu1_intr_init(XScuGic *intc_ptr)
  • 64 {
  • 65 //初始化中断控制器
  • 66 XScuGic_Config *intc_cfg_ptr;
  • 67 intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID);
  • 68 XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr,
  • 69 intc_cfg_ptr->CpuBaseAddress);
  • 70 //设置并打开中断异常处理功能
  • 71 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
  • 72 (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr);
  • 73 Xil_ExceptionEnable();
  • 74
  • 75 XScuGic_Connect(intc_ptr, SOFT_INTR_ID_TO_CPU1,
  • 76 (Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr);
  • 77
  • 78 XScuGic_Enable(intc_ptr, SOFT_INTR_ID_TO_CPU1); //CPU1软件中断
  • 79 }
  • 80
  • 81 //软件中断函数
  • 82 void soft_intr_handler(void *CallbackRef)
  • 83 {
  • 84 xil_printf("This is CUP1,Soft Interrupt from CPU0\r\n") ;
  • 85 soft_intr_flag = 1;
  • 86 }

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文件,工具会自动进行编译。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。
13.5下载验证
首先我们将下载器与领航者底板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用Mini USB连接线将开发板左侧的USB_UART接口与电脑连接,用于串口通信。最后连接开发板的电源,并打开电源开关
在SDK软件下方的SDK Terminal窗口中点击右上角的加号设置并连接串口。
右击应用程序cpu0_uart(或者cpu1_led),选择Run As→Run Configurations,在弹出的界面中双击Xilinx C/C++ application(System debug),即对下载的选项进行设置。
在Target Setup选项页中选中“Reset entire system”,此时“Program FPGA”也会自动选中,如下图所示:



图 13.5.1 “Target Setup”设置

将选项也切换至“Application”,把“ps_cortexa9_1”也选中,如下图所示:



图 13.5.2 “Application”设置

设置完成后,点击“Run”按钮开始运行。此时ZYNQ核心板上的LED1(PL LED)灯开始呈现呼吸灯的效果,如图 13.5.3所示:



图 13.5.3 开发板实验现象

与此同时,在SDK Terminal窗口中可以看到CPU1打印的字符。此时我们可以在SDK Terminal窗口中输入“5”,来改变LED灯的呼吸频率,如下图所示:



图 13.5.4 设置呼吸灯的频率

从SDK Terminal窗口中可以看到CPU1接收到了CPU0的数据,数值为5,如下图所示:



图 13.5.5 SDK Terminal窗口打印实验结果

与此同时,观察ZYNQ核心板上的LED1灯,其呼吸频率变快,说明双核AMP实验在ZYNQ FPGA开发板上验证成功。
注意:如果SDK Terminal窗口打印信息错误或者未打印信息,请检查cpu0_uart和cpu1_led应用程序的DDR3基地址和存储空间大小的设置是否正确。
在实验的最后,我们再来向大家介绍双核AMP实验的程序固化方法。由于本次实验会有两个应用程序的板级支持包,在建立FSBL的时候,大家可以选择创建一个新的BSP或者选择已经存在的cpu0_uart_bsp(不可以选择cpu1_led_bsp,CPU1的程序是由CPU0唤醒的)。其它的操作和单核CPU的实验基本一致,只是在Creat Boot Image时,在最后添加CPU1的elf即可,如下图所示:



图 13.5.6 创建Boot Image

需要注意的是,文件添加的顺序不能弄错。文件从上到下的顺序分别是FSBL.elf、sys_wrapper.bit、cpu0_uart.elf和cpu1_led.elf。

0
分享淘帖 显示全部楼层

评论

高级模式
您需要登录后才可以回帖 登录 | 注册

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
发资料
关闭

站长推荐 上一条 /7 下一条

快速回复 返回顶部 返回列表