Running Code In Task Init Function

hirohiro wrote on Thursday, November 28, 2019:

Hey all, I’m running into a strange issue with my FreeRTOS application. My basic structure is as follows:

  1. Create setup task
  2. Start scheduler
  3. Setup tasks calls Setup function for all my other application tasks and then creates them.
  4. Suspend setup task

The issue I’m running into is that if I run any significant code in the application tasks I encounter a hard fault. Two examples of this:

  1. I create a queue in TaskA_Setup, and then after verifying it was created successfully I attempt to push to it but it is not available and hangs forever regardless of delay. If I configure it with no_wait then it just hard faults

  2. TaskB drives a small SSD display and if I try to initialize the graphics library in TaskB_Setup it crashes, unless I step through with my debugger in which case it gets further before crashing.

If I move these activities to the task’s main function they work fine. Unfortunately my faults are all imprecise errors and I am having a tought time tracking them down. That being said my questions are:

A. Is there anything inherently wrong with the way I have structured my application?
B. It seems like my application doesn’t want to run from the context of the setup thread- any thoughts on why this is?
C. Any suggestions on how to properly trace back the source of these kinds of imprecise faults?

rtel wrote on Thursday, November 28, 2019:

There is some information on debugging imprecise faults at the bottom of
this page
https://freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html

While creating a queue before the scheduler has started is fine,
technically you are not supposed to write to the queue before the
scheduler has started (although as long as there is no chance of a
context switch being attempted, or the writing task blocking, I can’t
think of what the issue would be).

Ref the graphics library - is that attempting to use interrupts? Once
you have started calling FreeRTOS functions interrupts will deliberately
be left disabled until the first task runs (to prevent interrupt service
routines attempting to use the scheduler).

hirohiro wrote on Thursday, November 28, 2019:

Hey Richard, that link has been useful in the past but it leaves a little to be desired on the imprecise faults :stuck_out_tongue:

I understand what you’re saying and that was my first thought as well, however the scheduler is started when all of my task setups are run since they are running in the context of an rtos task already.

I believe the graphics library does use interrupts (SPI) but again, the RTOS scheduler is already running and it itself is scheduling these setup functions. I’ve attached a snipped of my code below to clarify (note: running on Ambiq Apollo3)

//*****************************************************************************
//
// High priority task to run immediately after the scheduler starts.
//
// This task is used for any global initialization that must occur after the
// scheduler starts, but before any functional tasks are running. This can be
// useful for enabling events, semaphores, and other global, RTOS-specific
// features.
//
//*****************************************************************************
void setup_task(void *pvParameters)
{
    am_util_debug_printf("Running setup tasks...\r\n");

    // Run setup functions.
    log_task_setup();
    ui_task_setup();

    // Create the functional tasks
    xTaskCreate(log_task_main, "LogTask", 512, 0, TASK_PRIORITY_LOG, &log_task_handle);
    xTaskCreate(ui_task_main,  "UiTask",  512, 0, TASK_PRIORITY_UI,  &ui_task_handle);

    // The setup operations are complete, so suspend the setup task now.
    vTaskSuspend(NULL);

    while (1);
}

//*****************************************************************************
// Initializes all tasks
//*****************************************************************************
void
run_tasks(void)
{
  // Set some interrupt priorities before we create tasks or start the scheduler.
  // Note: Timer priority is handled by the FreeRTOS kernel, so we won't touch it here.

  // Create essential tasks.
  xTaskCreate(setup_task, "Setup", 128, 0, 3, &xSetupTask);

  // Start the scheduler.
  vTaskStartScheduler();
}

richard_damon wrote on Thursday, November 28, 2019:

One suggestion is that I really like to create all the system objects before starting the scheduler, as otherwise it can be hard to verify that all execution paths only touch created objects.

For instance, in your form, an object that task A uses to communticate to task B, where is it initialized? You might think of it as primarily belonging to B (as B is waiting on it) so it should be created in the init for task B, but it the init task creates A first, and then inits task B, then you need code in task A to make sure that the object has been created before A tries to talk to B.

This tends to lead to wanting to FIRST run all the init functions, and then create all the tasks, but even that can run into issues of a task uses direct to task operations to communicate to another task that is created after it.

Ultimately, I find that it is easier to just make all the objects/task before starting the scheduler (unless some object NEEDS some information that needs the scheduler running to get) and then start the scheduler.

hirohiro wrote on Thursday, November 28, 2019:

Hmm, that useful food for thought. To be honest I just followed the template code but what you’re saying makes sense from a coordination of resources perspective.

One issue I see with that is initialization code that relies on rtos methods. For examples sometimes delays are required when writing to external sensors. In this case I would need to use a non-rtos delay, however if I were to reconfigure that sensor at run time it would need to use vTaskDelay or similar

hirohiro wrote on Wednesday, December 04, 2019:

I just wanted to follow up for completeness. As it turns out this was caused by the stack of setup_task in the example above being too small. Oddly enough it was working with one toolchain but not another. Must have been very marginal.

Thanks for the help!