From 3b9393fec6b49cd8e389b05e7f45f3922bb66f75 Mon Sep 17 00:00:00 2001 From: Rahix <rahix@rahix.de> Date: Sun, 11 Aug 2019 22:11:16 +0200 Subject: [PATCH] feat(epicardium): Implement basic core 1 lifecycle This commit introduces a way to control core 1. This is archieved by a predefined API-Interrupt: The reset interrupt. When triggered, it will bring the core back into its default state and wait for a new vector address from Epicardium. Once this vector address is transferred, it will start the new payload. This method only works as long as core 1 is responsive to the API interrupts. Cases where this might not be the case: - During times where core 1 has interrupts disabled - When in a higher priority exception handler - When core 1 has corrupted its IVT Signed-off-by: Rahix <rahix@rahix.de> --- epicardium/api/common.h | 10 +- epicardium/api/control.c | 233 ++++++++++++++++++++++++++++ epicardium/api/dispatcher.c | 8 +- epicardium/api/dispatcher.h | 16 ++ epicardium/api/interrupt-receiver.c | 7 + epicardium/main.c | 8 +- epicardium/meson.build | 3 +- 7 files changed, 278 insertions(+), 7 deletions(-) create mode 100644 epicardium/api/control.c diff --git a/epicardium/api/common.h b/epicardium/api/common.h index c884f37c..ff0c9f3a 100644 --- a/epicardium/api/common.h +++ b/epicardium/api/common.h @@ -8,7 +8,8 @@ * Semaphore used for API synchronization. * TODO: Replace this with a LDREX/STREX based implementation */ -#define _API_SEMAPHORE 0 +#define _API_SEMAPHORE 0 +#define _CONTROL_SEMAPHORE 1 /* Type of API IDs */ typedef uint32_t api_id_t; @@ -19,6 +20,13 @@ typedef uint32_t api_id_t; /* Layout of the shared memory for API calls */ struct api_call_mem { + /* + * Reset stub. The reset stub is a small function provided by + * epicardium that should be called by a payload when receiving the + * reset interrupt. + */ + void (*reset_stub)(); + /* * Flag for synchronization of API calls. When this flag * is set, the caller has issued a call and is waiting for diff --git a/epicardium/api/control.c b/epicardium/api/control.c new file mode 100644 index 00000000..daa1ff52 --- /dev/null +++ b/epicardium/api/control.c @@ -0,0 +1,233 @@ +#include "epicardium.h" +#include "api/dispatcher.h" +#include "api/interrupt-sender.h" +#include "modules/log.h" + +#include "card10.h" + +#include "max32665.h" +#include "sema.h" +#include "tmr.h" + +static void __core1_init(void); + +struct core1_info { + /* Location of core1's interrupt vector table */ + volatile uintptr_t ivt_addr; + /* Whether core 1 is ready for a new IVT */ + volatile bool ready; +}; + +/* + * Information passing structure for controlling core 1. + */ +static volatile struct core1_info core1_info = { + .ivt_addr = 0x00, + .ready = false, +}; + +/* + * Minimal IVT needed for initial startup. This IVT only contains the initial + * stack pointer and reset-handler and is used to startup core 1. Afterwards, + * the payload's IVT is loaded into VTOR and used from then on. + */ +static uintptr_t core1_initial_ivt[] = { + /* Initial Stack Pointer */ + 0x20080000, + /* Reset Handler */ + (uintptr_t)__core1_reset, +}; + +/* + * Reset Handler + * + * Calls __core1_init() to reset & prepare the core for loading a new payload. + */ +__attribute__((naked)) void __core1_reset(void) +{ + __asm volatile("mov sp, %0\n\t" + : /* No Outputs */ + : "r"(core1_initial_ivt[0])); + __core1_init(); +} + +/* + * Init core 1. This function will reset the core and wait for a new IVT + * address from Epicardium. Once this address is received, it will start + * execution with the supplied reset handler. + */ +void __core1_init(void) +{ + /* + * Clear any pending API interrupts. + */ + TMR_IntClear(MXC_TMR5); + + /* + * Reset Interrupts + * + * To ensure proper operation of the new payload, disable all interrupts + * and clear all pending ones. + */ + for (int i = 0; i < MXC_IRQ_EXT_COUNT; i++) { + NVIC_DisableIRQ(i); + NVIC_ClearPendingIRQ(i); + NVIC_SetPriority(i, 0); + } + + /* + * Check whether we catched the core during an interrupt. If this is + * the case, try returning from the exception handler first and call + * __core1_reset() again in thread context. + */ + if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0) { + /* + * Construct an exception frame so the CPU will jump back to our + * __core1_reset() function once we exit from the exception + * handler. + * + * To exit the exception, a special "EXC_RETURN" value is loaded + * into the link register and then branched to. + */ + __asm volatile( + "ldr r0, =0x41000000\n\t" + "ldr r1, =0\n\t" + "push { r0 }\n\t" /* xPSR */ + "push { %0 }\n\t" /* PC */ + "push { %0 }\n\t" /* LR */ + "push { r1 }\n\t" /* R12 */ + "push { r1 }\n\t" /* R3 */ + "push { r1 }\n\t" /* R2 */ + "push { r1 }\n\t" /* R1 */ + "push { r1 }\n\t" /* R0 */ + + "ldr lr, =0xFFFFFFF9\n\t" + "bx lr\n\t" + : /* No Outputs */ + : "r"((uintptr_t)__core1_reset) + : "pc", "lr"); + + /* unreachable */ + while (1) + ; + } + + /* Wait for the IVT address */ + while (1) { + while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) { + } + + __DMB(); + __ISB(); + + /* + * The IVT address is reset to 0 by Epicardium before execution + * gets here. Once a new address has been set, core 1 can use + * the new IVT. + */ + if (core1_info.ivt_addr != 0x00) { + break; + } + + /* Signal that we are ready for an IVT address */ + core1_info.ready = true; + + SEMA_FreeSema(_CONTROL_SEMAPHORE); + + __WFE(); + } + + uintptr_t *ivt = (uintptr_t *)core1_info.ivt_addr; + core1_info.ivt_addr = 0x00; + + SEMA_FreeSema(_CONTROL_SEMAPHORE); + + /* + * Reset the call-flag before entering the payload so API calls behave + * properly. This is necessary because epic_exec() will set the flag + * to "returning" on exit. + */ + API_CALL_MEM->call_flag = _API_FLAG_IDLE; + + /* + * Set the IVT + */ + SCB->VTOR = (uintptr_t)ivt; + + /* + * Clear any pending API interrupts. + */ + TMR_IntClear(MXC_TMR5); + NVIC_ClearPendingIRQ(TMR5_IRQn); + + /* + * Jump to payload's reset handler + */ + __asm volatile( + "ldr r0, %0\n\t" + "blx r0\n\r" + : /* No Outputs */ + : "m"(*(ivt + 1)) + : "r0"); +} + +void core1_boot(void) +{ + /* + * Boot using the initial IVT. This will place core 1 into a loop, + * waiting for a payload. + */ + core1_start(&core1_initial_ivt); +} + +void core1_reset(void) +{ + /* Signal core 1 that we intend to load a new payload. */ + api_interrupt_trigger(EPIC_INT_RESET); + + /* Wait for the core to accept */ + while (1) { + while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) { + } + + /* + * core 1 will set the ready flag once it is spinning in the + * above loop, waiting for a new IVT. + */ + if (core1_info.ready) { + break; + } + + SEMA_FreeSema(_CONTROL_SEMAPHORE); + + for (int i = 0; i < 10000; i++) { + } + } + + /* + * TODO: If the other core does not respond within a certain grace + * period, we need to force it into our desired state by overwriting + * all of its memory. Yes, I don't like this method either ... + */ +} + +void core1_load(void *ivt, char *args) +{ + /* If the core is currently in an API call, reset it. */ + API_CALL_MEM->call_flag = _API_FLAG_IDLE; + API_CALL_MEM->id = 0; + API_CALL_MEM->int_id = (-1); + + api_prepare_args(args); + + core1_info.ivt_addr = (uintptr_t)ivt; + core1_info.ready = false; + + __DMB(); + __ISB(); + + SEMA_FreeSema(_CONTROL_SEMAPHORE); + + __SEV(); + __WFE(); +} diff --git a/epicardium/api/dispatcher.c b/epicardium/api/dispatcher.c index 73057975..4ffac422 100644 --- a/epicardium/api/dispatcher.c +++ b/epicardium/api/dispatcher.c @@ -15,8 +15,12 @@ int api_dispatcher_init() { int ret; - ret = SEMA_Init(NULL); - API_CALL_MEM->call_flag = _API_FLAG_IDLE; + ret = SEMA_Init(NULL); + SEMA_FreeSema(_API_SEMAPHORE); + API_CALL_MEM->reset_stub = __core1_reset; + API_CALL_MEM->call_flag = _API_FLAG_IDLE; + API_CALL_MEM->id = 0; + API_CALL_MEM->int_id = (-1); /* * Enable TX events for both cores. diff --git a/epicardium/api/dispatcher.h b/epicardium/api/dispatcher.h index 1294592d..2299f54d 100644 --- a/epicardium/api/dispatcher.h +++ b/epicardium/api/dispatcher.h @@ -28,3 +28,19 @@ api_id_t api_dispatcher_exec(); * The data is a NULL-terminated string. */ void api_prepare_args(char *args); + +/********************************************************************* + * core 1 control * + *********************************************************************/ + +/* Startup core1 into a state where it is ready to receive a payload. */ +void core1_boot(void); + +/* Reset core 1 into a state where it can accept a new payload */ +void core1_reset(void); + +/* Load a payload into core 1 */ +void core1_load(void *ivt, char *args); + +/* core 1 reset stub. See epicardium/api/control.c for details. */ +void __core1_reset(void); diff --git a/epicardium/api/interrupt-receiver.c b/epicardium/api/interrupt-receiver.c index 7684c6bb..f9856423 100644 --- a/epicardium/api/interrupt-receiver.c +++ b/epicardium/api/interrupt-receiver.c @@ -12,3 +12,10 @@ void TMR5_IRQHandler(void) __dispatch_isr(API_CALL_MEM->int_id); API_CALL_MEM->int_id = (-1); } + +/* Reset Handler */ +void __epic_isr_reset(void) +{ + API_CALL_MEM->int_id = (-1); + API_CALL_MEM->reset_stub(); +} diff --git a/epicardium/main.c b/epicardium/main.c index dc3dfd25..84abf8d9 100644 --- a/epicardium/main.c +++ b/epicardium/main.c @@ -148,6 +148,8 @@ int main(void) LOG_INFO("startup", "starting light sensor ..."); epic_light_sensor_run(); + core1_boot(); + /* * See if there's a l0dable.elf to run. If not, run pycardium. * This is temporary until epicardium gets a l0dable API from pycardium. @@ -163,11 +165,11 @@ int main(void) LOG_INFO( "startup", "Starting %s on core1 ...", l0dable ); - core1_start(info.isr_vector); + core1_load(info.isr_vector, ""); } } else { - LOG_INFO("startup", "Starting pycardium on core1 ..."); - core1_start((void *)0x10080000); + LOG_INFO("startup", "Starting pycardium on core 1 ..."); + core1_load((void *)0x10080000, "main.py"); } LOG_INFO("startup", "Starting FreeRTOS ..."); diff --git a/epicardium/meson.build b/epicardium/meson.build index c9b128f7..220f0bae 100644 --- a/epicardium/meson.build +++ b/epicardium/meson.build @@ -37,8 +37,9 @@ api_dispatcher_lib = static_library( 'api-dispatcher', 'api/dispatcher.c', 'api/interrupt-sender.c', + 'api/control.c', api[1], # Dispatcher - dependencies: periphdriver, + dependencies: [libcard10, periphdriver], ) ########################################################################## -- GitLab