ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤

ZYNQ7 双核处理的运行机制的原理和实现步骤
ZYNQ7 的 PS 系统有两个 ARM 处理器核,我们要发挥最大化系统性能,就要将这两个核合理分配都用起来。我们这一集视频的目的是和大家一起来看看双核运行的原理以及实现步骤。原理比较多,大家要结合资料多研究分析。
1 OCM 相关 ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
文章图片

1, 我们首先看 ZYNY 结构的框图,各自内核有独立 32K 字节私有的 I-CACHE 和 D-CACHE,两个核有公用的 512K 混合 CACHE(保存指令和数据)。
2, OCM 是片内高速存储器,是我们要重点要强调的。大家注意看到,处理器对 OCM 的访问经过了各自独立 CACHE。我们考虑如果要用 OCM 进行通讯的时候要注意适时刷新私CACHE,详细说来就是 FLUSH 和 INVALID。当然直接关闭掉对 OCM 进行的 CACHE 缓存是最简单的方法。
3, OCM 总共是 256K,分成 192K+64K,
192K 放在寻址空间的最底部,从 0 开始编址,即 0x0000_0000 ~ 0x0002_FFFF
64K 放在寻址空间的最高位置, 即 0xFFFF_0000 ~ 0xFFFF_FFFF
4, OCM做为 PS 内唯一的一块高速存储器除了可以用做处理器的加速算法外,还有如下两个妙用:
A,我们所知道的 FSBL,其实就是被拷贝到 OCM 的 192K 位置执行(这个拷贝的过程是 ZYNQ7 芯片内部的 BOOTROM 来完成的)。
B,64K 中的 0XFFFF_FFF0 还有特殊的意义:CPU1 被 CPU0 唤醒后就是从这个地址读取位数据,当做程序指针 PC 来执行。
2 ZYNQ 的启动过程 【ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤】我们来从新回顾一下 ZYNQ 的启动过程的三个阶段。
1,BOOTROM 阶段 :选择从何种存储介质获取 FSBL,拷贝 OCM 的 192K 部
分,并执行之,此代码用户不可见,也修改不了。
2,FSBL 阶段 :(1),根据用户不同配置,初始化硬件,(2),根据启动模式,从不同的存储介质读取 BOOT,按照跟 BOOTGEN 文件约定的格式进行解析,拷贝数据到制定的 DDR3 存储器,如果存在 PL 配置文件完成 PL 配置,之后将执行第一个出现的可执行。(3),FSBL 阶段是 SDK 生产直接的模板,用户可以修改,也可以不改一字直接使用。如果修改的话,可以完全改成自己的程序,限制仅仅在数据和指令的总大小部超过 192K。
3,SSBL 或者用户程序:被 FSBL 搬运到 DDR3 空间并执行的可执行程序不在有192K 的空间限制,可以是非常大。这里可以是 UBOOT 之类的系统引导程序也可以是用户程序。
另外:
使用 BOOT 映像生成的文件是和 FSBL 对应使用的,我们这里使用了 BOOT 映像工具BOOTGEN,生成的 BOOT.BIN 是拷贝到 SD 卡的第一个扇区,生成的 BOOT.MCS 是可以烧写到 NAND 或者 QSPI FLASH 里。
BOOTGEN 生成的映像本身就是小型的文件系统。在 22 讲我讲到使用 BOOT 映像工具保存多个文件并指定让 FSBL 拷贝到 DDR3 的某个具体位置。这里我们也是采样了类似的方式,保存 CPU0 和 CPU1 的可执行文件。大家注意由于 ELF 文件本身含有地址信息 BOOTGEN 会自动识别出来,所以不需要指定拷贝到 DDR3 的目的地址。
关于程序的存放和运行的地址
1, 在 SDK 生成的每一个项目的时候都对应生成了一个脚本文件。这个脚本文件制定了设置了各个段放在对应存储器位置。可以看到 FSBL 是放在我们说的 OCM 的 192K 的空间,而普通的用户程序是放在 1M 地址开始的空间。
2, SDK 项目最终生成的是 ELF 后缀的文件,这个 ELF 文件内部有指示程序运行在哪一段地址空间。用户程序使用 BOOTGEN 生成启动文件也包含进了这些信息,FSBL 处理 BOOT 启动文件时候就拷贝到对应的位置。
4 实际操作实验步骤 1, VIVADO 配置处理器,我们这里只用到 PS 部分,但是也按照正规的软硬件开发步骤来。
2, 导入 SDK。
3, 生成 FSBL,生成做两个处理器程序,一个是 HELLO WORD,一个是 PS 控制小灯闪烁。这里两个程序可以分别下载到板子上运行进行调试,或者是生成启动映像放在 SD 卡里面。
4, 上面生成的两个单独的 PS 的程序单独运行是我们之前所讲的方法步骤,之运行在 CPU0。CPU1 是处于空闲状态。我们接下来就让处理器 CPU1 也跑起来。
5, 我们运行双核,确定 CPU0 和 CPU1,这里面独立运行不存在交互,所以任意分配,我们这里分配闪烁小灯的程序在 CPU0 上运行。
6, CPU1 模式是休眠模式,需要 CPU0 唤醒。CPU0 要做的事情有三样:
A,确保 CPU1 的程序已经装入到存储器指定未知,并将这个地址(32BIT)写
入到 0XFFFF_FFF0 这个地址。
B,确保 A 步骤的地址写入 OCM 生效。可以关闭 DCACHE 对 OCM 的缓存,也可以 FLASH 一下 DCACHE。
C,调用 SEV 指令,唤醒 CPU1。
7, 对应修改一下 HELLO WORD 的代码。
8, 另外我们之前有提到成 SDK 的链接文件,这里我们简单看一下各自含义。很显然要将两个程序放在不同的 DDR3 存储器地址,所以我们要对应修改一下 CPU1 程序的存放地址。也对应修改一下 CPU0 写入 0XFFFF_FFF0 地址的数值。
9, 使用 BOOTGEN 生成 BOOT 启动文件,拷贝到板子即可看到运行效果。
A- Creating a project in Vivado to target the Zedboard

  1. 双击Vivado2016.3 启动Vivado;
  2. 双击Create New Project来新建一个工程,点击Next;
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片
  3. 在下一个窗口中输入“Dual_Core”作为项目名称,项目文件指定存储在 “D:/Workspace_Vivado”的目录下,勾选Create project subdirectory,点击Next;
  4. 接下来,指定项目类型,使用默认选择的RTL Project,并勾选Do not
    specify sources at this time。RTL代表寄存器传送语言(Register Transfer Language),通过选择这个选项,我们可以在以后灵活地添加/修改源文件,点击Next;
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片
  5. 接下来,指定要测试项目的电路板,选择Zedboard Zynq Evaluation and Development Kit,点击Next然后Finish完成;
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    “新建项目”向导将关闭,刚刚创建的项目将在Vivado中打开。
    B- Creating the ARM processor system
  6. 单击Vivado左上方的Project Manager中的Create Block Design;
  7. 输入模块的名称,然后单击OK,对于这个例子,使用名称:system;
  8. ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片
  9. 点击OK 后,将会在Vivado 中显示一个空白框图视图图形用户界面。在图
    中,我们将添加所需的硬件块系统。
  10. 现在,我们将添加包含ARM 处理器的ZYNQ7 Processing System 块
    通过启动Add IP 向导来完成,或者可以在程序框图空白处右击选择Add IP…,
    IP 目录窗口将会出现,显示在这个设计中添加所有可能的IP;
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    5.向下滚动到底部或使用关键字zynq 进行搜索,双击ZYNQ7 Processing
    System。 ZYNQ7 处理系统块已被放置在框图视图中。程序框图中显示的端口由
    目标开发板指定的该块的默认设置定义。在绿色信息栏中单击 Run Block
    Automation。确认processing_system7_0 并确保选中Apply Board Preset,然后单
    击OK。
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    这将为处理系统创建外部端口,并对这些端口应用物理约束,如下图所示。
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    6.连线的作用就是把PS的时钟可以接入PL 部分,当然这里我们暂时用不到PL部分的资源。在Block文件中,我们进行连线,将鼠标放在引脚处,鼠标变成铅笔后迚行拖拽,连线如下图所示
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    7双击ZYNQ IP这个大图进入内部观察下,首先看到的是ZYNQ 内部的构架由于本实验不需要改动任何东西,而且是基于zedboard 的构架因此不做任何改动。但是让用户自己进行设计的时候可以根据情况做一些改动,使之更加适合自己的项目。
    单击Tools,选择单击Validate Design,检查程序框图是否有误,结果直接点击Ok。
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    8.在源窗格中,选择系统框图“system.bd”,右击并选择Generate Output Products,默认设置,直接点generate,运行结束后,点击OK。
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    9.在源窗格中,选择系统框图“system.bd”,右击并选择Create HDL Wrapper,选择第二项 Let Vivado manage Wrapper and auto-update,点击OK,
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    10.之后看下源码的层次结构,可以看到system_wrapper.v 就是顶层文件,调用了CPU。
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    C- Generating Bitstream
    在 Flow Navigator窗口中的Program and Debug部分,单击Generate Bitstream,将出现一个对话框,要求保存所做的修改,单击Save保存;也可能会出现一个对话框,显示“No Implementation Result Available”,单击Yes运行综合和实现。 生成比特流可能会在综合后执行整个实现过程,单击是以在出现提示时运行实现。 实现可能需要一段时间才能完成,具体取决于计算机的性能。
    执行Generate Bitstream这个过程时首先会自动进行分析综合和实现。(这里也可以直接先点击Run Synthesis进行综合,然后点击Run Implementation进行实现,然后再点击Generate Bitstream生成比特流) 。完成后,可直接点Cancel.
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    2.选择File>Launch SDK,打开SDK,注意所有与设计相关的文件(包括IP)已经在上一步导出到SDK中,导出的资源中包括比特流。
    3.新建FSBL工程,生成fsbl.elf文件
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    4.在下一个窗口中, 输入文件名fsbl以及默认如下参数:
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    5.选择Zynq FSBL工程,点击Finish。
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    工程完成后,编译完成,产生fsbl.elf文件。
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    分别新建工程cpu0选择ps7_cortexa9_0和新建工程cpu1选择ps7_cortexa9_1
    建立完成,分别展开cpu0和cpu1并做如下修改。
    cpu0
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    cpu1
    ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
    文章图片

    打开cpu0的helloworld,键入如下代码:
#include #include "platform.h" #include "xgpiops.h" XGpioPs Instance ; XGpioPs_Config Config ; static void init_gpio( void ){ static int init_done = 0; if (init_done ==1) return ; init_done = 1; XGpioPs_CfgInitialize ( &Instance , &Config,0xe000a000 ); } void set_gpio(int pin ,int val)//0-117 { init_gpio(); XGpioPs_SetOutputEnablePin(&Instance , pin, 1 ); XGpioPs_SetDirectionPin(&Instance ,pin, 1 ); XGpioPs_WritePin(&Instance ,pin , (val!=0) ); } int get_gpio(int pin ){ //0-117 init_gpio(); XGpioPs_SetDirectionPin(&Instance, pin, 0); XGpioPs_SetOutputEnablePin(&Instance,pin, 0); return ( 0!= XGpioPs_ReadPin(&Instance, pin ) ) ; } void start_CPU1(unsigned int PC ){ //Disable cache on OCM Xil_SetTlbAttributes(0xFFFF0000,0x14de2); // S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0 *(volatile unsigned int *) (0xfffffff0) = PC ; dmb(); __asm__("sev"); } void test_MIO7(){ while(1){ usleep(1000*200); set_gpio(7,1); usleep(1000*200); set_gpio(7,0); } } int main() { init_platform(); start_CPU1(0x200000); test_MIO7(); // print("Hello World\n\r"); cleanup_platform(); return 0; } 打开cpu1的helloworld,键入如下代码: #include #include "platform.h" void print(char *str); int main() { init_platform(); while (1){ print("Hello World from CPU1 \n\r"); usleep(1000*100); } cleanup_platform(); return 0; }

Ctrl+S编译完成,生成了cpu0.elf、cpu1.elf文件。
把fsbl.elf、cpu0.elf、cpu1.elf文件准备好根据前面的方法生成.bin文件.mcs文件。
ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
文章图片

然后点击makeboot,即可生成.bin文件.mcs文件。
ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
文章图片

ZYNQ7000|ZYNQ7000 学习(二十一)ZYNQ7 双核处理的运行机制的原理和实现步骤
文章图片

可以把BOOT.BIN复制到sd卡设置成sd卡启动,把BOOT.mcs制作成QSPI FLASH启动文件。

    推荐阅读