前面的例程都是采用单核CPU,有些情况比如多任务处理,并行处理,需要用到双核CPU,本章将开始介绍双核CPU
1.CPU0实现PS端按键中断,控制PS端的LED的亮灭,并向CPU1发出软件中断,让CPU1打印CPU0内存空间的一串字符。
2.CPU1实现PL端按键中断,控制PL端的LED的亮灭,并向CPU0发出软件中断,让CPU0打印CPU1内存空间的一串字符。
3.内存空间的划分,共享内存空间的使用
4.FSBL启动Flash
一、硬件环境搭建
本实验以“ps_hello”例程为基础,添加PL端GPIO,添加axi_gpio_0设置为输出,位宽为1位,连接PL端LED。
添加axi_gpio_0设置为输出,位宽为1位,连接PL端LED
添加axi_gpio_1,连接pl端按键,设置为输入,位宽为1位,并使能中断
自动连接布线
全选
将axi_gpio_1的中断输出连接到CPU的IRQ_F2P端口
修改axi_gpio_1输出为key,修改axi_gpio_0的输出为led
点击,整理布线
Generate Outputs
绑定按键和LED灯的引脚,生成bitstream
led.xdc
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[0]}]
set_property PACKAGE_PIN M14 [get_ports {led_tri_o[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key_tri_i[0]}]
set_property PACKAGE_PIN N15 [get_ports {key_tri_i[0]}]
二、新建工程,在Processor选择ps7_cortexa9_0,也就是CPU0,选择Empty,工程名字为cpu0_app
选择Empty,点击Finish
三、分别在cpu0_app与cpu1_app中分别添加cpu0_app.c与share.h,cpu1_app.c与share.c
cpu0_app.c
/*
* cpu0_app.c
*
* Created on: 2018Äê9ÔÂ17ÈÕ
* Author: myj
*/
#include "xparameters.h"
#include "xscugic.h"
#include "xgpiops.h"
#include "xil_printf.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#include "xpseudo_asm.h"
#include "stdio.h"
#include "share.h"
#include "xil_cache.h"
#include "xtime_l.h"
/* GPIO paramter */
#define MIO_0_ID XPAR_PS7_GPIO_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define KEY_INTR_ID XPAR_XGPIOPS_0_INTR
#define GPIO_INPUT 0
#define GPIO_OUTPUT 1
int key_flag = 0 ;
int soft_flag = 0 ;
XGpioPs GPIO_PTR ;
XScuGic InterrruptInst;
u16 SoftIntrIdToCpu0 = 1 ;
u16 SoftIntrIdToCpu1 = 2 ;
ShareMem *SharePtr ;
u32 CPU1 = 0x2 ;
unsigned char Cpu0_Data[12] = "Hello Cpu1!" ;
unsigned char *Cpu1Data ;
XTime TimerCurr, TimerLast;
#define SHARE_BASE 0x3FFFFF00
int InterruptInit(XScuGic *InstancePtr, u16 DeviceId) ;
int InterruptConnnect(XScuGic *InstancePtr, u16 IntId, void * Handler,void *CallBackRef) ;
int PsGpioSetup(XScuGic *InstancePtr) ;
int GpioHandler(void *CallbackRef);
void SoftHandler(void *CallbackRef) ;
#define sev() __asm__("sev")
#define CPU1STARTADR 0xFFFFFFF0
#define CPU1STARTMEM 0x20000000
void StartCpu1(void)
{
printf("Write the address of the application for CPU1 to 0xFFFFFFF0\r\n");
Xil_Out32(CPU1STARTADR, CPU1STARTMEM);
dmb();
printf("Execute the SEV instruction to cause CPU1 to wake up and jump to the application\r\n");
sev();
}
int main()
{
int Status ;
int LedVal = 0 ;
/*
* Disable cache on OCM S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
*/
Xil_SetTlbAttributes(0xFFFF0000,0x14de2);
/*
* Wake up CPU1
*/
StartCpu1();
SharePtr = (ShareMem *)SHARE_BASE ;
/*
* Initial interrupt
*/
Status = InterruptInit(&InterrruptInst, INTC_DEVICE_ID) ;
if (Status != XST_SUCCESS)
return XST_FAILURE ;
/*
* Setup Ps Gpio and Enable Gpio interrupt
*/
PsGpioSetup(&InterrruptInst) ;
/*
* Connect interrupt
*/
InterruptConnnect(&InterrruptInst, SoftIntrIdToCpu0, (void *)SoftHandler, (void *)&InterrruptInst) ;
while(1)
{
if (key_flag)
{
/*
* initial shared Struct
*/
SharePtr->addr = (unsigned int *)&Cpu0_Data ;
SharePtr->length = sizeof(Cpu0_Data) ;
/*
* Write led value
*/
XGpioPs_WritePin(&GPIO_PTR, 0, LedVal) ;
LedVal = ~LedVal ;
/*
* Software interrupt to CPU1
*/
XScuGic_SoftwareIntr(&InterrruptInst, SoftIntrIdToCpu1, CPU1) ;
key_flag = 0 ;
}
else if (soft_flag)
{
/*
* When Software interrupt, print data in CPU1
*/
Cpu1Data = (unsigned char *)SharePtr->addr ;
xil_printf("This is CPU0, Now Start to Print:\r\n") ;
xil_printf("%s\r\n", Cpu1Data) ;
soft_flag = 0 ;
}
}
return 0 ;
}
int PsGpioSetup(XScuGic *InstancePtr)
{
XGpioPs_Config *GPIO_CONFIG ;
int Status ;
GPIO_CONFIG = XGpioPs_LookupConfig(MIO_0_ID) ;
Status = XGpioPs_CfgInitialize(&GPIO_PTR, GPIO_CONFIG, GPIO_CONFIG->BaseAddr) ;
if (Status != XST_SUCCESS)
{
return XST_FAILURE ;
}
/* set MIO 50 as input */
XGpioPs_SetDirectionPin(&GPIO_PTR, 50, GPIO_INPUT) ;
/* set MIO 0 as output */
XGpioPs_SetDirectionPin(&GPIO_PTR, 0, GPIO_OUTPUT) ;
/* enable MIO 0 output */
XGpioPs_SetOutputEnablePin(&GPIO_PTR, 0, GPIO_OUTPUT) ;
/* set interrupt type */
XGpioPs_SetIntrTypePin(&GPIO_PTR, 50, XGPIOPS_IRQ_TYPE_EDGE_RISING) ;
/* enable GPIO interrupt */
XGpioPs_IntrEnablePin(&GPIO_PTR, 50) ;
InterruptConnnect(InstancePtr, KEY_INTR_ID, (void *)GpioHandler, (void *)&GPIO_PTR) ;
return XST_SUCCESS ;
}
int InterruptInit(XScuGic *InstancePtr, u16 DeviceId)
{
XScuGic_Config *IntcConfig;
int Status ;
IntcConfig = XScuGic_LookupConfig(DeviceId);
Status = XScuGic_CfgInitialize(InstancePtr, IntcConfig, IntcConfig->CpuBaseAddress) ;
if (Status != XST_SUCCESS)
return XST_FAILURE ;
/*
* Initialize the exception table
*/
Xil_ExceptionInit();
/*
* Register the interrupt controller handler with the exception table
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
InstancePtr);
/*
* Enable non-critical exceptions
*/
Xil_ExceptionEnable();
return XST_SUCCESS ;
}
int InterruptConnnect(XScuGic *InstancePtr, u16 IntId, void * Handler,void *CallBackRef)
{
XScuGic_Connect(InstancePtr, IntId,
(Xil_InterruptHandler)Handler,
CallBackRef) ;
XScuGic_Enable(InstancePtr, IntId) ;
return XST_SUCCESS ;
}
int GpioHandler(void *CallbackRef)
{
XGpioPs *GpioInstancePtr = (XGpioPs *)CallbackRef ;
int Int_val ;
float Interval_time ;
Int_val = XGpioPs_IntrGetStatusPin(GpioInstancePtr, 50) ;
/* clear key interrupt */
XGpioPs_IntrClearPin(GpioInstancePtr, 50) ;
if (Int_val)
{
TimerLast = TimerCurr ;
XTime_GetTime(&TimerCurr) ;
Interval_time = ((float)(TimerCurr-TimerLast))/((float)COUNTS_PER_SECOND) ;
/* Key debounce, set decounce time to 50ms*/
if (Interval_time < 0.05)
{
key_flag = 0 ;
return 0 ;
}
else
{
key_flag = 1 ;
}
}
return 1 ;
}
void SoftHandler(void *CallbackRef)
{
xil_printf("Soft Interrupt from CPU1\r\n") ;
soft_flag = 1 ;
}
share.h
typedef struct
{
unsigned int length;
unsigned int *addr;
}ShareMem;
cpu1_app.c
/*
* cpu1_app.c
*
* Created on: 2018Äê9ÔÂ17ÈÕ
* Author: myj
*/
#include "xparameters.h"
#include "xscugic.h"
#include "xgpio.h"
#include "xil_printf.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#include "sleep.h"
#include "share.h"
#include "xtime_l.h"
#define KEY_DEVICE_ID XPAR_AXI_GPIO_1_DEVICE_ID
#define LED_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID
#define KEY_INTR_ID XPAR_FABRIC_AXI_GPIO_1_IP2INTC_IRPT_INTR
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define LED_DIR 0x0f
#define LED_CHANNEL 1
#define KEY_DIR 0x0f
#define KEY_CHANNEL 1
#define BTN_INT XGPIO_IR_CH1_MASK
ShareMem *SharePtr ;
unsigned char Cpu0_Data[12] = "Hello Cpu0!" ;
#define SHARE_BASE 0x3FFFFF00
XGpio Gpio_key ;
XGpio Gpio_led ;
XScuGic InterrruptInst;
int key_flag = 0 ;
int soft_flag = 0 ;
u32 CPU0 = 0x1 ;
u16 SoftIntrIdToCpu0 = 1 ;
u16 SoftIntrIdToCpu1 = 2 ;
unsigned char Cpu1_Data[12] = "Hello Cpu0!" ;
unsigned char *Cpu0Data ;
XTime TimerCurr, TimerLast;
int InterruptInit(XScuGic *InstancePtr, u16 DeviceId);
int InterruptConnnect(XScuGic *InstancePtr, u16 IntId, void * Handler,void *CallBackRef) ;
int PLGpioSetup(XScuGic *InstancePtr) ;
int GpioHandler(void *InstancePtr);
void SoftHandler(void *CallbackRef) ;
int main()
{
int Status ;
int LedVal = 0x1;
/*
* Disable cache on OCM S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
*/
Xil_SetTlbAttributes(0xFFFF0000,0x14de2);
SharePtr = (ShareMem *)SHARE_BASE ;
/*
* Initial interrupt
*/
Status = InterruptInit(&InterrruptInst, INTC_DEVICE_ID) ;
if (Status != XST_SUCCESS)
return XST_FAILURE ;
/*
* Setup Gpio and Enable Gpio interrupt
*/
PLGpioSetup(&InterrruptInst);
/*
* Connect interrupt
*/
InterruptConnnect(&InterrruptInst, SoftIntrIdToCpu1, (void *)SoftHandler, (void *)&InterrruptInst) ;
while(1)
{
if (key_flag)
{
/*
* initial shared Struct
*/
SharePtr->addr = (unsigned int *)&Cpu1_Data ;
SharePtr->length = sizeof(Cpu1_Data) ;
/*
* Write led value
*/
XGpio_DiscreteWrite(&Gpio_led, LED_CHANNEL, LedVal);
LedVal = ~LedVal ;
/*
* Software interrupt to CPU0
*/
XScuGic_SoftwareIntr(&InterrruptInst, SoftIntrIdToCpu0, CPU0) ;
key_flag = 0 ;
}
else if (soft_flag)
{
/*
* When Software interrupt, print data in CPU0
*/
Cpu0Data = (unsigned char *)SharePtr->addr ;
xil_printf("This is CPU1, Now Start to Print:\r\n") ;
xil_printf("%s\r\n", Cpu0Data) ;
soft_flag = 0 ;
}
}
return 0 ;
}
int PLGpioSetup(XScuGic *InstancePtr)
{
int Status ;
/* initial gpio key */
Status = XGpio_Initialize(&Gpio_key, KEY_DEVICE_ID) ;
if (Status != XST_SUCCESS)
return XST_FAILURE ;
/* initial gpio led */
Status = XGpio_Initialize(&Gpio_led, LED_DEVICE_ID) ;
if (Status != XST_SUCCESS)
return XST_FAILURE ;
/* set key as input */
XGpio_SetDataDirection(&Gpio_key, KEY_CHANNEL, 0x1);
/* set led as output */
XGpio_SetDataDirection(&Gpio_led, LED_CHANNEL, 0x0);
/* Enable key interrupt */
XGpio_InterruptGlobalEnable(&Gpio_key) ;
XGpio_InterruptEnable(&Gpio_key, BTN_INT) ;
/* Connect key interrupt to CPU1 */
XScuGic_InterruptMaptoCpu(InstancePtr, XPAR_CPU_ID, KEY_INTR_ID) ;
InterruptConnnect(InstancePtr, KEY_INTR_ID, (void *)GpioHandler, (void *)&Gpio_key) ;
return XST_SUCCESS ;
}
int InterruptInit(XScuGic *InstancePtr, u16 DeviceId)
{
XScuGic_Config *IntcConfig;
int Status ;
IntcConfig = XScuGic_LookupConfig(DeviceId);
Status = XScuGic_CfgInitialize(InstancePtr, IntcConfig, IntcConfig->CpuBaseAddress) ;
if (Status != XST_SUCCESS)
return XST_FAILURE ;
/*
* Initialize the exception table
*/
Xil_ExceptionInit();
/*
* Register the interrupt controller handler with the exception table
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
InstancePtr);
/*
* Enable non-critical exceptions
*/
Xil_ExceptionEnable();
return XST_SUCCESS ;
}
int InterruptConnnect(XScuGic *InstancePtr, u16 IntId, void * Handler,void *CallBackRef)
{
XScuGic_Connect(InstancePtr, IntId,
(Xil_InterruptHandler)Handler,
CallBackRef) ;
XScuGic_Enable(InstancePtr, IntId) ;
return XST_SUCCESS ;
}
int GpioHandler(void *CallbackRef)
{
XGpio *GpioInstancePtr = (XGpio *)CallbackRef ;
int Int_val ;
int key_val ;
float Interval_time ;
Int_val = XGpio_InterruptGetStatus(GpioInstancePtr);
key_val = XGpio_DiscreteRead(GpioInstancePtr, KEY_CHANNEL) ;
/* Clear Interrupt */
XGpio_InterruptClear(GpioInstancePtr, XGPIO_IR_CH1_MASK) ;
if (Int_val == 1 && key_val == 0)
{
TimerLast = TimerCurr ;
XTime_GetTime(&TimerCurr) ;
Interval_time = ((float)(TimerCurr-TimerLast))/((float)COUNTS_PER_SECOND) ;
/* Key debounce, set decounce time to 200ms*/
if (Interval_time < 0.2)
{
key_flag = 0 ;
return 0 ;
}
else
{
key_flag = 1 ;
}
}
return 1 ;
}
void SoftHandler(void *CallbackRef)
{
xil_printf("Soft Interrupt from CPU0\r\n") ;
soft_flag = 1 ;
}
share.h
typedef struct
{
unsigned int length;
unsigned int *addr;
}ShareMem;
在cpu0_app工程中的lscript.ld设置CPU0的访问空间,例如DDR3为1GByte,将CPU0空间设置为一半,当然也可以根据需要修改
修改CPU1内存空间,注意不要与CPU0重合,最后保留256字节空间,用于共享内存
四、CPU0程序介绍
1.在cpu0_app.c文件中,设置一个字符数组Cpu0_Data,存放在CPU0访问空间,指针Cpu1Data用于指向CPU1内的字符数组。
2.在程序中,需要CPU0唤醒CPU1,可以在UG585文档看到相关解释,第一步是向0xffffffff0地址写入CPU1的访问内存基地址,在本实验中也就是0x20000000,第二步通过SEV指令唤醒CPU1并且跳转到相应的程序。
CPU1STARTMEM即在lscript.ld里设置的CPU1 base address
在while循环语句中,将字符数组的地址与长度赋给共享结构体,这里要提一下共享内存结构体,在share.h中定义结构体ShareMem,用于共享内存中传递信息。
共享的地址
在while循环中判断来自CPU1的软件中断,打印出来CPU1内存空间中的字符串
五、CPU1程序介绍
1.在CPU1程序中同样有一个字符数组,Cpu0Data指向CPU0内存空间的字符串地址。
2.在main函数中首先也是关闭OCM的Cache
3.在PLGpioSetup函数中需要将按键中断号绑定到CPU1,其他部分都与CPU0类似
六、板上验证
1.Run Configurations配置如下
2.在Application窗口将两个核都勾选上,下载程序
3.打开串口助手,测试CPU0,按下按键,控制LED灯亮,表明CPU0在运行,同时CPU1接收到CPU0设置的软件中断,并打印信息。