原创 : Zynq-PS-SDK(8) 之双核通信 (AMP) 历史版本:
上次修改时间:
; CPU1 的交互方式(SGI 软中断);\n\n\n我们板端板载 DDR 容量为 1GB,DDR 的地址空间是:\n\n\n\n \n\n## 0、Hardware\n\n硬件上,基于前面的设计继续进行(参考《[Zynq-PS-SDK 之自定义 AXI-Lite IP](https://blog.csdn.net/zhoutaopower/article/details/115338677?spm=1001.2014.3001.5501)》和《[Zynq-PS-SDK 之 AXI GPIO](https://blog.csdn.net/zhoutaopower/article/details/115487481)》):\n\n\n\n \n\n## 1、SDK Configurations\n\n首先在 SDK 中增加两套 Applications,一个用于 CPU0,另一个用于 CPU1:\n\n\n\n编辑 CPU 0 的链接脚本,使得他的运行地址 0x0010_0000 和 Size 0x1FF0_0000 (目的是让两个工程在 DDR 中的运行地址分离开):\n\n\n\n \n\n编辑 CPU 1 的链接脚本,使得他的运行地址 0x2000_0000 和 Size 0x1FFF_FF00:\n\n\n\n \n\n选择 CPU1 的 bsp,右键进入 【**Board Support Package Settings**】,增加 CFLAG,导入 USE_AMP=1 这个宏;\n\n\n\n配置 CPU1 使用的处理器为 ps7_cortexa9_1:\n\n\n\n \n\n配置 Run Configurations:勾选如下:\n\n\n\n配置下载的时候,同时下载两个 elf:\n\n\n\n \n\n## 2、Source Code\n\n代码上,我在 CPU 0 的启动阶段去将 CPU1 唤醒,并将运行地址直接指向链接脚本那的起始地址(其实就是 CPU1 的中断向量表首地址);\n\n基于之前的设计中,我继承了一些东西,但这并不影响我这边验证 AMP:\n\n### 2.1、CPU 0\n\n在 CPU 0 的代码中,唤醒了 CPU1,初始化了 GIC,ScuTimer,使用软件中断来与 CPU 1 通信,自定义了 :\n\n```\n/***************************** Include Files *********************************/\n\n#include \"xparameters.h\"\n#include \"xscutimer.h\"\n#include \"xscugic.h\"\n#include \"xil_exception.h\"\n#include \"xil_printf.h\"\n#include \"xgpiops.h\"\n#include \"steph_axi_pwm.h\"\n#include \"sleep.h\"\n#include \"xil_mmu.h\"\n\n/************************** Constant Definitions *****************************/\n\n/*\n * The following constants map to the XPAR parameters created in the\n * xparameters.h file. They are only defined here such that a user can easily\n * change all the needed parameters in one place.\n */\n#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID\n#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID\n#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR\n\n#define TIMER_LOAD_VALUE (XPAR_PS7_CORTEXA9_0_CPU_CLK_FREQ_HZ/2 - 1)\n#define TIMEOUT_CNT (30)\n#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID\n\n/**************************** Type Definitions *******************************/\n\n/***************** Macros (Inline Functions) Definitions *********************/\n#define RD_32BIT_REG(addr) (*(volatile uint32_t *)(addr))\n#define WR_32BIT_REG(addr, value) (*(volatile uint32_t *)(addr)) = (value)\n\n#define STEPH_MIO_0 0\n#define STEPH_MIO_1 13\n\n#define STEPH_EMIO_0 54\n#define STEPH_EMIO_1 55\n#define STEPH_EMIO_2 56\n#define STEPH_EMIO_3 57\n\n#define MIO_OUTPUT 1\n#define MIO_INPUT 0\n\n#define MIO_OUTPUT_EN 1\n#define MIO_OUTPUT_DIS 0\n\n/* Accroding to the system.hdf file axi_gpio_0 */\n#define AXI_GPIO_0_BASE_ADDR 0x41200000\n#define GPIO_DATA_OFFSET 0x00\n#define GPIO_TRI_OFFSET 0x04\n#define GPIO_2_DATA_OFFSET 0x08\n#define GPIO_2_TRI_OFFSET 0x0C\n#define GIER_OFFSET 0x11C\n#define IP_ISR_OFFSET 0x120\n#define IP_IER_OFFSET 0x128\n\n/* PWM Configurations */\n#define PWM_CYCLE 2000\n#define PWM_DUTY_STEP 3\n\n/* CPU1 Address */\n#define WAKEUP_CPU1_ADDR 0xFFFFFFF0\n#define CPU1_RUN_ADDR 0x20000000\n#define SOFTWARE_IRQ_ID_TO_CPU0 1\n#define SOFTWARE_IRQ_ID_TO_CPU1 2\n\n#define CPU_0_ID 0x01\n#define CPU_1_ID 0x02\n\n#define sev() __asm__(\"sev\")\n\n#define FNC_CHECK_RETURN(Status) \\\n{ \\\n if (Status != XST_SUCCESS) \\\n return XST_FAILURE; \\\n}\n\n#define FNC_CHECK_NULL_POINTER(PTR) \\\n{ \\\n if (NULL == IntcConfig) \\\n return XST_FAILURE; \\\n}\n/************************** Function Prototypes ******************************/\n\nstatic void TimerIRQHandler(void *CallBackRef);\nstatic void SoftwareIRQHandler(void *CallBackRef);\n\n/************************** Variable Definitions *****************************/\n\nXScuTimer TimerInstance; /* Cortex A9 Scu Private Timer Instance */\nXScuGic IntcInstance; /* Interrupt Controller Instance */\nXGpioPs Gpio;\n\nvolatile int TimerExpired;\nvolatile uint8_t trigger_cpu1 = 1;\n\nstatic void Steph_AXI_LED_On(void)\n{\n WR_32BIT_REG(AXI_GPIO_0_BASE_ADDR + GPIO_DATA_OFFSET, 0x00);\n}\n\nstatic void Steph_AXI_LED_Off(void)\n{\n WR_32BIT_REG(AXI_GPIO_0_BASE_ADDR + GPIO_DATA_OFFSET, 0x0F);\n}\n\nstatic void Steph_AXI_GPIO_Init(void)\n{\n // Set as output\n Steph_AXI_LED_Off();\n WR_32BIT_REG(AXI_GPIO_0_BASE_ADDR + GPIO_TRI_OFFSET, 0x00);\n}\n\nstatic void Steph_PWM_ConfigCycle(uint32_t cycle)\n{\n STEPH_AXI_PWM_mWriteReg(XPAR_STEPH_AXI_PWM_0_S00_AXI_BASEADDR, STEPH_AXI_PWM_S00_AXI_SLV_REG0_OFFSET, cycle);\n}\n\nstatic void Steph_PWM_ConfigDuty(uint32_t duty)\n{\n STEPH_AXI_PWM_mWriteReg(XPAR_STEPH_AXI_PWM_0_S00_AXI_BASEADDR, STEPH_AXI_PWM_S00_AXI_SLV_REG1_OFFSET, duty);\n}\n\nstatic void Steph_AXI_PWM_Init(void)\n{\n // The frequency of the PWM controller is 50MHz\n Steph_PWM_ConfigCycle(PWM_CYCLE);\n Steph_PWM_ConfigDuty(0x00);\n}\n\nuint32_t Steph_LEDInit(void)\n{\n XGpioPs_Config *ConfigPtr = NULL;\n int Status = XST_SUCCESS;\n\n ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);\n if(!ConfigPtr)\n {\n xil_printf(\"XGpioPs_LookupConfig Error.\\r\\n\");\n return XST_FAILURE;\n }\n\n Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,\n ConfigPtr->;BaseAddr);\n\n if (Status != XST_SUCCESS) {\n xil_printf(\"XGpioPs_CfgInitialize Error.\\r\\n\");\n return XST_FAILURE;\n }\n\n // For MIO\n XGpioPs_SetDirectionPin(&Gpio, STEPH_MIO_0, MIO_OUTPUT);\n XGpioPs_SetDirectionPin(&Gpio, STEPH_MIO_1, MIO_OUTPUT);\n\n XGpioPs_SetOutputEnablePin(&Gpio, STEPH_MIO_0, MIO_OUTPUT_EN);\n XGpioPs_SetOutputEnablePin(&Gpio, STEPH_MIO_1, MIO_OUTPUT_EN);\n xil_printf(\"Steph_LED MIO Initialize Finished...\\r\\n\");\n\n // For EMIO\n XGpioPs_SetDirectionPin(&Gpio, STEPH_EMIO_0, MIO_OUTPUT);\n\n XGpioPs_SetOutputEnablePin(&Gpio, STEPH_EMIO_0, MIO_OUTPUT_EN);\n xil_printf(\"Steph_LED EMIO Initialize Finished...\\r\\n\");\n\n Steph_AXI_GPIO_Init();\n xil_printf(\"Steph_LED AXI GPIO Initialize Finished...\\r\\n\");\n\n Steph_AXI_PWM_Init();\n xil_printf(\"Steph_LED AXI PWM Initialize Finished...\\r\\n\");\n\n return Status;\n}\n\nvoid LED_OFF(uint32_t pin)\n{\n XGpioPs_WritePin(&Gpio, pin, 0x1);\n}\n\nvoid LED_ON(uint32_t pin)\n{\n XGpioPs_WritePin(&Gpio, pin, 0x0);\n}\n\n\nstatic void Steph_SetupCpu1(void)\n{\n xil_printf(\"Close Cache...\\r\\n\");\n Xil_SetTlbAttributes(0xFFFF0000, 0x14de2);\n xil_printf(\"Waking up CPU1...\\r\\n\");\n Xil_Out32(WAKEUP_CPU1_ADDR, CPU1_RUN_ADDR);\n dmb();\n sev();\n}\n\nunsigned long Steph_GetSiliconVersion (void) {\n // Read PS version from MCTRL register [31:28]\n unsigned long mask = 0xF0000000;\n unsigned long *addr = (unsigned long*) 0XF8007080;\n unsigned long ps_version = (*addr & mask) >;>; 28;\n return ps_version;\n}\n\nint _Steph_RegisterIRQHandler(XScuGic *InstancePtr, uint16_t IntId, void *Handler, void *CallBackRef)\n{\n int Status = XST_SUCCESS;\n\n Status = XScuGic_Connect(InstancePtr, IntId,\n (Xil_ExceptionHandler)Handler, (void *)CallBackRef);\n FNC_CHECK_RETURN(Status);\n\n XScuGic_Enable(InstancePtr, IntId);\n\n return Status;\n}\n\nint Steph_RegisterScuTimerHandler(void *Handler)\n{\n return _Steph_RegisterIRQHandler(&IntcInstance, TIMER_IRPT_INTR, (void *)Handler, (void *)&TimerInstance);\n}\n\nint Steph_RegisterSoftwareHandler(void *Handler)\n{\n return _Steph_RegisterIRQHandler(&IntcInstance, SOFTWARE_IRQ_ID_TO_CPU0, (void *)Handler, (void *)&IntcInstance); \n}\n\nint Steph_GicInit(void)\n{\n int Status = XST_SUCCESS;\n XScuGic_Config *IntcConfig;\n XScuGic *IntcInstancePtr = &IntcInstance;\n\n IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);\n FNC_CHECK_NULL_POINTER(IntcConfig);\n\n Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->;CpuBaseAddress);\n FNC_CHECK_RETURN(Status);\n\n Xil_ExceptionInit();\n\n Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, IntcInstancePtr);\n\n Xil_ExceptionEnable();\n\n return Status;\n}\n\nint Steph_ScuTimerInit(void)\n{\n int Status = XST_SUCCESS;\n XScuTimer_Config *ConfigPtr;\n XScuTimer *TimerInstancePtr = &TimerInstance;\n\n ConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);\n\n Status = XScuTimer_CfgInitialize(TimerInstancePtr, ConfigPtr, ConfigPtr->;BaseAddr);\n FNC_CHECK_RETURN(Status);\n\n Status = XScuTimer_SelfTest(TimerInstancePtr);\n FNC_CHECK_RETURN(Status);\n\n XScuTimer_EnableAutoReload(TimerInstancePtr);\n\n XScuTimer_LoadTimer(TimerInstancePtr, TIMER_LOAD_VALUE);\n\n XScuTimer_EnableInterrupt(TimerInstancePtr);\n\n Steph_RegisterScuTimerHandler((void *)TimerIRQHandler);\n\n XScuTimer_Start(TimerInstancePtr);\n\n return Status;\n}\n\nint Steph_SoftwareInterruptInit(void)\n{\n return Steph_RegisterSoftwareHandler((void *)SoftwareIRQHandler);\n}\n\nint main(void)\n{\n uint32_t pwm_duty = 0;\n\n xil_printf(\"####### StephenZhou CPU 0 #######\\r\\n\");\n xil_printf(\"SiliconVersion=0x%08X\\r\\n\", Steph_GetSiliconVersion());\n\n Steph_SetupCpu1();\n xil_printf(\"SetupCpu1 Finished...\\r\\n\");\n\n Steph_LEDInit();\n xil_printf(\"LED Init Finished...\\r\\n\");\n\n Steph_GicInit();\n xil_printf(\"GIC Init Finished...\\r\\n\");\n\n Steph_ScuTimerInit();\n xil_printf(\"Scu Timer Init Finished...\\r\\n\");\n\n Steph_SoftwareInterruptInit();\n xil_printf(\"Software Interrupt Init Finished...\\r\\n\");\n\n while (1)\n {\n if (pwm_duty <; PWM_CYCLE)\n {\n pwm_duty += PWM_DUTY_STEP;\n }\n else\n {\n pwm_duty = 0;\n }\n Steph_PWM_ConfigDuty(pwm_duty);\n\n usleep(1000);\n\n if (trigger_cpu1)\n {\n // Software interrupt to CPU1\n XScuGic_SoftwareIntr(&IntcInstance, SOFTWARE_IRQ_ID_TO_CPU1, CPU_1_ID);\n\n trigger_cpu1 = 0;\n }\n }\n \n return XST_SUCCESS;\n}\n\nstatic void TimerIRQHandler(void *CallBackRef)\n{\n XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;\n\n if (XScuTimer_IsExpired(TimerInstancePtr)) {\n XScuTimer_ClearInterruptStatus(TimerInstancePtr);\n TimerExpired++;\n if(TimerExpired%2)\n {\n LED_OFF(STEPH_MIO_0);\n LED_OFF(STEPH_MIO_1);\n\n LED_OFF(STEPH_EMIO_0);\n\n Steph_AXI_LED_Off();\n }\n else\n {\n LED_ON(STEPH_MIO_0);\n LED_ON(STEPH_MIO_1);\n\n LED_ON(STEPH_EMIO_0);\n\n Steph_AXI_LED_On();\n }\n xil_printf(\"Timer Counter=%d\\r\\n\", TimerExpired);\n\n }\n}\n\nstatic void SoftwareIRQHandler(void *CallBackRef)\n{\n xil_printf(\"Soft Interrupt from CPU1\\r\\n\") ;\n trigger_cpu1 = 1;\n}\n\n```\n\n \n\n \n\n### 2.2、CPU 1\n\n在 CPU 1 代码中,接收到来自 CPU 0 的中断,进行回应,并拉另一个中断(SGI)用于通知 CPU 0:\n\n```\n#include <;stdio.h>;\n#include \"platform.h\"\n#include \"xil_printf.h\"\n#include \"xil_mmu.h\"\n#include \"xparameters.h\"\n#include \"xscutimer.h\"\n#include \"xscugic.h\"\n#include \"xil_exception.h\"\n#include \"xgpiops.h\"\n#include \"sleep.h\"\n\n#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID\n\n#define SOFTWARE_IRQ_ID_TO_CPU0 1\n#define SOFTWARE_IRQ_ID_TO_CPU1 2\n\n#define CPU_0_ID 0x01\n#define CPU_1_ID 0x02\n\n#define FNC_CHECK_RETURN(Status) \\\n{ \\\n if (Status != XST_SUCCESS) \\\n return XST_FAILURE; \\\n}\n\n#define FNC_CHECK_NULL_POINTER(PTR) \\\n{ \\\n if (NULL == IntcConfig) \\\n return XST_FAILURE; \\\n}\n\nstatic void SoftwareIRQHandler(void *CallBackRef);\n\nXScuGic IntcInstance;\n\nvolatile uint8_t trigger_cpu0 = 1;\n\nint Steph_GicInit(void)\n{\n int Status = XST_SUCCESS;\n XScuGic_Config *IntcConfig;\n XScuGic *IntcInstancePtr = &IntcInstance;\n\n IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);\n FNC_CHECK_NULL_POINTER(IntcConfig);\n\n Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->;CpuBaseAddress);\n FNC_CHECK_RETURN(Status);\n\n Xil_ExceptionInit();\n\n Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, IntcInstancePtr);\n\n Xil_ExceptionEnable();\n\n return Status;\n}\n\nstatic void SoftwareIRQHandler(void *CallBackRef)\n{\n xil_printf(\"Soft Interrupt from CPU0\\r\\n\") ;\n trigger_cpu0 = 1;\n}\n\nint _Steph_RegisterIRQHandler(XScuGic *InstancePtr, uint16_t IntId, void *Handler, void *CallBackRef)\n{\n int Status = XST_SUCCESS;\n\n Status = XScuGic_Connect(InstancePtr, IntId,\n (Xil_ExceptionHandler)Handler, (void *)CallBackRef);\n FNC_CHECK_RETURN(Status);\n\n XScuGic_Enable(InstancePtr, IntId);\n\n return Status;\n}\n\nint Steph_RegisterSoftwareHandler(void *Handler)\n{\n return _Steph_RegisterIRQHandler(&IntcInstance, SOFTWARE_IRQ_ID_TO_CPU1, (void *)Handler, (void *)&IntcInstance);\n}\n\nint Steph_SoftwareInterruptInit(void)\n{\n return Steph_RegisterSoftwareHandler((void *)SoftwareIRQHandler);\n}\n\nint main()\n{\n init_platform();\n\n Xil_SetTlbAttributes(0xFFFF0000, 0x14de2);\n\n Steph_GicInit();\n Steph_SoftwareInterruptInit();\n\n while (1)\n {\n if(trigger_cpu0)\n {\n XScuGic_SoftwareIntr(&IntcInstance, SOFTWARE_IRQ_ID_TO_CPU0, CPU_0_ID);\n trigger_cpu0 = 0;\n }\n usleep(1000000);\n }\n\n cleanup_platform();\n return 0;\n}\n```\n\n串口的打印如下:\n\n\n\n \n\n相关代码和工程在 gitee 持续更新:\n\n> \n[https://gitee.com/stephenzhou-tech/Zynq7020_PS](https://gitee.com/stephenzhou-tech/Zynq7020_PS)\n\n\n \n -->