ZYNQ从放弃到入门(十二)- AMP — Zynq 上的非对称多核处理器
之前介绍的所有文章我们都只使用了一个 ARM Cortex-A9 处理器内核(Core 0)。然而,PS 端包含两个处理器内核,对于许多应用程序,我们希望同时使用两个 Zynq 内核以获得最佳性能。将两个 Zynq 处理器内核用于不同的任务可以称为非对称多核处理 (AMP,Asymmetric Multiprocessing),并且可以涉及以下任意组合:
Core 0 和 Core 1 上运行不同操作系统
Core 0 上运行操作系统,Core 1 上运行裸机代码(反之亦然)
两个Core上的裸机代码执行不同的程序
AMP介绍
有两种多核处理方式:对称和非对称。在我们定义两者之间的区别之前,我们首先必须定义什么是多核处理:“多核处理是在系统中使用多个处理器。这可以允许同时执行多条指令。但是,它不一定非要如此。” 对称和非对称多核处理之间的区别是
对称多核处理通过将处理进程分布在多个微处理器内核上来同时运行多个软件任务
非对称多处理使用专门的处理器来运行特定的应用程序或在相同的处理器上运行专门的应用程序
在接下来的几篇博客中,我们将介绍 Zynq SoC 上的 AMP。首先,我们研究两个裸机应用程序,每个应用程序都运行在不同的core上。在 Zynq SoC 上运行 AMP 时,必须考虑 Zynq 处理器core混合了私有资源和共享资源。两个处理器都有私有的 L1 指令和数据缓存、定时器和看门狗以及共享的中断控制器(共享和私有中断)。然而,Zynq 上的中断并不那么简单,因为 PS 中的每个内核都能够使用软件中断来中断自己、另一个处理器或两个处理器,这些中断是通过中断控制器来分发的。
Zynq SoC 还拥有大量共享资源,常见示例包括 I/O 外设、片上存储器、中断控制器分配器、L2 高速缓存和位于 DDR 存储器内的系统存储器。下图显示了其中一些资源。
我们将从 DDR 内存运行两个处理器内核,因此我们必须非常小心地对每个处理器使用的地址区域进行分段。地址是通过每个应用程序的链接描述文件确定的。如果我们对此处理不当,在不同内核上运行的应用程序可能会干扰彼此的操作。
我们还必须修改 SDK 自动生成的文件以使系统启动并运行。第一步将根据XAPP1079(http://www.xilinx.com/support/documentation/application_notes/xapp1079-amp-bare-metal-cortex-a9.pdf)修改第一阶段引导加载程序,它将检查裸机/裸机 AMP。
我最初打算创建一个非常简单的系统,一旦它启动并运行,我们就可以对其进行扩展。第一个应用程序将让 Zynq SoC 的处理器 Core 0 通过 RS232 与用户通信,而 Core 1 在驱动连接到 ZYNQ IO 的 LEDS 。这两个应用程序无需交互即可同时运行。
启动和运行AMP
虽然启动和运行 AMP 需要几个步骤,但它实际上是非常简单直接的过程,当然没什么好害怕的。
让 AMP 在 Zynq SoC 上运行的关键方面是引导加载程序,它在将第一个可执行文件加载到内存后查找第二个可执行文件。为了简单化,我将使用赛灵思应用笔记 XAPP1079 中提供的修改后的 FSBL 和修改后的独立操作系统。(源文件可在此处获得http://www.xilinx.com/support/documentation/application_notes/xapp1079-amp-bare-metal-cortex-a9.pdf)
下载 zip 文件后,第一步是将压缩文件解压缩到所需的工作目录中,并将名为 SRC 的文件夹重命名为 design。这些文件包含修改后的 FSBL 和修改后的独立操作系统。我们需要 SDK 了解这些文件,因此下一步是更新 SDK 存储库(repository)以使 SDK 了解它们的存在。在 SDK的Xilinx 工具菜单下中,选择存储库,然后选择新建,导航到目录位置 <工作目录>app1079designworksdk_repo,如下所示:
在存储库中添加后,下一阶段是生成以下内容:
AMP 第一阶段引导加载程序
core 0 应用程序
core 1 应用程序
我们将为其中的每一个core生成一个 BSP(板级支持包)。
首先要做的是创建一个新的 FSBL。选择file -> new application -> project,这使我们能够创建一个支持 AMP 的 FSBL 项目。这与我们之前所做的相同,但是我们将选择 Zynq FSBL for AMP 模板来代替 Zynq FSBL 模板。
在创建 AMP FSBL 之后,我们需要为第一个内核创建应用程序。这又很简单,我们以前做过很多次了。确保选择 Core 0 和独立操作系统并允许它创建自己的 BSP。
一旦我们创建了这个应用程序,我们需要在 DDR 内存中正确定义应用程序将执行的位置。为此,我们如下编辑链接描述文件以显示 DDR 基地址和大小。这个很重要,如果我们没有对 Core 0 和 Core 1 应用程序的 DDR 内存进行正确分段,我们就有可能无意中损坏另一个应用程序。
我们现在可以编写我们希望在core 0 上执行的应用程序。我们需要将以下代码部分包含在应用中。
此代码禁用 Zynq SoC 片上存储器上的缓存,并将 Core 1 程序的起始地址写入 Core 1 将在 Core 0 执行 Set Event (SEV) 命令后访问的地址。SEV 命令使 Core 1 开始执行其程序。
下一步是为 Core1 创建 BSP。我们想使用修改后的独立操作系统 (standalone_amp),它可以防止重新初始化 PS Snoop 控制单元 (SCU)。因此,我们不能像为 Core 0 那样创建项目时允许自动生成 BSP。请务必在 CPU 选择选项中选择 Core 1。
现在我们已经为 Core 1 创建了 BSP,我们需要修改 BSP 的设置,然后才能继续创建要在 Core 1 上运行的应用程序。这非常简单,需要添加一个额外的编译器标志-DUSE_AMP=1 到 BSP 的驱动程序部分的配置:
完成后,我们可以自由地为 Core 1 创建应用程序。确保选择 Core 1 作为处理器并使用我们刚刚创建的 BSP:
同样,在创建新应用程序后,我们需要再次在 DDR 内存中正确定义内存位置,Core 1 程序将从该位置执行。这是通过编辑 Core 1 应用程序的链接器脚本来实现的:
与此应用程序中的第一个内核一样,我们还必须禁用片上内存上的缓存,因为我们将在以后的博客中使用此内存在两个处理器之间进行通信. 一旦我们完成了我们的应用程序并构建了项目,我们现在应该拥有以下内容:
AMP FSBL ELF
core 0 ELF
core 1 ELF
定义设备配置的位文件。
我们现在需要一个 .bin 文件来使 Zynq SoC 从选择的配置存储器启动。我们还需要一个 bif 文件,它定义了用于创建 bin 的文件,并且我们需要定义文件的顺序。
我们将使用 XAPP 1079 的 directorydesignworkootgen 下提供的 bat 文件,而不是使用在 SDK 中创建的 Zynq 启动映像。该目录包含一个 bif 文件和一个 cpu1_bootvec.bin,该文件用作修改后的 FSBL 的一部分,以阻止它寻找更多要加载的应用程序。
为了生成bin文件,我们将生成的三个ELF文件复制到bootgen目录并编辑BIF文件,确保bif文件中的elf名称正确。
我们现在可以打开 ISE/Vivado 命令提示符,导航到 bootgen 目录,然后运行 createboot.bat,将创建 boot.bin 文件:
然后可以将该文件下载到 Zynq SoC 上的flash中。引导设备运行两个内核启动并执行各自的程序。
下一节我们在聊聊一些细节。
上一节文章创建了两个core上启动并运行简单的软件。它的简单性使我能够展示如何让两个 Zynq SoC 处理器内核通过 OCM(片上存储器)进行通信。但是,两个核上运行的软件目前正在做一些简单的事情,因此我们有一个可以继续前进的基线。
Core 0 是主控并控制执行 Core 1。它还使用 UART 向终端程序打印消息。
一旦由 Core 0 启动,Core 1 就会初始化其私有资源并驱动八个 LED 。我们需要使用 Core 1 的私有定时器并通过 GIC 启用中断。
这些应用程序没有任何关联,也不共享资源。但是,实际的应用程序希望能够做到这一点。在 Core 0 上运行的应用程序非常简单。它在 Core 1 上启动软件,然后使用 UART 0 在循环中打印出一条简单的消息:
但是,我们计划使用 Core 1 的中断控制器,因此我们必须首先使用以下代码配置 GIC(通用中断控制器):
Core 1 代码必须稍微复杂一些,因为我们使用 Zynq SoC 的 PL(可编程逻辑)端的 GPIO 模块来驱动 ZYNQ 上的 LED。与赛灵思的所有其他接口一样,独立操作系统为此通过#include提供了一组简单的驱动程序,“xgpio.h” 这个文件与我们之前用来驱动连接到 Zynq SoC 的 PS(处理系统)端的 MIO / EMIO GPIO 的 xgpio_ps.h 文件略有不同。然而,在本例中,我想展示如何在 Zynq SoC 的 PL 端使用 GPIO。为了确保我们可以看到 LED 的开关,我们将使用 Core 1 的私有计时器,这与我们过去在核心 0 上使用私有计时器相同。
在 Core 1 的程序开始执行其主应用程序之前,我们需要禁用片上存储器 (OCM) 上的缓存,初始化 GPIO,初始化私有定时器,并配置中断控制器,以便可以使用私有定时器的中断来切换LED。我们现在可以开始编写相当简单的中断服务例程,在专用定时器结束并重新启动时切换 LED。这个过程将永远持续下去。
以下是程序执行的结果,正如 Core 0 向终端窗口输出的报告:
代码地址
https://github.com/suisuisi/zynq_guide/blob/main/core_0_main_part50.c
审核编辑 :李倩
全部0条评论
快来发表一下你的评论吧 !