使用的是正点原子zynq开发板

Zynq配置AMP模式(cpu0跑linuxc+cpu1跑裸机)

在 AMP 运行环境下,必须要小心以防止两个 CPU 争夺这些共享资源,在 SoC 硬件系统当中,有一些
资源是每个 CPU 私有的,而有一些资源则是公用的;

CPU 私有资源如下所示:
1)L1 cache(一级缓存);
2)CPU 私有外设中断(PPI);
3)内存管理单元(MMU);
4)CPU 私有定时器。
CPU 共享资源如下所示:
1)中断控制分配器(ICD);
2)DDR 内存;
3)片内存储器(OCM);
4)全局定时器(Global Timer);
5)SUC 和 L2 cache(二级缓存)。
6)片上外设,例如 GPIO、SD、QSPI、UART、I2C、SPI(共享外设中断)、USB、CAN、SPI 等等。
私有资源可以不用管,但是对于双核公共资源来说,必须要小心处理,避免两个 CPU 同时使用这些资源的情况下导致混乱、数据错误,例如同时控制某一个 GPIO、同时使用串口等。
对于 ZYNQ 来说,当硬件上电启动之后,BootROM 是第一个运行的代码(BootROM 代码被固化在ZYNQ SoC 片内 RoM 存储器中),并且运行于 CPU0 上;BootROM 代码会从启动介质中读取 BotROM 头信息,BootROM 头信息就存储在 BOOT.BIN 文件中,BootROM 头信息记录了 bitstream、用户代码等数据存放在 BOOT.BIN 文件中的位置偏移量以及对应在内存中加载地址,根据这些信息来加载 PL 端 bitstream流文件以及在 PS 端启动用户代码。
BootROM 启动之后会使 CPU1 进入等待事件模式(WFE),没有对其启用任何功能,也就是说此时CPU1 处于一个等待指令的状态;而这个指令由 CPU0 给它,说白了就是必须由 CPU0 来启动 CPU1,解除它的等待指令状态,使其运行我们的用户代码。CPU0 在 CPU1 上启动应用程序需要少量协议,当 CPU1 接收到 CPU0 给到的 SEV 指令后,它会被唤醒,然后立即读取 0xFFFFFFF0 这个内存地址中存放的数据,并把这个读取到的数据作为应用程序的入口地址,接着跳转到该地址启动应用程序。所以由此可知,如果在执行 SEV 指令的时候,地址 0xFFFFFFF0 中存放的是一个无效的应用程序入口地址,则结果是不可预测的。

创建CPU1工程

点击 File–>New–>Application Project 新建应用工程。
在这里插入图片描述

注意在 Processor 栏选择“ps7_cortexa9_1”,点击 Next 按钮进入工程模板选择界面,同样这里我们也是创建一个空工程,首先双击 CPU1 工程的链接脚本文件 lscript.ld 打开它,修改 CPU1 应用程序内存空间的基地址和大小,这里笔者将基地址设置为 0x21000000。大小设置为 0x200000;
在这里插入图片描述
此外我们还需要对 CPU1 工程的板级支持包做额外的设置
在这里插入图片描述

在配置界面当中,选择 drivers–>ps7_cortexa9_1,在 extra_compiler_flags 栏添加“-DUSE_AMP=1”; 该页面用于配置工程的交叉编译工具,例如compiler指定了编译工程源码时所使用的交叉编译工具arm none-eabi-gcc,而 compiler_flags 和 extra_compiler_flags 则是执行 arm-none-eabi-gcc 命令时所携带的选项,“-D”选项的作用则是定义一个宏,所以我们这里添加“-DUSE_AMP=1”其实就类似于定义了一个宏“#defineUSE_AMP 1”,这里定义的宏与普通.c、.h 文件中定义宏有所不同,这个宏在整个工程源码中都是有效的,你不需要包含任何的头文件都能使用它。
那问题来了?我们为什么需要定义这个宏,主要是跟 L2 Cache 有关系,因为 L2 Cache 属于 CPU0 和CPU1 的共享资源,都可以使用,这对于 AMP 运行模式来说是一个麻烦,所以这里直接让 CPU1 禁止使用L2 Cache,避免出现问题。当定义了这个宏之后,CPU1 就不可以使用 L2 Cache 了。
在这里插入图片描述

将共享内存地址设置为 0x25000000,同样这个地址既不在 CPU0 Linux 系统内存空间中、
也不在 CPU1 裸机应用程序内存空间中。

#define SHARE_MEM_ADDR 0x25000000U // 共享内存地址
  /*
   * 禁止共享内存 Cache,保持数据的一致性
   * S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
   */
Xil_SetTlbAttributes(SHARE_MEM_ADDR, 0x14de2);
创建FSBL工程

菜单栏中选择“File->New->Application Project”
在这里插入图片描述

接下来点击 Next,并在示例工程中选择“Zynq FSBL”,最后点击“Finish”,如下图所示
在这里插入图片描述

FSBL 工程创建完成之后,SDK 软件会自动执行编译过程,生成 FSBL.elf 文件。 双击打开 fsbl 工程中的 main.c 源文件,在 main 函数前添加如下代码

#define sev() __asm__ ("sev") // C 语言内嵌汇编写法
#define CPU1_RUN_ADDR 0x21000000U
#define CPU1_COPY_ADDR 0xFFFFFFF0U
static void StartCpu1(void)
{
 Xil_Out32(CPU1_COPY_ADDR, CPU1_RUN_ADDR); // 将 0x10000000 拷贝到 0xFFFFFFF0 地址处
 dmb(); // 等待内存写入完成(同步)
 sev(); // 执行 sev 指令唤醒 CPU1
}

添加完成之后,然后在“HandoffAddress = LoadBootImage()”代码后面调用上面定义的函数 StartCpu1();
选中FSBL后点击工具栏Xilinx->Create boot image
在这里插入图片描述

u-boot在编译之前打开设备树文件,arch/arm/dts/atk-zynq-7020.dts找到 memory 节点reg 属性描述了 u-boot 和 Linux 系统所能使用的内存空间,起始地址为 0x0,大小为 0x40000000,也就是 1GB;这里我们将 0x40000000 修改为 0x20000000,也就是该为 512MB,修改完成之后保存退出;修改u-boot环境变量bootargs设置成只有一个cpu核,在u-boot源码board/xilinx/zynq/board.c

bootargs=console=ttyPS0,115200 maxcpus=1 earlyprintk root=/dev/mmcblk0p2 rw rootwait

目的是为了让 Linux 系统只能使用一个 CPU,也就是 CPU0,不要使用 CPU1,把 CPU1
独立出来运行一个裸机程序;

Logo

更多推荐