写在前面
承接前文:ZYNQ-双核AMP通信(一),前文对双核AMP架构通信的相关内容进行了简单的介绍,本文完成AMP架构通信的程序并验证。
开发环境
vivado 18.3&SDK,PYNQ-Z2开发板。
工程设计
CPU0 接收串口的数据,并写入OCM 中,然后利用软件产生中断触发 CPU1; CPU1 接收到中断后,根据从 OCM 中读出的数据并用串口打印,并在控制结束后触发 CPU0 中断,实现了双核 CPU 通信的功能。
系统框图
硬件平台搭建
首先新建工程,创建 block design。添加ZYNQ7 ip,根据本次工程需要对IP进行配置。勾选本次工程使用的资源。
这里添加SPI和SD的资源是为了进行双核的固化程序的验证。
硬件系统构建完成如下:
然后我们进行generate output product 然后生成HDL封装。这里只用到了MIO引脚,所以不需要进行管脚分配,XADC测量是内部的电压信息,并且使用的是PS_XADC接口。点击导出硬件资源(不包含bit流文件,因为只用到了PS资源),接着launch SDK。
SDK软件部分
新建应用工程,这里可以先进行创建CPU0的程序,这里和之前配置相同,无需特殊更改。点击NEXT后,完成建立一个空工程。
然后修改cpu0的DDR的地址空间,打开src文件夹中的lscript.ld文件,该文件是链接脚本,可以进行配置应用程序的地址空间的大小。在图中可以看到OCM的两块区域的对应的大小和起始地址以及该应用程序的DDR的起始地址和大小。
这里修改cpu0占用ddr的一半空间也即把0x1FF00000修改为0x0FF00000。
完成修改后ctrl + s 保存即可完成地址映射的修改。
同样的操作进行新建xpu1工程创建。处理器选择ps7_1。
创建完成后修改ddr的地址映射,这里需要进行简单的计算,cpu0的起始地址为0x100000,大小为0x0FF00000,所以cpu1的起始地址为两者之和,也即为0x1000000
所以对cpu1的程序修改地址映射如下图:
打开板级支持包的设置界面
同时加入AMP的一个交叉编译的宏定义-DUSE_AMP=1
,添加在末尾。
cpu0程序
#include "xparameters.h" #include "xscugic.h" #include "xil_printf.h" #include "xil_exception.h" #include "xil_mmu.h" #include "stdio.h" //宏定义 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断ID #define SHARE_BASE 0xffff0000 //共享OCM首地址 #define CPU1_COPY_ADDR 0xfffffff0 //存放CPU1应用起始地址的地址 #define CPU1_START_ADDR 0x10000000 //CPU1应用起始地址 #define CPU1_ID XSCUGIC_SPI_CPU1_MASK //CPU1 ID #define SOFT_INTR_ID_TO_CPU0 0 //软件中断号 0 ,范围:0~15 #define SOFT_INTR_ID_TO_CPU1 1 //软件中断号 1 ,范围:0~15 //"SEV"指令唤醒CPU1并跳转至相应的程序 #define sev() __asm__("sev") //C语言内嵌汇编写法 send event指令 //函数声明 void start_cpu1(); void cpu0_intr_init(XScuGic *intc_ptr); void soft_intr_handler(void *CallbackRef); //全局变量 XScuGic Intc; //中断控制器驱动程序实例 int rec_flag = 0; //接收标志 char char_input='\0'; //CPU0 main函数 int main() { //S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0 Xil_SetTlbAttributes(SHARE_BASE,0x14de2); //禁用OCM的Cache属性 //S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0 Xil_SetTlbAttributes(CPU1_COPY_ADDR,0x14de2);//禁用0xfffffff0的Cache属性 //启动CPU1 start_cpu1(); //CPU0中断初始化 cpu0_intr_init(&Intc); while(1){ if(rec_flag == 0){ xil_printf("CPU0:请输入字符\r\n"); scanf("%c",&char_input); if(char_input != 13){ //向共享的地址中写入输入的数据 Xil_Out8(SHARE_BASE,char_input); xil_printf("CPU0: %c\n",char_input) ; //给CPU1触发中断 XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_TO_CPU1,CPU1_ID); } rec_flag = 1; } } return 0 ; } //启动CPU1,用于固化程序 void start_cpu1() { //向 CPU1_COPY_ADDR(0Xffffffff0)地址写入 CPU1 的访问内存基地址 Xil_Out32(CPU1_COPY_ADDR, CPU1_START_ADDR); dmb(); //等待内存写入完成(同步) sev(); //通过"SEV"指令唤醒CPU1并跳转至相应的程序 } //CPU0中断初始化 void cpu0_intr_init(XScuGic *intc_ptr) { //初始化中断控制器 XScuGic_Config *intc_cfg_ptr; intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID); XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr, intc_cfg_ptr->CpuBaseAddress); //设置并打开中断异常处理功能 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr); Xil_ExceptionEnable(); XScuGic_Connect(intc_ptr, SOFT_INTR_ID_TO_CPU0, (Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr); XScuGic_Enable(intc_ptr, SOFT_INTR_ID_TO_CPU0); //CPU0软件中断 } //软件中断函数 void soft_intr_handler(void *CallbackRef) { xil_printf("CPU0 : Soft Interrupt from CPU1\n"); rec_flag = 0; }
cpu1程序
#include "xparameters.h" #include "xscugic.h" #include "xil_printf.h" #include "xil_exception.h" #include "xil_mmu.h" #include "stdio.h" //宏定义 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断ID #define SHARE_BASE 0xffff0000 //共享OCM首地址 #define CPU0_ID XSCUGIC_SPI_CPU0_MASK //CPU0 ID #define SOFT_INTR_ID_TO_CPU0 0 //软件中断号 0 ,范围:0~15 #define SOFT_INTR_ID_TO_CPU1 1 //软件中断号 1 ,范围:0~15 //函数声明 void cpu1_intr_init(XScuGic *intc_ptr); void soft_intr_handler(void *CallbackRef); //全局变量 XScuGic Intc; //中断控制器驱动程序实例 int soft_intr_flag = 0; //软件中断的标志 char read_data; //CPU1 main函数 int main() { //S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0 Xil_SetTlbAttributes(SHARE_BASE,0x14de2); //禁用OCM的Cache属性 //CPU1中断初始化 cpu1_intr_init(&Intc); while(1){ if(soft_intr_flag){ read_data = Xil_In8(SHARE_BASE);//从共享OCM中读出数据 xil_printf("CPU1:%c\n",read_data) ; //给给CPU0触发中断 XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_TO_CPU0,CPU0_ID); soft_intr_flag = 0; } } return 0 ; } //CPU1中断初始化 void cpu1_intr_init(XScuGic *intc_ptr) { //初始化中断控制器 XScuGic_Config *intc_cfg_ptr; intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID); XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr, intc_cfg_ptr->CpuBaseAddress); //设置并打开中断异常处理功能 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr); Xil_ExceptionEnable(); XScuGic_Connect(intc_ptr, SOFT_INTR_ID_TO_CPU1, (Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr); XScuGic_Enable(intc_ptr, SOFT_INTR_ID_TO_CPU1); //CPU1软件中断 } //软件中断函数 void soft_intr_handler(void *CallbackRef) { xil_printf("CUP1:Soft Interrupt from CPU0\n") ; soft_intr_flag = 1; }
下载程序
和之前不同这次需要在config界面进行勾选,确保两个cpu的程序都下载进去。如下图:
运行效果
固化双核程序
创建fsbl程序。
点击next,选择FSBL点击finish。
选中cpu0的工程,右击创建镜像工程,
添加cpu1的elf文件
添加完成创建镜像即可。
烧录flash操作如下图:
references
- 正点原子开发视频
- UG585
- xapp1079