diff --git a/epicardium/FreeRTOSConfig.h b/epicardium/FreeRTOSConfig.h index 26e51bc4c8192d0fce4f34d0b5cb71b84eb428af..76ae165a98e879ca655b8bf66093c78ebf054a40 100644 --- a/epicardium/FreeRTOSConfig.h +++ b/epicardium/FreeRTOSConfig.h @@ -1,6 +1,9 @@ #ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H +#define MXC_ASSERT_ENABLE +#include "mxc_assert.h" + #include "max32665.h" /* CMSIS keeps a global updated with current system clock in Hz */ @@ -22,7 +25,14 @@ #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( ( unsigned char ) 5 << ( 8 - configPRIO_BITS) ) /* We want to use preemption to easier integrate components */ -#define configUSE_PREEMPTION 1 +/* TODO: Figure out why turning this on does not work ... */ +#define configUSE_PREEMPTION 0 + +/* + * Tickless idle from the FreeRTOS port + our own hooks (defined further down in + * this file) + */ +#define configUSE_TICKLESS_IDLE 1 /* TODO: Adjust */ #define configUSE_IDLE_HOOK 0 @@ -42,4 +52,17 @@ #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler +/* Assert */ +#define configASSERT(x) MXC_ASSERT(x) + +/* Tickless idle hooks */ +typedef uint32_t TickType_t; +void pre_idle_sleep(TickType_t xExpectedIdleTime); +#define configPRE_SLEEP_PROCESSING(xModifiableIdleTime) \ + pre_idle_sleep(xModifiableIdleTime); xModifiableIdleTime = 0 + +void post_idle_sleep(TickType_t xExpectedIdleTime); +#define configPOST_SLEEP_PROCESSING(xExpectedIdleTime) \ + post_idle_sleep(xExpectedIdleTime) + #endif /* FREERTOS_CONFIG_H */ diff --git a/epicardium/api/common.h b/epicardium/api/common.h index 70b24a35a067b417939f3008c17f0b2a19cf26f0..5b435fa587479faf12e1a59d7f592c0d4a717384 100644 --- a/epicardium/api/common.h +++ b/epicardium/api/common.h @@ -1,4 +1,5 @@ #include <stdint.h> +#include <stdbool.h> /* * Semaphore used for API synchronization. diff --git a/epicardium/api/dispatcher.c b/epicardium/api/dispatcher.c index d499bd16f261689a667a1ebf95c6bd1da3b8e281..a6e5cf415e8f5ce4f0902f4e86e138835c0b0a92 100644 --- a/epicardium/api/dispatcher.c +++ b/epicardium/api/dispatcher.c @@ -18,20 +18,45 @@ int api_dispatcher_init() return ret; } -api_id_t api_dispatcher_poll() +static bool event_ready = false; + +bool api_dispatcher_poll_once() { - api_id_t id = 0; + if (event_ready) { + return false; + } + while (SEMA_GetSema(_API_SEMAPHORE) == E_BUSY) {} if (API_CALL_MEM->call_flag != _API_FLAG_CALLING) { SEMA_FreeSema(_API_SEMAPHORE); + return false; + } + + event_ready = true; + return true; +} + +bool api_dispatcher_poll() +{ + if (event_ready) { + return true; + } + + return api_dispatcher_poll_once(); +} + +api_id_t api_dispatcher_exec() +{ + if (!event_ready) { return 0; } - id = API_CALL_MEM->id; + api_id_t id = API_CALL_MEM->id; __api_dispatch_call(id, API_CALL_MEM->buffer); API_CALL_MEM->call_flag = _API_FLAG_RETURNED; + event_ready = false; SEMA_FreeSema(_API_SEMAPHORE); /* Notify the caller that we returned */ diff --git a/epicardium/api/dispatcher.h b/epicardium/api/dispatcher.h index 1385b719487b578f93c7294ba0b7863822aab265..4098b162bfed61097b39b3ae753399fe6b4018b8 100644 --- a/epicardium/api/dispatcher.h +++ b/epicardium/api/dispatcher.h @@ -7,11 +7,20 @@ int api_dispatcher_init(); /* - * Attempt to dispatch a call, if the caller has requested one. - * Will return 0 if no call was dispatched and the ID of the dispatched - * call otherwise. + * Check whether the other core requested a call. If this function returns + * true, the dispatcher should call api_dispatcher_exec() to actually dispatch + * the call. Consecutive calls to this function will return false. Use + * api_dispatcher_poll() if your need to recheck. */ -api_id_t api_dispatcher_poll(); +bool api_dispatcher_poll_once(); +bool api_dispatcher_poll(); + +/* + * Attempt to dispatch a call, if one had been polled using + * api_dispatcher_poll(). Will return 0 if no call was dispatched or the ID of + * the dispatched call otherwise. + */ +api_id_t api_dispatcher_exec(); /* This function is defined by the generated dispatcher code */ void __api_dispatch_call(api_id_t id, void*buffer); diff --git a/epicardium/main.c b/epicardium/main.c index 3490b026d9443d6c21994b5cd906c2e9ebee144f..cc2d0a4f38fff6e8d2d90bd5220f85eb3b861272 100644 --- a/epicardium/main.c +++ b/epicardium/main.c @@ -12,17 +12,65 @@ #include "FreeRTOS.h" #include "task.h" +static TaskHandle_t dispatcher_task_id; + +/* TODO: Move out of main.c */ void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b) { leds_set(led, r, g, b); leds_update(); } +/* + * This hook is called before FreeRTOS enters tickless idle. + */ +void pre_idle_sleep(TickType_t xExpectedIdleTime) +{ + if (xExpectedIdleTime > 0) { + /* + * WFE because the other core should be able to notify + * epicardium if it wants to issue an API call. + */ + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfe" ); + __asm volatile( "isb" ); + } +} + +/* + * This hook is called after FreeRTOS exits tickless idle. + */ +void post_idle_sleep(TickType_t xExpectedIdleTime) +{ + /* Check whether a new API call was issued. */ + if (api_dispatcher_poll_once()) { + xTaskNotifyGive(dispatcher_task_id); + } +} + +#if 0 +void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime) +{ + if (xExpectedIdleTime > 0) { + __WFE(); + if (api_dispatcher_poll()) { + xTaskNotifyGive(dispatcher_task_id); + } + } +} +#endif + +/* + * API dispatcher task. This task will sleep until an API call is issued and + * then wake up to dispatch it. + */ void vApiDispatcher(void*pvParameters) { while (1) { - api_dispatcher_poll(); - vTaskDelay(portTICK_PERIOD_MS * 10); + if (api_dispatcher_poll()) { + api_dispatcher_exec(); + } + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); } } @@ -84,7 +132,7 @@ int main(void) configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, - NULL + &dispatcher_task_id ) != pdPASS) { printf("Failed to create api dispatcher task!\n"); abort(); @@ -97,12 +145,5 @@ int main(void) core1_start(); vTaskStartScheduler(); - printf("ERROR: FreeRTOS did not start due to above error!\n"); - -#if 0 - while(1) { - __WFE(); - api_dispatcher_poll(); - } -#endif + printf("ERROR: FreeRTOS did not start due to unknown error!\n"); } diff --git a/epicardium/serial.c b/epicardium/serial.c index a891f5103911d532a732ad69103e0752b0bbe54d..0b956422a98123a2287a59f8cd1c45cf7a7af980 100644 --- a/epicardium/serial.c +++ b/epicardium/serial.c @@ -3,6 +3,7 @@ #include "serial.h" +#include "max32665.h" #include "cdcacm.h" #include "uart.h" #include "tmr_utils.h" @@ -11,16 +12,26 @@ #include "task.h" #include "queue.h" -extern mxc_uart_regs_t * ConsoleUart; +/* Task ID for the serial handler */ +TaskHandle_t serial_task_id; +/* The serial console in use (UART0) */ +extern mxc_uart_regs_t* ConsoleUart; +/* Read queue, filled by both UART and CDCACM */ static QueueHandle_t read_queue; +/* + * API-call to write a string. Output goes to both CDCACM and UART + */ void epic_uart_write_str(char*str, intptr_t length) { UART_Write(ConsoleUart, (uint8_t*)str, length); cdcacm_write((uint8_t*)str, length); } +/* + * Blocking API-call to read a character from the queue. + */ char epic_uart_read_chr(void) { char chr; @@ -28,11 +39,39 @@ char epic_uart_read_chr(void) return chr; } +/* Interrupt handler needed for SDK UART implementation */ +void UART0_IRQHandler(void) +{ + UART_Handler(ConsoleUart); +} + +static void uart_callback(uart_req_t*req, int error) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + vTaskNotifyGiveFromISR(serial_task_id, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +static void enqueue_char(char chr) +{ + if (chr == 0x3) { + /* Control-C */ + TMR_TO_Start(MXC_TMR5, 1, 0); + } + + if (xQueueSend(read_queue, &chr, 100) == errQUEUE_FULL) { + /* Queue overran, wait a bit */ + vTaskDelay(portTICK_PERIOD_MS * 50); + } +} + void vSerialTask(void*pvParameters) { static uint8_t buffer[sizeof(char) * SERIAL_READ_BUFFER_SIZE]; static StaticQueue_t read_queue_data; + serial_task_id = xTaskGetCurrentTaskHandle(); + /* Setup read queue */ read_queue = xQueueCreateStatic( SERIAL_READ_BUFFER_SIZE, @@ -44,31 +83,31 @@ void vSerialTask(void*pvParameters) /* Setup UART interrupt */ NVIC_ClearPendingIRQ(UART0_IRQn); NVIC_DisableIRQ(UART0_IRQn); - NVIC_SetPriority(UART0_IRQn, 1); + NVIC_SetPriority(UART0_IRQn, 6); NVIC_EnableIRQ(UART0_IRQn); - while (1) { - char chr; - - /* TODO: Wait for interrupt on either device */ - vTaskDelay(portTICK_PERIOD_MS * 10); + unsigned char data; + uart_req_t read_req = { + .data = &data, + .len = 1, + .callback = uart_callback, + }; - if(UART_NumReadAvail(ConsoleUart) > 0) { - chr = UART_ReadByte(ConsoleUart); - } else if(cdcacm_num_read_avail() > 0) { - chr = cdcacm_read(); - } else { - continue; + while (1) { + int ret = UART_ReadAsync(ConsoleUart, &read_req); + if (ret != E_NO_ERROR && ret != E_BUSY) { + printf("error reading uart: %d\n", ret); + vTaskDelay(portMAX_DELAY); } - if (chr == 0x3) { - /* Control-C */ - TMR_TO_Start(MXC_TMR5, 1, 0); + ulTaskNotifyTake(pdTRUE, portTICK_PERIOD_MS * 1000); + + if (read_req.num > 0) { + enqueue_char(*read_req.data); } - if (xQueueSend(read_queue, &chr, 100) == errQUEUE_FULL) { - /* Queue overran, wait a bit */ - vTaskDelay(portTICK_PERIOD_MS * 50); + while (UART_NumReadAvail(ConsoleUart) > 0) { + enqueue_char(UART_ReadByte(ConsoleUart)); } } }