diff --git a/sdk/freertos_app_cpu0/src/main.c b/sdk/freertos_app_cpu0/src/main.c index 9a77b434..b2611557 100644 --- a/sdk/freertos_app_cpu0/src/main.c +++ b/sdk/freertos_app_cpu0/src/main.c @@ -135,7 +135,6 @@ int main(void) __asm__("sev"); #endif - Xil_ExceptionInit(); intr_init(); icc_init(); vPortInstallFreeRTOSVectorTable(); @@ -148,14 +147,12 @@ int main(void) const TickType_t x10seconds = pdMS_TO_TICKS(DELAY_10_SECONDS); - xil_printf("CPU0 - Hello from FreeRTOS example main()!\r\n"); - /* Create the three tasks */ xTaskCreate(prvTxTask, /* The function that implements the task. */ (const char *) "CPU0_Tx", /* Text name for the task, provided to assist debugging only. */ configMINIMAL_STACK_SIZE, /* The stack allocated to the task. */ NULL, /* The task parameter is not used, so set to NULL. */ - tskIDLE_PRIORITY, /* The task runs at the idle priority. */ + tskIDLE_PRIORITY, &xTxTaskHandle); xTaskCreate(prvRxTask, /* The function that implements the task. */ @@ -233,15 +230,18 @@ static void prvTxTask(void *pvParameters) // HWstring, /* The address of the data being sent. */ // 0UL); /* The block time. */ - xil_printf("DEBUG: CPU 0 about to attempt send\r\n"); + // xil_printf("DEBUG: CPU0 about to attempt send\r\n"); + xil_printf("A\r\n"); // Send a message to the other core - size_t bytes_sent = xMessageBufferSend(xCPU0to1MessageBuffer, HWstring, sizeof(HWstring), 0UL); + size_t bytes_sent = xMessageBufferSend(xCPU0to1MessageBufferHandle, HWstring, sizeof(HWstring), 0UL); - xil_printf("DEBUG: CPU0 sent %d bytes to ICC buffer\r\n", bytes_sent); + // xil_printf("DEBUG: CPU0 sent %d bytes to ICC buffer\r\n", bytes_sent); + xil_printf("B\r\n"); if (bytes_sent == 0) { - xil_printf("ERROR: CPU 0 failed to write to ICC buffer\r\n"); + // xil_printf("ERROR: CPU0 failed to write to ICC buffer\r\n"); + xil_printf("C\r\n"); } } } @@ -263,17 +263,21 @@ static void prvRxTask(void *pvParameters) // Rcvdstring, /* Data is read into this address. */ // portMAX_DELAY); /* Wait without a timeout for data. */ - xil_printf("DEBUG: CPU 0 about to attempt rcv\r\n"); + // xil_printf("DEBUG: CPU0 about to attempt rcv\r\n"); + xil_printf("D\r\n"); - size_t bytes_rcvd = xMessageBufferReceive(xCPU1to0MessageBuffer, Rcvdstring, 32, portMAX_DELAY); + size_t bytes_rcvd = xMessageBufferReceive(xCPU1to0MessageBufferHandle, Rcvdstring, 32, portMAX_DELAY); - xil_printf("DEBUG: CPU0 rcvd %d bytes from ICC buffer\r\n", bytes_rcvd); + // xil_printf("DEBUG: CPU0 rcvd %d bytes from ICC buffer\r\n", bytes_rcvd); + xil_printf("E\r\n"); if (bytes_rcvd == 0) { - xil_printf("CPU 0 failed to receive from ICC buffer\r\n"); + // xil_printf("CPU0 failed to receive from ICC buffer\r\n"); + xil_printf("F\r\n"); } else { /* Print the received data. */ - xil_printf("CPU0 - Rx task received string from CPU1 Tx: %s\r\n", Rcvdstring); + // xil_printf("CPU0 - Rx task received string from CPU1 Tx: %s\r\n", Rcvdstring); + xil_printf("G\r\n"); RxtaskCntr++; } } @@ -341,7 +345,8 @@ static void vTimerCallback(TimerHandle_t pxTimer) lTimerId = (long) pvTimerGetTimerID(pxTimer); if (lTimerId != TIMER_ID) { - xil_printf("CPU0 - FreeRTOS Hello World Example FAILED"); + // xil_printf("CPU0 - FreeRTOS Hello World Example FAILED"); + xil_printf("H\r\n"); } /* If the RxtaskCntr is updated every time the Rx task is called. The @@ -351,10 +356,12 @@ static void vTimerCallback(TimerHandle_t pxTimer) have a value of 9 (TIMER_CHECK_THRESHOLD) when the timer expires. */ if (RxtaskCntr >= TIMER_CHECK_THRESHOLD) { message_status = 1; - xil_printf("CPU0 - FreeRTOS Hello World Example PASSED\r\n"); + // xil_printf("CPU0 - FreeRTOS Hello World Example PASSED\r\n"); + xil_printf("I\r\n"); } else { message_status = 2; - xil_printf("CPU0 - FreeRTOS Hello World Example FAILED\r\n"); + // xil_printf("CPU0 - FreeRTOS Hello World Example FAILED\r\n"); + xil_printf("J\r\n"); } } diff --git a/sdk/freertos_app_cpu1/src/main.c b/sdk/freertos_app_cpu1/src/main.c index 3565a044..f4a58afb 100644 --- a/sdk/freertos_app_cpu1/src/main.c +++ b/sdk/freertos_app_cpu1/src/main.c @@ -120,8 +120,6 @@ int main(void) const TickType_t x10seconds = pdMS_TO_TICKS(DELAY_10_SECONDS); - xil_printf("CPU1 - Hello from FreeRTOS example main()!\r\n"); - /* Create the two tasks. The Tx task is given a lower priority than the Rx task, so the Rx task will leave the Blocked state and pre-empt the Tx task as soon as the Tx task places an item in the queue. */ @@ -129,7 +127,7 @@ int main(void) (const char *) "CPU1_Tx", /* Text name for the task, provided to assist debugging only. */ configMINIMAL_STACK_SIZE, /* The stack allocated to the task. */ NULL, /* The task parameter is not used, so set to NULL. */ - tskIDLE_PRIORITY, /* The task runs at the idle priority. */ + tskIDLE_PRIORITY, &xTxTaskHandle); xTaskCreate(prvRxTask, /* The function that implements the task. */ @@ -199,15 +197,18 @@ static void prvTxTask(void *pvParameters) // HWstring, /* The address of the data being sent. */ // 0UL); /* The block time. */ - xil_printf("DEBUG: CPU 1 about to attempt send\r\n"); + // xil_printf("DEBUG: CPU1 about to attempt send\r\n"); + xil_printf("a\r\n"); // Send a message to the other core - size_t bytes_sent = xMessageBufferSend(xCPU1to0MessageBuffer, HWstring, sizeof(HWstring), 0UL); + size_t bytes_sent = xMessageBufferSend(xCPU1to0MessageBufferHandle, HWstring, sizeof(HWstring), 0UL); - xil_printf("DEBUG: CPU1 sent %d bytes to ICC buffer\r\n", bytes_sent); + // xil_printf("DEBUG: CPU1 sent %d bytes to ICC buffer\r\n", bytes_sent); + xil_printf("b\r\n"); if (bytes_sent == 0) { - xil_printf("ERROR: CPU 1 failed to write to ICC buffer\r\n"); + // xil_printf("ERROR: CPU1 failed to write to ICC buffer\r\n"); + xil_printf("c\r\n"); } } } @@ -229,17 +230,21 @@ static void prvRxTask(void *pvParameters) // Rcvdstring, /* Data is read into this address. */ // portMAX_DELAY); /* Wait without a timeout for data. */ - xil_printf("DEBUG: CPU 1 about to attempt rcv\r\n"); + // xil_printf("DEBUG: CPU1 about to attempt rcv\r\n"); + xil_printf("d\r\n"); - size_t bytes_rcvd = xMessageBufferReceive(xCPU0to1MessageBuffer, Rcvdstring, 32, portMAX_DELAY); + size_t bytes_rcvd = xMessageBufferReceive(xCPU0to1MessageBufferHandle, Rcvdstring, 32, portMAX_DELAY); - xil_printf("DEBUG: CPU1 rcvd %d bytes from ICC buffer", bytes_rcvd); + // xil_printf("DEBUG: CPU1 rcvd %d bytes from ICC buffer\r\n", bytes_rcvd); + xil_printf("e\r\n"); if (bytes_rcvd == 0) { - xil_printf("CPU 1 failed to receive from ICC buffer\r\n"); + // xil_printf("CPU1 failed to receive from ICC buffer\r\n"); + xil_printf("f\r\n"); } else { /* Print the received data. */ - xil_printf("CPU1 - Rx task received string from CPU0 Tx: %s\r\n", Rcvdstring); + // xil_printf("CPU1 - Rx task received string from CPU0 Tx: %s\r\n", Rcvdstring); + xil_printf("g\r\n"); RxtaskCntr++; } } @@ -255,7 +260,8 @@ static void vTimerCallback(TimerHandle_t pxTimer) lTimerId = (long) pvTimerGetTimerID(pxTimer); if (lTimerId != TIMER_ID) { - xil_printf("CPU1 - FreeRTOS Hello World Example FAILED"); + // xil_printf("CPU1 - FreeRTOS Hello World Example FAILED"); + xil_printf("h\r\n"); } /* If the RxtaskCntr is updated every time the Rx task is called. The @@ -265,10 +271,12 @@ static void vTimerCallback(TimerHandle_t pxTimer) have a value of 9 (TIMER_CHECK_THRESHOLD) when the timer expires. */ if (RxtaskCntr >= TIMER_CHECK_THRESHOLD) { message_status = 1; - xil_printf("CPU1 - FreeRTOS Hello World Example PASSED\r\n"); + // xil_printf("CPU1 - FreeRTOS Hello World Example PASSED\r\n"); + xil_printf("i\r\n"); } else { message_status = 2; - xil_printf("CPU1 - FreeRTOS Hello World Example FAILED\r\n"); + // xil_printf("CPU1 - FreeRTOS Hello World Example FAILED\r\n"); + xil_printf("j\r\n"); } } diff --git a/sdk/shared/sys/icc.c b/sdk/shared/sys/icc.c index 2673a593..3b0611c5 100644 --- a/sdk/shared/sys/icc.c +++ b/sdk/shared/sys/icc.c @@ -8,31 +8,72 @@ // CPUs, use "#if XPAR_CPU_ID == ?" /////////////////////////////////////////////////////// -void icc_init(uint32_t cpu_num) +void icc_init() { #if XPAR_CPU_ID == 0 - // ONLY CPU 0 INITIALIZES THE MESSAGE BUFFERS + // CPU0 HANDLES INITIALIZING THE MESSAGE BUFFERS + + // Wait for CPU1 to provide the function pointers to its callbacks () + while (!ICC_getFunctionPointersReady) + ; + + // Use the getters once ready + void (*vCPU0to1ReceiveCallback)() = ICC_getCPU0to1ReceiveCallback; + // xil_printf("DEBUG: CPU 0 got 0to1 Receive Callback %p\r\n", (void *) vCPU0to1ReceiveCallback); + void (*vCPU1to0SendCallback)() = ICC_getCPU1to0SendCallback; + // xil_printf("DEBUG: CPU 0 got 1to0 Send Callback %p\r\n", (void *) vCPU1to0SendCallback); /* Create two message buffers for inter-core communication that use the callback * functions below as send and receive completed callback functions. */ - xCPU0to1MessageBuffer = xMessageBufferCreateStaticWithCallback(ICC_BUFFER_SIZE - 1, - ICC_CPU0to1_BufferSpaceAddr, - ICC_CPU0to1_BufferStructAddr, - vCPU0to1SendCallback, - vCPU0to1ReceiveCallback); - - xCPU1to0MessageBuffer = xMessageBufferCreateStaticWithCallback(ICC_BUFFER_SIZE - 1, - ICC_CPU1to0_BufferSpaceAddr, - ICC_CPU1to0_BufferStructAddr, - vCPU1to0SendCallback, - vCPU1to0ReceiveCallback); + xCPU0to1MessageBufferHandle = xMessageBufferCreateStaticWithCallback( + ICC_BUFFER_SIZE - 1, + ICC_CPU0to1BufferSpaceAddr, + ICC_CPU0to1BufferStructAddr, + vCPU0to1SendCallback, // Called by CPU0 after placing message in 0to1 buffer + vCPU0to1ReceiveCallback); // Called by CPU1 after removing message from 0to1 buffer + + xCPU1to0MessageBufferHandle = xMessageBufferCreateStaticWithCallback( + ICC_BUFFER_SIZE - 1, + ICC_CPU1to0BufferSpaceAddr, + ICC_CPU1to0BufferStructAddr, + vCPU1to0SendCallback, // Called by CPU1 after placing message in 1to0 buffer + vCPU1to0ReceiveCallback); // Called by CPU0 after removing message from 1to0 buffer + + ICC_setCPU0to1Handle(xCPU0to1MessageBufferHandle); + // xil_printf("DEBUG: CPU 0 set 0to1 Handle %p\r\n", (void *) xCPU0to1MessageBufferHandle); + ICC_setCPU1to0Handle(xCPU1to0MessageBufferHandle); + // xil_printf("DEBUG: CPU 0 set 1to0 Handle %p\r\n", (void *) xCPU1to0MessageBufferHandle); + + ICC_setHandleComplete; +#elif XPAR_CPU_ID == 1 + /* need to stall 10ms to "guarantee" that CPU1 does not get before CPU0 sets + * The APU freq is 666,666,687 Hz (per ps7_init.h), so a single NOP is 1/(666,666,687) or 1.5ns + * Therefore we need 6.66E6 NOPs to stall 10ms + */ + + // Make CPU1's callback function pointers available to CPU0 + ICC_setCPU1to0SendCallback(&vCPU1to0SendCallback); + // xil_printf("DEBUG: CPU 1 set 1to0 Send Callback %p\r\n", &vCPU1to0SendCallback); + ICC_setCPU0to1ReceiveCallback(&vCPU0to1ReceiveCallback); + // xil_printf("DEBUG: CPU 1 set 0to1 Receive Callback %p\r\n", &vCPU0to1ReceiveCallback); + + ICC_setFunctionPointersReady; + + // Wait for CPU0 to finish creating buffers and providing the handles + while (!ICC_getHandleComplete) + ; + + xCPU0to1MessageBufferHandle = ICC_getCPU0to1Handle; + // xil_printf("DEBUG: CPU 1 got 0to1 Handle %p\r\n", (void *) xCPU0to1MessageBufferHandle); + xCPU1to0MessageBufferHandle = ICC_getCPU1to0Handle; + // xil_printf("DEBUG: CPU 1 got 1to0 Handle %p\r\n", (void *) xCPU1to0MessageBufferHandle); #endif } /* From FreeRTOS: * Insert code into callback which is invoked when a message is written to the message buffer. * This is useful when a message buffer is used to pass messages between - * cores on a multicore processor. In that scenario, this callback + * cores on a multi-core processor. In that scenario, this callback * can be implemented to generate an interrupt in the other CPU core, * and the interrupt's service routine can then use the * xMessageBufferSendCompletedFromISR() API function to check, and if @@ -46,46 +87,52 @@ void icc_init(uint32_t cpu_num) * send to the 0 to 1 buffer, so in CPU 1 this callback doesn't need to DO ANYTHING except exist. * - Patrick */ +#if XPAR_CPU_ID == 0 + void vCPU0to1SendCallback(MessageBufferHandle_t xMessageBuffer, BaseType_t xIsInsideISR, BaseType_t *const pxHigherPriorityTaskWoken) { -#if XPAR_CPU_ID == 0 - xil_printf("DEBUG: CPU 0 to 1 Send Callback reached\r\n"); - // In CPU 0, this callback should send an interrupt to CPU 1's Rx task - XScuGic_SoftwareIntr(&InterruptController, INTC_0TO1_SEND_INTERRUPT_ID, CPU1_ID); -#endif + // xil_printf("DEBUG: CPU 0 to 1 Send Callback reached (in CPU0)\r\n"); + xil_printf("K\r\n"); + + // CPU 0 should send an interrupt to CPU1 to unblock its Rx task + int status = XScuGic_SoftwareIntr(INTR_GIC_INSTANCE_ADDR, INTR_UNBLOCK_CPU1_RX_INT_ID, XSCUGIC_SPI_CPU1_MASK); } void vCPU1to0ReceiveCallback(MessageBufferHandle_t xMessageBuffer, BaseType_t xIsInsideISR, BaseType_t *const pxHigherPriorityTaskWoken) { -#if XPAR_CPU_ID == 0 - xil_printf("DEBUG: CPU 1 to 0 Receive Callback reached\r\n"); - // In CPU 0, this callback should send an interrupt to CPU 1's Tx task - XScuGic_SoftwareIntr(&InterruptController, INTC_1TO0_RCVE_INTERRUPT_ID, CPU1_ID); -#endif + // xil_printf("DEBUG: CPU 1 to 0 Receive Callback reached (in CPU0)\r\n"); + xil_printf("L\r\n"); + + // CPU0 should send an interrupt to CPU1 to unblock its Tx task (the buffer might have an open space now) + int status = XScuGic_SoftwareIntr(INTR_GIC_INSTANCE_ADDR, INTR_UNBLOCK_CPU1_TX_INT_ID, XSCUGIC_SPI_CPU1_MASK); } +#elif XPAR_CPU_ID == 1 + void vCPU1to0SendCallback(MessageBufferHandle_t xMessageBuffer, BaseType_t xIsInsideISR, BaseType_t *const pxHigherPriorityTaskWoken) { -#if XPAR_CPU_ID == 1 - xil_printf("DEBUG: CPU 1 to 0 Send Callback reached\r\n"); - // In CPU 1, this callback should send an interrupt to CPU 0's Rx task - XScuGic_SoftwareIntr(&InterruptController, INTC_1TO0_SEND_INTERRUPT_ID, CPU0_ID); -#endif + // xil_printf("DEBUG: CPU 1 to 0 Send Callback reached (in CPU1)\r\n"); + xil_printf("k\r\n"); + + // CPU 1 should send an interrupt to CPU0 to unblock its Rx task + int status = XScuGic_SoftwareIntr(INTR_GIC_INSTANCE_ADDR, INTR_UNBLOCK_CPU0_RX_INT_ID, XSCUGIC_SPI_CPU0_MASK); } void vCPU0to1ReceiveCallback(MessageBufferHandle_t xMessageBuffer, BaseType_t xIsInsideISR, BaseType_t *const pxHigherPriorityTaskWoken) { -#if XPAR_CPU_ID == 1 - xil_printf("DEBUG: CPU 0 to 1 Receive Callback reached\r\n"); - // In CPU 1, this callback should send an interrupt to CPU 0's Tx task - XScuGic_SoftwareIntr(&InterruptController, INTC_0TO1_RCVE_INTERRUPT_ID, CPU0_ID); -#endif + // xil_printf("DEBUG: CPU 0 to 1 Receive Callback reached (in CPU1)\r\n"); + xil_printf("l\r\n"); + + // CPU 1 should send an interrupt to CPU0 to unblock its Tx task (the buffer might have an open space) + int status = XScuGic_SoftwareIntr(INTR_GIC_INSTANCE_ADDR, INTR_UNBLOCK_CPU0_TX_INT_ID, XSCUGIC_SPI_CPU0_MASK); } + +#endif diff --git a/sdk/shared/sys/icc.h b/sdk/shared/sys/icc.h index c8d82879..5e365b96 100644 --- a/sdk/shared/sys/icc.h +++ b/sdk/shared/sys/icc.h @@ -2,8 +2,8 @@ #define ICC_H #include "FreeRTOS.h" -#include "intr.h" #include "message_buffer.h" +#include "shared_memory.h" #include "xil_printf.h" #include @@ -32,58 +32,21 @@ // https://www.freertos.org/RTOS-message-buffer-API.html // https://www.freertos.org/2020/02/simple-multicore-core-to-core-communication-using-freertos-message-buffers.html -// Per Zynq-7000 TRM Ch. 4: System Addresses (page 106), the initial mapping -// of OCM is split between low addresses and high addresses in 64 KB chunks. -// -// We will pick to use the highest 64 KB chunk as our base address: -#define OCM_BASE_ADDR (0xFFFF0000) -#define ICC_BUFFER_STRUCT_SIZE (sizeof(StaticMessageBuffer_t)) -#define ICC_BUFFER_SIZE (4 * 1024) -#define ICC_HANDLE_SIZE (sizeof(MessageBufferHandle_t)) - - -/* Define the pointers to the two structs (that store the metadata) and two message spaces (that hold the messages) in shared memory. - * The ICC_BUFFER_SIZE Should be one more than the value passed in the xBufferSizeBytes parameter. - * The two structs will be located back-to-back right at the base addr of the shared OCM, followed thereafter by the actual message buffers. */ -#define ICC_CPU0to1_BufferStructAddr ((uint8_t *) (OCM_BASE_ADDR + (0 * ICC_BUFFER_STRUCT_SIZE))) -#define ICC_CPU1to0_BufferStructAddr ((uint8_t *) (OCM_BASE_ADDR + (1 * ICC_BUFFER_STRUCT_SIZE))) - -#define ICC_CPU0to1_BufferSpaceAddr ((uint8_t *) (OCM_BASE_ADDR + (2 * ICC_BUFFER_STRUCT_SIZE) + (0 * ICC_BUFFER_SIZE))) -#define ICC_CPU1to0_BufferSpaceAddr ((uint8_t *) (OCM_BASE_ADDR + (2 * ICC_BUFFER_STRUCT_SIZE) + (1 * ICC_BUFFER_SIZE))) - - -/* These memory spaces are used to transfer the Message Buffer Handles from CPU0 (who does the initialization work, and gets the handles - * from the xMessageBufferCreateStaticWithCallback function) to CPU1 (who doesn't initialize anything and gets the handles from CPU0, via - * these drop-zones) */ -#define ICC_CPU0to1_HandleDropzoneAddr (OCM_BASE_ADDR + (2 * ICC_BUFFER_STRUCT_SIZE) + (2 * ICC_BUFFER_SIZE) + (0 * ICC_HANDLE_SIZE))) -#define ICC_CPU1to0_HandleDropzoneAddr (OCM_BASE_ADDR + (2 * ICC_BUFFER_STRUCT_SIZE) + (2 * ICC_BUFFER_SIZE) + (1 * ICC_HANDLE_SIZE))) - -#define ICC_getCPU0to1Handle (*((MessageBufferHandle_t *) ICC_CPU0to1_HandleDropzoneAddr)) -#define ICC_setCPU0to1Handle(handle) (*((MessageBufferHandle_t *) ICC_CPU0to1_HandleDropzoneAddr) = handle) -#define ICC_getCPU1to0Handle (*((MessageBufferHandle_t *) ICC_CPU1to0_HandleDropzoneAddr)) -#define ICC_setCPU1to0Handle(handle) (*((MessageBufferHandle_t *) ICC_CPU1to0_HandleDropzoneAddr) = handle) - - - -/* These are the handles for the Message Buffers that need to be used by other tasks - * In reality, the handle is just the pointer to the message buffer struct (its memory address) - * These should end up being the addresses computed above */ -MessageBufferHandle_t xCPU0to1MessageBuffer; -MessageBufferHandle_t xCPU1to0MessageBuffer; - - void icc_init(); +#if XPAR_CPU_ID == 0 void vCPU0to1SendCallback(MessageBufferHandle_t xMessageBuffer, BaseType_t xIsInsideISR, BaseType_t *const pxHigherPriorityTaskWoken); -void vCPU0to1ReceiveCallback(MessageBufferHandle_t xMessageBuffer, +void vCPU1to0ReceiveCallback(MessageBufferHandle_t xMessageBuffer, BaseType_t xIsInsideISR, BaseType_t *const pxHigherPriorityTaskWoken); +#elif XPAR_CPU_ID == 1 void vCPU1to0SendCallback(MessageBufferHandle_t xMessageBuffer, BaseType_t xIsInsideISR, BaseType_t *const pxHigherPriorityTaskWoken); -void vCPU1to0ReceiveCallback(MessageBufferHandle_t xMessageBuffer, +void vCPU0to1ReceiveCallback(MessageBufferHandle_t xMessageBuffer, BaseType_t xIsInsideISR, BaseType_t *const pxHigherPriorityTaskWoken); +#endif /* Callback declarations */ #endif /* ICC_H */ diff --git a/sdk/shared/sys/intr.c b/sdk/shared/sys/intr.c index 253dcbbc..fcaa9c9a 100644 --- a/sdk/shared/sys/intr.c +++ b/sdk/shared/sys/intr.c @@ -8,67 +8,121 @@ // CPUs, use "#if XPAR_CPU_ID == ?" /////////////////////////////////////////////////////// +/// GENERAL INFO /* The functions in this file are responsible for setting up the * Xilinx Generic Interrupt Controller (GIC). * * Interrupts are needed for Inter-Core Communication, specifically - * the ability to trigger an interrupt in the receiving CPU after a - * message is placed into an empty ICC Message Buffer, + * the ability to trigger an interrupt in the receiving CPU after a + * message is placed into an empty ICC Message Buffer, * OR - * the ability to trigger an interrupt in the sending CPU after a - * message is removed from a full ICC Message Buffer + * the ability to trigger an interrupt in the sending CPU after a + * message is removed from a full ICC Message Buffer * * See sys/icc.c for more info. + * +// RELEVENT TRM SECTIONS + * Read Chapter 7: Interrupts in the Zynq-7000 TRM + * It starts on page 231 of the PDF + * Appendix A contains all the memory-mapped register details + * Relevant subsection is the "Application Processing Unit (mpcore)" section + * that starts on page 1483. + * Registers starting with ICD are the ones we care about + * These are the "distributor" registers starting with ICDDCR at absolute address 0xF8F01000 + * +// USEFUL XILINX-PROVIDED FILES TO READ + * - xparameters.h : Search for "SCUGIC" related definitions + * - XPAR_PS7_SCUGIC_0_DEVICE_ID 0U + * - XPAR_PS7_SCUGIC_0_BASEADDR 0xF8F00100U + * - XPAR_PS7_SCUGIC_0_HIGHADDR 0xF8F001FFU + * - XPAR_PS7_SCUGIC_0_DIST_BASEADDR 0xF8F01000U + * - xscugic_sinit.c : Contains the necessary LookupConfig() function + * - xscugic.c : Contains most of the useful GIC functions + * - XScuGic_CfgInitialize() + * Uses the ConfigPtr* from lookup to initialize a GIC Instance struct + * - XScuGic_DistWriteReg() + * Write a GIC register + * - XScuGic_DistReadReg() + * Read a GIC register + * - XScuGic_Connect() + * - XScuGic_Disconnect() + * - XScuGic_Enable() + * - XScuGic_Disable() + * - XScuGic_SoftwareIntr() + * - XScuGic_GetPriorityTriggerType() + * - XScuGic_SetPriorityTriggerType() + * - XScuGic_InterruptMaptoCpu() + * - XScuGic_InterruptUnmapFromCpu() + * - XScuGic_UnmapAllInterruptsFromCpu() + * - XScuGic_Stop() + * - XScuGic_SetCpuID() + * - XScuGic_GetCpuID() */ int intr_init() { - int Status = XST_FAILURE; - - XScuGic_Config *IntcConfig; - - IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); - XScuGic_CfgInitialize(&InterruptController, IntcConfig, IntcConfig->CpuBaseAddress); - - /* - * Perform a self-test to ensure that the hardware was built - * correctly - */ - Status = XScuGic_SelfTest(&InterruptController); - if (Status != XST_SUCCESS) { +#if XPAR_CPU_ID == 0 + // CPU0 handles initialization of the Generic Interrrupt Controller + xil_printf("INTR: Initializing GIC...\n"); + XScuGic_Config *gic_config_ptr = XScuGic_LookupConfig(INTR_GIC_DEVICE_ID); + + // gic_config_ptr provides the Xilinx base addresses of: + // - the GIC's distributor registers (the distributor is a shared resource that distributes interrupts to the + // CPUs) + // - the "CPU Interface" registers, each CPU has an interface that needs to be configured to interact with the GIC + s32 gic_init_status = XScuGic_CfgInitialize(INTR_GIC_INSTANCE_ADDR, gic_config_ptr, gic_config_ptr->CpuBaseAddress); + if (gic_init_status != XST_SUCCESS) { + xil_printf("INTR: GIC Initialization Failed\n"); return XST_FAILURE; } + xil_printf("INTR: GIC Initialization Success\n"); + INTR_setGicInitReady; +#elif XPAR_CPU_ID == 1 + // Wait for CPU0 to finish GIC initialization + while (!INTR_getGicInitReady) + ; + + // CPU1 still needs to configure its own CPU Interface, using these lines pulled from CPUInitialize() in xscugic.c + // CPU0 does this by calling CPUInitialize() within XScuGic_CfgInitialize() (xscugic.c, Line 481), but because of + // the GIC ready check on Line 439 of xscugic.c, if CPU1 calls XScuGic_CfgInitialize(), it will skip the call to + // CPUInitialize() if CPU0 has already marked the GIC as ready. + XScuGic_CPUWriteReg(INTR_GIC_INSTANCE_ADDR, XSCUGIC_CPU_PRIOR_OFFSET, 0xF0U); + XScuGic_CPUWriteReg(INTR_GIC_INSTANCE_ADDR, XSCUGIC_CONTROL_OFFSET, 0x07U); +#endif - // Initialize the interrupt controller + // BOTH CORES: Connect the ARM processor's InterruptHandler logic to the initialized GIC Xil_ExceptionRegisterHandler( - XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler, &InterruptController); + XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler, INTR_GIC_INSTANCE_ADDR); Xil_ExceptionEnable(); #if XPAR_CPU_ID == 0 - // Connect the given interrupt with its handler - XScuGic_Connect(&InterruptController, INTC_1TO0_SEND_INTERRUPT_ID, (Xil_ExceptionHandler) CPU0WakeRxHandler, NULL); - XScuGic_Connect(&InterruptController, INTC_0TO1_RCVE_INTERRUPT_ID, (Xil_ExceptionHandler) CPU0WakeTxHandler, NULL); + // Each CPU needs to connect its software interrupts to the appropriate handlers defined below, then enable + XScuGic_Connect(INTR_GIC_INSTANCE_ADDR, + INTR_UNBLOCK_CPU0_RX_INT_ID, + (Xil_ExceptionHandler) CPU0UnblockRxHandler, + (void *) INTR_GIC_INSTANCE_ADDR); + XScuGic_Enable(INTR_GIC_INSTANCE_ADDR, INTR_UNBLOCK_CPU0_RX_INT_ID); + + XScuGic_Connect(INTR_GIC_INSTANCE_ADDR, + INTR_UNBLOCK_CPU0_TX_INT_ID, + (Xil_ExceptionHandler) CPU0UnblockTxHandler, + (void *) INTR_GIC_INSTANCE_ADDR); + XScuGic_Enable(INTR_GIC_INSTANCE_ADDR, INTR_UNBLOCK_CPU0_TX_INT_ID); #elif XPAR_CPU_ID == 1 - // Connect the given interrupt with its handler - XScuGic_Connect(&InterruptController, INTC_0TO1_SEND_INTERRUPT_ID, (Xil_ExceptionHandler) CPU1WakeRxHandler, NULL); - XScuGic_Connect(&InterruptController, INTC_1TO0_RCVE_INTERRUPT_ID, (Xil_ExceptionHandler) CPU1WakeTxHandler, NULL); -#endif - - /* - // Enable the interrupt for the second CPU core - XScuGic_SetPriorityTriggerType(&InterruptController, INTC_INTERRUPT_ID, 0xA0, 3); // Set priority and trigger type - XScuGic_Enable(&InterruptController, INTC_INTERRUPT_ID); + XScuGic_Connect(INTR_GIC_INSTANCE_ADDR, + INTR_UNBLOCK_CPU1_RX_INT_ID, + (Xil_ExceptionHandler) CPU1UnblockRxHandler, + (void *) INTR_GIC_INSTANCE_ADDR); + XScuGic_Enable(INTR_GIC_INSTANCE_ADDR, INTR_UNBLOCK_CPU1_RX_INT_ID); + + XScuGic_Connect(INTR_GIC_INSTANCE_ADDR, + INTR_UNBLOCK_CPU1_TX_INT_ID, + (Xil_ExceptionHandler) CPU1UnblockTxHandler, + (void *) INTR_GIC_INSTANCE_ADDR); + XScuGic_Enable(INTR_GIC_INSTANCE_ADDR, INTR_UNBLOCK_CPU1_TX_INT_ID); - // Enable the interrupt for CPU1 - Xil_Out32(XPAR_PS7_SCUGIC_DIST_BASEADDR + XSCUGIC_CPU_PRIOR_OFFSET + (CPU1_ID * 4), 0xFF); - Xil_Out32(XPAR_PS7_SCUGIC_DIST_BASEADDR + XSCUGIC_CPU_TARGET_OFFSET + (CPU1_ID * 4), 0x1); - - // Enable interrupts globally - Xil_ExceptionEnableMask(XIL_EXCEPTION_NON_CRITICAL); - - print("Interrupt system setup complete.\r\n"); - */ +#endif return XST_SUCCESS; } @@ -76,39 +130,39 @@ int intr_init() /* We only need to define the handlers in the appropriate core */ #if XPAR_CPU_ID == 0 -void CPU0WakeTxHandler() +void CPU0UnblockRxHandler() { - xil_printf("CPU 0 - WakeTxHandler reached\r\n"); + xil_printf("M\r\n"); BaseType_t xHigherPriorityTaskWoken = pdFALSE; - xMessageBufferReceiveCompletedFromISR(xCPU0to1MessageBuffer, &xHigherPriorityTaskWoken); + xMessageBufferSendCompletedFromISR(xCPU1to0MessageBufferHandle, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } -void CPU0WakeRxHandler() +void CPU0UnblockTxHandler() { - xil_printf("CPU 0 - WakeRxHandler reached\r\n"); + xil_printf("N\r\n"); - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - xMessageBufferSendCompletedFromISR(xCPU1to0MessageBuffer, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + // BaseType_t xHigherPriorityTaskWoken = pdFALSE; + // xMessageBufferReceiveCompletedFromISR(xCPU0to1MessageBufferHandle, &xHigherPriorityTaskWoken); + // portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } #elif XPAR_CPU_ID == 1 -void CPU1WakeTxHandler() +void CPU1UnblockRxHandler() { - xil_printf("CPU 1 - WakeTxHandler reached\r\n"); + xil_printf("m\r\n"); - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - xMessageBufferReceiveCompletedFromISR(xCPU1to0MessageBuffer, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + // BaseType_t xHigherPriorityTaskWoken = pdFALSE; + // xMessageBufferSendCompletedFromISR(xCPU0to1MessageBufferHandle, &xHigherPriorityTaskWoken); + // portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } -void CPU1WakeRxHandler() +void CPU1UnblockTxHandler() { - xil_printf("CPU 1 - WakeRxHandler reached\r\n"); + xil_printf("n\r\n"); - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - xMessageBufferSendCompletedFromISR(xCPU0to1MessageBuffer, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + // BaseType_t xHigherPriorityTaskWoken = pdFALSE; + // xMessageBufferReceiveCompletedFromISR(xCPU1to0MessageBufferHandle, &xHigherPriorityTaskWoken); + // portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } #endif diff --git a/sdk/shared/sys/intr.h b/sdk/shared/sys/intr.h index 137771a4..51308e35 100644 --- a/sdk/shared/sys/intr.h +++ b/sdk/shared/sys/intr.h @@ -2,7 +2,7 @@ #define INTR_H #include "FreeRTOS.h" -#include "icc.h" +#include "shared_memory.h" #include "xil_exception.h" #include "xil_printf.h" #include "xparameters.h" @@ -12,8 +12,6 @@ #include #include -// test - /////////////////////////////////////////////////////// // THIS IS A SHARED FILE, SO IT IS ALWAYS // IN SYNC IN BOTH CPU0 AND CPU1 @@ -22,30 +20,17 @@ // CPUs, use "#if XPAR_CPU_ID == ?" /////////////////////////////////////////////////////// -#define CPU0_ID (XSCUGIC_SPI_CPU0_MASK << 0) -#define CPU1_ID (XSCUGIC_SPI_CPU0_MASK << 1) - -#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID - -#define INTC_0TO1_SEND_INTERRUPT_ID 0U -#define INTC_1TO0_RCVE_INTERRUPT_ID 1U -#define INTC_1TO0_SEND_INTERRUPT_ID 2U -#define INTC_0TO1_RCVE_INTERRUPT_ID 3U - -// Interrupt Controller Instance -// Defined here to be accessable in sys/icc.c -static XScuGic InterruptController; +#define INTR_GIC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID // good int intr_init(); -/* We only need to define the handlers in the appropriate core - */ +// We only need to define the handlers in the appropriate core #if XPAR_CPU_ID == 0 -void CPU0WakeTxHandler(); -void CPU0WakeRxHandler(); +void CPU0UnblockRxHandler(); +void CPU0UnblockTxHandler(); #elif XPAR_CPU_ID == 1 -void CPU1WakeTxHandler(); -void CPU1WakeRxHandler(); +void CPU1UnblockRxHandler(); +void CPU1UnblockTxHandler(); #endif #endif /* INTR_H */ diff --git a/sdk/shared/sys/shared_memory.h b/sdk/shared/sys/shared_memory.h new file mode 100644 index 00000000..43aa6422 --- /dev/null +++ b/sdk/shared/sys/shared_memory.h @@ -0,0 +1,111 @@ +#ifndef SHARED_MEMORY_H +#define SHARED_MEMORY_H + +#include "message_buffer.h" +#include "xil_printf.h" +#include "xparameters.h" +#include "xscugic.h" + +/////////////////////////////////////////////////////// +// THIS IS A SHARED FILE, SO IT IS ALWAYS +// IN SYNC IN BOTH CPU0 AND CPU1 +// +// If you need to differentiate something between +// CPUs, use "#if XPAR_CPU_ID == ?" +/////////////////////////////////////////////////////// + +/* This file contains all the macro definitions that need to be shared between CPU0 and CPU1 + * This includes definitions for Inter-Core Communication, Inter-Core Interrupts, + * the Generic Interrupt Controller (GIC) definitions and metadata, etc + * + * Per Zynq-7000 TRM Ch. 4: System Addresses (page 106), the initial mapping + * of OCM is split between low addresses and high addresses in 64 KB chunks. + * We will pick to use the highest 64 KB chunk as our base address: */ +#define SHARED_OCM_BASE_ADDR (0xFFFF0000) + +/////////////////////////////////// +// INTER-CORE COMMUNICATION +///////////////////////////////// +#define ICC_BUFFER_STRUCT_SIZE (sizeof(StaticMessageBuffer_t)) +#define ICC_BUFFER_SIZE (4 * 1024) +#define ICC_HANDLE_SIZE (sizeof(MessageBufferHandle_t)) +#define ICC_LOCK_SIZE (sizeof(uint8_t)) +#define ICC_FUNC_PTR_SIZE (sizeof(void *)) + +/* These are the handles for the Message Buffers that need to be used by other tasks + * In reality, the handle is just the pointer to the message buffer struct (its memory address) + * These should end up being the addresses computed above */ +MessageBufferHandle_t xCPU0to1MessageBufferHandle; +MessageBufferHandle_t xCPU1to0MessageBufferHandle; + +/* Define the pointers to the two structs (that store the metadata) and two message spaces (that hold the messages) in + * shared memory. The ICC_BUFFER_SIZE Should be one more than the value passed in the xBufferSizeBytes parameter. The + * two structs will be located back-to-back right at the base addr of the shared OCM, followed thereafter by the actual + * message buffers. */ +#define ICC_CPU0to1BufferStructAddr ((uint8_t *) (SHARED_OCM_BASE_ADDR + (0 * ICC_BUFFER_STRUCT_SIZE))) +#define ICC_CPU1to0BufferStructAddr ((uint8_t *) (SHARED_OCM_BASE_ADDR + (1 * ICC_BUFFER_STRUCT_SIZE))) + +#define ICC_CPU0to1BufferSpaceAddr \ + ((uint8_t *) (SHARED_OCM_BASE_ADDR + (2 * ICC_BUFFER_STRUCT_SIZE) + (0 * ICC_BUFFER_SIZE))) +#define ICC_CPU1to0BufferSpaceAddr \ + ((uint8_t *) (SHARED_OCM_BASE_ADDR + (2 * ICC_BUFFER_STRUCT_SIZE) + (1 * ICC_BUFFER_SIZE))) + +/* These memory spaces are used to transfer the Message Buffer Handles from CPU0 (who does the initialization work, and + * gets the handles from the xMessageBufferCreateStaticWithCallback function) to CPU1 (who doesn't initialize anything + * and gets the handles from CPU0, via these drop-zones) */ +#define ICC_CPU0to1HandleDropzoneAddr (SHARED_OCM_BASE_ADDR + (2 * ICC_BUFFER_STRUCT_SIZE) + (2 * ICC_BUFFER_SIZE) + (0 * ICC_HANDLE_SIZE))) +#define ICC_CPU1to0HandleDropzoneAddr (SHARED_OCM_BASE_ADDR + (2 * ICC_BUFFER_STRUCT_SIZE) + (2 * ICC_BUFFER_SIZE) + (1 * ICC_HANDLE_SIZE))) + +#define ICC_getCPU0to1Handle ((*((MessageBufferHandle_t *) ICC_CPU0to1HandleDropzoneAddr)) +#define ICC_setCPU0to1Handle(handle) ((*((MessageBufferHandle_t *) ICC_CPU0to1HandleDropzoneAddr) = handle) +#define ICC_getCPU1to0Handle ((*((MessageBufferHandle_t *) ICC_CPU1to0HandleDropzoneAddr)) +#define ICC_setCPU1to0Handle(handle) ((*((MessageBufferHandle_t *) ICC_CPU1to0HandleDropzoneAddr) = handle) + +/* We need a concurrency lock in the shared memory to make sure that CPU0 always sets the handles before + * CPU1 attempts to get them. This is initialized to 0, and CPU0 sets it to 1. CPU1 will wait till it sees + * this change before attempting to get the handles. */ +#define ICC_initLockAddr (SHARED_OCM_BASE_ADDR + (2 * ICC_BUFFER_STRUCT_SIZE) + (2 * ICC_BUFFER_SIZE) + (2 * ICC_HANDLE_SIZE))) + +#define ICC_getHandleComplete ((*((uint8_t *) ICC_initLockAddr)) +#define ICC_setHandleComplete ((*((uint8_t *) ICC_initLockAddr) = 1) + +/* These memory spaces are used to transfer callback function pointers from CPU1 to CPU0 for initialization */ +#define ICC_CPU1to0SendCallbackAddr (ICC_initLockAddr + ICC_LOCK_SIZE + (0 * ICC_FUNC_PTR_SIZE)) +#define ICC_CPU0to1ReceiveCallbackAddr (ICC_initLockAddr + ICC_LOCK_SIZE + (1 * ICC_FUNC_PTR_SIZE)) + +#define ICC_getCPU1to0SendCallback ((*((void **) ICC_CPU1to0SendCallbackAddr)) +#define ICC_setCPU1to0SendCallback(func_ptr) ((*((void **) ICC_CPU1to0SendCallbackAddr) = func_ptr) +#define ICC_getCPU0to1ReceiveCallback ((*((void **) ICC_CPU0to1ReceiveCallbackAddr)) +#define ICC_setCPU0to1ReceiveCallback(func_ptr) ((*((void **) ICC_CPU0to1ReceiveCallbackAddr) = func_ptr) + +/* We need a concurrency lock in the shared memory to make sure that CPU1 always provides the function pointers before + * CPU0 attempts to get them. This is initialized to 0, and CPU1 sets it to 1. CPU0 will wait till it sees + * this change before attempting to get the function pointers. */ +#define ICC_functionPointersLockAddr (ICC_initLockAddr + ICC_LOCK_SIZE + (2 * ICC_FUNC_PTR_SIZE)) + +#define ICC_getFunctionPointersReady ((*((uint8_t *) ICC_functionPointersLockAddr)) +#define ICC_setFunctionPointersReady ((*((uint8_t *) ICC_functionPointersLockAddr) = 1) + +/////////////////////////////////// +// INTERRUPTS / GIC +///////////////////////////////// +#define INTR_UNBLOCK_CPU0_RX_INT_ID 0 +#define INTR_UNBLOCK_CPU0_TX_INT_ID 1 +#define INTR_UNBLOCK_CPU1_RX_INT_ID 2 +#define INTR_UNBLOCK_CPU1_TX_INT_ID 3 + +#define INTR_SHARED_MEMORY_BASE_ADDR (ICC_functionPointersLockAddr + sizeof(uint8_t)) +#define INTR_GIC_INSTANCE_SIZE (sizeof(XScuGic)) + +// Interrupt Controller Instance +// Defined here to be accessible in both sys/icc.c and sys/intr.h +#define INTR_GIC_INSTANCE_ADDR ((XScuGic *) (INTR_SHARED_MEMORY_BASE_ADDR) +#define INTR_gicInstance (*((XScuGic *) INTR_GIC_INSTANCE_ADDR)) + +// Interrupt Controller Initializaton Lock w/ getter & setter +#define INTR_gicInitLockAddr (INTR_GIC_INSTANCE_ADDR + INTR_GIC_INSTANCE_SIZE) + +#define INTR_getGicInitReady (*((uint8_t *) INTR_gicInitLockAddr)) +#define INTR_setGicInitReady (*((uint8_t *) INTR_gicInitLockAddr) = 1) + +#endif /* SHARED_MEMORY_H */