Hi, I am trying to write an "event dispatcher" so it's possible to listen to types of event and send events. You pass a function pointer and it will be called when that event is sent. Though I am having issues with unaligned memory access and I am unsure what is the cause.
Here is the code for the event dispatcher. I have removed some checks to simplify the code but essentially it's a 2d array of "listener". Each listener has a callback associated with it.
void eventDispatcher_task(void* context){ event_dispatcher_t* dispatcher = (event_dispatcher_t*)context; while(true) { event_t event; if(xQueueReceive(dispatcher->queue.handle, &event, portMAX_DELAY) == pdPASS){ // The enum value is it's idx in the array uint16_t idx = event.type; event_watchers_t* watcher = &dispatcher->watchers[idx]; // Send to all event listener of that type for(uint16_t i=0; i < watcher->length; i++){ void* context = watcher->listeners[i].context; watcher->listeners[i].callback(event->type, event->data, context); } } } } // Create task and queue using static allocation bool EventDispatcher_init(event_dispatcher_t* dispatcher) { for(uint16_t i = 0; i<EVENT_TYPES_COUNT; i++){ dispatcher->watchers[i].length = 0; } dispatcher->queue.handle = xQueueCreateStatic(EVENT_LISTENER_QUEUE_SIZE, sizeof(event_t), dispatcher->queue.queueBuffer, &dispatcher->queue.queueMem); dispatcher->task.handle = xTaskCreateStatic( eventDispatcher_task, "evt_dispatch", EVENT_LISTENER_STACK_SIZE, dispatcher, EVENT_LISTENER_PRIORITY, dispatcher->task.taskStack, &dispatcher->task.taskBuffer); return true; } bool EventDispatcher_registerListener(event_dispatcher_t* dispatcher, event_type_t type, event_callback_t callback, void* context){ uint16_t idx = type; event_watchers_t* watcher = &dispatcher->watchers[idx]; // Add listener to the list and increment it's length watcher->listeners[watcher->length].callback = callback; watcher->listeners[watcher->length].context = context; watcher->length++; return true; } bool EventDispatcher_dispatch(event_dispatcher_t* dispatcher, event_type_t type, event_data_t data) { event_t event; event.type = type; return xQueueSend(dispatcher->queue.handle, &event, TIME_MS_TO_RTOS_TICK(10)) == pdPASS; }
And the .h with the types definitions
#include "stdint.h" #include "FreeRTOS.h" #include "queue.h" #include "task.h" #define MAX_EVENT_LISTENER (16) #define EVENT_LISTENER_QUEUE_SIZE (16) #define EVENT_LISTENER_STACK_SIZE (512) #define EVENT_LISTENER_PRIORITY (3) /** * @breif The types of event */ typedef enum { EVENT_ALL_EVENT, /** @brief Listen to all event */ EVENT_START_SESSION, /** @brief Listen to start session events */ EVENT_TYPES_COUNT, /** @brief The number of event types */ } event_type_t; /** * @brief The data associated with the events * */ typedef union { struct { uint8_t test; } start_session; } event_data_t; /** @brief The struct containing the event and the data*/ typedef struct { event_type_t type; // The event type event_data_t data; // The data linked to the event } event_t; /** @brief Type definition for the callback of the event listener * @param event_type The type of the event * @param data The data of the event * @param context The context passed at the registration of the callback */ typedef void (*event_callback_t)(event_type_t event_type, event_data_t data, void* context); /** @brief The event listener storing information for the callback */ typedef struct { event_callback_t callback; /** @brief The callback to call */ void* context; /** @brief The context to pass to the callback */ } event_listener_t; /** @brief An array of event listener associated with a certain type */ typedef struct { event_listener_t listeners[MAX_EVENT_LISTENER]; // The listeners uint32_t length; // Length of the listeners } event_watchers_t; /** @brief queue structure for static allocation */ typedef struct { QueueHandle_t handle; StaticQueue_t queueMem; uint8_t queueBuffer[EVENT_LISTENER_QUEUE_SIZE]; } event_dispatcher_queue_t; /** @brief task structure for static allocation */ typedef struct { TaskHandle_t handle; StaticTask_t taskBuffer; StackType_t taskStack[EVENT_LISTENER_STACK_SIZE]; } event_dispatcher_task_t; typedef struct { event_watchers_t watchers[EVENT_TYPES_COUNT]; // One watcher for each event type event_dispatcher_task_t task; // Static task allocation event_dispatcher_queue_t queue; // STatic queue allocation } event_dispatcher_t; /** @brief Initialize the dispatcher * @param dispatcher The dispatcher to initialize * @return If the operation was successful or not */ bool EventDispatcher_init(event_dispatcher_t* dispatcher); /** @brief Register a listener to the dispatcher * @param dispatcher The dispatcher to register to * @param type The type of event to listen to * @param callback The callback to call on the event * @param context The context passed to the callback on event * @return True if the operation was successful, false if not */ bool EventDispatcher_registerListener(event_dispatcher_t* dispatcher, event_type_t type, event_callback_t callback, void* context); /** @brief Dispatch an event to the listeners * @param type The type of event to send * @param data The data of the event * @return True if the operation was successful, false if not */ bool EventDispatcher_dispatch(event_dispatcher_t* dispatcher, event_type_t type, event_data_t data);
Here is where the code is used.
void test_callback(event_type_t event_type, event_data_t data, void* context){ uint8_t* val = context; (*val)++; LOG_INFO("Event %d with data %d, context %d", event_type, data.start_session.test, *val); } event_dispatcher_t event_dispatcher; int main_freertos(void) { // ... Some initialisation code // Called after freertos is initialized and the scheduler is running EventDispatcher_init(&event_dispatcher); static uint8_t counter = 0; // Register listener EventDispatcher_registerListener(&event_dispatcher, EVENT_START_SESSION, test_callback, &counter); // Send event, the callback should be called everytime while(true){ EventDispatcher_dispatch(&event_dispatcher, EVENT_START_SESSION, (event_data_t){.start_session.test = 42}); vTaskDelay(10); } }
The event is trigger three to four times and after a hard fault stops the application. Here is the message displayed by the hard fault handler.
// Initialization logs.... <info> app: [main.c:577] Event 1 with data 42, context 1 <info> app: [main.c:577] Event 1 with data 42, context 2 <info> app: [main.c:577] Event 1 with data 42, context 3 <info> app: [main.c:577] Event 1 with data 42, context 2 <error> hardfault: HARD FAULT at 0x0002A86A <error> hardfault: R0: 0x00000013 R1: 0x20008FF4 R2: 0x20008FF8 R3: 0x200098A0 <error> hardfault: R12: 0x20011008 LR: 0x0002A85D PSR: 0x6100000E <error> hardfault: Cause: The processor has made an unaligned memory access.
I tried aligned my struct using
__attribute__ ((aligned (4)))
on the typedef but it didn't fix the issue since I though this was the issue.
I am using Segger Embedded Studio 4.5 on Windows 10
SDK 15.3.0 on the nrf52840
I know this is a lot of information at once, but since don't know where the alignment issue can come from and no stack trace is available, I've tried to provide all the necessary info.
I don't know if it's caused by the static freertos stack allocation or when the even triggers.
Any help is appreciated and I am more than happy to provide more info if necessary
UPDATE: It seems even when not listening to an event, the issue is present. Thus the issue is necessarily in `eventDispatcher_task`, `EventDispatcher_dispatch` (though not much is done here) or `main_freertos` and the callbacks are not a problem