S03_CH13_ZYNQ A9 TCP UART双核AMP例程

S03_CH13_ZYNQ A9 TCP UART双核AMP例程

13.1概述

ZYNQ中存在两个独立的ARM核,在很多应用场景中往往只需使用其中的1个核心即可。然而,对于复杂的设计,例如多任务,并行控制、处理等,单个核心将难以胜任。因此,为了尽可能发挥ZYNQ中双ARM核的优势和性能,进行双核应用的开发显得尤为重要。同时,也进一步为Xilinx下一代MPSOC多核异构处理器的使用打下基础。

在ZYNQ中实现双ARM核AMP应用可以参考Xilinx官方的XAPP1078和XAPP1079。在SDK中也有用于双核应用开发的openamp库可以使用。

本例程未使用openamp库,通过自行设计的代码实现了一个简单的双核应用,旨在说明:

l 双核通过软件中断进行核间通信的原理及方法。

l 双核通过共享内存进行数据交互的基本原理和设计方法。

l 双核协同工作的基本模式。

l 双核BOOT的方法。

本例程基于vivado 2015.4开发。

13.2基本原理

本例程在ARM核的CORE0建立一个TCP Server,CORE0通过TCP Server从外部TCP Client接收数据。当CORE0接收到1个TCP包后,将数据复制到DDR3中的缓存区域内,然后将数据信息(长度、首地址)放入CORE0和CORE1在DDR3的共享内存中。

接着,CORE0通过触发软件中断通知CORE1数据信息已存入共享内存中,CORE1接到中断后从共享内存读出数据信息,并将对应长度的数据复制到缓存区中,然后通过UART将数据输出。当CORE1通过串口完成数据输出后,同样通过触发软件中断通知CORE0数据发送已完成,CORE0接到中断后通过串口打印完成信息,然后开始接收下一个TCP包,重复上述过程。

最后,通过FSBL实现双核的QSPI BOOT。

13.2.1软件中断

在UG585的Interrupts 部分7.2.1章节可以找到关于软件中断(SGI)的说明。简而言之,软件中断就是CPU自己产生的中断,可用于触发自身和其他CPU。ZYNQ中共有16个软件中断可以使用,对应的中断号为0~15,如下图所示。通过软件中断可以实现CPU之间的相互通信。本例程使用了编号1和2的软件中断。

wpsE551.tmp

13.2.2共享内存通信

所谓共享内存就是,CORE0和CORE1在DDR3内存中约定一块地址及长度已知的内存区域。然后,两者之间便可通过这片区域进行数据的传递。

两个核心各自拥有独立的L1 DCache,并且共享同一个L2 DCache,在ZYNQ中存在一个Snoop Control Unit (SCU)用于维护CORE0和CORE1的L1 DCache与L2 DCache之间的一致性,无需用户干预。因此,虽然CORE0和CORE1的共享内存区域位于DDR中,两者之间的数据传递并不需要考虑DCache一致性的维护。但是,为了更好阐明多级存储器结构的特性以及DCache一致性维护的问题,本例程在两核间通过DDR3共享内存进行数据交互时加入了DCache一致性操作,最终达到的效果与不使用DCache一致性操作时相同。

DCache一致性维护的原理为:在多级存储器结构中,CPU通过1级或多级Cache与DDR产生连接,CPU本身不直接访问DDR,而是通过Cache访问DDR。Cache中始终会暂存一小部分(通常是KB~几MB量级)CPU最近访问的DDR某些地址区域中的数据。因此,在应用程序中对DDR进行读或写操作,实际上都是CPU对Cache进行读或写操作。当DDR中某个地址范围内的数据突然被除CPU以外的Master(如DMA)改变时,若此时Cache中保存了这些区域的数据,且这些数据在Cache中状态为有效时,当CPU需要再次读取DDR这片区域的数据时,就不会让Cache去读取DDR中此区域内最新的数据来更新Cache,再从Cache里读取最新的数据,而是直接从Cache中读取原来的旧数据,显然这不是我们所期望的结果。这时,便引入了所谓的Cache一致性问题。

ZYNQ中存在ICache和DCache,ICache用于缓存可执行程序,DCache用于缓存数据。一般情况下,用于保存可执行程序的DDR地址范围不会被除CPU以外的对象访问。因此,一般不存在ICache的一致性问题。而DCache在很多应用中却经常会被除CPU以外的对象访问,所以存在一致性问题。

ZYNQ中维护DCache一致性的方法为:写入方将数据写入DDR对应地址区域后,需将残留在DCache中相应地址范围内的数据全部刷入DDR3中。读取方在从DDR相应地址读取数据之前,需将DCache中DDR相应地址范围内的数据全部设置为invalid,然后CPU会再次通过DCache从DDR3中读取该地址范围内最新的数据。

13.2.3双核BOOT

裸机双核BOOT的方法参考自Xilinx的XAPP1079。首先,通过FSBL实现CORE0的BOOT。当CORE0启动进入main函数后,配合FSBL再实现CORE1的启动。

具体原理参考XAPP1079,此处不作赘述。

13.3驱动程序

CORE0工程的驱动程序文件位于c_driver文件夹中的core0文件夹中,CORE1工程的驱动程序文件位于c_driver文件夹中的core1文件夹中。需要说明的是,core0和core1的工程在DDR所占用的地址区域进行如下划分:

l CORE0: 0x00100000~0x01FFFFFF,见下图core0的lscript.ld文件设置。

wpsE561.tmp

l CORE1: 0x02000000~0x02FFFFFF,见下图core1的lscript.ld文件设置。

wpsE562.tmp

设计双核应用,两个工程的内存分配是一个关键的前提,程序所占用的DDR空间不能发生重合,应完全分隔开。

13.3.3 CORE0工程
13.3.3.1 main函数

main函数的完成的功能如下:

l 配置Timer及其中断

l 初始化中断控制器

l 初始化软件中断

l 初始化LWIP协议栈

l 建立TCP Server,启动Timer

l 配合FSBL启动CORE1

l 持续从LWIP协议栈接收数据,若接收到CORE1触发的软件中断,则作出响应。

13.3.3.2建立TCP Server

基于LWIP库在ARM中建立一个TCP Server,IP地址为192.168.1.10,端口号为5010。

l lwip库设置

见“基于TCP的QSPI Flash bin文件网络烧写”例程。

l 程序解析

见“基于TCP的QSPI Flash bin文件网络烧写”例程。

13.3.3.3 初始化软件中断

通过software_intr.c中的Init_Software_Intr()函数初始化软件中断,对于CORE0,该函数完成如下功能。

l 初始化CORE1到CORE0的软件中断,中断号为2,设置中断优先级和触发方式。

l 绑定该中断的中断服务函数为Cpu0_Intr_Hanedler。

l 将该中断映射至CORE0,并使能。

13.3.3.4 启动CORE1

通过main.c中的Start_cpu1()函数配合FSBL完成CORE1的启动。Start_cpu1()原理如下:

l 将FSBL工程位于OCM中的.stack区域和vector table区域的cache禁用。

l 将CORE1工程代码的位于DDR中的起始地址0x02000000(见core1的lscript.ld)写入FSBL的vector table中定义的cpu1_catch地址内。CORE1工程代码的起始地址由如下宏所定义。若core1的lscript.ld中代码位于DDR的起始地址发生更改,则该宏定义也必须进行相应更改。

#define APP_CPU1_ADDR 0x02000000

l 复位CORE1及其时钟

l 使能CORE1及其时钟

13.3.3.5 数据写入共享内存

CORE0通过TCP协议接收外部TCP Client发送的数据包,并将数据信息写入CORE0和CORE1共享内存中。共享内存首地址定义为:

#define SHARED_BASE_ADDR0x08000000

为共享内存区域在shared_mem.h中定义结构体shared region,其中包含了两核间所需交互的数据长度及数据指针。

typedefstruct

{

u32data_length;

u8* dataload;

}shared_region;

CORE0通过shared_mem.c中的put_data_to_region()函数将所需传递的数据长度及指针存入shared region结构体中。在put_data_to_region函数中调用Xil_DCacheFlushRange函数将DCache中该数据指针所指向内存区域的数据刷入DDR中,进行DCache一致性维护。

CORE0将此结构体放置于共享内存首地址SHARED_BASE_ADDR,CORE1便可以从该地址读取CORE0所需传递的数据信息,从而进一步获取数据。

13.3.3.6 触发软件中断

CORE0通过调用shared_mem.c中的Gen_Software_Intr函数触发CORE0到CORE1的软件中断,中断号为1,中断目标CPU设置为CORE1。

13.3.3.7响应软件中断

当CORE1向CORE0触发中断号为2的软件中断时,CORE0调用Cpu0_Intr_Hanedler函数响应此中断。然后,CORE0通过串口打印相应信息。

13.4 CORE1工程

13.4.1 main函数

main函数的完成的功能如下:

l 配置Timer及其中断

l 初始化中断控制器

l 初始化软件中断

l 等待CORE0触发的软件中断,将CORE0通过共享内存传递的数据由串口输出

l 串口数据输出完成后触发CORE1到CORE0的软件中断

13.4.2初始化软件中断

通过software_intr.c中的Init_Software_Intr()函数初始化软件中断,对于CORE1,该函数完成如下功能。

l 初始化CORE0到CORE1的软件中断,中断号为1,设置中断优先级和触发方式。

l 绑定该中断的中断服务函数为Cp1_Intr_Hanedler。

l 将该中断映射至CORE1,并使能。

13.4.3响应软件中断

当CORE0向CORE1触发中断号为1的软件中断时,CORE1调用Cpu1_Intr_Hanedler函数响应此中断。然后,CORE1开始从共享内存中读取CORE0所传递的数据。

13.4.4共享内存数据读出

CORE1从共享内存区域SHARED_BASE_ADD地址中获取CORE0传递的数据信息,通过shared_mem.c中的get_data_from_region()函数将CORE0传递的数据长度及指针读出,并复制到本地缓存中。

在get_data_from_region函数中,在将CORE0传递的数据复制到本地缓存区域之前,调用Xil_DCacheInvalidateRange函数将DCache中该数据指针所指向内存区域的数据设置为invalid,进行DCache一致性维护。

13.4.5触发软件中断

当CORE1将从共享内存中读取的数据通过串口输出完毕后,CORE1通过调用shared_mem.c中的Gen_Software_Intr函数触发CORE1到CORE0的软件中断,中断号为2,中断目标CPU设置为CORE0。以此通知CORE0,CORE1串口输出数据完成。

13.5工程创建及设置关键步骤

l CORE1工程创建时Processor要选择ps_cortexa7_1,不要选成ps_cortexa7_0,如下图所示。

wpsE573.tmp

l 创建FSBL工程后,需替换其bsp的standalone库。

首先,设置工程目录下sdk_repo文件夹的路径,sdk_repo文件夹中包含了需要使用的standalone库,如下图所示。用户需要根据自己所建工程的实际路径进行修改,使用原工程的路径设置将产生错误。

wpsE574.tmp

其次,更改FSBL工程的bsp中standalone的版本,将其更换为sdk_repo文件夹中的5.19版本,如下图所示。该版本standalone库来自xapp1079,只有使用该版本的standalone库才可实现双核BOOT,自带的standalone库无法实现。

wpsE575.tmp

l 设置CORE1工程的编译选项

在CORE1工程的bsp中要增加编译选项“-DUSE_AMP=1”,如下图所示。该编译选项将影响到CORE1工程代码里中断控制器SCUGIC的初始化函数以及Cache操作函数的编译,若不增加该选项,可能会出现CORE0和CORE1中断异常和Cache一致性维护异常。

wpsE585.tmp

13.6工程调试关键步骤

l system debugger里同时添加core0和core1工程文件,如下图所示。Debug时先让CORE0运行,再让CORE1运行。

wpsE586.tmp

wpsE587.tmp

l Debug时一定要注释CORE0工程中main函数里的Start_cpu1函数,否则在debug时,CORE1将无法正常工作。只有当需要生成BOOT.bin文件时,才需要使用该函数。

13.7网络调试助手操作方法

在SDK中下载程序至ZYNQ中。打开网络调试助手,选择TCP Client方式,输入ARM中定义的TCP Server的IP地址和端口号,然后点击连接按键,建立TCP连接。输入任意文字信息发送。如图所示。

wpsE588.tmp

SDK终端串口输出的信息如下图所示。

wpsE599.tmp

继续通过网络调试助手发送信息,串口输出如下图所示。

wpsE59A.tmp

wpsE59B.tmp

13.8生成BOOT.bin

切记在生成BOOT.bin文件时不要注释CORE0工程中main函数里的Start_cpu1函数,否则将无法BOOT CORE1。生成BOOT.bin文件时依次添加FSBL工程的elf,bit文件,CORE0和CORE1的elf文件,如下图所示。

wpsE5AC.tmp

13.9双核BOOT验证

将BOOT.bin文件烧入QSPI flash中,重启开发板。SDK串口终端输出信息,如下图所示。当提示“core1:application start”,代表CORE0和CORE1都已成功启动。

wpsE5AD.tmp

使用网络调试助手进行TCP连接并发送数据,串口输出如下图所示。此时,验证了双核BOOT后,CORE0和CORE1均运行正常。

wpsE5AE.tmp

原文地址:https://www.cnblogs.com/milinker/p/6484459.html