zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Web前端浏览器兼容性问题及解决方案
    JS
    vue element-ui 重置样式问题
    学习的一些文章链接
    打开新世界的第一步:学习servlet
    java学习初体验之课后习题
    下载、安装jdk8(Windows下)并配置变量环境
    下载PhpStorm并进行激活
    WCF+NHibernate 序列化
    wcf 证书+ssl+自定义用户名密码
  • 原文地址:https://www.cnblogs.com/milinker/p/6484459.html
Copyright © 2011-2022 走看看