第九节,ZYNQ的双核启动
ZYNQ的双核启动
1 双核运行原理
ZYNQ是一种主从关系的AMP架构,通过松散耦合共享资源,允许两个处理器同时运行自己的操作系统或者裸机应用程序,在各自运行自己的程序或者系统的时候,可以通过共享内存进行双核之间的交互。双核启动中,cpu0完成系统的初始化,控制cpu1的启动,与cpu1通信,读写共享内存;与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个阶段:
- 阶段0:在芯片上电运行后,处理器自动开始stage0-boot,就是片内的BOORROM中的代码,上电复位或者热复位后,处理器执行不可修改的代码;
- 阶段1:BOORROM初始化CPU和一些外设后,读取下一个阶段所需的程序代码FSBL(first stage boot loader),它是可以由用户修改控制的代码;
- 阶段2:这是用户基于BSP(板级支持包),也可以是操作系统的启动引导程序,这个阶段完全是在用户的控制下实现的;
系统上电启动后,第0阶段启动代码判断启动模式,将第一阶段启动代码FSBL下载到DDR中并且执行。FSBL会配置硬件比特流文件,加载CPU0可执行文件和CPU1可执行文件到DDR对应的链接地址,在这一阶段,所有代码在CPU0中执行,在执行CPU0程序的时候,把CPU1上将要执行的应用程序执行地址写入到OCM的0xFFFFFFF0地址,然后执行 SEV汇编指令,激活CPU1,CPU1激活后,将会到OCM的0xFFFFFFF0地址读取其数值,其数值就是CPU1执行可执行程序的地址,CPU1将从该地址执行。
2 双核运行的配置
2.1 建立工程的区别:
核0建立工程和以前一样,但是核1建立工程与以前不同,需要单独建立一个板级支持包bsp。建立CPU1的板级支持包步骤:
- 在SDK主界面主菜单下,选择File->New->Board Support Package;
- 出现“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),建立好双核的应用工程和板级开发包后,进行软件的设计。
2.2 软件设计:
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);
3 双核源码与测试
3.1 利用共享内存做通讯的例子:
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 程序测试
3.2 利用软件中断做通讯的例子:
该例子中,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 < count;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 < count;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 测试结果
第九节,ZYNQ的双核启动相关推荐
- JavaWeb学习笔记(狂神版)--- 第九节 Cookie与Session
第九节 Cookie与Session 目录 第九节 Cookie与Session 9.1 会话 9.2 Cookie 9.3 Session 9.1 会话 会话:用户打开一个浏览器,点了很多超链接,访 ...
- zynq7000创建fsbl启动_Xilinx Zynq 7000 FSBL启动分析(一)
花了几天看完了FSBL的代码,在这里做个总结,分析一下zynq的启动过程. 众所周知,xilinx zynq 7000系列的芯片中包括两个部分,PS和PL,也就是FPGA的逻辑编程的部分跟嵌入式ARM ...
- 【java】兴唐课程第五节到第九节知识点总结
第九节 1. 代码:void readBook(String- bookNames) 表示不确定参数的个数,此时变量为一个数组. 2.当方法中的参数名称(如stuname)和属性名称相同时. this ...
- Python编程基础:第九节 逻辑运算Logical Operators
第九节 逻辑运算Logical Operators 前言 实践 前言 常用的逻辑运算共分为三种:与(and).或(or).非(not).与运算就是同真才真,有假则假:或运算就是有真则真,同假才假:非运 ...
- 【Python基础知识-pycharm版】第九节_面向对象的三大特征
第九节 方法 方法没有重载 私有属性和私有方法(实现封装) @property装饰器_get和set方法 面向对象的三大特征说明(封装.继承.多态) 继承 方法的重写(类成员的继承和重写) 查看类的继 ...
- 第九节--绑定 -- Classes and Objects in PHP5 [9](转)
/* +-------------------------------------------------------------------------------+ | = 本文为Haohappy ...
- Kotlin学习笔记 第二章 类与对象 第九节 泛型
参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...
- 《如何搭建小微企业风控模型》第九节 单变量分析(上)节选
<如何搭建小微企业风控模型>第九节 单变量分析(上)节选 第一章 小微企业数据风控技术的框架 小微企业数据贷发展情况概述 搭建小微企业风控模型所需知识 风控模型概览 第二章 强相关变量:企 ...
- 第四章第九节数据资产盘点-数据资产目录分类
第四章第九节数据资产盘点-数据资产目录分类 在形成数据资产清单以后,如何将清单进行分类?关于数据资产目录的分类,有几种方法,一是参考行业数据分类框架.二是参考监管数据分类.三是根据数据管理实践,结合企 ...
最新文章
- 腾讯成联合国全球合作伙伴,TDSQL如何支撑史上最大规模全球会议
- 我们团队设计的一个基于微服务的高并发服务器架构
- 操作系统内存管理--简单、页式、段式、段页式
- 故障转移集群仲裁盘_MongoDB负载均衡、故障转移及海量数据应对方案
- ‘cross-env‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
- python找出矩阵中的最大值_Python / Scipy:找到矩阵的“有界”最小值/最大值
- java 数组 null值_数组的元素String在java中包含null
- 更新sdk_即构ZegoLiveRoom SDK版本更新,新增多项功能及自定义设置
- 基于python的电影推荐系统
- java 获取中文拼音首字母(缩写) 含pinyin4j maven包
- 锐浪报表数据源access_锐浪报表应用系列三
- WZ-S甲醛传感器使用说明代码应用案例笔记
- java中console_java的Console类的使用方法及实例
- 服务器上很多iOS临时文件,iOS的临时文件夹位置(iOS temporary folder location)
- win10计算机丢失msvcr,计算机中丢失msvcr110.dll怎么办?Win10系统中丢失msvcr110.dll解决方法...
- RN动画Animated
- 以太联盟 基于区块链技术的角色扮演对战游戏
- 《2022中国企业数字化办公创新与实践产业研究报告》附下载丨三叠云
- Docker镜像创建的三种方式详解——dockerfile制作apache镜像
- oracle修改数据前备份,Oracle 之利用BBED修改数据块SCN—-没有备份数据文件的数据恢复...