ZYNQ双核处理器独立运行AMP

一、简介

多核处理器从多核的结构上是否一致,分为两种基本架构:同构多核架构和异构多核架构。同构多核处理器是指系统中的处理器在结构上是相同的;而异构处理器是指系统中的处理器在结构上是不同的,这些处理器可以是通用处理器,也可以是解决某些特定应用的专用硬核。同构多核架构相比于异构多核架构,在硬件和软件设计上较为简单,通用性较高。但在某些特定应用场合下,如异构多核架构专用的硬件加速硬核,异构多核架构的性能会更高。

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 与 SMP 运行模式的框图

ZYNQ-7000 SOC 提供了两个 Cortex-A9 处理器,这两个处理器在 AMP 的机制下,可以运行各自独立的操作系统或者裸机应用程序。本次试验采用的是双核 AMP 的运行模式,两个 CPU 分别运行不同的裸机应用程序。

ZYNQ 中的 OCM 包括 256KB 的 RAM 和 128KB 的 ROM(BootRom),其中可以被两个 CPU 进行改写的,就是 256KB 的 RAM。OCM 分为 4 个 64KB RAM,存储空间较小,而外置的 DDR3 存储器一般存储空间较大。当两个 CPU 需要进行大量数据交互的时候,可以使用 DDR3 存储器作为共享内存;而当交互的数据较少时,既可以使用 OCM 作为共享内存,也可以使用 DDR3 存储器作为共享内存。值得一提的是,当交互的数据量较少时,OCM 作为共享内存有着独特的优势,与 DDR 内存相比,OCM 提供了非常高的性能和来自两个处理器的低延迟访问。

本次试验的两个 CPU 都会使用到串口,并且 CPU0 会对 OCM 进行写操作,CPU1 对 OCM 进行读操作,这就要求共享外设和内存不能同时访问,以免产生冲突。我们可以利用软件产生中断(SGI)的方式来规避冲突。在初始状态下,CPU0 先使用串口,在接收到用户数据后,将数据写入 OCM 中,并产生中断来触发CPU1 中断,此时 CPU0 不在访问串口和 OCM;CPU1 触发中断后,开始访问串口和 OCM,先从 OCM 中读出数据,通过串口来输出信息,并产生中断触发 CPU0 中断,之后不再访问串口和 OCM;CPU0 接收到CPU1 的中断后,此时可以重新通过串口来接收用户输入的数据,从而避免了共享外设和内存的同时访问。

二、硬件设计

三、软件设计

在SDK中新建两个APP ,一个是a9_cpu0_app,另一个是a9_cpu1_app,配置CPU的时候一个选cpu0,另一个选cpu1

两个工程都用hello world模板

其中,a9_cpu0_app里面的代码如下

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xscugic.h"
#include "sleep.h"

#define sev() __asm__("sev")
#define CPU1STARTADR 0xfffffff0   //存放 CPU1 应用起始地址的地址
#define CPU1STARTMEM 0x10000000   //CPU1 应用起始地址

//启动 CPU1,用于固化程序
void StartCpu1(void)
{
    Xil_Out32(CPU1STARTADR,CPU1STARTMEM);  //向 CPU1STARTADR(0Xffffffff0)地址写入 CPU1 的访问内存基地址
    dmb();    //等待内存写入完成(同步)
    sev();    //通过"SEV"指令唤醒 CPU1 并跳转至相应的程序
}

int main()
{
    init_platform();
    Xil_SetTlbAttributes(0xffff0000,0x14de2);  //禁用 0xfffffff0 的 Cache 属性

    print("Hello World cpu0\n\r");
    print("try to start cpu1...\r\n");
    StartCpu1();
    while(1)
    {
        print("Hello World cpu0\r\n");
        sleep(1);
    }
    cleanup_platform();
    return 0;
}

里面包含了启动CPU1的函数,实际我们启动DEBUG的时候这个函数不会发生作用,需要程序固化到FLASH之后,启动才会生效。

a9_cpu1_app里面的代码如下

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"


int main()
{
    init_platform();
    sleep(1);
    print("Hello World cpu1\n\r");

    while(1)
    {
        print("Hello World cpu1\r\n");
        sleep(1);
    }
    cleanup_platform();
    return 0;
}

Xil_SetTlbAttributes(0xffff0000,0x14de2);是禁用 0xfffffff0 的 Cache 属性

接下来设置cpu0的APP地址设置,起始地址和大小

设置cpu1的APP地址设置,起始地址和大小

注意上图中第一行左侧为基地址,右侧为存储空间大小,以字节为单位,存储空间大小设置

为 0x00100000(十进制 1048576),即 1MByte(1048576/1024/1024)。修改完成后,按下“Ctrl”+“S”保存。另外上图中的 ps7_ram_0 和 ps_ram_1 为 OCM 共享内存的基地址和存储空间大小。注意,两个CPU的基地址要设置不同

DEBUG配置如下

接下来验证

选择A9 #0处理器,点击运行,串口打印CPU0

在选择A9 #1,CPU1也运行起来了,二者交互打印信息

四、固化到FLASH

我们SDK新建一个FSBL启动工程

FSBL工程新建完成之后,等待SDK自动编译结束

结束之后打包BIN文件,固化到FLASH

然后我们烧写到FLASH,根据板子的背面设置成QSPI模式

注意,烧写之前要先断开开发板的SDK连接

烧写结束,板子重启,打开串口助手,可以看到两个处理器愉快的运行起来了

  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值