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的软件中断。


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文件设置。

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

设计双核应用,两个工程的内存分配是一个关键的前提,程序所占用的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,如下图所示。

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

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

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

l 设置CORE1工程的编译选项

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


13.6工程调试关键步骤

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

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

13.7网络调试助手操作方法

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

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

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


13.8生成BOOT.bin

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


13.9双核BOOT验证

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

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

S03_CH13_ZYNQ A9 TCP UART双核AMP例程的更多相关文章

  1. F28379D烧写双核程序(在线&离线)

    烧写双核程序前需知在分别对F28379D的CPU1和CPU2两个核进行烧写程序时,需要在CCS中建立两个工程,独立编写两个核的程序.如controlSUITE中提供的双核程序例程: 1. 在线1.1 ...

  2. S03_CH11_基于TCP的QSPI Flash bin文件网络烧写

    S03_CH11_基于TCP的QSPI Flash bin文件网络烧写 11.1概述 针对ZYNQ中使用QSPI BOOT的应用,将BOOT.bin文件烧写至QSPI Flash基本都是通过USB C ...

  3. tcp与串口透传(select)

    介绍 tcp作为服务端,监听端口8888,实现串口透传,这里是使用select监听tcp的receive和串口的read,单工通信 -p 指定tcp端口 -s 指定串口 -b 指定波特率 支持4800 ...

  4. 【Python】使用socketserver建立一个异步TCP服务器

    概述 这篇文章是讲解如何使用socketserver建立一个异步TCP服务器,其中Python版本为3.5.1. socketserver主要的类 socketserver模块中的类主要有以下几个:1 ...

  5. 【资料下载区】【iCore3相关代码、资料下载地址】更新日期2017/1/5

    [iCore3 ARM代码下载地址][全部]DEMO1.0测试程序发布例程一:ARM驱动三色LED例程二:读取arm按键状态例程三:EXTI中断输入实验——读取ARM按键状态例程四:USART通信实验 ...

  6. PLC

    https://www.cnblogs.com/dathlin/p/7469679.html C#读写三菱PLC和西门子PLC数据 使用TCP/IP 协议https://blog.csdn.net/x ...

  7. java 的11个特性

    以下11个特性来自于著名的"java白皮书"中,博主会针对这些特性一一进行粗略的解释.相信看完博主的这篇文章,以后在和小伙伴们吹牛逼,可就有了切实可靠的理论依据了. 11个特性分别 ...

  8. S03_CH08_DMA_LWIP以太网传输

    S03_CH08_DMA_LWIP以太网传输 8.1概述 本例程详细创建过程和本季课程第一课<S03_CH01_AXI_DMA_LOOP 环路测试>非常类似,因此如果读者不清楚如何创建工程 ...

  9. 王艳 201771010127《面向对象程序设计(java)》第一周学习总结

    王艳 201771010127<面向对象程序设计(java)>第一周学习总结 第一部分:课程准备部分 填写课程学习 平台注册账号, 平台名称 注册账号 博客园:www.cnblogs.co ...

随机推荐

  1. [APIO2018]铁人两项——圆方树+树形DP

    题目链接: [APIO2018]铁人两项 对于点双连通分量有一个性质:在同一个点双里的三个点$a,b,c$,一定存在一条从$a$到$c$的路径经过$b$且经过的点只被经过一次. 那么我们建出原图的圆方 ...

  2. CSS子选择器与后代选择器的区别

    p > span{ color:blue; } <p> 嵌套使用<span>css好牛逼!</span><span>是啊<b>也影响孙 ...

  3. 为什么HashMap桶(链表)的长度超过8才会转换成红黑树

    百度了一下,感觉能说清楚的并不多,所以在此记录一下. 首先说一说转换为红黑树的必要性:红黑树的插入.删除和遍历的最坏时间复杂度都是log(n),因此,意外的情况或者恶意使用下导致hashCode()方 ...

  4. 使用IOCP完成端口队列做任务队列

    使用IOCP完成端口队列做任务队列 与其自己费力设计异步任务队列,不如使用WINDOWS内核级的IOCP完成端口队列做任务队列. 1)引用单元 uses windows; 2)定义完成端口句柄 var ...

  5. LoadRunne遇到的一些问题FAQ(持续更新...)

    1.LR11破解完成,添加License失败,报错License security violation Loadrunner11破解成功后,用deletelicense.exe工具把License全删 ...

  6. LC 990. Satisfiability of Equality Equations

    Given an array equations of strings that represent relationships between variables, each string equa ...

  7. 小数组的读写和带Buffer的读写哪个快

    定义小数组如果是8192个字节大小和Buffered比较的话 定义小数组会略胜一筹,因为读和写操作的是同一个数组 而Buffered操作的是两个数组

  8. js的Map实例

    1.创建实例 let map= new Map(); // 创建 2.对map的写入 // 要添加的对象 let obj1 = {name:'张三', sex:'boy',age: 21}; let ...

  9. “kill -9” 和 “kill -15” 有什么不同

    来看下图,其中关键参数 -n signum 表示的是信号编码.   kill   kill 可以用 kill -l 来查看具体有哪些信号编码,这里重点关注 9) SIGKILL 和 15) SIGTE ...

  10. 阶段5 3.微服务项目【学成在线】_day16 Spring Security Oauth2_04-用户认证技术方案-SpringSecurityOauth2

    2.3 Spring security Oauth2认证解决方案 本项目采用 Spring security + Oauth2完成用户认证及用户授权,Spring security 是一个强大的和高度 ...