前面的例程都是采用单核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设置的软件中断,并打印信息。