This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Hard Fault: Unaligned memory access

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

  • I've found the problem. The queue buffer was wrong.
    It should be

    /** @brief queue structure for static allocation */
    typedef struct {
        QueueHandle_t handle;
        StaticQueue_t queueMem;
        uint8_t queueBuffer[sizeof(event_t)*EVENT_LISTENER_QUEUE_SIZE]; // FIX HERE
    } event_dispatcher_queue_t;


    This was a buffer overflow error, so the error reported by the hard fault handler was wrong

Related