学习内容
本文首先介绍了双核AMP通信的相关内容,接着进行设计AMP双核通信的工程,完成功能验证。
开发环境
vivado 18.3&SDK,PYNQ-Z2开发板。
双核通信
多核处理器从结构上划分:
同构多核: 同构多核处理器是指系统中的处理器在结构上是相同的,在软硬件设计上较为简单,通用性高。
异构多核: 异构多核处理器是指系统中的处理器在结构上不同的,在特定场合中可以进行加速,提高性能。
Xilinx 的 ZYNQ SOC 融合了这两种架构, ZYNQ SOC 芯片包含两个独立的 Cortex-A9 处理器,这两个处理器核在结构上是相同的,同时又包括了可编程的逻辑单元( PL),使得 ZYNQ 整体系统成为了一个异构多核系统,同时具有较高的通用性和性能。
多核处理器软件运行方式
多核处理器的运行模式有 AMP(非对称多处理)、 SMP(对称多处理)和 BMP(受约束多处理)三种运行模式。
AMP(非对称多处理): 多个内核相对独立的运行不同的任务,每个内核相互隔离,可以运行不同的操作系统。SMP(对称多处理): 多个处理器运行一个操作系统,这个操作系统管理多个内核。
BMP(受约束多处理): BMP运行和SMP运行模式类似,开发者可以指定将某个任务仅在某个指定内核上执行。
下图为SMP和AMP的示意图:
AMP非对称多处理运行
详细介绍
由于AMP多用于裸机开发,在两个CPU上可以运行不同的应用程序,所以本次工程主要使用该方式进行双核通信。对于AMP的相关设计这里可以参考XAPP1079的文档进行设计验证。
ZYNQ-7000 AP SoC 提供两个共享通用内存和外设的 Cortex-A9 处理器。 非对称多处理 (AMP) 是一种机制,允许两个处理器运行自己的操作系统或裸机应用程序,并可以通过共享资源松散耦合这些应用程序。
在AMP模式中,每个CPU可以访问自己独立的程序任务,但是需要注意的是要防止CPU在共享资源上发生冲突,否则会发生错误。
资源分布
对于该架构,CPU会有一些私有资源和共享资源,具体如下:
私有资源:
- L1 cache
- Private peripheral interrupts (PPI)(私有外设中断)
- Memory management unit (MMU)(内存管理单元)
- Private timers
共享资源:
- Interrupt control distributor (ICD)(中断控制分配器)
- DDR memory(DDR内存)
- On-chip memory (OCM)(片上资源)
- Global timer(全局时钟)
- Snoop control unit (SCU) and L2 cache
- UART0(串口)
两个处理器都使用 OCM 来相互通信。 与 DDR 内存相比,OCM 提供了非常高的性能和来自两个处理器的低延迟访问。 通过禁用两个处理器对 OCM 的缓存访问,进一步确保了确定性访问。
此设计为防止共享资源出现问题而采取的措施包括:
- DDR 内存:CPU0 使用 0x00100000 到 0x001FFFFF 的内存。 CPU1 使用从 0x00200000 到 0x002FFFFF 的内存用于其裸机应用程序。
- L2 缓存:CPU1 不使用L2 缓存。 L2 缓存是共享资源,CPU0 拥有此资源。如果 CPU1 使用 L2 缓存,则需要从 CPU0 请求 L2 缓存刷新和失效,而 CPU0 将执行该操作。包含使 CPU1 能够请求 L2 缓存交互的通信通道超出了本示例设计的范围。
- ICD:来自PL 内核的中断被路由到CPU1 的PPI 控制器。通过使用 PPI,CPU1 可以自由地处理中断,而无需访问 ICD。
- 定时器:CPU1 使用专用定时器。
- OCM:每个CPU 都非常小心地处理对OCM 的访问以防止争用。单个 OCM 地址位置用作标志以在两个处理器之间进行通信。 CPU0 在启动 CPU1 之前将标志初始化为 0。当标志为零时,CPU0 拥有 UART。当标志不为零时,CPU1 拥有 UART。只有 CPU0 设置标志,只有 CPU1 清除它。
参考设计
参考设计为赛灵思的官方文档xapp1079,这个文档设计的AMP架构是运行在ISE软件下,但是对我们进行AMP架构开发依旧有参考意义。
参考设计硬件部分
PL块包含一个自定义的嵌入式核心,连接到ChipScope分析仪VIO核心的同步输出。VIO核心为用户提供了与ChipScope分析仪硬件交互的机制。
在这个设计中,当VIO产生一个脉冲时,自定义核心将一个中断转发给PS Core1_nlRQ引脚。核心还连接到PS主通用端口(M AXI_GP0),通过一个允许CPU0和CPU1访问核心内的控制寄存器的AXI互连。在中断服务程序期间,CPU1访问控制寄存器以清除中断请求(IRQ)。CPU0可以选择使用控制寄存器来创建一个指向CPU1的中断。Core1_nlRQ引脚直接连接到CPU1 PPI块,因此不需要修改共享ICD的配置。还包括一个ChipScope分析仪axis监视器核心,允许用户测量正在服务的IRQ的延迟。
参考设计软件设计
该软件可以分为三个部分:
- 第一阶段引导加载程序(FSBL)
- 用于CPU0的裸机应用程序
- 用于CPU1的裸机应用程序
FSBL总是在CPU0上运行,是PS上电复位后第一个运行的软件应用程序。FSBL负责编程PL,并将应用程序可执行文件和可链接格式(ELF)文件复制到DDR内存。在将应用程序加载到DDR内存后,FSBL然后开始执行加载的第一个应用程序。
参考设计让CPU0和CPU1运行它们自己的裸机应用程序代码。CPU0负责初始化共享资源并且启动CPU1。CPU0的应用程序位于从地址0x00100000开始的内存中。链接器脚本用于设置起始地址。
CPU0的设计步骤:
- 配置MMU对0xFFFF0000 ~ oxfffffe地址范围内的OCM访问关闭缓存功能。OCM的地址映射是不变的,所以OCM存在于地址0x00000000到0x0002FFFF和地址0xFFFF0000到0XFFFFFFFF。示例设计只使用了高64kb的OCM,因此在地址0xFFFFo000到oxfffffe上禁用缓存。
- 初始化ICD;
- CPU1开始启动;
- 打印到UART;
- 在OCM中设置一个用作信号量标志的内存位置;
- 等待OCM中用作信号量标志的内存位置被清除。
CPUO应用程序无限期地重复步骤3到步骤6。
PS启动后,内部 boot ROM完成执行,CPU1被重定向到0xFFFFFE00的OCM中的一小段代码。这段代码是一个连续循环,它等待一个事件,检查地址位置0xFFFFFFF0的非零值,然后继续循环。如果0xFFFFFFF0包含一个非零值,CPU1将跳转到获取的地址。
CPU0通过将Ox00200000的值写入地址0xFFFFFFF0,然后运行Set Event (SEV)命令来启动CPU1(两者都运行裸机程序)。SEV唤醒CPU1,从地址0xFFFFFFF0读取值0x00200000,然后跳转到地址0x00200000。FSBL负责将CPU1 ELF放置在0x00200000。
CPU1应用程序位于从地址Ox00200000开始的内存中。链接器脚本用于设置起始地址。
CPU1应用程序执行以下操作:
- 配置MMU对地址范围为0xFFFF0000 --0XFFFFFFFF的OCM访问关闭缓存功能。OCM的地址映射是不变的,因此OCM存在于地址0x00000000到0x0002FEEF和地址0xFFEF0000到xFFFFFFFE。这个应用程序笔记只使用了OCM的高64kb,所以缓存在地址0xFFFF0000到0XFFFFFFFF上被禁用。
- 初始化PPl中断控制器和中断子系统。
- 等待OCM中用于设置信号量标志的内存位置。
- 打印到UART。打印的字符串的选择取决于中断服务例程是否增加了一个全局变量。如果全局变量irq_count不为零,CPU1将该值设置为零
- 清除OCM中用作信号量标志的内存位置。
CPU1应用程序无限期地重复步骤3到步骤5。
一些细节
启动CPU1
CPU 0负责在CPU 1上启动代码执行。BootROM将CPU 1设置为Wait for Event模式。什么都没有启用,只有少数通用寄存器被修改,以将其置于等待WFE指令的状态。
CPU0在CPU1上启动应用程序需要少量的协议。CPU 1收到系统事件后,立即读取地址0xFFFFFFF0的内容并跳转到该地址。如果SEV在更新目的地址位置(0xFFEEFFE0)之前发出,CPU1继续处于WFE状态,因为0xFFFFFFF0将WFE指令的地址作为安全网。如果编写0xFFFFFFF0地址的软件无效或指向未初始化的内存,则结果是不可预测的。
CPU 1上的初始跳转只支持Arm-32 ISA代码。在跳转的目的地不支持Thumb和Thumb-ll代码。这意味着目的地址必须是32位对齐的,并且必须是有效的Arm-32指令。如果不满足这些条件,结果是不可预测的。
CPU0在CPU1上启动应用的步骤如下:
- 将cpu1的应用程序地址写入0xFFFFFFF0。
- 执行SEV指令,唤醒CPU1并跳转到应用程序。
地址范围0xFFFFEE00到0xFFFFFFF0是保留的,在第1阶段或以上的应用程序完全可用之前不能使用。在第二个CPU成功启动之前对这些区域的任何访问都会导致不可预测的结果。
软件中断
前面提到两个处理器都使用 OCM 来相互通信。 而OCM属于两个CPU共用的资源,如果同时调用会出现问题,所以为了防止共享资源出现问题,可以进行使用中断的办法进行控制。也即让CPU0在完成对OCM的相关操作并完成释放OCM后,产生软件中断,CPU1接收到软件中断即可知道CPU0完成对OCM的操作,即可进行CPU1的对OCM的相关操作。
每个CPU都有一组私有外设中断(PPls),它们使用存储的reqisters进行私有访问。PPls包括全局定时器、私有看门狗定时器、私有定时器和来自PL的FIQ/IRQ。软件生成的中断(SGIs)被路由到一个或两个cpu。SGIs是通过写入通用中断控制器(GIC)中的寄存器而生成的。共享外围中断(SPls)是由PS和PL中的各种I/O和内存控制器产生的。它们被路由到其中一个或两个cpu。来自PS外围设备的SPI中断也被路由到PL。
通过一个SGI (software generated interrupt,软件生成中断),每个CPU可以中断自己,也可以中断另一个CPU,也可以中断两个CPU。软件生成中断共有16个,如表所示。
通过将SG中断号写入ICDSGIR寄存器并指定目标CPU,生成一个SGl。这个写操作通过CPU自己的私有总线进行。每个CPU都有自己的一组SGl寄存器,用于生成16个软件生成的中断中的一个或多个。通过读取ICCIAR(中断确认)寄存器或在ICDICPR(中断清除等待)寄存器的相应位写一个1来清除中断。所有的SGis都是边缘触发。sgl的灵敏度类型是固定的,不能改变;ICDICFRO寄存器是只读的,因为它指定了所有16个sgl的敏感类型。
OCM
OCM模块包含256kb的RAM和128kb的ROM (BootROM)。它支持两个64位的axis从接口端口,一个专用于通过APU snoop控制单元(SCU)的CPU/ACP访问,另一个由处理系统(PS)和可编程逻辑(PL)中的所有其他总线主共享。BootROM内存只被引导进程使用,用户不可见。
OCM分布
OCM分布在两个区域,一个是地址的开始端,有连续的3个64k字节的存储空间,另外一个在最后,用户可以根据自己的传输交互的数据量大小进行选择使用哪一块存储空间。
程序设计
见下篇 ZYNQ-双核AMP通信(二)。