0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

ZYNQ开发双核运行原理及过程

454398 来源:FPGA技术联盟 作者:FPGA技术联盟 2020-12-05 10:48 次阅读

双核运行原理

ZYNQ是一种主从关系的AMP架构,通过松散耦合共享资源,允许两个处理器同时运行自己的操作系统或者裸机应用程序,在各自运行自己的程序或者系统的时候,可以通过共享内存进行双核之间的交互。双核启动中,cpu0完成系统的初始化,与cpu1进行通信,读写共享内存。

共享资源防止冲突

1. DDR的内存使用,CPU0使用内存地址为0x00100000-0x001fffff,CPU1的使用地址应该避开这段地址,使用地址设为0x00200000-0x003fffff;

2. CPU1不使用L2内存,仅仅CPU0使用;

3. CPU1从核心在PL的中断路由到PPI控制器,通过使用PPI将中断路由到核心,而CPU0通过ICD路由到核心;

4. 当CPU0访问共享内存的时候,CPU1不访问,同理,CPU1访问共享内存的时候,CPU0不访问。

双核运行的过程

ZYNQ是一个可扩展平台,就是有FPGA作为外设的A9双核处理器,它的启动流程与FPGA完全不同,而与传统的ARM处理器类似,ZYNQ的启动配置需要多个处理步骤,通常情况,需要包含以下3个阶段:

1. 阶段1:在芯片上电运行后,处理器自动开始stage0-boot,就是片内的BOORROM中的代码,上电复位或者热复位后,处理器执行不可修改的代码;

2. 阶段2:BOORROM初始化CPU和一些外设后,读取下一个阶段所需的程序代码FSBL(first stage boot loader),它是可以由用户修改控制的代码;

3. 阶段3:这是用户基于BSP(板级支持包),也可以是操作系统的启动引导程序,这个阶段完全是在用户的控制下实现的。

系统上电启动后,第0阶段启动代码判断启动模式,将第一阶段启动代码FSBL下载到DDR中并且执行。FSBL会配置硬件比特流文件,加载CPU0可执行文件和CPU1可执行文件到DDR对应的链接地址,在这一阶段,所有代码在CPU0中执行,在执行CPU0程序的时候,把CPU1上将要执行的应用程序执行地址写入到OCM的0xFFFFFFF0地址,然后执行 SEV汇编指令,激活CPU1,CPU1激活后,将会到OCM的0xFFFFFFF0地址读取其数值,其数值就是CPU1执行可执行程序的地址,CPU1将从该地址执行。

双核运行的配置

建立工程的区别

核0建立工程和以前一样,但是核1建立工程与以前不同,需要单独建立一个板级支持包bsp。建立CPU1的板级支持包步骤:

1. 在SDK主界面主菜单下,选择File->New->Board Support Package;

2. 出现“New Board Support Package Project”对话框,如图1所示;

图1 新建cpu1板级支持包

点击finish建立好支持包后,出现“Board Support Package Settings”对话框,在界面左侧窗口中,展开Overview,在展开项中,找到并展开drivers,找到ps_cortexa9_1,并选择它;

在右侧的Configuration for OS界面中,找到名字为extra_compiler_flags一行,将其对应的Value一列的值改为-g –DUSE_AMP_ = 1,如图2所示;

图2 板级开发包的属性设置

建立好板级开发包后,建立cpu1的sdk工程,该工程的配置与和0也有不同,就是在新建工程对话框的参数配置要与核0不同,其核心选择核心1,板级支持包选择刚刚建立的cpu1的板级支持包(proccessor:ps7_cortexa9_1;Borad Support Package:app_cpu1_bsp),建立好双核的应用工程和板级开发包后,进行软件的设计。

软件设计

1. cpu0的软件,在fsbl启动cpu0程序后,其程序需要增加启动cpu1的流程代码;

2. cpu0和cpu1的软件需要有一片共享内存,该内存不能被cache化;

3. 通过共享内存的分时访问,设计两个cpu的程序流程。

增加启动cpu1的代码如下:

#define sev() __asm__("sev")

#define CPU1STARTADDR 0xFFFFFFF0

#define CPU1STARTMEM 0x10000000

void StartCpu1(void)

{

Xil_Out32(CPU1STARTADDR,CPU1STARTMEM);

dmb();

sev();

}

禁用共享内存的代码:

#define COMM_VAL (*(volatile unsigned long *)(0xffff0000))

Xil_SetTlbAttributes(0xffff0000,0x14de2);

双核源码与测试

利用共享内存做通讯的例子

Cpu0代码:

#include "stdio.h"

#include "xil_io.h"

#include "xil_mmu.h"

#include "xil_exception.h"

#include "xpseudo_asm.h"

#include "sleep.h"

#define COMM_VAL (*(volatile unsigned long *)(0xffff0000))

#define sev() __asm__("sev")

#define CPU1STARTADDR 0xFFFFFFF0

#define CPU1STARTMEM 0x10000000

void StartCpu1(void)

{

Xil_Out32(CPU1STARTADDR,CPU1STARTMEM);

dmb();

sev();

}

int main(void)

{

Xil_SetTlbAttributes(0xffff0000,0x14de2);

StartCpu1();

COMM_VAL = 0;

while(1)

{

print("CPU0:hello world CPU0/r/n");

sleep(2);

COMM_VAL = 1;

while(COMM_VAL == 1);

}

return 0;

}

Cpu1代码:

#include "stdio.h"

#include "xil_io.h"

#include "xil_mmu.h"

#include "xil_exception.h"

#include "xpseudo_asm.h"

#include "xparameters.h"

#include "sleep.h"

#define COMM_VAL (*(volatile unsigned long *)(0xffff0000))

int main(void)

{

Xil_SetTlbAttributes(0xffff0000,0x14de2);

while(1)

{

while(COMM_VAL == 0);

print("CPU1:hello world CPU1/r/n");

sleep(2);

COMM_VAL = 0;

}

return 0;

}

测试结果:

将上述程序生成boot.bin文件,然后下载到flash,启动后通过串口助手可以收到cpu0与cpu1的打印信息,每间隔两秒打印一次,如图3所示。

图3 程序测试

利用软件中断做通讯的例子

该例子中,cpu0和cpu1都注册两个软件中断,将1号软件中断注册给cpu1,表示cpu0发送中断给cpu1,将2号软件中断注册给cpu0,表示cpu1发送中断给cpu0;然后在程序运行时,cpu0触发1号软件中断,此时cpu1正在运行主程序被该中断中断,进入中断服务函数,其处理完中断后触发2号软件中断,此时该中断会中断cpu0,进入中断服务函数,cpu0处理完中断后回到主函数,再触发1号软件中断,往复运行。

Cpu0代码:

/*

* app_cpu0.c

*

* Created on: 2019年3月27日

* Author: dz

*/

#include "xil_cache.h"

#include "xparameters.h"

#include "xil_mmu.h"

#include "xil_misc_psreset_api.h"

#include "xil_exception.h"

#include "xscugic.h"

#include "xuartps.h"

#include "stdio.h"

#include "sleep.h"

volatile u8 software_intr_received = 0;

#define CORE0_TO_CORE1_INTR_ID 0x01

#define CORE1_TO_CORE0_INTR_ID 0x02

void sys_intr_init(void);

void Setup_Intr_Exception(XScuGic * IntcInstancePtr);

void Scu_Intr_Init(XScuGic* IntancePtr,int Scu_Int_ID);

void Init_Software_Intr(XScuGic *GicInstancePtr, Xil_InterruptHandler IntrHanedler, u16 SoftwareIntrId, u8 CpuId);

void Gen_Software_Intr(XScuGic *GicInstancePtr, u16 SoftwareIntrId, u32 CpuId);

void Cpu0_Intr_Hanedler(void *Callback);

void Cpu1_Intr_Hanedler(void *Callback);

XScuGic ScuGic;

void delay(unsigned int count)

{

int i = 0;

for(i = 0;i

}

int main(void)

{

sys_intr_init();

xil_printf("cpu0 start!/r/n");

while(1)

{

if(XPAR_CPU_ID == 0)

Gen_Software_Intr(&ScuGic, CORE0_TO_CORE1_INTR_ID, XSCUGIC_SPI_CPU1_MASK);

/*Gen_Software_Intr(&ScuGic, CORE0_TO_CORE1_INTR_ID, XSCUGIC_SPI_CPU0_MASK);*/

else

Gen_Software_Intr(&ScuGic, CORE1_TO_CORE0_INTR_ID, XSCUGIC_SPI_CPU0_MASK);

delay(200000000);

if(1 == software_intr_received)

{

xil_printf("cpu0_main/r/n");

software_intr_received = 0;

}

}

return 0;

}

void sys_intr_init(void)

{ Scu_Intr_Init(&ScuGic,XPAR_SCUGIC_SINGLE_DEVICE_ID);

// if(XPAR_CPU_ID == 0)

Init_Software_Intr(&ScuGic, Cpu0_Intr_Hanedler, CORE0_TO_CORE1_INTR_ID, 0);

// else

Init_Software_Intr(&ScuGic, Cpu1_Intr_Hanedler, CORE1_TO_CORE0_INTR_ID, 1);

Setup_Intr_Exception(&ScuGic);

}

void Setup_Intr_Exception(XScuGic * IntcInstancePtr)

{

//connect hardware interrupt

Xil_ExceptionInit();

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr);

//enable hardware interrupt

Xil_ExceptionEnable();

}

void Scu_Intr_Init(XScuGic* IntancePtr,int Scu_Int_ID)

{

XScuGic_Config *ScuConfigPtr;

//find device

ScuConfigPtr = XScuGic_LookupConfig(Scu_Int_ID);

//config scuint

XScuGic_CfgInitialize(IntancePtr, ScuConfigPtr,ScuConfigPtr->CpuBaseAddress);

}

void Cpu0_Intr_Hanedler(void *Callback)

{

xil_printf("receive interrupt from core1!/r/n");

software_intr_received = 1;

}

void Cpu1_Intr_Hanedler(void *Callback)

{

xil_printf("receive interrupt from core0!/r/n");

software_intr_received = 1;

}

void Init_Software_Intr(XScuGic *GicInstancePtr, Xil_InterruptHandler IntrHanedler, u16 SoftwareIntrId, u8 CpuId)

{

int Status;

// XScuGic_SetPriorityTriggerType(GicInstancePtr, SoftwareIntrId, 0xB0, 0x2);

XScuGic_SetPriorityTriggerType(GicInstancePtr, SoftwareIntrId, 0xd0, 0x3);

Status = XScuGic_Connect(GicInstancePtr, SoftwareIntrId,

(Xil_InterruptHandler)IntrHanedler, NULL);

if (Status != XST_SUCCESS) {

xil_printf("core%d: interrupt %d set fail!/r/n", XPAR_CPU_ID, SoftwareIntrId);

return;

}

XScuGic_InterruptMaptoCpu(GicInstancePtr, CpuId, SoftwareIntrId); //map the the Software interrupt to target CPU

XScuGic_Enable(GicInstancePtr, SoftwareIntrId);//enable the interrupt for the Software interrupt at GIC

}

void Gen_Software_Intr(XScuGic *GicInstancePtr, u16 SoftwareIntrId, u32 CpuId)

{

int Status;

Status = XScuGic_SoftwareIntr(GicInstancePtr, SoftwareIntrId, CpuId);

if (Status != XST_SUCCESS) {

xil_printf("core%d: interrupt %d gen fail!/r/n", XPAR_CPU_ID, SoftwareIntrId);

return;

}

}

Cpu1代码:

/*

* app_cpu0.c

*

* Created on: 2019年3月27日

* Author: dz

*/

#include "xil_cache.h"

#include "xparameters.h"

#include "xil_mmu.h"

#include "xil_misc_psreset_api.h"

#include "xil_exception.h"

#include "xscugic.h"

#include "xuartps.h"

#include "stdio.h"

#include "sleep.h"

volatile u8 software_intr_received = 0;

#define CORE0_TO_CORE1_INTR_ID 0x01

#define CORE1_TO_CORE0_INTR_ID 0x02

void sys_intr_init(void);

void Setup_Intr_Exception(XScuGic * IntcInstancePtr);

void Scu_Intr_Init(XScuGic* IntancePtr,int Scu_Int_ID);

void Init_Software_Intr(XScuGic *GicInstancePtr, Xil_InterruptHandler IntrHanedler, u16 SoftwareIntrId, u8 CpuId);

void Gen_Software_Intr(XScuGic *GicInstancePtr, u16 SoftwareIntrId, u32 CpuId);

void Cpu0_Intr_Hanedler(void *Callback);

void Cpu1_Intr_Hanedler(void *Callback);

XScuGic ScuGic;

void delay(unsigned int count)

{

int i = 0;

for(i = 0;i

;

}

int main(void)

{

sys_intr_init();

// Gen_Software_Intr(&ScuGic, CORE1_TO_CORE0_INTR_ID, XSCUGIC_SPI_CPU1_MASK);

xil_printf("cpu1 start!/r/n");

while(1)

{

if(1 == software_intr_received)

{

software_intr_received = 0;

if(XPAR_CPU_ID == 0)

Gen_Software_Intr(&ScuGic, CORE0_TO_CORE1_INTR_ID, XSCUGIC_SPI_CPU1_MASK);

/* Gen_Software_Intr(&ScuGic, CORE0_TO_CORE1_INTR_ID, XSCUGIC_SPI_CPU0_MASK);*/

else

Gen_Software_Intr(&ScuGic, CORE1_TO_CORE0_INTR_ID, XSCUGIC_SPI_CPU0_MASK);

delay(200000000);

xil_printf("cpu1_main/r/n");

}

}

return 0;

}

void sys_intr_init(void)

{ Scu_Intr_Init(&ScuGic,XPAR_SCUGIC_SINGLE_DEVICE_ID);

// if(XPAR_CPU_ID == 0)

Init_Software_Intr(&ScuGic, Cpu0_Intr_Hanedler, CORE0_TO_CORE1_INTR_ID, 0);

// else

Init_Software_Intr(&ScuGic, Cpu1_Intr_Hanedler, CORE1_TO_CORE0_INTR_ID, 1);

Setup_Intr_Exception(&ScuGic);

}

void Setup_Intr_Exception(XScuGic * IntcInstancePtr)

{

//connect hardware interrupt

Xil_ExceptionInit();

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr);

//enable hardware interrupt

Xil_ExceptionEnable();

}

void Scu_Intr_Init(XScuGic* IntancePtr,int Scu_Int_ID)

{

XScuGic_Config *ScuConfigPtr;

//find device

ScuConfigPtr = XScuGic_LookupConfig(Scu_Int_ID);

//config scuint

XScuGic_CfgInitialize(IntancePtr, ScuConfigPtr,ScuConfigPtr->CpuBaseAddress);

}

void Cpu0_Intr_Hanedler(void *Callback)

{

xil_printf("receive interrupt from core1!/r/n");

software_intr_received = 1;

}

void Cpu1_Intr_Hanedler(void *Callback)

{

xil_printf("receive interrupt from core0!/r/n");

software_intr_received = 1;

}

void Init_Software_Intr(XScuGic *GicInstancePtr, Xil_InterruptHandler IntrHanedler, u16 SoftwareIntrId, u8 CpuId)

{

int Status;

XScuGic_SetPriorityTriggerType(GicInstancePtr, SoftwareIntrId, 0xB0, 0x2);

// XScuGic_SetPriorityTriggerType(GicInstancePtr, SoftwareIntrId, 0xd0, 0x3);

Status = XScuGic_Connect(GicInstancePtr, SoftwareIntrId,

(Xil_InterruptHandler)IntrHanedler, NULL);

if (Status != XST_SUCCESS) {

xil_printf("core%d: interrupt %d set fail!/r/n", XPAR_CPU_ID, SoftwareIntrId);

return;

}

XScuGic_InterruptMaptoCpu(GicInstancePtr, CpuId, SoftwareIntrId); //map the the Software interrupt to target CPU

XScuGic_Enable(GicInstancePtr, SoftwareIntrId);//enable the interrupt for the Software interrupt at GIC

}

void Gen_Software_Intr(XScuGic *GicInstancePtr, u16 SoftwareIntrId, u32 CpuId)

{

int Status;

Status = XScuGic_SoftwareIntr(GicInstancePtr, SoftwareIntrId, CpuId);

if (Status != XST_SUCCESS) {

xil_printf("core%d: interrupt %d gen fail!/r/n", XPAR_CPU_ID, SoftwareIntrId);

return;

}

}

将上述程序生成boot.bin文件,然后下载到flash,启动后通过串口助手可以收到cpu0与cpu1的打印信息,每间隔两秒打印一次,如图4所示。

图4 测试结果
编辑:hfy


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Zynq
    +关注

    关注

    9

    文章

    607

    浏览量

    47091
收藏 人收藏

    评论

    相关推荐

    如何实现的AMP启动

    之前章节中涉及到PS端的裸机程序都是在CPU0上运行的,弄的差点就忘了ZYNQ是拥有两个A9硬核的强劲芯片,所以我们必须要学会怎么让两个CPU核心同时运行,不然就和之前开发单片机有什么
    的头像 发表于 09-14 09:07 2295次阅读

    [XILINX] 正点原子ZYNQ7035/7045/7100开发板发布、ZYNQ 7000系列、ARM、PCIe2.0、SFPX2!

    正点原子FPGA新品ZYNQ7035/7045/7100开发板,ZYNQ 7000系列、ARM、PCIe2.0、SFPX2! 正点原子Z
    发表于 09-02 17:18

    正点原子ZYNQ7015开发板!ZYNQ 7000系列、ARM、PCIe2.0、SFPX2,性能强悍,资料丰富!

    本帖最后由 jf_85110202 于 2024-9-14 10:33 编辑 正点原子ZYNQ7015开发板!ZYNQ 7000系列、
    发表于 09-14 10:12

    基于Zynq的嵌入式开发流程

    A53都有,对于ZYNQ7020来说,它集成了一块ARM Cortex-A9处理器,性能足够运行Linux下图为Zynq-7000系列S
    发表于 08-23 08:15

    小白求助如何对ARM进行分运行

    ARM如何分运行,一个运行操作系统 一个跑裸机程序?有何思路?
    发表于 01-13 07:27

    imx6dl是单核运行还是运行

    1.飞凌imx6dl的板子,请问是运行在单核模式还是模式。uboot的maxcpus参数的值是1,应该是单核,但是内核里看到了cpu0cpu1,有点疑惑。2.如果是
    发表于 12-05 06:39

    Linux驱动开发笔记:对zynq PL部分IP的驱动开发过程

    在对zynq进行Linux驱动开发时,除了需要针对zynq内ARM自带的控制器适配驱动外,还需要对zynq PL部分的IP进行驱动
    发表于 06-30 15:10 9600次阅读

    Zynq UltraScale+ MPSoC在仿真板上的运行展示

    Xilinx展示Zynq UltraScale + MPSoC在由6个FPGA组成的硬件仿真板上运行,以实现四ARM Cortex-A53,
    的头像 发表于 11-26 06:44 3410次阅读

    在OMAPL138的ARM与DSP上实现TL IPC通信开发

    TL_IPC是广州创龙独立开发的一种通讯协议,这种开发方式适用于通信逻辑相对简单的程序的
    发表于 08-06 08:34 1365次阅读
    在OMAPL138的ARM<b class='flag-5'>核</b>与DSP<b class='flag-5'>核</b>上实现TL IPC<b class='flag-5'>双</b><b class='flag-5'>核</b>通信<b class='flag-5'>开发</b>

    ZYNQ学习要点:通信

    今天,我们聊聊通信。通信的基础是已经建立好了核工程,且配置完成。两个CPU之间传递数据,采用了共享内存,共享内存设置在OCM(On
    的头像 发表于 11-26 13:47 3665次阅读
    <b class='flag-5'>ZYNQ</b>学习要点:<b class='flag-5'>双</b><b class='flag-5'>核</b>通信

    典型的ZYNQ开发流程

    Zynq UltraScale+ MPSoC系列是Xilinx第二代Zynq平台。其亮点在于FPGA里包含了完整的ARM处理子系统(PS),包含了四Cortex-A53处理器或
    的头像 发表于 02-08 14:39 7223次阅读
    典型的<b class='flag-5'>ZYNQ</b><b class='flag-5'>开发</b>流程

    ZYNQ OpenAMPARM通信案例开发手册

    ZYNQ OpenAMPARM通信案例开发手册
    发表于 07-06 10:27 32次下载

    关于zynq 运行的流水灯工程

    zynq 7000 一般有2个cpu (arm A9),我们一般都用一个cpu0,本实验让2个cpu 都运行起来,cpu0 运行操作系统petalinux 2018.2, cpu1: 裸机流水灯。同时通过共享内存的方式,实现2个
    发表于 09-13 09:22 1035次阅读

    ZYNQARM分别运行不同的操作系统(基于OpenAMP)

    ZYNQ系列是Xilinx最近几年推出的多核异构SoC,集成了FPGA和ARM处理器,ARM部分是ARM Cortex-A9处理器,核可以同时对称使用,还可以非对称使用。
    的头像 发表于 12-05 13:46 4422次阅读

    Xilinx ZYNQARM通信开发实例

    前言:本文主要介绍基于OpenAMP框架的ARM通信案例的使用说明,CPU0(Master)运行Linux系统,CPU1(Remote)运行裸机或FreeRTOS程序。CPU0
    的头像 发表于 12-27 13:48 1937次阅读
    Xilinx <b class='flag-5'>ZYNQ</b><b class='flag-5'>双</b><b class='flag-5'>核</b>ARM通信<b class='flag-5'>开发</b>实例